Contract 0x29da5213c75a1976452a27c8054e4c65ab0a3c53 1

 
Txn Hash Method
Index
From
To
Value
0x2ffa0d1e589448fa46c46cc743a238d2c4f23c7f463d7254b065a434fc0c73fd0x60806040172980302022-08-02 16:29:4957 days 6 hrs ago0x3204ac6f848e05557c6c7876e09059882e07962f IN  Contract Creation0 Ether0.0101656281820.001
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x6ab00aff523296e4702c44e8153506e50a33f17c3234fa203da62180914b80c0260217342022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
0x0dfa2d73e8aff4e42196de1f40896153d9f9bb0f4099d47ee74c16c22047702d260217142022-09-28 14:15:278 hrs 58 mins ago Rubicon: Bath House 0x29da5213c75a1976452a27c8054e4c65ab0a3c530 Ether
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Similar Match Source Code
Note: This contract matches the deployed ByteCode of the Source Code for Contract 0x5C8C6d48e771935A6cB14e5aCC7020c026Ec4844

Contract Name:
BathHouse

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion
File 1 of 16 : BathHouse.sol
// SPDX-License-Identifier: BUSL-1.1

/// @title  The administrator contract of Rubicon Pools
/// @author Rubicon DeFi Inc. - bghughes.eth
/// @notice The BathHouse initializes proxy-wrapped bathTokens, manages approved strategists, and sets system variables

pragma solidity =0.7.6;

import "./BathToken.sol";
import "../interfaces/IBathPair.sol";
import "../interfaces/IBathToken.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";

contract BathHouse {
    using SafeERC20 for IERC20;

    /// *** Storage Variables ***

    /// @notice Rubicon Bath House
    string public name;

    /// @notice The administrator of the Bath House contract
    address public admin;

    /// @notice The proxy administrator of Bath Tokens
    address public proxyManager;

    /// @notice The core Rubicon Market of the Pools system
    address public RubiconMarketAddress;

    /// @notice A mapping of approved strategists to access Pools liquidity
    mapping(address => bool) public approvedStrategists;

    /// @notice The initialization status of BathHouse
    bool public initialized;

    /// @notice If true, strategists are permissioned and must be approved by admin
    bool public permissionedStrategists;

    /// @notice Key, system-wide risk parameter for all liquity Pools
    /// @notice This represents the proportion of a pool's underlying assets that must remain in the pool
    /// @dev This protects a run on the bank scenario and ensures users can withdraw while allowing funds to be utilized for yield in the market
    uint256 public reserveRatio;

    /// @notice A variable time delay after which a strategist must return funds to the Bath Token
    uint256 public timeDelay;

    /// @notice The lone Bath Pair contract of the system which acts as the strategist entry point and logic contract
    address public approvedPairContract;

    /// @notice The basis point fee that is paid to strategists from LPs on capital that is successfully rebalanced to a Bath Token
    uint8 public bpsToStrategists;

    /// @notice Key mapping for determining the address of a Bath Token based on its underlying asset
    /// @dev Source of truth mapping that logs all ERC20 Liquidity pools underlying asset => bathToken Address
    mapping(address => address) public tokenToBathToken;

    /// @notice The BathToken.sol implementation that any new bathTokens inherit
    /// @dev The implementation of any ~newly spawned~ proxy-wrapped Bath Tokens via _createBathToken
    address public newBathTokenImplementation;

    bool locked;

    /// *** Events ***

    /// @notice An event that signals the creation of a new Bath Token
    event LogNewBathToken(
        address underlyingToken,
        address bathTokenAddress,
        address bathTokenFeeAdmin,
        uint256 timestamp,
        address bathTokenCreator
    );

    /// @notice An event that signals the permissionless spawning of a new Bath Token
    event LogOpenCreationSignal(
        IERC20 newERC20Underlying,
        address spawnedBathToken,
        uint256 initialNewBathTokenDeposit,
        IERC20 pairedExistingAsset,
        address pairedExistingBathToken,
        uint256 pairedBathTokenDeposit,
        address signaler
    );

    /// *** Modifiers ***

    /// @notice This modifier enforces that only the admin can call these functions
    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    /// @dev nonReentrant
    modifier beGoneReentrantScum() {
        require(!locked);
        locked = true;
        _;
        locked = false;
    }

    /// *** External Functions ***

    /// @notice The constructor-like initialization function
    /// @dev Proxy-safe initialization of storage that sets key storage variables
    /// @dev Admin is set to msg.sender
    function initialize(
        address market,
        uint256 _reserveRatio,
        uint256 _timeDelay,
        address _newBathTokenImplementation,
        address _proxyAdmin
    ) external {
        require(!initialized);
        name = "Rubicon Bath House";
        admin = msg.sender;
        timeDelay = _timeDelay;

        // Set Bath Token reserve ratio globally
        require(_reserveRatio <= 100);
        require(_reserveRatio > 0);
        reserveRatio = _reserveRatio;

        // Set BPS reward fee for successful strategist market-making
        /// @notice [(10000 - {bpsToStrategists}) / 10000] BPS of MM-ing activity is passed to users
        bpsToStrategists = 20;

        // Set key storage variables
        RubiconMarketAddress = market;
        permissionedStrategists = true;
        newBathTokenImplementation = _newBathTokenImplementation;
        proxyManager = _proxyAdmin;

        // Automatically approve admin as an approved strategist
        approveStrategist(admin);

        // Complete contract instantiation
        initialized = true;
    }

    /// @notice Permissionless entry point to spawn a Bath Token while posting liquidity to a ~pair of Bath Tokens~
    /// @notice Please note, creating a Bath Token in this fashion ~does not~ gaurentee markets will be made for the new pair. This function signals the desire to have a new pair supported on Rubicon for strategists to consider market-making for
    /// @notice The best desiredPairedAsset to select is a popular quote currency. Many traditional systems quote in USD while the ETH quote is superior - the choice is yours sweet msg.sender
    /// @dev The user must approve the bathHouse to spend their ERC20s
    /// @dev The user can only spawn a Bath Token for an ERC20 that is not yet in the Pools system and they must post liquidity on the other side of the pair for an ~extant Bath Token~
    function openBathTokenSpawnAndSignal(
        IERC20 newBathTokenUnderlying,
        uint256 initialLiquidityNew, // Must approve this contract to spend
        IERC20 desiredPairedAsset, // Must be paired with an existing quote for v1
        uint256 initialLiquidityExistingBathToken
    ) external beGoneReentrantScum returns (address newBathToken) {
        // Check that it doesn't already exist
        require(
            getBathTokenfromAsset(newBathTokenUnderlying) == address(0),
            "bathToken already exists for that IERC20"
        );
        require(
            getBathTokenfromAsset(desiredPairedAsset) != address(0),
            "bathToken does not exist for that desiredPairedAsset"
        );

        // Spawn a bathToken for the new asset
        address newOne = _createBathToken(newBathTokenUnderlying, address(0)); // NOTE: address(0) as feeAdmin means fee is paid to pool holders

        // Deposit initial liquidity posted of newBathTokenUnderlying
        require(
            newBathTokenUnderlying.transferFrom(
                msg.sender,
                address(this),
                initialLiquidityNew
            ),
            "Couldn't transferFrom your initial liquidity - make sure to approve BathHouse.sol"
        );

        newBathTokenUnderlying.safeApprove(newOne, initialLiquidityNew);

        // Deposit assets and send Bath Token shares to msg.sender
        IBathToken(newOne).deposit(initialLiquidityNew, msg.sender);

        // desiredPairedAsset must be pulled and deposited into bathToken
        desiredPairedAsset.safeTransferFrom(
            msg.sender,
            address(this),
            initialLiquidityExistingBathToken
        );

        address pairedPool = getBathTokenfromAsset((desiredPairedAsset));
        desiredPairedAsset.safeApprove(
            pairedPool,
            initialLiquidityExistingBathToken
        );

        // Deposit assets and send Bath Token shares to msg.sender
        IBathToken(pairedPool).deposit(
            initialLiquidityExistingBathToken,
            msg.sender
        );

        // emit an event describing the new pair, underlyings and bathTokens
        emit LogOpenCreationSignal(
            newBathTokenUnderlying,
            newOne,
            initialLiquidityNew,
            desiredPairedAsset,
            pairedPool,
            initialLiquidityExistingBathToken,
            msg.sender
        );

        newBathToken = newOne;
    }

    /// ** Admin-Only Functions **

    /// @notice An admin-only function to create a new Bath Token for any IERC20
    function createBathToken(IERC20 underlyingERC20, address _feeAdmin)
        external
        onlyAdmin
        returns (address newBathTokenAddress)
    {
        newBathTokenAddress = _createBathToken(underlyingERC20, _feeAdmin);
    }

    /// @notice A migration function that allows the admin to write arbitrarily to tokenToBathToken
    function adminWriteBathToken(IERC20 overwriteERC20, address newBathToken)
        external
        onlyAdmin
    {
        tokenToBathToken[address(overwriteERC20)] = newBathToken;
        emit LogNewBathToken(
            address(overwriteERC20),
            newBathToken,
            address(0),
            block.timestamp,
            msg.sender
        );
    }

    /// @notice Function to initialize and store the address of the ~lone~ bathPair contract for the Rubicon protocol
    function initBathPair(
        address _bathPairAddress,
        uint256 _maxOrderSizeBPS,
        int128 _shapeCoefNum
    ) external onlyAdmin returns (address newPair) {
        require(
            approvedPairContract == address(0),
            "BathPair already approved"
        );
        require(
            IBathPair(_bathPairAddress).initialized() != true,
            "BathPair already initialized"
        );
        newPair = _bathPairAddress;

        IBathPair(newPair).initialize(_maxOrderSizeBPS, _shapeCoefNum);

        approvedPairContract = newPair;
    }

    /// @notice Admin-only function to set a new Admin
    function setBathHouseAdmin(address newAdmin) external onlyAdmin {
        admin = newAdmin;
    }

    /// @notice Admin-only function to set a new Bath Token implementation
    /// @dev Please note that all bathTokens created will use this abi
    function setNewBathTokenImplementation(address newImplementation)
        external
        onlyAdmin
    {
        newBathTokenImplementation = newImplementation;
    }

    /// @notice Admin-only function to approve a new permissioned strategist
    function approveStrategist(address strategist) public onlyAdmin {
        approvedStrategists[strategist] = true;
    }

    /// @notice Admin-only function to remove a permissioned strategist
    function removeStrategist(address strategist) public onlyAdmin {
        approvedStrategists[strategist] = false;
    }

    /// @notice Admin-only function to set whether or not strategists are permissioned
    function setPermissionedStrategists(bool _new) external onlyAdmin {
        permissionedStrategists = _new;
    }

    /// @notice Admin-only function to set timeDelay
    function setCancelTimeDelay(uint256 value) external onlyAdmin {
        timeDelay = value;
    }

    /// @notice Admin-only function to set reserveRatio
    function setReserveRatio(uint256 rr) external onlyAdmin {
        require(rr <= 100);
        require(rr > 0);
        reserveRatio = rr;
    }

    /// @notice Admin-only function to set a Bath Token's timeDelay
    function setBathTokenMarket(address bathToken, address newMarket)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setMarket(newMarket);
    }

    /// @notice Admin-only function to add a bonus token to a Bath Token's reward schema
    function setBonusToken(address bathToken, address newBonusToken)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setBonusToken(newBonusToken);
    }

    /// @notice Admin-only function to set the BathBuddy rewards contract on a given Bath Token
    function setBathTokenBathBuddy(address bathToken, address newBathBuddy)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setBathBuddy(newBathBuddy);
    }

    /// @notice Admin-only function to set a Bath Token's Bath House admin
    function setBathTokenBathHouse(address bathToken, address newAdmin)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setBathHouse(newAdmin);
    }

    /// @notice Admin-only function to set a Bath Token's feeBPS
    function setBathTokenFeeBPS(address bathToken, uint256 newBPS)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setFeeBPS(newBPS);
    }

    /// @notice Admin-only function to approve the Bath Token's underlying token on the assigned market
    /// @dev required in case the market address ever changes.. #battleScars
    function bathTokenApproveSetMarket(address targetBathToken)
        external
        onlyAdmin
    {
        IBathToken(targetBathToken).approveMarket();
    }

    /// @notice Admin-only function to set a Bath Token's fee recipient (typically the Bath Token itself)
    function setBathTokenFeeTo(address bathToken, address feeTo)
        external
        onlyAdmin
    {
        IBathToken(bathToken).setFeeTo(feeTo);
    }

    /// @notice Admin-only function to set a Bath Token's target Rubicon Market
    function setMarket(address newMarket) external onlyAdmin {
        RubiconMarketAddress = newMarket;
    }

    /// @notice funtion to set bpsToStrategists
    function setBPSToStrategists(uint8 newBPS) external onlyAdmin {
        bpsToStrategists = newBPS;
    }

    /// *** View Functions ***

    // Getter Functions for parameters
    function getMarket() external view returns (address) {
        return RubiconMarketAddress;
    }

    function getReserveRatio() external view returns (uint256) {
        return reserveRatio;
    }

    function getCancelTimeDelay() external view returns (uint256) {
        return timeDelay;
    }

    /// @notice Returns the address of any bathToken in the system based on its corresponding underlying asset
    function getBathTokenfromAsset(IERC20 asset) public view returns (address) {
        return tokenToBathToken[address(asset)];
    }

    function getBPSToStrats() public view returns (uint8) {
        return bpsToStrategists;
    }

    /// *** System Security Checks ***

    /// @notice A function to check whether or not an address is an approved strategist
    function isApprovedStrategist(address wouldBeStrategist)
        external
        view
        returns (bool)
    {
        if (
            approvedStrategists[wouldBeStrategist] == true ||
            !permissionedStrategists
        ) {
            return true;
        } else {
            return false;
        }
    }

    /// @notice A function to check whether or not an address is the approved system instance of BathPair.sol
    function isApprovedPair(address pair) public view returns (bool outcome) {
        pair == approvedPairContract ? outcome = true : outcome = false;
    }

    /// *** Internal Functions ***

    /// @dev Low-level functionality to spawn a Bath Token using the OZ Transparent Upgradeable Proxy standard
    /// @param underlyingERC20 The underlying ERC-20 asset that underlies the newBathTokenAddress
    /// @param _feeAdmin Recipient of pool withdrawal fees, typically the pool itself
    function _createBathToken(IERC20 underlyingERC20, address _feeAdmin)
        internal
        returns (address newBathTokenAddress)
    {
        require(initialized, "BathHouse not initialized");
        address _underlyingERC20 = address(underlyingERC20);
        require(
            _underlyingERC20 != address(0),
            "Cant create bathToken for zero address"
        );

        // Check that it isn't already logged in the registry
        require(
            tokenToBathToken[_underlyingERC20] == address(0),
            "bathToken already exists"
        );

        // Creates a new bathToken that is upgradeable by the proxyManager
        require(
            newBathTokenImplementation != address(0),
            "no implementation set for bathTokens"
        );

        // Note, the option of a fee recipient for pool withdrawls exists. For all pools this is set to the pool itself in production and is visible via ~feeTo~ on any respective contract
        // Note, fee admin presently ignored in the Bath Token initialization() call via defaulting to itself; though, this is still upgradeable by the Bath House admin via
        bytes memory _initData = abi.encodeWithSignature(
            "initialize(address,address,address)",
            _underlyingERC20,
            (RubiconMarketAddress),
            (_feeAdmin)
        );

        TransparentUpgradeableProxy newBathToken = new TransparentUpgradeableProxy(
                newBathTokenImplementation,
                proxyManager,
                _initData
            );

        // New Bath Token Address
        newBathTokenAddress = address(newBathToken);

        // Write to source-of-truth router mapping for this ERC-20 => Bath Token
        tokenToBathToken[_underlyingERC20] = newBathTokenAddress;

        // Log Data
        emit LogNewBathToken(
            _underlyingERC20,
            newBathTokenAddress,
            _feeAdmin,
            block.timestamp,
            msg.sender
        );
    }
}

File 2 of 16 : BathToken.sol
// SPDX-License-Identifier: BUSL-1.1

/// @author Rubicon DeFi Inc. - bghughes.eth
/// @notice This contract represents a single-asset liquidity pool for Rubicon Pools
/// @notice Any user can deposit assets into this pool and earn yield from successful strategist market making with their liquidity
/// @notice This contract looks to both BathPairs and the BathHouse as its admin

pragma solidity =0.7.6;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
// import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../interfaces/IBathHouse.sol";
import "../interfaces/IRubiconMarket.sol";
import "../interfaces/IBathBuddy.sol";

contract BathToken {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    /// *** Storage Variables ***

    /// @notice The initialization status of the Bath Token
    bool public initialized;

    /// @notice  ** ERC-20 **
    string public symbol;
    string public name;
    uint8 public decimals;

    /// @notice The RubiconMarket.sol instance that all pool liquidity is intially directed to as market-making offers
    address public RubiconMarketAddress;

    /// @notice The Bath House admin of the Bath Token
    address public bathHouse;

    /// @notice The withdrawal fee recipient, typically the Bath Token itself
    address public feeTo;

    /// @notice The underlying ERC-20 token which is the core asset of the Bath Token vault
    IERC20 public underlyingToken;

    /// @notice The basis point fee rate that is paid on withdrawing the underlyingToken and bonusTokens
    uint256 public feeBPS;

    /// @notice ** ERC-20 **
    uint256 public totalSupply;

    /// @notice The amount of underlying deposits that are outstanding attempting market-making on the order book for yield
    /// @dev quantity of underlyingToken that is in the orderbook that the pool still has a claim on
    /// @dev The underlyingToken is effectively mark-to-marketed when it enters the book and it could be returned at a loss due to poor strategist performance
    /// @dev outstandingAmount is NOT inclusive of any non-underlyingToken assets sitting on the Bath Tokens that have filled to here and are awaiting rebalancing to the underlyingToken by strategists
    uint256 public outstandingAmount;

    /// @dev Intentionally unused DEPRECATED STORAGE VARIABLE to maintain contiguous state on proxy-wrapped contracts. Consider it a beautiful scar of incremental progress 📈
    /// @dev Keeping deprecated variables maintains consistent network-agnostic contract abis when moving to new chains and versions
    uint256[] deprecatedStorageArray; // Kept in to avoid storage collision bathTokens that are proxy upgraded

    /// @dev Intentionally unused DEPRECATED STORAGE VARIABLE to maintain contiguous state on proxy-wrapped contracts. Consider it a beautiful scar of incremental progress 📈
    mapping(uint256 => uint256) deprecatedMapping; // Kept in to avoid storage collision on bathTokens that are upgraded
    // *******************************************

    /// @notice  ** ERC-20 **
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    /// @notice EIP-2612
    bytes32 public DOMAIN_SEPARATOR;

    /// @notice EIP-2612
    /// @dev keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /// @notice EIP-2612
    mapping(address => uint256) public nonces;

    /// @notice Array of Bonus ERC-20 tokens that are given as liquidity incentives to pool withdrawers
    address[] public bonusTokens;

    /// @notice Address of the OZ Vesting Wallet which acts as means to vest bonusToken incentives to pool HODLers
    IBathBuddy public bathBuddy;

    /// @dev Reentrancy protection
    bool locked;

    /// *** Events ***

    /// @notice ** ERC-20 **
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    /// @notice ** ERC-20 **
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Time of Bath Token instantiation
    event LogInit(uint256 timeOfInit);

    /// @notice Log details about a pool deposit
    event LogDeposit(
        uint256 depositedAmt,
        IERC20 asset,
        uint256 sharesReceived,
        address depositor,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice Log details about a pool withdraw
    event LogWithdraw(
        uint256 amountWithdrawn,
        IERC20 asset,
        uint256 sharesWithdrawn,
        address withdrawer,
        uint256 fee,
        address feeTo,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice Log details about a pool rebalance
    event LogRebalance(
        IERC20 pool_asset,
        address destination,
        IERC20 transferAsset,
        uint256 rebalAmt,
        uint256 stratReward,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice Log details about a pool order canceled in the Rubicon Market book
    event LogPoolCancel(
        uint256 orderId,
        IERC20 pool_asset,
        uint256 outstandingAmountToCancel,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice Log details about a pool order placed in the Rubicon Market book
    event LogPoolOffer(
        uint256 id,
        IERC20 pool_asset,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice Log the credit to outstanding amount for funds that have been filled market-making
    event LogRemoveFilledTradeAmount(
        IERC20 pool_asset,
        uint256 fillAmount,
        uint256 underlyingBalance,
        uint256 outstandingAmount,
        uint256 totalSupply
    );

    /// @notice * EIP 4626 *
    event Deposit(
        address indexed caller,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /// @notice * EIP 4626 *
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /// @notice Log bonus token reward event
    event LogClaimBonusTokn(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares,
        IERC20 bonusToken
    );

    /// *** Constructor ***

    /// @notice Proxy-safe initialization of storage; the constructor
    function initialize(
        ERC20 token,
        address market,
        address _feeTo
    ) external nonReentrant {
        require(!initialized);
        string memory _symbol = string(
            abi.encodePacked(("bath"), token.symbol())
        );
        symbol = _symbol;
        underlyingToken = token;
        RubiconMarketAddress = market;
        bathHouse = msg.sender; //NOTE: assumed admin is creator on BathHouse

        name = string(abi.encodePacked(_symbol, (" v1")));
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                chainId,
                address(this)
            )
        );
        decimals = token.decimals(); // v1 Change - 4626 Adherence

        // Complete constract instantiation via CEI pattern
        initialized = true;

        // Add infinite approval of Rubicon Market for this asset
        IERC20(address(token)).approve(RubiconMarketAddress, 2**256 - 1);
        emit LogInit(block.timestamp);

        feeTo = address(this); //This contract is the fee recipient, rewarding HODLers
        feeBPS = 3; //Fee set to 3 BPS initially
    }

    /// *** Modifiers ***

    modifier onlyPair() {
        require(
            IBathHouse(bathHouse).isApprovedPair(msg.sender) == true,
            "not an approved pair - bathToken"
        );
        _;
    }

    modifier onlyBathHouse() {
        require(
            msg.sender == bathHouse,
            "caller is not bathHouse - BathToken.sol"
        );
        _;
    }

    /// @dev nonReentrant
    modifier nonReentrant() {
        require(!locked);
        locked = true;
        _;
        locked = false;
    }

    /// *** External Functions - Only Bath House / Admin ***

    /// @notice Admin-only function to set a Bath Token's market address
    function setMarket(address newRubiconMarket) external onlyBathHouse {
        RubiconMarketAddress = newRubiconMarket;
    }

    /// @notice Admin-only function to set a Bath Token's Bath House admin
    function setBathHouse(address newBathHouse) external onlyBathHouse {
        bathHouse = newBathHouse;
    }

    /// @notice Admin-only function to approve Bath Token's RubiconMarketAddress with the maximum integer value (infinite approval)
    function approveMarket() external onlyBathHouse {
        underlyingToken.approve(RubiconMarketAddress, 2**256 - 1);
    }

    /// @notice Admin-only function to set a Bath Token's feeBPS
    function setFeeBPS(uint256 _feeBPS) external onlyBathHouse {
        require(_feeBPS <= 300, "Fee can never exceed 300 bps");
        feeBPS = _feeBPS;
    }

    /// @notice Admin-only function to set a Bath Token's fee recipient, typically the pool itself
    function setFeeTo(address _feeTo) external onlyBathHouse {
        feeTo = _feeTo;
    }

    /// @notice Admin-only function to set THE BathBuddy which holds all ERC20 rewards
    function setBathBuddy(address newBuddy) external onlyBathHouse {
        bathBuddy = IBathBuddy(newBuddy);
    }

    /// @notice Admin-only function to add a bonus token to bonusTokens for pool incentives
    function setBonusToken(address newBonusERC20) external onlyBathHouse {
        bonusTokens.push(newBonusERC20);
        require(bonusTokens.length < 5, "too many tokens in this party");
    }

    /// *** External Functions - Only Approved Bath Pair / Strategist Contract ***

    /// ** Rubicon Market Functions **

    /// @notice The function for a strategist to cancel an outstanding Market Offer
    function cancel(uint256 id, uint256 amt) external onlyPair {
        outstandingAmount = outstandingAmount.sub(amt);
        IRubiconMarket(RubiconMarketAddress).cancel(id);

        emit LogPoolCancel(
            id,
            IERC20(underlyingToken),
            amt,
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
    }

    /// @notice A function called by BathPair to maintain proper accounting of outstandingAmount
    function removeFilledTradeAmount(uint256 amt) external onlyPair {
        outstandingAmount = outstandingAmount.sub(amt);
        emit LogRemoveFilledTradeAmount(
            IERC20(underlyingToken),
            amt,
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
    }

    /// @notice The function that places a bid and/or ask in the orderbook for a given pair from this pool
    function placeOffer(
        uint256 pay_amt,
        ERC20 pay_gem,
        uint256 buy_amt,
        ERC20 buy_gem
    ) external onlyPair returns (uint256) {
        // Place an offer in RubiconMarket
        // If incomplete offer return 0
        if (
            pay_amt == 0 ||
            pay_gem == ERC20(0) ||
            buy_amt == 0 ||
            buy_gem == ERC20(0)
        ) {
            return 0;
        }

        uint256 id = IRubiconMarket(RubiconMarketAddress).offer(
            pay_amt,
            pay_gem,
            buy_amt,
            buy_gem,
            0,
            false
        );
        outstandingAmount = outstandingAmount.add(pay_amt);

        emit LogPoolOffer(
            id,
            IERC20(underlyingToken),
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
        return (id);
    }

    /// @notice This function returns filled orders to the correct liquidity pool and sends strategist rewards to the Bath Pair
    /// @dev Sends non-underlyingToken fill elsewhere in the Pools system, typically it's sister asset within a trading pair (e.g. ETH-USDC)
    /// @dev Strategists presently accrue rewards in the filled asset not underlyingToken
    function rebalance(
        address destination,
        address filledAssetToRebalance, /* sister or fill asset */
        uint256 stratProportion,
        uint256 rebalAmt
    ) external onlyPair {
        require(filledAssetToRebalance != asset(), "must not be underlying");
        uint256 stratReward = (stratProportion.mul(rebalAmt)).div(10000);
        IERC20(filledAssetToRebalance).safeTransfer(
            destination,
            rebalAmt.sub(stratReward)
        );
        IERC20(filledAssetToRebalance).safeTransfer(msg.sender, stratReward);

        emit LogRebalance(
            IERC20(underlyingToken),
            destination,
            IERC20(filledAssetToRebalance),
            rebalAmt,
            stratReward,
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
    }

    /// *** EIP 4626 Implementation ***
    // https://eips.ethereum.org/EIPS/eip-4626#specification

    /// @notice Withdraw your bathTokens for the underlyingToken
    function withdraw(uint256 _shares)
        external
        nonReentrant
        returns (uint256 amountWithdrawn)
    {
        return _withdraw(_shares, msg.sender);
    }

    /// @notice * EIP 4626 *
    function asset() public view returns (address assetTokenAddress) {
        assetTokenAddress = address(underlyingToken);
    }

    /// @notice * EIP 4626 *
    function totalAssets() public view returns (uint256 totalManagedAssets) {
        return underlyingBalance();
    }

    /// @notice * EIP 4626 *
    function convertToShares(uint256 assets)
        public
        view
        returns (uint256 shares)
    {
        // Note: Inflationary tokens may affect this logic
        (totalSupply == 0) ? shares = assets : shares = (
            assets.mul(totalSupply)
        ).div(totalAssets());
    }

    // Note: MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// @notice * EIP 4626 *
    function convertToAssets(uint256 shares)
        public
        view
        returns (uint256 assets)
    {
        assets = (totalAssets().mul(shares)).div(totalSupply);
    }

    // Note: Unused function param to adhere to standard
    /// @notice * EIP 4626 *
    function maxDeposit(address receiver)
        public
        pure
        returns (uint256 maxAssets)
    {
        maxAssets = 2**256 - 1; // No limit on deposits in current implementation  = Max UINT
    }

    /// @notice * EIP 4626 *
    function previewDeposit(uint256 assets)
        public
        view
        returns (uint256 shares)
    {
        // The exact same logic is used, no deposit fee - only difference is deflationary token check (rare condition and probably redundant)
        shares = convertToShares(assets);
    }

    // Single asset override to reflect old functionality
    function deposit(uint256 assets)
        public
        nonReentrant
        returns (uint256 shares)
    {
        // Note: msg.sender is the same throughout the same contract context
        return _deposit(assets, msg.sender);
    }

    /// @notice * EIP 4626 *
    function deposit(uint256 assets, address receiver)
        public
        nonReentrant
        returns (uint256 shares)
    {
        return _deposit(assets, receiver);
    }

    // Note: Unused function param to adhere to standard
    /// @notice * EIP 4626 *
    function maxMint(address receiver) public pure returns (uint256 maxShares) {
        maxShares = 2**256 - 1; // No limit on shares that could be created via deposit in current implementation - Max UINT
    }

    // Given I want these shares, how much do I have to deposit
    /// @notice * EIP 4626 *
    function previewMint(uint256 shares) public view returns (uint256 assets) {
        (totalSupply == 0) ? assets = shares : assets = (
            shares.mul(totalAssets())
        ).div(totalSupply);
    }

    // Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    /// @notice * EIP 4626 *
    function mint(uint256 shares, address receiver)
        public
        nonReentrant
        returns (uint256 assets)
    {
        assets = previewMint(shares);
        uint256 _shares = _deposit(assets, receiver);
        require(_shares == shares, "did not mint expected share count");
    }

    // A user can withdraw whatever they hold
    /// @notice * EIP 4626 *
    function maxWithdraw(address owner)
        public
        view
        returns (uint256 maxAssets)
    {
        if (totalSupply == 0) {
            maxAssets = 0;
        } else {
            uint256 ownerShares = balanceOf[owner];
            maxAssets = convertToAssets(ownerShares);
        }
    }

    /// @notice * EIP 4626 *
    function previewWithdraw(uint256 assets)
        public
        view
        returns (uint256 shares)
    {
        if (totalSupply == 0) {
            shares = 0;
        } else {
            shares = convertToShares(
                assets.add(assets.mul(feeBPS).div((uint256(10000).sub(feeBPS))))
            );
        }
    }

    /// @notice * EIP 4626 *
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public nonReentrant returns (uint256 shares) {
        require(
            owner == msg.sender,
            "This implementation does not support non-sender owners from withdrawing user shares"
        );
        uint256 expectedShares = previewWithdraw(assets);
        uint256 assetsReceived = _withdraw(expectedShares, receiver);
        require(
            assetsReceived >= assets,
            "You cannot withdraw the amount of assets you expected"
        );
        shares = expectedShares;
    }

    // Constraint: msg.sender is owner of shares when withdrawing
    /// @notice * EIP 4626 *
    function maxRedeem(address owner) public view returns (uint256 maxShares) {
        return balanceOf[owner];
    }

    // Constraint: msg.sender is owner of shares when withdrawing
    /// @notice * EIP 4626 *
    function previewRedeem(uint256 shares)
        public
        view
        returns (uint256 assets)
    {
        uint256 r = (underlyingBalance().mul(shares)).div(totalSupply);
        uint256 _fee = r.mul(feeBPS).div(10000);
        assets = r.sub(_fee);
    }

    /// @notice * EIP 4626 *
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public nonReentrant returns (uint256 assets) {
        require(
            owner == msg.sender,
            "This implementation does not support non-sender owners from withdrawing user shares"
        );
        assets = _withdraw(shares, receiver);
    }

    /// *** Internal Functions ***

    /// @notice Deposit assets for the user and mint Bath Token shares to receiver
    function _deposit(uint256 assets, address receiver)
        internal
        returns (uint256 shares)
    {
        uint256 _pool = underlyingBalance();
        uint256 _before = underlyingToken.balanceOf(address(this));

        // **Assume caller is depositor**
        underlyingToken.safeTransferFrom(msg.sender, address(this), assets);
        uint256 _after = underlyingToken.balanceOf(address(this));
        assets = _after.sub(_before); // Additional check for deflationary tokens

        if (totalSupply == 0) {
            uint256 minLiquidityShare = 10**3;
            shares = assets.sub(minLiquidityShare);
            // Handle protecting from an initial supply spoof attack
            _mint(address(0), (minLiquidityShare));
        } else {
            shares = (assets.mul(totalSupply)).div(_pool);
        }

        // Send shares to designated target
        _mint(receiver, shares);

        require(shares != 0, "No shares minted");
        emit LogDeposit(
            assets,
            underlyingToken,
            shares,
            msg.sender,
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
        emit Deposit(msg.sender, msg.sender, assets, shares);
    }

    /// @dev assumes that msg.sender is the shareholder
    /// @notice Withdraw share for the user and send underlyingToken to receiver with any accrued yield and incentive tokens
    function _withdraw(uint256 _shares, address receiver)
        internal
        returns (uint256 amountWithdrawn)
    {
        uint256 r = (underlyingBalance().mul(_shares)).div(totalSupply);
        _burn(msg.sender, _shares);
        uint256 _fee = r.mul(feeBPS).div(10000);
        // If FeeTo == address(0) then the fee is effectively accrued by the pool
        if (feeTo != address(0)) {
            underlyingToken.safeTransfer(feeTo, _fee);
        }
        amountWithdrawn = r.sub(_fee);
        underlyingToken.safeTransfer(receiver, amountWithdrawn);

        emit LogWithdraw(
            amountWithdrawn,
            underlyingToken,
            _shares,
            msg.sender,
            _fee,
            feeTo,
            underlyingBalance(),
            outstandingAmount,
            totalSupply
        );
        emit Withdraw(
            msg.sender,
            receiver,
            msg.sender,
            amountWithdrawn,
            _shares
        );
    }

    // // User can recieve the awards they have accrued on BathBuddy
    function getBonusTokenReward(address rewardToken) public {
        IBathBuddy(bathBuddy).getReward(IERC20(rewardToken), msg.sender);
    }

    // // User can recieve the awards they have accrued on BathBuddy
    function getAllBonusTokenReward() public {
        distributeBonusTokenRewards(msg.sender);
    }

    /// Must allow the custom receiver option of the 4626 withdraw call path
    /// @notice Function to distibute non-underlyingToken Bath Token incentives to pool withdrawers
    /// @dev Note that bonusTokens adhere to the same feeTo and feeBPS pattern
    /// @dev Note the edge case in which the bonus token is the underlyingToken, here we simply release() to the pool and skip
    function distributeBonusTokenRewards(address receiver) internal {
        // Note, receiver must be owner <- enforced in the two withdraw entry paths
        // require(msg.sender == receiver, "You cannot claim someone else`s bonus tokens");
        // Verbose check:
        // require(initialTotalSupply == sharesWithdrawn + totalSupply);
        if (bonusTokens.length > 0) {
            for (uint256 index = 0; index < bonusTokens.length; index++) {
                IERC20 token = IERC20(bonusTokens[index]);
                require(address(token) != address(0), "bad bonus token");
                // Hit BathBuddy permissioned, nonReentrant function to handle paying out
                IBathBuddy(bathBuddy).getReward(token, receiver);

                emit LogClaimBonusTokn(
                    msg.sender,
                    receiver,
                    msg.sender,
                    IBathBuddy(bathBuddy).earned(msg.sender, address(token)),
                    balanceOf[msg.sender],
                    token
                );
            }
        }
    }

    /// *** ERC - 20 Standard ***

    function _mint(address to, uint256 value) internal {
        // Used for bonus token accounting
        distributeBonusTokenRewards(to);
        totalSupply = totalSupply.add(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(address(0), to, value);
    }

    function _burn(address from, uint256 value) internal {
        // Used for bonus token accounting
        distributeBonusTokenRewards(from);
        balanceOf[from] = balanceOf[from].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Transfer(from, address(0), value);
    }

    function _approve(
        address owner,
        address spender,
        uint256 value
    ) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _transfer(
        address from,
        address to,
        uint256 value
    ) private {
        // Bonus tokens
        distributeBonusTokenRewards(from);
        balanceOf[from] = balanceOf[from].sub(value);
        // Bonus tokens
        distributeBonusTokenRewards(to);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(from, to, value);
    }

    function approve(address spender, uint256 value) external returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }

    function transfer(address to, uint256 value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool) {
        if (allowance[from][msg.sender] != uint256(-1)) {
            allowance[from][msg.sender] = allowance[from][msg.sender].sub(
                value
            );
        }
        _transfer(from, to, value);
        return true;
    }

    /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(deadline >= block.timestamp, "bathToken: EXPIRED");
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        PERMIT_TYPEHASH,
                        owner,
                        spender,
                        value,
                        nonces[owner]++,
                        deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == owner,
            "bathToken: INVALID_SIGNATURE"
        );
        _approve(owner, spender, value);
    }

    /// *** View Functions ***

    /// @notice The underlying ERC-20 that this bathToken handles
    function underlyingERC20() external view returns (address) {
        return address(underlyingToken);
    }

    /// @notice The best-guess total claim on assets the Bath Token has
    /// @dev returns the amount of underlying ERC20 tokens in this pool in addition to any tokens that are outstanding in the Rubicon order book seeking market-making yield (outstandingAmount)
    function underlyingBalance() public view returns (uint256) {
        uint256 _pool = IERC20(underlyingToken).balanceOf(address(this));
        return _pool.add(outstandingAmount);
    }
}

File 3 of 16 : IBathPair.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.6;

interface IBathPair {
    function initialized() external returns (bool);

    function initialize(uint256 _maxOrderSizeBPS, int128 _shapeCoefNum)
        external;

    function setMaxOrderSizeBPS(uint16 val) external;

    function setShapeCoefNum(int128 val) external;
}

File 4 of 16 : IBathToken.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IBathToken is IERC20 {
    function removeFilledTradeAmount(uint256 amt) external;

    function cancel(uint256 id, uint256 amt) external;

    function placeOffer(
        uint256 pay_amt,
        IERC20 pay_gem,
        uint256 buy_amt,
        IERC20 buy_gem
    ) external returns (uint256);

    function rebalance(
        address destination,
        address filledAssetToRebalance,
        uint256 stratTakeProportion,
        uint256 rebalAmt
    ) external;

    function approveMarket() external;

    function asset() external view returns (address assetTokenAddress); //4626

    // Storage var that hold rewards tokens
    function bathBuddy() external view returns (address);

    function setBathBuddy(address newBathHouse) external;

    function underlyingToken() external returns (IERC20 erc20);

    function bathHouse() external returns (address admin);

    function setBathHouse(address newBathHouse) external;

    function setMarket(address newRubiconMarket) external;

    function setBonusToken(address newBonusToken) external;

    function setFeeBPS(uint256 _feeBPS) external;

    function setFeeTo(address _feeTo) external;

    function RubiconMarketAddress() external returns (address market);

    function outstandingAmount() external returns (uint256 amount);

    function underlyingBalance() external view returns (uint256);

    function deposit(uint256 amount) external returns (uint256 shares);

    function deposit(uint256 assets, address receiver)
        external
        returns (uint256 shares);

    function withdraw(uint256 shares) external returns (uint256 amount);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

    function nonces(address owner) external view returns (uint256);

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

File 5 of 16 : IERC20.sol
// 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);
}

File 6 of 16 : SafeERC20.sol
// 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");
        }
    }
}

File 7 of 16 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./UpgradeableProxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is UpgradeableProxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
     */
    constructor(address _logic, address admin_, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _setAdmin(admin_);
    }

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _admin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _admin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
        emit AdminChanged(_admin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external virtual ifAdmin {
        _upgradeTo(newImplementation);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
        _upgradeTo(newImplementation);
        Address.functionDelegateCall(newImplementation, data);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address adm) {
        bytes32 slot = _ADMIN_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            adm := sload(slot)
        }
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        bytes32 slot = _ADMIN_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newAdmin)
        }
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 8 of 16 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @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 guidelines: functions revert instead
 * of 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 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 {_setupDecimals} is
     * called.
     *
     * 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 returns (uint8) {
        return _decimals;
    }

    /**
     * @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);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        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].add(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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(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:
     *
     * - `to` 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 = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(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);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @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 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 from, address to, uint256 amount) internal virtual { }
}

File 9 of 16 : SafeMath.sol
// 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;
    }
}

File 10 of 16 : IBathHouse.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.6;

interface IBathHouse {
    function getMarket() external view returns (address);

    function initialized() external returns (bool);

    function reserveRatio() external view returns (uint256);

    function tokenToBathToken(address erc20Address)
        external
        view
        returns (address bathTokenAddress);

    function isApprovedStrategist(address wouldBeStrategist)
        external
        view
        returns (bool);

    function getBPSToStrats() external view returns (uint8);

    function isApprovedPair(address pair) external view returns (bool);
}

File 11 of 16 : IRubiconMarket.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IRubiconMarket {
    function cancel(uint256 id) external;

    function offer(
        uint256 pay_amt, //maker (ask) sell how much
        IERC20 pay_gem, //maker (ask) sell which token
        uint256 buy_amt, //maker (ask) buy how much
        IERC20 buy_gem, //maker (ask) buy which token
        uint256 pos, //position to insert offer, 0 should be used if unknown
        bool matching //match "close enough" orders?
    ) external returns (uint256);

    // Get best offer
    function getBestOffer(IERC20 sell_gem, IERC20 buy_gem)
        external
        view
        returns (uint256);

    // get offer
    function getOffer(uint256 id)
        external
        view
        returns (
            uint256,
            IERC20,
            uint256,
            IERC20
        );
}

File 12 of 16 : IBathBuddy.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.7.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IBathBuddy {
    /// @notice Releases the withdrawer's relative share of all vested tokens directly to them with their withdrawal
    /// @dev function that only the single, permissioned bathtoken can call that rewards a user their accrued rewards
    ///            for a given token during the current rewards period ongoing on bathBuddy
    function getReward(IERC20 token, address recipient) external;

    // Determines a user rewards
    // Note, uses share logic from bathToken
    function earned(address account, address token)
        external
        view
        returns (uint256);
}

File 13 of 16 : Context.sol
// 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;
    }
}

File 14 of 16 : Address.sol
// 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);
            }
        }
    }
}

File 15 of 16 : UpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./Proxy.sol";
import "../utils/Address.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 *
 * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
 * {TransparentUpgradeableProxy}.
 */
contract UpgradeableProxy is Proxy {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) public payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
        if(_data.length > 0) {
            Address.functionDelegateCall(_logic, _data);
        }
    }

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal view virtual override returns (address impl) {
        bytes32 slot = _IMPLEMENTATION_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            impl := sload(slot)
        }
    }

    /**
     * @dev Upgrades the proxy to a new implementation.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal virtual {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");

        bytes32 slot = _IMPLEMENTATION_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newImplementation)
        }
    }
}

File 16 of 16 : Proxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback () external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive () external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"address","name":"bathTokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"bathTokenFeeAdmin","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"bathTokenCreator","type":"address"}],"name":"LogNewBathToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"newERC20Underlying","type":"address"},{"indexed":false,"internalType":"address","name":"spawnedBathToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"initialNewBathTokenDeposit","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"pairedExistingAsset","type":"address"},{"indexed":false,"internalType":"address","name":"pairedExistingBathToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"pairedBathTokenDeposit","type":"uint256"},{"indexed":false,"internalType":"address","name":"signaler","type":"address"}],"name":"LogOpenCreationSignal","type":"event"},{"inputs":[],"name":"RubiconMarketAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"overwriteERC20","type":"address"},{"internalType":"address","name":"newBathToken","type":"address"}],"name":"adminWriteBathToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategist","type":"address"}],"name":"approveStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"approvedPairContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedStrategists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"targetBathToken","type":"address"}],"name":"bathTokenApproveSetMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bpsToStrategists","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"underlyingERC20","type":"address"},{"internalType":"address","name":"_feeAdmin","type":"address"}],"name":"createBathToken","outputs":[{"internalType":"address","name":"newBathTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBPSToStrats","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset","type":"address"}],"name":"getBathTokenfromAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancelTimeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarket","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bathPairAddress","type":"address"},{"internalType":"uint256","name":"_maxOrderSizeBPS","type":"uint256"},{"internalType":"int128","name":"_shapeCoefNum","type":"int128"}],"name":"initBathPair","outputs":[{"internalType":"address","name":"newPair","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"_reserveRatio","type":"uint256"},{"internalType":"uint256","name":"_timeDelay","type":"uint256"},{"internalType":"address","name":"_newBathTokenImplementation","type":"address"},{"internalType":"address","name":"_proxyAdmin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"isApprovedPair","outputs":[{"internalType":"bool","name":"outcome","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wouldBeStrategist","type":"address"}],"name":"isApprovedStrategist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newBathTokenImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"newBathTokenUnderlying","type":"address"},{"internalType":"uint256","name":"initialLiquidityNew","type":"uint256"},{"internalType":"contract IERC20","name":"desiredPairedAsset","type":"address"},{"internalType":"uint256","name":"initialLiquidityExistingBathToken","type":"uint256"}],"name":"openBathTokenSpawnAndSignal","outputs":[{"internalType":"address","name":"newBathToken","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"permissionedStrategists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategist","type":"address"}],"name":"removeStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"newBPS","type":"uint8"}],"name":"setBPSToStrategists","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setBathHouseAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newBathBuddy","type":"address"}],"name":"setBathTokenBathBuddy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setBathTokenBathHouse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"uint256","name":"newBPS","type":"uint256"}],"name":"setBathTokenFeeBPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"feeTo","type":"address"}],"name":"setBathTokenFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newMarket","type":"address"}],"name":"setBathTokenMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newBonusToken","type":"address"}],"name":"setBonusToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setCancelTimeDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMarket","type":"address"}],"name":"setMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"setNewBathTokenImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_new","type":"bool"}],"name":"setPermissionedStrategists","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rr","type":"uint256"}],"name":"setReserveRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenToBathToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50612c31806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200020e5760003560e01c8063023fa1d0146200021357806303b54d52146200023557806306fdde03146200027c5780630c7d5cd814620002fe5780630f64afd0146200031a578063158ef93e1462000349578063191a437814620003675780632988ef0f14620003985780632f6bed9e14620003c9578063338beff614620003eb5780633de73484146200041157806345aeb856146200041b5780634816644a146200043b5780635674def6146200044557806356b6d0d5146200046e5780636071015d146200047857806360f71b2414620004a157806368763e5114620004ab5780636dcea85f14620004d4578063770281d814620004fd57806378851f7c146200053a5780637c04772014620005635780638994cd04146200058c5780638b4f3b2c14620005b557806396b4ca7014620005d55780639d21577714620005df578063a8f096a41462000608578063abde81fe1462000639578063b4dee241146200066a578063bc3a2fbb1462000693578063bf8d52bb146200069d578063c9dec36114620006ce578063ca3a86cd14620006d8578063db1e201d1462000701578063e7ac3d901462000739578063e91651b8146200076a578063f1be16791462000793578063f2c83b43146200079d578063f2e3ef4d14620007c0578063f851a44014620007f1578063fd58498e14620007fb575b600080fd5b62000233600480360360208110156200022b57600080fd5b503562000805565b005b62000233600480360360a08110156200024d57600080fd5b506001600160a01b03813581169160208101359160408201359160608101358216916080909101351662000822565b620002866200092a565b6040805160208082528351818301528351919283929083019185019080838360005b83811015620002c2578181015183820152602001620002a8565b50505050905090810190601f168015620002f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b62000308620009bc565b60408051918252519081900360200190f35b62000233600480360360408110156200033257600080fd5b506001600160a01b038135169060200135620009c2565b6200035362000a3e565b604080519115158252519081900360200190f35b62000233600480360360408110156200037f57600080fd5b506001600160a01b038135811691602001351662000a47565b6200023360048036036040811015620003b057600080fd5b506001600160a01b038135811691602001351662000aaf565b6200023360048036036020811015620003e157600080fd5b5035151562000b17565b620003f562000b49565b604080516001600160a01b039092168252519081900360200190f35b6200035362000b58565b6200042562000b66565b6040805160ff9092168252519081900360200190f35b620003f562000b76565b62000233600480360360208110156200045d57600080fd5b50356001600160a01b031662000b85565b6200030862000bbf565b62000353600480360360208110156200049057600080fd5b50356001600160a01b031662000bc5565b620003f562000c12565b6200023360048036036020811015620004c357600080fd5b50356001600160a01b031662000c21565b6200023360048036036020811015620004ec57600080fd5b50356001600160a01b031662000c5d565b620003f5600480360360808110156200051557600080fd5b506001600160a01b038135811691602081013591604082013516906060013562000c97565b62000353600480360360208110156200055257600080fd5b50356001600160a01b03166200100f565b620003f5600480360360208110156200057b57600080fd5b50356001600160a01b031662001024565b6200023360048036036020811015620005a457600080fd5b50356001600160a01b031662001042565b6200023360048036036020811015620005cd57600080fd5b50356200107c565b620003f5620010b6565b6200023360048036036020811015620005f757600080fd5b50356001600160a01b0316620010c5565b62000233600480360360408110156200062057600080fd5b506001600160a01b0381358116916020013516620010fe565b62000233600480360360408110156200065157600080fd5b506001600160a01b038135811691602001351662001166565b62000233600480360360208110156200068257600080fd5b50356001600160a01b0316620011ea565b620004256200125a565b6200023360048036036040811015620006b557600080fd5b506001600160a01b03813581169160200135166200126a565b62000308620012d2565b6200035360048036036020811015620006f057600080fd5b50356001600160a01b0316620012d8565b620003f5600480360360608110156200071957600080fd5b506001600160a01b038135169060208101359060400135600f0b62001306565b620003f5600480360360408110156200075157600080fd5b506001600160a01b0381358116916020013516620014ce565b620003f5600480360360208110156200078257600080fd5b50356001600160a01b0316620014fc565b620003f562001517565b6200023360048036036020811015620007b557600080fd5b503560ff1662001526565b6200023360048036036040811015620007d857600080fd5b506001600160a01b03813581169160200135166200155e565b620003f5620015c6565b62000308620015d5565b6001546001600160a01b031633146200081d57600080fd5b600755565b60055460ff16156200083357600080fd5b6040805180820190915260128082527152756269636f6e204261746820486f75736560701b60209092019182526200086e9160009162001d32565b50600180546001600160a01b03191633179055600783905560648411156200089557600080fd5b60008411620008a357600080fd5b600684905560088054600560a21b60ff60a01b19909116179055600380546001600160a01b038088166001600160a01b0319928316179092556005805461ff001916610100179055600a805485841690831617905560028054848416921691909117905560015462000916911662000c21565b50506005805460ff19166001179055505050565b6000805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015620009b45780601f106200098857610100808354040283529160200191620009b4565b820191906000526020600020905b8154815290600101906020018083116200099657829003601f168201915b505050505081565b60065481565b6001546001600160a01b03163314620009da57600080fd5b816001600160a01b031663604b6a9c826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801562000a2157600080fd5b505af115801562000a36573d6000803e3d6000fd5b505050505050565b60055460ff1681565b6001546001600160a01b0316331462000a5f57600080fd5b816001600160a01b0316636dcea85f826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801562000a2157600080fd5b6001546001600160a01b0316331462000ac757600080fd5b816001600160a01b031663f46901ed826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801562000a2157600080fd5b6001546001600160a01b0316331462000b2f57600080fd5b600580549115156101000261ff0019909216919091179055565b600a546001600160a01b031681565b600554610100900460ff1681565b600854600160a01b900460ff1681565b6008546001600160a01b031681565b6001546001600160a01b0316331462000b9d57600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60065490565b6001600160a01b03811660009081526004602052604081205460ff1615156001148062000bfa5750600554610100900460ff16155b1562000c095750600162000c0d565b5060005b919050565b6003546001600160a01b031681565b6001546001600160a01b0316331462000c3957600080fd5b6001600160a01b03166000908152600460205260409020805460ff19166001179055565b6001546001600160a01b0316331462000c7557600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600a54600090600160a01b900460ff161562000cb257600080fd5b600a805460ff60a01b1916600160a01b179055600062000cd28662001024565b6001600160a01b03161462000d195760405162461bcd60e51b815260040180806020018281038252602881526020018062002afc6028913960400191505060405180910390fd5b600062000d268462001024565b6001600160a01b0316141562000d6e5760405162461bcd60e51b815260040180806020018281038252603481526020018062002bc86034913960400191505060405180910390fd5b600062000d7d866000620015db565b604080516323b872dd60e01b81523360048201523060248201526044810188905290519192506001600160a01b038816916323b872dd916064808201926020929091908290030181600087803b15801562000dd757600080fd5b505af115801562000dec573d6000803e3d6000fd5b505050506040513d602081101562000e0357600080fd5b505162000e425760405162461bcd60e51b815260040180806020018281038252605181526020018062002a5f6051913960600191505060405180910390fd5b62000e586001600160a01b0387168287620018ca565b60408051636e553f6560e01b81526004810187905233602482015290516001600160a01b03831691636e553f659160448083019260209291908290030181600087803b15801562000ea857600080fd5b505af115801562000ebd573d6000803e3d6000fd5b505050506040513d602081101562000ed457600080fd5b5062000eee90506001600160a01b038516333086620019ea565b600062000efb8562001024565b905062000f136001600160a01b0386168286620018ca565b60408051636e553f6560e01b81526004810186905233602482015290516001600160a01b03831691636e553f659160448083019260209291908290030181600087803b15801562000f6357600080fd5b505af115801562000f78573d6000803e3d6000fd5b505050506040513d602081101562000f8f57600080fd5b5050604080516001600160a01b03808a168252808516602083015281830189905280881660608301528316608082015260a081018690523360c082015290517f9eb6cc4b72fbb43e01228aeeb5dd940751498582f294e6f77658cb3ccd6a4f509181900360e00190a150600a805460ff60a01b1916905595945050505050565b60046020526000908152604090205460ff1681565b6001600160a01b039081166000908152600960205260409020541690565b6001546001600160a01b031633146200105a57600080fd5b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b031633146200109457600080fd5b6064811115620010a357600080fd5b60008111620010b157600080fd5b600655565b6002546001600160a01b031681565b6001546001600160a01b03163314620010dd57600080fd5b6001600160a01b03166000908152600460205260409020805460ff19169055565b6001546001600160a01b031633146200111657600080fd5b816001600160a01b031663ebbc2082826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801562000a2157600080fd5b6001546001600160a01b031633146200117e57600080fd5b6001600160a01b03808316600081815260096020908152604080832080549587166001600160a01b03199096168617905580519384529083019390935281830152426060820152336080820152905160008051602062002b248339815191529181900360a00190a15050565b6001546001600160a01b031633146200120257600080fd5b806001600160a01b031663c2560f2a6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156200123e57600080fd5b505af115801562001253573d6000803e3d6000fd5b5050505050565b600854600160a01b900460ff1690565b6001546001600160a01b031633146200128257600080fd5b816001600160a01b031663b2eaeaaa826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801562000a2157600080fd5b60075481565b6008546000906001600160a01b03838116911614620012fb575060008062001300565b506001805b50919050565b6001546000906001600160a01b031633146200132157600080fd5b6008546001600160a01b0316156200137c576040805162461bcd60e51b815260206004820152601960248201527810985d1a14185a5c88185b1c9958591e48185c1c1c9bdd9959603a1b604482015290519081900360640190fd5b836001600160a01b031663158ef93e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015620013b857600080fd5b505af1158015620013cd573d6000803e3d6000fd5b505050506040513d6020811015620013e457600080fd5b50511515600114156200143d576040805162461bcd60e51b815260206004820152601c60248201527b10985d1a14185a5c88185b1c9958591e481a5b9a5d1a585b1a5e995960221b604482015290519081900360640190fd5b839050806001600160a01b0316634a36610084846040518363ffffffff1660e01b81526004018083815260200182600f0b815260200192505050600060405180830381600087803b1580156200149257600080fd5b505af1158015620014a7573d6000803e3d6000fd5b5050600880546001600160a01b0319166001600160a01b0385161790555090949350505050565b6001546000906001600160a01b03163314620014e957600080fd5b620014f58383620015db565b9392505050565b6009602052600090815260409020546001600160a01b031681565b6003546001600160a01b031690565b6001546001600160a01b031633146200153e57600080fd5b6008805460ff909216600160a01b0260ff60a01b19909216919091179055565b6001546001600160a01b031633146200157657600080fd5b816001600160a01b03166315f937bf826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801562000a2157600080fd5b6001546001600160a01b031681565b60075490565b60055460009060ff1662001632576040805162461bcd60e51b815260206004820152601960248201527810985d1a121bdd5cd9481b9bdd081a5b9a5d1a585b1a5e9959603a1b604482015290519081900360640190fd5b826001600160a01b0381166200167a5760405162461bcd60e51b815260040180806020018281038252602681526020018062002ad66026913960400191505060405180910390fd5b6001600160a01b038181166000908152600960205260409020541615620016e3576040805162461bcd60e51b815260206004820152601860248201527762617468546f6b656e20616c72656164792065786973747360401b604482015290519081900360640190fd5b600a546001600160a01b03166200172c5760405162461bcd60e51b815260040180806020018281038252602481526020018062002b6e6024913960400191505060405180910390fd5b600354604080516001600160a01b038085166024830152928316604482015285831660648083019190915282518083039091018152608490910182526020810180516001600160e01b031663c0c53b8b60e01b179052600a5460025492519193600093918116929116908490620017a39062001dc7565b80846001600160a01b03168152602001836001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b83811015620017fd578181015183820152602001620017e3565b50505050905090810190601f1680156200182b5780820380516001836020036101000a031916815260200191505b50945050505050604051809103906000f0801580156200184f573d6000803e3d6000fd5b506001600160a01b0380851660008181526009602090815260409182902080548587166001600160a01b0319909116811790915582519384529083015291881681830152426060820152336080820152905191955085925060008051602062002b24833981519152919081900360a00190a150505092915050565b80158062001954575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156200192457600080fd5b505afa15801562001939573d6000803e3d6000fd5b505050506040513d60208110156200195057600080fd5b5051155b620019915760405162461bcd60e51b815260040180806020018281038252603681526020018062002b926036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052620019e590849062001a4c565b505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905262001a4690859062001a4c565b50505050565b600062001aa3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031662001b039092919063ffffffff16565b805190915015620019e55780806020019051602081101562001ac457600080fd5b5051620019e55760405162461bcd60e51b815260040180806020018281038252602a81526020018062002b44602a913960400191505060405180910390fd5b606062001b14848460008562001b1c565b949350505050565b60608247101562001b5f5760405162461bcd60e51b815260040180806020018281038252602681526020018062002ab06026913960400191505060405180910390fd5b62001b6a8562001c82565b62001bbc576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b6020831062001bfc5780518252601f19909201916020918201910162001bdb565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811462001c60576040519150601f19603f3d011682016040523d82523d6000602084013e62001c65565b606091505b509150915062001c7782828662001c88565b979650505050505050565b3b151590565b6060831562001c99575081620014f5565b82511562001caa5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101562001cf657818101518382015260200162001cdc565b50505050905090810190601f16801562001d245780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b828054600181600116156101000203166002900490600052602060002090601f01602090048101928262001d6a576000855562001db5565b82601f1062001d8557805160ff191683800117855562001db5565b8280016001018555821562001db5579182015b8281111562001db557825182559160200191906001019062001d98565b5062001dc392915062001dd5565b5090565b610c728062001ded83390190565b5b8082111562001dc3576000815560010162001dd656fe608060405260405162000c7238038062000c72833981810160405260608110156200002957600080fd5b815160208301516040808501805191519395929483019291846401000000008211156200005557600080fd5b9083019060208201858111156200006b57600080fd5b82516401000000008111828201881017156200008657600080fd5b82525081516020918201929091019080838360005b83811015620000b55781810151838201526020016200009b565b50505050905090810190601f168015620000e35780820380516001836020036101000a031916815260200191505b5060405250849150829050620000f98262000137565b8051156200011a57620001188282620001ae60201b620003821760201c565b505b50620001239050565b6200012e82620001dd565b505050620003be565b6200014d816200020160201b620003ae1760201c565b6200018a5760405162461bcd60e51b815260040180806020018281038252603681526020018062000c166036913960400191505060405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b6060620001d6838360405180606001604052806027815260200162000bef6027913962000207565b9392505050565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355565b3b151590565b6060620002148462000201565b620002515760405162461bcd60e51b815260040180806020018281038252602681526020018062000c4c6026913960400191505060405180910390fd5b600080856001600160a01b0316856040518082805190602001908083835b60208310620002905780518252601f1990920191602091820191016200026f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114620002f2576040519150601f19603f3d011682016040523d82523d6000602084013e620002f7565b606091505b5090925090506200030a82828662000314565b9695505050505050565b6060831562000325575081620001d6565b825115620003365782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156200038257818101518382015260200162000368565b50505050905090810190601f168015620003b05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b61082180620003ce6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100985780635c60da1b146101165780638f28397014610147578063f851a4401461017a5761005d565b3661005d5761005b61018f565b005b61005b61018f565b34801561007157600080fd5b5061005b6004803603602081101561008857600080fd5b50356001600160a01b03166101a9565b61005b600480360360408110156100ae57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156100d857600080fd5b8201836020820111156100ea57600080fd5b803590602001918460018302840111600160201b8311171561010b57600080fd5b5090925090506101e3565b34801561012257600080fd5b5061012b610260565b604080516001600160a01b039092168252519081900360200190f35b34801561015357600080fd5b5061005b6004803603602081101561016a57600080fd5b50356001600160a01b031661029d565b34801561018657600080fd5b5061012b610357565b6101976103b4565b6101a76101a2610414565b610427565b565b6101b161044b565b6001600160a01b0316336001600160a01b031614156101d8576101d38161045e565b6101e0565b6101e061018f565b50565b6101eb61044b565b6001600160a01b0316336001600160a01b031614156102535761020d8361045e565b61024d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061038292505050565b5061025b565b61025b61018f565b505050565b600061026a61044b565b6001600160a01b0316336001600160a01b031614156102925761028b610414565b905061029a565b61029a61018f565b90565b6102a561044b565b6001600160a01b0316336001600160a01b031614156101d8576001600160a01b0381166103035760405162461bcd60e51b815260040180806020018281038252603a8152602001806106cd603a913960400191505060405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61032c61044b565b604080516001600160a01b03928316815291841660208301528051918290030190a16101d38161049e565b600061036161044b565b6001600160a01b0316336001600160a01b031614156102925761028b61044b565b60606103a78383604051806060016040528060278152602001610727602791396104b0565b9392505050565b3b151590565b6103bc61044b565b6001600160a01b0316336001600160a01b0316141561040c5760405162461bcd60e51b81526004018080602001828103825260428152602001806107aa6042913960600191505060405180910390fd5b6101a76101a7565b6000805160206107078339815191525490565b3660008037600080366000845af43d6000803e808015610446573d6000f35b3d6000fd5b6000805160206106ad8339815191525490565b610467816105b2565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6000805160206106ad83398151915255565b60606104bb846103ae565b6104f65760405162461bcd60e51b81526004018080602001828103825260268152602001806107846026913960400191505060405180910390fd5b600080856001600160a01b0316856040518082805190602001908083835b602083106105335780518252601f199092019160209182019101610514565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114610593576040519150601f19603f3d011682016040523d82523d6000602084013e610598565b606091505b50915091506105a8828286610608565b9695505050505050565b6105bb816103ae565b6105f65760405162461bcd60e51b815260040180806020018281038252603681526020018061074e6036913960400191505060405180910390fd5b60008051602061070783398151915255565b606083156106175750816103a7565b8251156106275782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610671578181015183820152602001610659565b50505050905090810190601f16801561069e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035472616e73706172656e745570677261646561626c6550726f78793a206e65772061646d696e20697320746865207a65726f2061646472657373360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65645570677261646561626c6550726f78793a206e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e7472616374416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6e74726163745472616e73706172656e745570677261646561626c6550726f78793a2061646d696e2063616e6e6f742066616c6c6261636b20746f2070726f787920746172676574a26469706673582212204050f57875d2af9d630e52d4f6e3ec0ef3316e30595876c8c983f3c8632d2b0364736f6c63430007060033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65645570677261646561626c6550726f78793a206e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e7472616374416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6e7472616374436f756c646e2774207472616e7366657246726f6d20796f757220696e697469616c206c6971756964697479202d206d616b65207375726520746f20617070726f76652042617468486f7573652e736f6c416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c43616e74206372656174652062617468546f6b656e20666f72207a65726f206164647265737362617468546f6b656e20616c72656164792065786973747320666f722074686174204945524332303042e0d13ca8ee0bdf1dc720371ee18ade19c59a8dfc8db85869f433cedaf0155361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565646e6f20696d706c656d656e746174696f6e2073657420666f722062617468546f6b656e735361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e636562617468546f6b656e20646f6573206e6f7420657869737420666f72207468617420646573697265645061697265644173736574a2646970667358221220cb1767f934f01f51abd90ca1b05e3982e35e8a7df5ed925465072e9bb4287e0464736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.