ETH Price: $2,192.46 (+4.57%)

Transaction Decoder

Block:
16846544 at Mar-17-2023 09:08:11 AM +UTC
Transaction Fee:
0.001009616450958056 ETH $2.21
Gas Used:
62,002 Gas / 16.283611028 Gwei

Emitted Events:

294 Umbra.Announcement( receiver=0xf5a9fb2b4955f624b996f3386e05045f81eed715, amount=153516895869263110, token=0xEeeeeEee...eeeeeEEeE, pkx=219D12B9328F545EA8A63674DD233E74AD936F5B4E0C91F75A19A1E79206E963, ciphertext=54F626BE83BB94494C320E7D31CBF719720EFC503BEAC1D04C8EA163EB626C72 )

Account State Difference:

  Address   Before After State Difference Code
0xda386506...7D6E147e8
0.15576966176254368 Eth
Nonce: 1
0.001243149442322514 Eth
Nonce: 2
0.154526512320221166
(Flashbots: Builder)
1.236985752681674371 Eth1.236991580869674371 Eth0.000005828188
0xf5A9fb2b...F81eed715
0 Eth
Nonce: 0
0.15351689586926311 Eth
Nonce: 0
0.15351689586926311From: 0 To: 0

Execution Trace

ETH 0.15351689586926311 Umbra.sendEth( _receiver=0xf5A9fb2b4955f624B996f3386e05045F81eed715, _tollCommitment=0, _pkx=219D12B9328F545EA8A63674DD233E74AD936F5B4E0C91F75A19A1E79206E963, _ciphertext=54F626BE83BB94494C320E7D31CBF719720EFC503BEAC1D04C8EA163EB626C72 )
  • ETH 0.15351689586926311 0xf5a9fb2b4955f624b996f3386e05045f81eed715.CALL( )
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.6;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "./IUmbraHookReceiver.sol";
    contract Umbra is Ownable {
      // =========================================== Events ============================================
      /// @notice Emitted when a payment is sent
      event Announcement(
        address indexed receiver, // stealth address
        uint256 amount, // funds
        address indexed token, // token address or ETH placeholder
        bytes32 pkx, // ephemeral public key x coordinate
        bytes32 ciphertext // encrypted entropy and payload extension
      );
      /// @notice Emitted when a token is withdrawn
      event TokenWithdrawal(
        address indexed receiver, // stealth address
        address indexed acceptor, // destination of funds
        uint256 amount, // funds
        address indexed token // token address
      );
      // ======================================= State variables =======================================
      /// @dev Placeholder address used to identify transfer of native ETH
      address internal constant ETH_TOKEN_PLACHOLDER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
      /// @notice An ETH amount that must be sent alongside each payment; used as an anti-spam measure
      uint256 public toll;
      /// @notice A privileged address, set by the admin, that can sweep all collected ETH tolls
      address public tollCollector;
      /// @notice The address where ETH funds are sent when collected by the tollCollector
      address payable public tollReceiver;
      /// @notice Token payments pending withdrawal; stealth address => token address => amount
      mapping(address => mapping(address => uint256)) public tokenPayments;
      // ======================================= Setup & Admin ========================================
      /**
       * @param _toll Amount of ETH required per send
       * @param _tollCollector Address that can sweep collected funds
       * @param _tollReceiver Address that receives collected funds
       */
      constructor(
        uint256 _toll,
        address _tollCollector,
        address payable _tollReceiver
      ) {
        toll = _toll;
        tollCollector = _tollCollector;
        tollReceiver = _tollReceiver;
      }
      /**
       * @notice Admin only function to update the toll
       * @param _newToll New ETH toll in wei
       */
      function setToll(uint256 _newToll) external onlyOwner {
        toll = _newToll;
      }
      /**
       * @notice Admin only function to update the toll collector
       * @param _newTollCollector New address which has fund sweeping privileges
       */
      function setTollCollector(address _newTollCollector) external onlyOwner {
        tollCollector = _newTollCollector;
      }
      /**
       * @notice Admin only function to update the toll receiver
       * @param _newTollReceiver New address which receives collected funds
       */
      function setTollReceiver(address payable _newTollReceiver) external onlyOwner {
        tollReceiver = _newTollReceiver;
      }
      /**
       * @notice Function only the toll collector can call to sweep funds to the toll receiver
       */
      function collectTolls() external {
        require(msg.sender == tollCollector, "Umbra: Not toll collector");
        tollReceiver.transfer(address(this).balance);
      }
      // ======================================= Send =================================================
      /**
       * @notice Send and announce ETH payment to a stealth address
       * @param _receiver Stealth address receiving the payment
       * @param _tollCommitment Exact toll the sender is paying; should equal contract toll;
       * the committment is used to prevent frontrunning attacks by the owner;
       * see https://github.com/ScopeLift/umbra-protocol/issues/54 for more information
       * @param _pkx X-coordinate of the ephemeral public key used to encrypt the payload
       * @param _ciphertext Encrypted entropy (used to generated the stealth address) and payload extension
       */
      function sendEth(
        address payable _receiver,
        uint256 _tollCommitment,
        bytes32 _pkx, // ephemeral public key x coordinate
        bytes32 _ciphertext
      ) external payable {
        require(_tollCommitment == toll, "Umbra: Invalid or outdated toll commitment");
        // also protects from underflow
        require(msg.value > toll, "Umbra: Must pay more than the toll");
        uint256 amount = msg.value - toll;
        emit Announcement(_receiver, amount, ETH_TOKEN_PLACHOLDER, _pkx, _ciphertext);
        _receiver.transfer(amount);
      }
      /**
       * @notice Send and announce an ERC20 payment to a stealth address
       * @param _receiver Stealth address receiving the payment
       * @param _tokenAddr Address of the ERC20 token being sent
       * @param _amount Amount of the token to send, in its own base units
       * @param _pkx X-coordinate of the ephemeral public key used to encrypt the payload
       * @param _ciphertext Encrypted entropy (used to generated the stealth address) and payload extension
       */
      function sendToken(
        address _receiver,
        address _tokenAddr,
        uint256 _amount,
        bytes32 _pkx, // ephemeral public key x coordinate
        bytes32 _ciphertext
      ) external payable {
        require(msg.value == toll, "Umbra: Must pay the exact toll");
        require(tokenPayments[_receiver][_tokenAddr] == 0, "Umbra: Cannot send more tokens to stealth address");
        tokenPayments[_receiver][_tokenAddr] = _amount;
        emit Announcement(_receiver, _amount, _tokenAddr, _pkx, _ciphertext);
        SafeERC20.safeTransferFrom(IERC20(_tokenAddr), msg.sender, address(this), _amount);
      }
      // ======================================= Withdraw =============================================
      /**
       * @notice Withdraw an ERC20 token payment sent to a stealth address
       * @dev This method must be directly called by the stealth address
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       */
      function withdrawToken(address _acceptor, address _tokenAddr) external {
        _withdrawTokenInternal(msg.sender, _acceptor, _tokenAddr, address(0), 0, IUmbraHookReceiver(0), "");
      }
      /**
       * @notice Withdraw an ERC20 token payment sent to a stealth address
       * @dev This method must be directly called by the stealth address
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       * @param _hook Contract that will be called after the token withdrawal has completed
       * @param _data Arbitrary data that will be passed to the post-withdraw hook contract
       */
      function withdrawTokenAndCall(
        address _acceptor,
        address _tokenAddr,
        IUmbraHookReceiver _hook,
        bytes memory _data
      ) external {
        _withdrawTokenInternal(msg.sender, _acceptor, _tokenAddr, address(0), 0, _hook, _data);
      }
      /**
       * @notice Withdraw an ERC20 token payment on behalf of a stealth address via signed authorization
       * @param _stealthAddr The stealth address whose token balance will be withdrawn
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       * @param _sponsor Address which is compensated for submitting the withdrawal tx
       * @param _sponsorFee Amount of the token to pay to the sponsor
       * @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
       * @param _r ECDSA signature component: x-coordinate of `R`
       * @param _s ECDSA signature component: `s` value of the signature
       */
      function withdrawTokenOnBehalf(
        address _stealthAddr,
        address _acceptor,
        address _tokenAddr,
        address _sponsor,
        uint256 _sponsorFee,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
      ) external {
        _validateWithdrawSignature(
          _stealthAddr,
          _acceptor,
          _tokenAddr,
          _sponsor,
          _sponsorFee,
          IUmbraHookReceiver(0),
          "",
          _v,
          _r,
          _s
        );
        _withdrawTokenInternal(_stealthAddr, _acceptor, _tokenAddr, _sponsor, _sponsorFee, IUmbraHookReceiver(0), "");
      }
      /**
       * @notice Withdraw an ERC20 token payment on behalf of a stealth address via signed authorization
       * @param _stealthAddr The stealth address whose token balance will be withdrawn
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       * @param _sponsor Address which is compensated for submitting the withdrawal tx
       * @param _sponsorFee Amount of the token to pay to the sponsor
       * @param _hook Contract that will be called after the token withdrawal has completed
       * @param _data Arbitrary data that will be passed to the post-withdraw hook contract
       * @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
       * @param _r ECDSA signature component: x-coordinate of `R`
       * @param _s ECDSA signature component: `s` value of the signature
       */
      function withdrawTokenAndCallOnBehalf(
        address _stealthAddr,
        address _acceptor,
        address _tokenAddr,
        address _sponsor,
        uint256 _sponsorFee,
        IUmbraHookReceiver _hook,
        bytes memory _data,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
      ) external {
        _validateWithdrawSignature(_stealthAddr, _acceptor, _tokenAddr, _sponsor, _sponsorFee, _hook, _data, _v, _r, _s);
        _withdrawTokenInternal(_stealthAddr, _acceptor, _tokenAddr, _sponsor, _sponsorFee, _hook, _data);
      }
      /**
       * @notice Low level withdrawal function that should only be called after safety checks
       * @param _stealthAddr The stealth address whose token balance will be withdrawn
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       * @param _sponsor Address which is compensated for submitting the withdrawal tx
       * @param _sponsorFee Amount of the token to pay to the sponsor
       * @param _hook Contract that will be called after the token withdrawal has completed
       * @param _data Arbitrary data that will be passed to the post-withdraw hook contract
       */
      function _withdrawTokenInternal(
        address _stealthAddr,
        address _acceptor,
        address _tokenAddr,
        address _sponsor,
        uint256 _sponsorFee,
        IUmbraHookReceiver _hook,
        bytes memory _data
      ) internal {
        uint256 _amount = tokenPayments[_stealthAddr][_tokenAddr];
        // also protects from underflow
        require(_amount > _sponsorFee, "Umbra: No balance to withdraw or fee exceeds balance");
        uint256 _withdrawalAmount = _amount - _sponsorFee;
        delete tokenPayments[_stealthAddr][_tokenAddr];
        emit TokenWithdrawal(_stealthAddr, _acceptor, _withdrawalAmount, _tokenAddr);
        SafeERC20.safeTransfer(IERC20(_tokenAddr), _acceptor, _withdrawalAmount);
        if (_sponsorFee > 0) {
          SafeERC20.safeTransfer(IERC20(_tokenAddr), _sponsor, _sponsorFee);
        }
        if (address(_hook) != address(0)) {
          _hook.tokensWithdrawn(_withdrawalAmount, _stealthAddr, _acceptor, _tokenAddr, _sponsor, _sponsorFee, _data);
        }
      }
      /**
       * @notice Internal method which recovers address from signature of the parameters and throws if not _stealthAddr
       * @param _stealthAddr The stealth address whose token balance will be withdrawn
       * @param _acceptor Address where withdrawn funds should be sent
       * @param _tokenAddr Address of the ERC20 token being withdrawn
       * @param _sponsor Address which is compensated for submitting the withdrawal tx
       * @param _sponsorFee Amount of the token to pay to the sponsor
       * @param _hook Contract that will be called after the token withdrawal has completed
       * @param _data Arbitrary data that will be passed to the post-withdraw hook contract
       * @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
       * @param _r ECDSA signature component: x-coordinate of `R`
       * @param _s ECDSA signature component: `s` value of the signature
       */
      function _validateWithdrawSignature(
        address _stealthAddr,
        address _acceptor,
        address _tokenAddr,
        address _sponsor,
        uint256 _sponsorFee,
        IUmbraHookReceiver _hook,
        bytes memory _data,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
      ) internal view {
        uint256 _chainId;
        assembly {
          _chainId := chainid()
        }
        bytes32 _digest =
          keccak256(
            abi.encodePacked(
              "\\x19Ethereum Signed Message:\
    32",
              keccak256(
                abi.encode(_chainId, address(this), _acceptor, _tokenAddr, _sponsor, _sponsorFee, address(_hook), _data)
              )
            )
          );
        address _recoveredAddress = ecrecover(_digest, _v, _r, _s);
        require(_recoveredAddress != address(0) && _recoveredAddress == _stealthAddr, "Umbra: Invalid Signature");
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    import "../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 () internal {
            address msgSender = _msgSender();
            _owner = msgSender;
            emit OwnershipTransferred(address(0), 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 {
            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.6.0 <0.8.0;
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    /**
     * @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 SafeMath for uint256;
        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'
            // solhint-disable-next-line max-line-length
            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).add(value);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            _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
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.6;
    /// @dev Interface that post-withdraw hooks must implement to interop with Umbra
    interface IUmbraHookReceiver {
      /**
       * @notice Method called after a user completes an Umbra token withdrawal
       * @param _amount The amount of the token withdrawn _after_ subtracting the sponsor fee
       * @param _stealthAddr The stealth address whose token balance was withdrawn
       * @param _acceptor Address where withdrawn funds were sent; can be this contract
       * @param _tokenAddr Address of the ERC20 token that was withdrawn
       * @param _sponsor Address which was compensated for submitting the withdrawal tx
       * @param _sponsorFee Amount of the token that was paid to the sponsor
       * @param _data Arbitrary data passed to this hook by the withdrawer
       */
      function tokensWithdrawn(
        uint256 _amount,
        address _stealthAddr,
        address _acceptor,
        address _tokenAddr,
        address _sponsor,
        uint256 _sponsorFee,
        bytes memory _data
      ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <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 GSN 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 payable) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes memory) {
            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.6.0 <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.6.0 <0.8.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "SafeMath: subtraction overflow");
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) return 0;
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: division by zero");
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: modulo by zero");
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            return a - b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryDiv}.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.2 <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);
                }
            }
        }
    }