contract Meowshi is ERC20, BaseBoringBatchable {
    IBentoBoxBasic immutable bentoBox; // BENTO vault contract - 0xF5BCE5077908a1b7370B9ae04AdC565EBd643966 (multinet)
    ISushiBar immutable sushiToken; // SUSHI token contract - 0x6B3595068778DD592e39A122f4f5a5cF09C90fE2 (mainnet)
    address immutable sushiBar; // xSUSHI token contract for staking SUSHI - 0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272 (mainnet)

    string public constant name = "Meowshi";
    string public constant symbol = "NYAN";
    uint8 public constant decimals = 18;
    uint256 constant multiplier = 10000; // 1 xSUSHI BENTO share = 10,000 NYAN
    uint256 public totalSupply;
    
    constructor(IBentoBoxBasic bentoBox_, ISushiBar sushiToken_, address sushiBar_) {
        sushiToken_.approve(sushiBar_, type(uint256).max); // max {approve} xSUSHI to draw SUSHI from this contract
        ISushiBar(sushiBar_).approve(address(bentoBox_), type(uint256).max); // max {approve} BENTO to draw xSUSHI from this contract
        bentoBox = bentoBox_;
        sushiToken = sushiToken_;
        sushiBar = sushiBar_;
    }
    
    // **** xSUSHI
    /// @notice Enter Meowshi. Deposit xSUSHI `amount`. Mint NYAN for `to`.
    function nyan(address to, uint256 amount) external returns (uint256 shares) {
        ISushiBar(sushiBar).transferFrom(msg.sender, address(bentoBox), amount); // forward to BENTO for skim
        (, shares) = bentoBox.deposit(IERC20(sushiBar), address(bentoBox), address(this), amount, 0);
        nyanMint(to, shares * multiplier);
    }

    /// @notice Leave Meowshi. Burn NYAN `amount`. Claim xSUSHI for `to`.
    function unyan(address to, uint256 amount) external returns (uint256 amountOut) {
        nyanBurn(amount);
        (amountOut, ) = bentoBox.withdraw(IERC20(sushiBar), address(this), to, 0, amount / multiplier);
    }
    
    // **** SUSHI
    /// @notice Enter Meowshi. Deposit SUSHI `amount`. Mint NYAN for `to`.
    function nyanSushi(address to, uint256 amount) external returns (uint256 shares) {
        sushiToken.transferFrom(msg.sender, address(this), amount);
        ISushiBar(sushiBar).enter(amount);
        (, shares) = bentoBox.deposit(IERC20(sushiBar), address(this), address(this), ISushiBar(sushiBar).balanceOf(address(this)), 0);
        nyanMint(to, shares * multiplier);
    }

    /// @notice Leave Meowshi. Burn NYAN `amount`. Claim SUSHI for `to`.
    function unyanSushi(address to, uint256 amount) external returns (uint256 amountOut) {
        nyanBurn(amount);
        (amountOut, ) = bentoBox.withdraw(IERC20(sushiBar), address(this), address(this), 0, amount / multiplier);
        ISushiBar(sushiBar).leave(amountOut);
        sushiToken.transfer(to, sushiToken.balanceOf(address(this))); 
    }

    // **** SUPPLY MGMT
    /// @notice Internal mint function for *nyan*.
    function nyanMint(address to, uint256 amount) private {
        balanceOf[to] += amount;
        totalSupply += amount;
        emit Transfer(address(0), to, amount);
    }
    
    /// @notice Internal burn function for *unyan*.
    function nyanBurn(uint256 amount) private {
        balanceOf[msg.sender] -= amount;
        totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
}
//  ៱˳_˳៱   ∫
function unyanSushi(address to, uint256 amount) external returns (uint256 amountOut) {
    nyanBurn(amount);
    (amountOut, ) = bentoBox.withdraw(IERC20(sushiBar), address(this), address(this), 0, amount / multiplier);
    ISushiBar(sushiBar).leave(amountOut);
    sushiToken.transfer(to, sushiToken.balanceOf(address(this))); 
}

/// @notice Leave Meowshi. Burn NYAN `amount`. Claim SUSHI for `to`.