{
    "language": "Solidity",
    "sources": {
        "contracts/flat/KashiPairFlat.sol": {
            "content": "// SPDX-License-Identifier: UNLICENSED\n// Kashi Lending Medium Risk\n\n//  __  __             __    __      _____                  __ __\n// |  |/  .---.-.-----|  |--|__|    |     |_.-----.-----.--|  |__.-----.-----.\n// |     <|  _  |__ --|     |  |    |       |  -__|     |  _  |  |     |  _  |\n// |__|\\__|___._|_____|__|__|__|    |_______|_____|__|__|_____|__|__|__|___  |\n//                                                                     |_____|\n\n// Copyright (c) 2021 BoringCrypto - All rights reserved\n// Twitter: @Boring_Crypto\n\n// Special thanks to:\n// @0xKeno - for all his invaluable contributions\n// @burger_crypto - for the idea of trying to let the LPs benefit from liquidations\n\n// Version: 22-Feb-2021\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\n// solhint-disable avoid-low-level-calls\n// solhint-disable no-inline-assembly\n// solhint-disable not-rely-on-time\n\n// File @boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol@v1.2.0\n// License-Identifier: MIT\n\n/// @notice A library for performing overflow-/underflow-safe math,\n/// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).\nlibrary BoringMath {\n    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {\n        require((c = a + b) >= b, \"BoringMath: Add Overflow\");\n    }\n\n    function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {\n        require((c = a - b) <= a, \"BoringMath: Underflow\");\n    }\n\n    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {\n        require(b == 0 || (c = a * b) / b == a, \"BoringMath: Mul Overflow\");\n    }\n\n    function to128(uint256 a) internal pure returns (uint128 c) {\n        require(a <= uint128(-1), \"BoringMath: uint128 Overflow\");\n        c = uint128(a);\n    }\n\n    function to64(uint256 a) internal pure returns (uint64 c) {\n        require(a <= uint64(-1), \"BoringMath: uint64 Overflow\");\n        c = uint64(a);\n    }\n\n    function to32(uint256 a) internal pure returns (uint32 c) {\n        require(a <= uint32(-1), \"BoringMath: uint32 Overflow\");\n        c = uint32(a);\n    }\n}\n\n/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.\nlibrary BoringMath128 {\n    function add(uint128 a, uint128 b) internal pure returns (uint128 c) {\n        require((c = a + b) >= b, \"BoringMath: Add Overflow\");\n    }\n\n    function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {\n        require((c = a - b) <= a, \"BoringMath: Underflow\");\n    }\n}\n\n// File @boringcrypto/boring-solidity/contracts/BoringOwnable.sol@v1.2.0\n// License-Identifier: MIT\n\n// Audit on 5-Jan-2021 by Keno and BoringCrypto\n// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol\n// Edited by BoringCrypto\n\ncontract BoringOwnableData {\n    address public owner;\n    address public pendingOwner;\n}\n\ncontract BoringOwnable is BoringOwnableData {\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /// @notice `owner` defaults to msg.sender on construction.\n    constructor() public {\n        owner = msg.sender;\n        emit OwnershipTransferred(address(0), msg.sender);\n    }\n\n    /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.\n    /// Can only be invoked by the current `owner`.\n    /// @param newOwner Address of the new owner.\n    /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.\n    /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.\n    function transferOwnership(\n        address newOwner,\n        bool direct,\n        bool renounce\n    ) public onlyOwner {\n        if (direct) {\n            // Checks\n            require(newOwner != address(0) || renounce, \"Ownable: zero address\");\n\n            // Effects\n            emit OwnershipTransferred(owner, newOwner);\n            owner = newOwner;\n            pendingOwner = address(0);\n        } else {\n            // Effects\n            pendingOwner = newOwner;\n        }\n    }\n\n    /// @notice Needs to be called by `pendingOwner` to claim ownership.\n    function claimOwnership() public {\n        address _pendingOwner = pendingOwner;\n\n        // Checks\n        require(msg.sender == _pendingOwner, \"Ownable: caller != pending owner\");\n\n        // Effects\n        emit OwnershipTransferred(owner, _pendingOwner);\n        owner = _pendingOwner;\n        pendingOwner = address(0);\n    }\n\n    /// @notice Only allows the `owner` to execute the function.\n    modifier onlyOwner() {\n        require(msg.sender == owner, \"Ownable: caller is not the owner\");\n        _;\n    }\n}\n\n// File @boringcrypto/boring-solidity/contracts/Domain.sol@v1.2.0\n// License-Identifier: MIT\n// Based on code and smartness by Ross Campbell and Keno\n// Uses immutable to store the domain separator to reduce gas usage\n// If the chain id changes due to a fork, the forked chain will calculate on the fly.\n\ncontract Domain {\n    bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256(\"EIP712Domain(uint256 chainId,address verifyingContract)\");\n    // See https://eips.ethereum.org/EIPS/eip-191\n    string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = \"\\x19\\x01\";\n\n    // solhint-disable var-name-mixedcase\n    bytes32 private immutable _DOMAIN_SEPARATOR;\n    uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;\n\n    /// @dev Calculate the DOMAIN_SEPARATOR\n    function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {\n        return keccak256(abi.encode(DOMAIN_SEPARATOR_SIGNATURE_HASH, chainId, address(this)));\n    }\n\n    constructor() public {\n        uint256 chainId;\n        assembly {\n            chainId := chainid()\n        }\n        _DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = chainId);\n    }\n\n    /// @dev Return the DOMAIN_SEPARATOR\n    // It's named internal to allow making it public from the contract that uses it by creating a simple view function\n    // with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() public view returns (bytes32) {\n        uint256 chainId;\n        assembly {\n            chainId := chainid()\n        }\n        return chainId == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(chainId);\n    }\n\n    function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {\n        digest = keccak256(abi.encodePacked(EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA, DOMAIN_SEPARATOR(), dataHash));\n    }\n}\n\n// File @boringcrypto/boring-solidity/contracts/ERC20.sol@v1.2.0\n// License-Identifier: MIT\n\n// solhint-disable no-inline-assembly\n// solhint-disable not-rely-on-time\n\n// Data part taken out for building of contracts that receive delegate calls\ncontract ERC20Data {\n    /// @notice owner > balance mapping.\n    mapping(address => uint256) public balanceOf;\n    /// @notice owner > spender > allowance mapping.\n    mapping(address => mapping(address => uint256)) public allowance;\n    /// @notice owner > nonce mapping. Used in `permit`.\n    mapping(address => uint256) public nonces;\n}\n\ncontract ERC20 is ERC20Data, Domain {\n    event Transfer(address indexed _from, address indexed _to, uint256 _value);\n    event Approval(address indexed _owner, address indexed _spender, uint256 _value);\n\n    /// @notice Transfers `amount` tokens from `msg.sender` to `to`.\n    /// @param to The address to move the tokens.\n    /// @param amount of the tokens to move.\n    /// @return (bool) Returns True if succeeded.\n    function transfer(address to, uint256 amount) public returns (bool) {\n        // If `amount` is 0, or `msg.sender` is `to` nothing happens\n        if (amount != 0) {\n            uint256 srcBalance = balanceOf[msg.sender];\n            require(srcBalance >= amount, \"ERC20: balance too low\");\n            if (msg.sender != to) {\n                require(to != address(0), \"ERC20: no zero address\"); // Moved down so low balance calls safe some gas\n\n                balanceOf[msg.sender] = srcBalance - amount; // Underflow is checked\n                balanceOf[to] += amount; // Can't overflow because totalSupply would be greater than 2^256-1\n            }\n        }\n        emit Transfer(msg.sender, to, amount);\n        return true;\n    }\n\n    /// @notice Transfers `amount` tokens from `from` to `to`. Caller needs approval for `from`.\n    /// @param from Address to draw tokens from.\n    /// @param to The address to move the tokens.\n    /// @param amount The token amount to move.\n    /// @return (bool) Returns True if succeeded.\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public returns (bool) {\n        // If `amount` is 0, or `from` is `to` nothing happens\n        if (amount != 0) {\n            uint256 srcBalance = balanceOf[from];\n            require(srcBalance >= amount, \"ERC20: balance too low\");\n\n            if (from != to) {\n                uint256 spenderAllowance = allowance[from][msg.sender];\n                // If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).\n                if (spenderAllowance != type(uint256).max) {\n                    require(spenderAllowance >= amount, \"ERC20: allowance too low\");\n                    allowance[from][msg.sender] = spenderAllowance - amount; // Underflow is checked\n                }\n                require(to != address(0), \"ERC20: no zero address\"); // Moved down so other failed calls safe some gas\n\n                balanceOf[from] = srcBalance - amount; // Underflow is checked\n                balanceOf[to] += amount; // Can't overflow because totalSupply would be greater than 2^256-1\n            }\n        }\n        emit Transfer(from, to, amount);\n        return true;\n    }\n\n    /// @notice Approves `amount` from sender to be spend by `spender`.\n    /// @param spender Address of the party that can draw from msg.sender's account.\n    /// @param amount The maximum collective amount that `spender` can draw.\n    /// @return (bool) Returns True if approved.\n    function approve(address spender, uint256 amount) public returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        emit Approval(msg.sender, spender, amount);\n        return true;\n    }\n\n    // keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n    bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;\n\n    /// @notice Approves `value` from `owner_` to be spend by `spender`.\n    /// @param owner_ Address of the owner.\n    /// @param spender The address of the spender that gets approved to draw from `owner_`.\n    /// @param value The maximum collective amount that `spender` can draw.\n    /// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).\n    function permit(\n        address owner_,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        require(owner_ != address(0), \"ERC20: Owner cannot be 0\");\n        require(block.timestamp < deadline, \"ERC20: Expired\");\n        require(\n            ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) ==\n                owner_,\n            \"ERC20: Invalid Signature\"\n        );\n        allowance[owner_][spender] = value;\n        emit Approval(owner_, spender, value);\n    }\n}\n\n// File @boringcrypto/boring-solidity/contracts/interfaces/IMasterContract.sol@v1.2.0\n// License-Identifier: MIT\n\ninterface IMasterContract {\n    /// @notice Init function that gets called from `BoringFactory.deploy`.\n    /// Also kown as the constructor for cloned contracts.\n    /// Any ETH send to `BoringFactory.deploy` ends up here.\n    /// @param data Can be abi encoded arguments or anything else.\n    function init(bytes calldata data) external payable;\n}\n\n// File @boringcrypto/boring-solidity/contracts/libraries/BoringRebase.sol@v1.2.0\n// License-Identifier: MIT\n\nstruct Rebase {\n    uint128 elastic;\n    uint128 base;\n}\n\n/// @notice A rebasing library using overflow-/underflow-safe math.\nlibrary RebaseLibrary {\n    using BoringMath for uint256;\n    using BoringMath128 for uint128;\n\n    /// @notice Calculates the base value in relationship to `elastic` and `total`.\n    function toBase(\n        Rebase memory total,\n        uint256 elastic,\n        bool roundUp\n    ) internal pure returns (uint256 base) {\n        if (total.elastic == 0) {\n            base = elastic;\n        } else {\n            base = elastic.mul(total.base) / total.elastic;\n            if (roundUp && base.mul(total.elastic) / total.base < elastic) {\n                base = base.add(1);\n            }\n        }\n    }\n\n    /// @notice Calculates the elastic value in relationship to `base` and `total`.\n    function toElastic(\n        Rebase memory total,\n        uint256 base,\n        bool roundUp\n    ) internal pure returns (uint256 elastic) {\n        if (total.base == 0) {\n            elastic = base;\n        } else {\n            elastic = base.mul(total.elastic) / total.base;\n            if (roundUp && elastic.mul(total.base) / total.elastic < base) {\n                elastic = elastic.add(1);\n            }\n        }\n    }\n\n    /// @notice Add `elastic` to `total` and doubles `total.base`.\n    /// @return (Rebase) The new total.\n    /// @return base in relationship to `elastic`.\n    function add(\n        Rebase memory total,\n        uint256 elastic,\n        bool roundUp\n    ) internal pure returns (Rebase memory, uint256 base) {\n        base = toBase(total, elastic, roundUp);\n        total.elastic = total.elastic.add(elastic.to128());\n        total.base = total.base.add(base.to128());\n        return (total, base);\n    }\n\n    /// @notice Sub `base` from `total` and update `total.elastic`.\n    /// @return (Rebase) The new total.\n    /// @return elastic in relationship to `base`.\n    function sub(\n        Rebase memory total,\n        uint256 base,\n        bool roundUp\n    ) internal pure returns (Rebase memory, uint256 elastic) {\n        elastic = toElastic(total, base, roundUp);\n        total.elastic = total.elastic.sub(elastic.to128());\n        total.base = total.base.sub(base.to128());\n        return (total, elastic);\n    }\n\n    /// @notice Add `elastic` and `base` to `total`.\n    function add(\n        Rebase memory total,\n        uint256 elastic,\n        uint256 base\n    ) internal pure returns (Rebase memory) {\n        total.elastic = total.elastic.add(elastic.to128());\n        total.base = total.base.add(base.to128());\n        return total;\n    }\n\n    /// @notice Subtract `elastic` and `base` to `total`.\n    function sub(\n        Rebase memory total,\n        uint256 elastic,\n        uint256 base\n    ) internal pure returns (Rebase memory) {\n        total.elastic = total.elastic.sub(elastic.to128());\n        total.base = total.base.sub(base.to128());\n        return total;\n    }\n}\n\n// File @boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol@v1.2.0\n// License-Identifier: MIT\n\ninterface IERC20 {\n    function totalSupply() external view returns (uint256);\n\n    function balanceOf(address account) external view returns (uint256);\n\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /// @notice EIP 2612\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n}\n\n// File @boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol@v1.2.0\n// License-Identifier: MIT\n\nlibrary BoringERC20 {\n    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()\n    bytes4 private constant SIG_NAME = 0x06fdde03; // name()\n    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()\n    bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)\n    bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)\n\n    function returnDataToString(bytes memory data) internal pure returns (string memory) {\n        if (data.length >= 64) {\n            return abi.decode(data, (string));\n        } else if (data.length == 32) {\n            uint8 i = 0;\n            while (i < 32 && data[i] != 0) {\n                i++;\n            }\n            bytes memory bytesArray = new bytes(i);\n            for (i = 0; i < 32 && data[i] != 0; i++) {\n                bytesArray[i] = data[i];\n            }\n            return string(bytesArray);\n        } else {\n            return \"???\";\n        }\n    }\n\n    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (string) Token symbol.\n    function safeSymbol(IERC20 token) internal view returns (string memory) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));\n        return success ? returnDataToString(data) : \"???\";\n    }\n\n    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (string) Token name.\n    function safeName(IERC20 token) internal view returns (string memory) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));\n        return success ? returnDataToString(data) : \"???\";\n    }\n\n    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (uint8) Token decimals.\n    function safeDecimals(IERC20 token) internal view returns (uint8) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));\n        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;\n    }\n}\n\n// File @sushiswap/bentobox-sdk/contracts/IBatchFlashBorrower.sol@v1.0.1\n// License-Identifier: MIT\n\ninterface IBatchFlashBorrower {\n    function onBatchFlashLoan(\n        address sender,\n        IERC20[] calldata tokens,\n        uint256[] calldata amounts,\n        uint256[] calldata fees,\n        bytes calldata data\n    ) external;\n}\n\n// File @sushiswap/bentobox-sdk/contracts/IFlashBorrower.sol@v1.0.1\n// License-Identifier: MIT\n\ninterface IFlashBorrower {\n    function onFlashLoan(\n        address sender,\n        IERC20 token,\n        uint256 amount,\n        uint256 fee,\n        bytes calldata data\n    ) external;\n}\n\n// File @sushiswap/bentobox-sdk/contracts/IStrategy.sol@v1.0.1\n// License-Identifier: MIT\n\ninterface IStrategy {\n    // Send the assets to the Strategy and call skim to invest them\n    function skim(uint256 amount) external;\n\n    // Harvest any profits made converted to the asset and pass them to the caller\n    function harvest(uint256 balance, address sender) external returns (int256 amountAdded);\n\n    // Withdraw assets. The returned amount can differ from the requested amount due to rounding.\n    // The actualAmount should be very close to the amount. The difference should NOT be used to report a loss. That's what harvest is for.\n    function withdraw(uint256 amount) external returns (uint256 actualAmount);\n\n    // Withdraw all assets in the safest way possible. This shouldn't fail.\n    function exit(uint256 balance) external returns (int256 amountAdded);\n}\n\n// File @sushiswap/bentobox-sdk/contracts/IBentoBoxV1.sol@v1.0.1\n// License-Identifier: MIT\n\ninterface IBentoBoxV1 {\n    event LogDeploy(address indexed masterContract, bytes data, address indexed cloneAddress);\n    event LogDeposit(address indexed token, address indexed from, address indexed to, uint256 amount, uint256 share);\n    event LogFlashLoan(address indexed borrower, address indexed token, uint256 amount, uint256 feeAmount, address indexed receiver);\n    event LogRegisterProtocol(address indexed protocol);\n    event LogSetMasterContractApproval(address indexed masterContract, address indexed user, bool approved);\n    event LogStrategyDivest(address indexed token, uint256 amount);\n    event LogStrategyInvest(address indexed token, uint256 amount);\n    event LogStrategyLoss(address indexed token, uint256 amount);\n    event LogStrategyProfit(address indexed token, uint256 amount);\n    event LogStrategyQueued(address indexed token, address indexed strategy);\n    event LogStrategySet(address indexed token, address indexed strategy);\n    event LogStrategyTargetPercentage(address indexed token, uint256 targetPercentage);\n    event LogTransfer(address indexed token, address indexed from, address indexed to, uint256 share);\n    event LogWhiteListMasterContract(address indexed masterContract, bool approved);\n    event LogWithdraw(address indexed token, address indexed from, address indexed to, uint256 amount, uint256 share);\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    function balanceOf(IERC20, address) external view returns (uint256);\n\n    function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results);\n\n    function batchFlashLoan(\n        IBatchFlashBorrower borrower,\n        address[] calldata receivers,\n        IERC20[] calldata tokens,\n        uint256[] calldata amounts,\n        bytes calldata data\n    ) external;\n\n    function claimOwnership() external;\n\n    function deploy(\n        address masterContract,\n        bytes calldata data,\n        bool useCreate2\n    ) external payable;\n\n    function deposit(\n        IERC20 token_,\n        address from,\n        address to,\n        uint256 amount,\n        uint256 share\n    ) external payable returns (uint256 amountOut, uint256 shareOut);\n\n    function flashLoan(\n        IFlashBorrower borrower,\n        address receiver,\n        IERC20 token,\n        uint256 amount,\n        bytes calldata data\n    ) external;\n\n    function harvest(\n        IERC20 token,\n        bool balance,\n        uint256 maxChangeAmount\n    ) external;\n\n    function masterContractApproved(address, address) external view returns (bool);\n\n    function masterContractOf(address) external view returns (address);\n\n    function nonces(address) external view returns (uint256);\n\n    function owner() external view returns (address);\n\n    function pendingOwner() external view returns (address);\n\n    function pendingStrategy(IERC20) external view returns (IStrategy);\n\n    function permitToken(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 amount,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    function registerProtocol() external;\n\n    function setMasterContractApproval(\n        address user,\n        address masterContract,\n        bool approved,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    function setStrategy(IERC20 token, IStrategy newStrategy) external;\n\n    function setStrategyTargetPercentage(IERC20 token, uint64 targetPercentage_) external;\n\n    function strategy(IERC20) external view returns (IStrategy);\n\n    function strategyData(IERC20)\n        external\n        view\n        returns (\n            uint64 strategyStartDate,\n            uint64 targetPercentage,\n            uint128 balance\n        );\n\n    function toAmount(\n        IERC20 token,\n        uint256 share,\n        bool roundUp\n    ) external view returns (uint256 amount);\n\n    function toShare(\n        IERC20 token,\n        uint256 amount,\n        bool roundUp\n    ) external view returns (uint256 share);\n\n    function totals(IERC20) external view returns (Rebase memory totals_);\n\n    function transfer(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 share\n    ) external;\n\n    function transferMultiple(\n        IERC20 token,\n        address from,\n        address[] calldata tos,\n        uint256[] calldata shares\n    ) external;\n\n    function transferOwnership(\n        address newOwner,\n        bool direct,\n        bool renounce\n    ) external;\n\n    function whitelistMasterContract(address masterContract, bool approved) external;\n\n    function whitelistedMasterContracts(address) external view returns (bool);\n\n    function withdraw(\n        IERC20 token_,\n        address from,\n        address to,\n        uint256 amount,\n        uint256 share\n    ) external returns (uint256 amountOut, uint256 shareOut);\n}\n\n// File contracts/interfaces/IOracle.sol\n// License-Identifier: MIT\n\ninterface IOracle {\n    /// @notice Get the latest exchange rate.\n    /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.\n    /// For example:\n    /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));\n    /// @return success if no valid (recent) rate is available, return false else true.\n    /// @return rate The rate of the requested asset / pair / pool.\n    function get(bytes calldata data) external returns (bool success, uint256 rate);\n\n    /// @notice Check the last exchange rate without any state changes.\n    /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.\n    /// For example:\n    /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));\n    /// @return success if no valid (recent) rate is available, return false else true.\n    /// @return rate The rate of the requested asset / pair / pool.\n    function peek(bytes calldata data) external view returns (bool success, uint256 rate);\n\n    /// @notice Check the current spot exchange rate without any state changes. For oracles like TWAP this will be different from peek().\n    /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.\n    /// For example:\n    /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));\n    /// @return rate The rate of the requested asset / pair / pool.\n    function peekSpot(bytes calldata data) external view returns (uint256 rate);\n\n    /// @notice Returns a human readable (short) name about this oracle.\n    /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.\n    /// For example:\n    /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));\n    /// @return (string) A human readable symbol name about this oracle.\n    function symbol(bytes calldata data) external view returns (string memory);\n\n    /// @notice Returns a human readable name about this oracle.\n    /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.\n    /// For example:\n    /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));\n    /// @return (string) A human readable name about this oracle.\n    function name(bytes calldata data) external view returns (string memory);\n}\n\n// File contracts/interfaces/ISwapper.sol\n// License-Identifier: MIT\n\ninterface ISwapper {\n    /// @notice Withdraws 'amountFrom' of token 'from' from the BentoBox account for this swapper.\n    /// Swaps it for at least 'amountToMin' of token 'to'.\n    /// Transfers the swapped tokens of 'to' into the BentoBox using a plain ERC20 transfer.\n    /// Returns the amount of tokens 'to' transferred to BentoBox.\n    /// (The BentoBox skim function will be used by the caller to get the swapped funds).\n    function swap(\n        IERC20 fromToken,\n        IERC20 toToken,\n        address recipient,\n        uint256 shareToMin,\n        uint256 shareFrom\n    ) external returns (uint256 extraShare, uint256 shareReturned);\n\n    /// @notice Calculates the amount of token 'from' needed to complete the swap (amountFrom),\n    /// this should be less than or equal to amountFromMax.\n    /// Withdraws 'amountFrom' of token 'from' from the BentoBox account for this swapper.\n    /// Swaps it for exactly 'exactAmountTo' of token 'to'.\n    /// Transfers the swapped tokens of 'to' into the BentoBox using a plain ERC20 transfer.\n    /// Transfers allocated, but unused 'from' tokens within the BentoBox to 'refundTo' (amountFromMax - amountFrom).\n    /// Returns the amount of 'from' tokens withdrawn from BentoBox (amountFrom).\n    /// (The BentoBox skim function will be used by the caller to get the swapped funds).\n    function swapExact(\n        IERC20 fromToken,\n        IERC20 toToken,\n        address recipient,\n        address refundTo,\n        uint256 shareFromSupplied,\n        uint256 shareToExact\n    ) external returns (uint256 shareUsed, uint256 shareReturned);\n}\n\n// File contracts/KashiPair.sol\n// License-Identifier: UNLICENSED\n// Kashi Lending Medium Risk\n\n/// @title KashiPair\n/// @dev This contract allows contract calls to any contract (except BentoBox)\n/// from arbitrary callers thus, don't trust calls from this contract in any circumstances.\ncontract KashiPairMediumRiskV1 is ERC20, BoringOwnable, IMasterContract {\n    using BoringMath for uint256;\n    using BoringMath128 for uint128;\n    using RebaseLibrary for Rebase;\n    using BoringERC20 for IERC20;\n\n    event LogExchangeRate(uint256 rate);\n    event LogAccrue(uint256 accruedAmount, uint256 feeFraction, uint64 rate, uint256 utilization);\n    event LogAddCollateral(address indexed from, address indexed to, uint256 share);\n    event LogAddAsset(address indexed from, address indexed to, uint256 share, uint256 fraction);\n    event LogRemoveCollateral(address indexed from, address indexed to, uint256 share);\n    event LogRemoveAsset(address indexed from, address indexed to, uint256 share, uint256 fraction);\n    event LogBorrow(address indexed from, address indexed to, uint256 amount, uint256 feeAmount, uint256 part);\n    event LogRepay(address indexed from, address indexed to, uint256 amount, uint256 part);\n    event LogFeeTo(address indexed newFeeTo);\n    event LogWithdrawFees(address indexed feeTo, uint256 feesEarnedFraction);\n\n    // Immutables (for MasterContract and all clones)\n    IBentoBoxV1 public immutable bentoBox;\n    KashiPairMediumRiskV1 public immutable masterContract;\n\n    // MasterContract variables\n    address public feeTo;\n    mapping(ISwapper => bool) public swappers;\n\n    // Per clone variables\n    // Clone init settings\n    IERC20 public collateral;\n    IERC20 public asset;\n    IOracle public oracle;\n    bytes public oracleData;\n\n    // Total amounts\n    uint256 public totalCollateralShare; // Total collateral supplied\n    Rebase public totalAsset; // elastic = BentoBox shares held by the KashiPair, base = Total fractions held by asset suppliers\n    Rebase public totalBorrow; // elastic = Total token amount to be repayed by borrowers, base = Total parts of the debt held by borrowers\n\n    // User balances\n    mapping(address => uint256) public userCollateralShare;\n    // userAssetFraction is called balanceOf for ERC20 compatibility (it's in ERC20.sol)\n    mapping(address => uint256) public userBorrowPart;\n\n    /// @notice Exchange and interest rate tracking.\n    /// This is 'cached' here because calls to Oracles can be very expensive.\n    uint256 public exchangeRate;\n\n    struct AccrueInfo {\n        uint64 interestPerSecond;\n        uint64 lastAccrued;\n        uint128 feesEarnedFraction;\n    }\n\n    AccrueInfo public accrueInfo;\n\n    // ERC20 'variables'\n    function symbol() external view returns (string memory) {\n        return string(abi.encodePacked(\"km\", collateral.safeSymbol(), \"/\", asset.safeSymbol(), \"-\", oracle.symbol(oracleData)));\n    }\n\n    function name() external view returns (string memory) {\n        return string(abi.encodePacked(\"Kashi Medium Risk \", collateral.safeName(), \"/\", asset.safeName(), \"-\", oracle.name(oracleData)));\n    }\n\n    function decimals() external view returns (uint8) {\n        return asset.safeDecimals();\n    }\n\n    // totalSupply for ERC20 compatibility\n    function totalSupply() public view returns (uint256) {\n        return totalAsset.base;\n    }\n\n    // Settings for the Medium Risk KashiPair\n    uint256 private constant CLOSED_COLLATERIZATION_RATE = 75000; // 75%\n    uint256 private constant OPEN_COLLATERIZATION_RATE = 77000; // 77%\n    uint256 private constant COLLATERIZATION_RATE_PRECISION = 1e5; // Must be less than EXCHANGE_RATE_PRECISION (due to optimization in math)\n    uint256 private constant MINIMUM_TARGET_UTILIZATION = 7e17; // 70%\n    uint256 private constant MAXIMUM_TARGET_UTILIZATION = 8e17; // 80%\n    uint256 private constant UTILIZATION_PRECISION = 1e18;\n    uint256 private constant FULL_UTILIZATION = 1e18;\n    uint256 private constant FULL_UTILIZATION_MINUS_MAX = FULL_UTILIZATION - MAXIMUM_TARGET_UTILIZATION;\n    uint256 private constant FACTOR_PRECISION = 1e18;\n\n    uint64 private constant STARTING_INTEREST_PER_SECOND = 317097920; // approx 1% APR\n    uint64 private constant MINIMUM_INTEREST_PER_SECOND = 79274480; // approx 0.25% APR\n    uint64 private constant MAXIMUM_INTEREST_PER_SECOND = 317097920000; // approx 1000% APR\n    uint256 private constant INTEREST_ELASTICITY = 28800e36; // Half or double in 28800 seconds (8 hours) if linear\n\n    uint256 private constant EXCHANGE_RATE_PRECISION = 1e18;\n\n    uint256 private constant LIQUIDATION_MULTIPLIER = 112000; // add 12%\n    uint256 private constant LIQUIDATION_MULTIPLIER_PRECISION = 1e5;\n\n    // Fees\n    uint256 private constant PROTOCOL_FEE = 10000; // 10%\n    uint256 private constant PROTOCOL_FEE_DIVISOR = 1e5;\n    uint256 private constant BORROW_OPENING_FEE = 50; // 0.05%\n    uint256 private constant BORROW_OPENING_FEE_PRECISION = 1e5;\n\n    /// @notice The constructor is only used for the initial master contract. Subsequent clones are initialised via `init`.\n    constructor(IBentoBoxV1 bentoBox_) public {\n        bentoBox = bentoBox_;\n        masterContract = this;\n    }\n\n    /// @notice Serves as the constructor for clones, as clones can't have a regular constructor\n    /// @dev `data` is abi encoded in the format: (IERC20 collateral, IERC20 asset, IOracle oracle, bytes oracleData)\n    function init(bytes calldata data) public payable override {\n        require(address(collateral) == address(0), \"KashiPair: already initialized\");\n        (collateral, asset, oracle, oracleData) = abi.decode(data, (IERC20, IERC20, IOracle, bytes));\n        require(address(collateral) != address(0), \"KashiPair: bad pair\");\n\n        accrueInfo.interestPerSecond = uint64(STARTING_INTEREST_PER_SECOND); // 1% APR, with 1e18 being 100%\n    }\n\n    /// @notice Accrues the interest on the borrowed tokens and handles the accumulation of fees.\n    function accrue() public {\n        AccrueInfo memory _accrueInfo = accrueInfo;\n        // Number of seconds since accrue was called\n        uint256 elapsedTime = block.timestamp - _accrueInfo.lastAccrued;\n        if (elapsedTime == 0) {\n            return;\n        }\n        _accrueInfo.lastAccrued = uint64(block.timestamp);\n\n        Rebase memory _totalBorrow = totalBorrow;\n        if (_totalBorrow.base == 0) {\n            // If there are no borrows, reset the interest rate\n            if (_accrueInfo.interestPerSecond != STARTING_INTEREST_PER_SECOND) {\n                _accrueInfo.interestPerSecond = STARTING_INTEREST_PER_SECOND;\n                emit LogAccrue(0, 0, STARTING_INTEREST_PER_SECOND, 0);\n            }\n            accrueInfo = _accrueInfo;\n            return;\n        }\n\n        uint256 extraAmount = 0;\n        uint256 feeFraction = 0;\n        Rebase memory _totalAsset = totalAsset;\n\n        // Accrue interest\n        extraAmount = uint256(_totalBorrow.elastic).mul(_accrueInfo.interestPerSecond).mul(elapsedTime) / 1e18;\n        _totalBorrow.elastic = _totalBorrow.elastic.add(extraAmount.to128());\n        uint256 fullAssetAmount = bentoBox.toAmount(asset, _totalAsset.elastic, false).add(_totalBorrow.elastic);\n\n        uint256 feeAmount = extraAmount.mul(PROTOCOL_FEE) / PROTOCOL_FEE_DIVISOR; // % of interest paid goes to fee\n        feeFraction = feeAmount.mul(_totalAsset.base) / fullAssetAmount;\n        _accrueInfo.feesEarnedFraction = _accrueInfo.feesEarnedFraction.add(feeFraction.to128());\n        totalAsset.base = _totalAsset.base.add(feeFraction.to128());\n        totalBorrow = _totalBorrow;\n\n        // Update interest rate\n        uint256 utilization = uint256(_totalBorrow.elastic).mul(UTILIZATION_PRECISION) / fullAssetAmount;\n        if (utilization < MINIMUM_TARGET_UTILIZATION) {\n            uint256 underFactor = MINIMUM_TARGET_UTILIZATION.sub(utilization).mul(FACTOR_PRECISION) / MINIMUM_TARGET_UTILIZATION;\n            uint256 scale = INTEREST_ELASTICITY.add(underFactor.mul(underFactor).mul(elapsedTime));\n            _accrueInfo.interestPerSecond = uint64(uint256(_accrueInfo.interestPerSecond).mul(INTEREST_ELASTICITY) / scale);\n\n            if (_accrueInfo.interestPerSecond < MINIMUM_INTEREST_PER_SECOND) {\n                _accrueInfo.interestPerSecond = MINIMUM_INTEREST_PER_SECOND; // 0.25% APR minimum\n            }\n        } else if (utilization > MAXIMUM_TARGET_UTILIZATION) {\n            uint256 overFactor = utilization.sub(MAXIMUM_TARGET_UTILIZATION).mul(FACTOR_PRECISION) / FULL_UTILIZATION_MINUS_MAX;\n            uint256 scale = INTEREST_ELASTICITY.add(overFactor.mul(overFactor).mul(elapsedTime));\n            uint256 newInterestPerSecond = uint256(_accrueInfo.interestPerSecond).mul(scale) / INTEREST_ELASTICITY;\n            if (newInterestPerSecond > MAXIMUM_INTEREST_PER_SECOND) {\n                newInterestPerSecond = MAXIMUM_INTEREST_PER_SECOND; // 1000% APR maximum\n            }\n            _accrueInfo.interestPerSecond = uint64(newInterestPerSecond);\n        }\n\n        emit LogAccrue(extraAmount, feeFraction, _accrueInfo.interestPerSecond, utilization);\n        accrueInfo = _accrueInfo;\n    }\n\n    /// @notice Concrete implementation of `isSolvent`. Includes a third parameter to allow caching `exchangeRate`.\n    /// @param _exchangeRate The exchange rate. Used to cache the `exchangeRate` between calls.\n    function _isSolvent(\n        address user,\n        bool open,\n        uint256 _exchangeRate\n    ) internal view returns (bool) {\n        // accrue must have already been called!\n        uint256 borrowPart = userBorrowPart[user];\n        if (borrowPart == 0) return true;\n        uint256 collateralShare = userCollateralShare[user];\n        if (collateralShare == 0) return false;\n\n        Rebase memory _totalBorrow = totalBorrow;\n\n        return\n            bentoBox.toAmount(\n                collateral,\n                collateralShare.mul(EXCHANGE_RATE_PRECISION / COLLATERIZATION_RATE_PRECISION).mul(\n                    open ? OPEN_COLLATERIZATION_RATE : CLOSED_COLLATERIZATION_RATE\n                ),\n                false\n            ) >=\n            // Moved exchangeRate here instead of dividing the other side to preserve more precision\n            borrowPart.mul(_totalBorrow.elastic).mul(_exchangeRate) / _totalBorrow.base;\n    }\n\n    /// @dev Checks if the user is solvent in the closed liquidation case at the end of the function body.\n    modifier solvent() {\n        _;\n        require(_isSolvent(msg.sender, false, exchangeRate), \"KashiPair: user insolvent\");\n    }\n\n    /// @notice Gets the exchange rate. I.e how much collateral to buy 1e18 asset.\n    /// This function is supposed to be invoked if needed because Oracle queries can be expensive.\n    /// @return updated True if `exchangeRate` was updated.\n    /// @return rate The new exchange rate.\n    function updateExchangeRate() public returns (bool updated, uint256 rate) {\n        (updated, rate) = oracle.get(oracleData);\n\n        if (updated) {\n            exchangeRate = rate;\n            emit LogExchangeRate(rate);\n        } else {\n            // Return the old rate if fetching wasn't successful\n            rate = exchangeRate;\n        }\n    }\n\n    /// @dev Helper function to move tokens.\n    /// @param token The ERC-20 token.\n    /// @param share The amount in shares to add.\n    /// @param total Grand total amount to deduct from this contract's balance. Only applicable if `skim` is True.\n    /// Only used for accounting checks.\n    /// @param skim If True, only does a balance check on this contract.\n    /// False if tokens from msg.sender in `bentoBox` should be transferred.\n    function _addTokens(\n        IERC20 token,\n        uint256 share,\n        uint256 total,\n        bool skim\n    ) internal {\n        if (skim) {\n            require(share <= bentoBox.balanceOf(token, address(this)).sub(total), \"KashiPair: Skim too much\");\n        } else {\n            bentoBox.transfer(token, msg.sender, address(this), share);\n        }\n    }\n\n    /// @notice Adds `collateral` from msg.sender to the account `to`.\n    /// @param to The receiver of the tokens.\n    /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.\n    /// False if tokens from msg.sender in `bentoBox` should be transferred.\n    /// @param share The amount of shares to add for `to`.\n    function addCollateral(\n        address to,\n        bool skim,\n        uint256 share\n    ) public {\n        userCollateralShare[to] = userCollateralShare[to].add(share);\n        uint256 oldTotalCollateralShare = totalCollateralShare;\n        totalCollateralShare = oldTotalCollateralShare.add(share);\n        _addTokens(collateral, share, oldTotalCollateralShare, skim);\n        emit LogAddCollateral(skim ? address(bentoBox) : msg.sender, to, share);\n    }\n\n    /// @dev Concrete implementation of `removeCollateral`.\n    function _removeCollateral(address to, uint256 share) internal {\n        userCollateralShare[msg.sender] = userCollateralShare[msg.sender].sub(share);\n        totalCollateralShare = totalCollateralShare.sub(share);\n        emit LogRemoveCollateral(msg.sender, to, share);\n        bentoBox.transfer(collateral, address(this), to, share);\n    }\n\n    /// @notice Removes `share` amount of collateral and transfers it to `to`.\n    /// @param to The receiver of the shares.\n    /// @param share Amount of shares to remove.\n    function removeCollateral(address to, uint256 share) public solvent {\n        // accrue must be called because we check solvency\n        accrue();\n        _removeCollateral(to, share);\n    }\n\n    /// @dev Concrete implementation of `addAsset`.\n    function _addAsset(\n        address to,\n        bool skim,\n        uint256 share\n    ) internal returns (uint256 fraction) {\n        Rebase memory _totalAsset = totalAsset;\n        uint256 totalAssetShare = _totalAsset.elastic;\n        uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic, true);\n        fraction = allShare == 0 ? share : share.mul(_totalAsset.base) / allShare;\n        if (_totalAsset.base.add(fraction.to128()) < 1000) {\n            return 0;\n        }\n        totalAsset = _totalAsset.add(share, fraction);\n        balanceOf[to] = balanceOf[to].add(fraction);\n        emit Transfer(address(0), to, fraction);\n        _addTokens(asset, share, totalAssetShare, skim);\n        emit LogAddAsset(skim ? address(bentoBox) : msg.sender, to, share, fraction);\n    }\n\n    /// @notice Adds assets to the lending pair.\n    /// @param to The address of the user to receive the assets.\n    /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.\n    /// False if tokens from msg.sender in `bentoBox` should be transferred.\n    /// @param share The amount of shares to add.\n    /// @return fraction Total fractions added.\n    function addAsset(\n        address to,\n        bool skim,\n        uint256 share\n    ) public returns (uint256 fraction) {\n        accrue();\n        fraction = _addAsset(to, skim, share);\n    }\n\n    /// @dev Concrete implementation of `removeAsset`.\n    function _removeAsset(address to, uint256 fraction) internal returns (uint256 share) {\n        Rebase memory _totalAsset = totalAsset;\n        uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic, true);\n        share = fraction.mul(allShare) / _totalAsset.base;\n        balanceOf[msg.sender] = balanceOf[msg.sender].sub(fraction);\n        emit Transfer(msg.sender, address(0), fraction);\n        _totalAsset.elastic = _totalAsset.elastic.sub(share.to128());\n        _totalAsset.base = _totalAsset.base.sub(fraction.to128());\n        require(_totalAsset.base >= 1000, \"Kashi: below minimum\");\n        totalAsset = _totalAsset;\n        emit LogRemoveAsset(msg.sender, to, share, fraction);\n        bentoBox.transfer(asset, address(this), to, share);\n    }\n\n    /// @notice Removes an asset from msg.sender and transfers it to `to`.\n    /// @param to The user that receives the removed assets.\n    /// @param fraction The amount/fraction of assets held to remove.\n    /// @return share The amount of shares transferred to `to`.\n    function removeAsset(address to, uint256 fraction) public returns (uint256 share) {\n        accrue();\n        share = _removeAsset(to, fraction);\n    }\n\n    /// @dev Concrete implementation of `borrow`.\n    function _borrow(address to, uint256 amount) internal returns (uint256 part, uint256 share) {\n        uint256 feeAmount = amount.mul(BORROW_OPENING_FEE) / BORROW_OPENING_FEE_PRECISION; // A flat % fee is charged for any borrow\n\n        (totalBorrow, part) = totalBorrow.add(amount.add(feeAmount), true);\n        userBorrowPart[msg.sender] = userBorrowPart[msg.sender].add(part);\n        emit LogBorrow(msg.sender, to, amount, feeAmount, part);\n\n        share = bentoBox.toShare(asset, amount, false);\n        Rebase memory _totalAsset = totalAsset;\n        require(_totalAsset.base >= 1000, \"Kashi: below minimum\");\n        _totalAsset.elastic = _totalAsset.elastic.sub(share.to128());\n        totalAsset = _totalAsset;\n        bentoBox.transfer(asset, address(this), to, share);\n    }\n\n    /// @notice Sender borrows `amount` and transfers it to `to`.\n    /// @return part Total part of the debt held by borrowers.\n    /// @return share Total amount in shares borrowed.\n    function borrow(address to, uint256 amount) public solvent returns (uint256 part, uint256 share) {\n        accrue();\n        (part, share) = _borrow(to, amount);\n    }\n\n    /// @dev Concrete implementation of `repay`.\n    function _repay(\n        address to,\n        bool skim,\n        uint256 part\n    ) internal returns (uint256 amount) {\n        (totalBorrow, amount) = totalBorrow.sub(part, true);\n        userBorrowPart[to] = userBorrowPart[to].sub(part);\n\n        uint256 share = bentoBox.toShare(asset, amount, true);\n        uint128 totalShare = totalAsset.elastic;\n        _addTokens(asset, share, uint256(totalShare), skim);\n        totalAsset.elastic = totalShare.add(share.to128());\n        emit LogRepay(skim ? address(bentoBox) : msg.sender, to, amount, part);\n    }\n\n    /// @notice Repays a loan.\n    /// @param to Address of the user this payment should go.\n    /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.\n    /// False if tokens from msg.sender in `bentoBox` should be transferred.\n    /// @param part The amount to repay. See `userBorrowPart`.\n    /// @return amount The total amount repayed.\n    function repay(\n        address to,\n        bool skim,\n        uint256 part\n    ) public returns (uint256 amount) {\n        accrue();\n        amount = _repay(to, skim, part);\n    }\n\n    // Functions that need accrue to be called\n    uint8 internal constant ACTION_ADD_ASSET = 1;\n    uint8 internal constant ACTION_REPAY = 2;\n    uint8 internal constant ACTION_REMOVE_ASSET = 3;\n    uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;\n    uint8 internal constant ACTION_BORROW = 5;\n    uint8 internal constant ACTION_GET_REPAY_SHARE = 6;\n    uint8 internal constant ACTION_GET_REPAY_PART = 7;\n    uint8 internal constant ACTION_ACCRUE = 8;\n\n    // Functions that don't need accrue to be called\n    uint8 internal constant ACTION_ADD_COLLATERAL = 10;\n    uint8 internal constant ACTION_UPDATE_EXCHANGE_RATE = 11;\n\n    // Function on BentoBox\n    uint8 internal constant ACTION_BENTO_DEPOSIT = 20;\n    uint8 internal constant ACTION_BENTO_WITHDRAW = 21;\n    uint8 internal constant ACTION_BENTO_TRANSFER = 22;\n    uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;\n    uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;\n\n    // Any external call (except to BentoBox)\n    uint8 internal constant ACTION_CALL = 30;\n\n    int256 internal constant USE_VALUE1 = -1;\n    int256 internal constant USE_VALUE2 = -2;\n\n    /// @dev Helper function for choosing the correct value (`value1` or `value2`) depending on `inNum`.\n    function _num(\n        int256 inNum,\n        uint256 value1,\n        uint256 value2\n    ) internal pure returns (uint256 outNum) {\n        outNum = inNum >= 0 ? uint256(inNum) : (inNum == USE_VALUE1 ? value1 : value2);\n    }\n\n    /// @dev Helper function for depositing into `bentoBox`.\n    function _bentoDeposit(\n        bytes memory data,\n        uint256 value,\n        uint256 value1,\n        uint256 value2\n    ) internal returns (uint256, uint256) {\n        (IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));\n        amount = int256(_num(amount, value1, value2)); // Done this way to avoid stack too deep errors\n        share = int256(_num(share, value1, value2));\n        return bentoBox.deposit{value: value}(token, msg.sender, to, uint256(amount), uint256(share));\n    }\n\n    /// @dev Helper function to withdraw from the `bentoBox`.\n    function _bentoWithdraw(\n        bytes memory data,\n        uint256 value1,\n        uint256 value2\n    ) internal returns (uint256, uint256) {\n        (IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));\n        return bentoBox.withdraw(token, msg.sender, to, _num(amount, value1, value2), _num(share, value1, value2));\n    }\n\n    /// @dev Helper function to perform a contract call and eventually extracting revert messages on failure.\n    /// Calls to `bentoBox` are not allowed for obvious security reasons.\n    /// This also means that calls made from this contract shall *not* be trusted.\n    function _call(\n        uint256 value,\n        bytes memory data,\n        uint256 value1,\n        uint256 value2\n    ) internal returns (bytes memory, uint8) {\n        (address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) =\n            abi.decode(data, (address, bytes, bool, bool, uint8));\n\n        if (useValue1 && !useValue2) {\n            callData = abi.encodePacked(callData, value1);\n        } else if (!useValue1 && useValue2) {\n            callData = abi.encodePacked(callData, value2);\n        } else if (useValue1 && useValue2) {\n            callData = abi.encodePacked(callData, value1, value2);\n        }\n\n        require(callee != address(bentoBox) && callee != address(this), \"KashiPair: can't call\");\n\n        (bool success, bytes memory returnData) = callee.call{value: value}(callData);\n        require(success, \"KashiPair: call failed\");\n        return (returnData, returnValues);\n    }\n\n    struct CookStatus {\n        bool needsSolvencyCheck;\n        bool hasAccrued;\n    }\n\n    /// @notice Executes a set of actions and allows composability (contract calls) to other contracts.\n    /// @param actions An array with a sequence of actions to execute (see ACTION_ declarations).\n    /// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions.\n    /// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`.\n    /// @param datas A one-to-one mapped array to `actions`. Contains abi encoded data of function arguments.\n    /// @return value1 May contain the first positioned return value of the last executed action (if applicable).\n    /// @return value2 May contain the second positioned return value of the last executed action which returns 2 values (if applicable).\n    function cook(\n        uint8[] calldata actions,\n        uint256[] calldata values,\n        bytes[] calldata datas\n    ) external payable returns (uint256 value1, uint256 value2) {\n        CookStatus memory status;\n        for (uint256 i = 0; i < actions.length; i++) {\n            uint8 action = actions[i];\n            if (!status.hasAccrued && action < 10) {\n                accrue();\n                status.hasAccrued = true;\n            }\n            if (action == ACTION_ADD_COLLATERAL) {\n                (int256 share, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));\n                addCollateral(to, skim, _num(share, value1, value2));\n            } else if (action == ACTION_ADD_ASSET) {\n                (int256 share, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));\n                value1 = _addAsset(to, skim, _num(share, value1, value2));\n            } else if (action == ACTION_REPAY) {\n                (int256 part, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));\n                _repay(to, skim, _num(part, value1, value2));\n            } else if (action == ACTION_REMOVE_ASSET) {\n                (int256 fraction, address to) = abi.decode(datas[i], (int256, address));\n                value1 = _removeAsset(to, _num(fraction, value1, value2));\n            } else if (action == ACTION_REMOVE_COLLATERAL) {\n                (int256 share, address to) = abi.decode(datas[i], (int256, address));\n                _removeCollateral(to, _num(share, value1, value2));\n                status.needsSolvencyCheck = true;\n            } else if (action == ACTION_BORROW) {\n                (int256 amount, address to) = abi.decode(datas[i], (int256, address));\n                (value1, value2) = _borrow(to, _num(amount, value1, value2));\n                status.needsSolvencyCheck = true;\n            } else if (action == ACTION_UPDATE_EXCHANGE_RATE) {\n                (bool must_update, uint256 minRate, uint256 maxRate) = abi.decode(datas[i], (bool, uint256, uint256));\n                (bool updated, uint256 rate) = updateExchangeRate();\n                require((!must_update || updated) && rate > minRate && (maxRate == 0 || rate > maxRate), \"KashiPair: rate not ok\");\n            } else if (action == ACTION_BENTO_SETAPPROVAL) {\n                (address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) =\n                    abi.decode(datas[i], (address, address, bool, uint8, bytes32, bytes32));\n                bentoBox.setMasterContractApproval(user, _masterContract, approved, v, r, s);\n            } else if (action == ACTION_BENTO_DEPOSIT) {\n                (value1, value2) = _bentoDeposit(datas[i], values[i], value1, value2);\n            } else if (action == ACTION_BENTO_WITHDRAW) {\n                (value1, value2) = _bentoWithdraw(datas[i], value1, value2);\n            } else if (action == ACTION_BENTO_TRANSFER) {\n                (IERC20 token, address to, int256 share) = abi.decode(datas[i], (IERC20, address, int256));\n                bentoBox.transfer(token, msg.sender, to, _num(share, value1, value2));\n            } else if (action == ACTION_BENTO_TRANSFER_MULTIPLE) {\n                (IERC20 token, address[] memory tos, uint256[] memory shares) = abi.decode(datas[i], (IERC20, address[], uint256[]));\n                bentoBox.transferMultiple(token, msg.sender, tos, shares);\n            } else if (action == ACTION_CALL) {\n                (bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2);\n\n                if (returnValues == 1) {\n                    (value1) = abi.decode(returnData, (uint256));\n                } else if (returnValues == 2) {\n                    (value1, value2) = abi.decode(returnData, (uint256, uint256));\n                }\n            } else if (action == ACTION_GET_REPAY_SHARE) {\n                int256 part = abi.decode(datas[i], (int256));\n                value1 = bentoBox.toShare(asset, totalBorrow.toElastic(_num(part, value1, value2), true), true);\n            } else if (action == ACTION_GET_REPAY_PART) {\n                int256 amount = abi.decode(datas[i], (int256));\n                value1 = totalBorrow.toBase(_num(amount, value1, value2), false);\n            }\n        }\n\n        if (status.needsSolvencyCheck) {\n            require(_isSolvent(msg.sender, false, exchangeRate), \"KashiPair: user insolvent\");\n        }\n    }\n\n    /// @notice Handles the liquidation of users' balances, once the users' amount of collateral is too low.\n    /// @param users An array of user addresses.\n    /// @param maxBorrowParts A one-to-one mapping to `users`, contains maximum (partial) borrow amounts (to liquidate) of the respective user.\n    /// @param to Address of the receiver in open liquidations if `swapper` is zero.\n    /// @param swapper Contract address of the `ISwapper` implementation. Swappers are restricted for closed liquidations. See `setSwapper`.\n    /// @param open True to perform a open liquidation else False.\n    function liquidate(\n        address[] calldata users,\n        uint256[] calldata maxBorrowParts,\n        address to,\n        ISwapper swapper,\n        bool open\n    ) public {\n        // Oracle can fail but we still need to allow liquidations\n        (, uint256 _exchangeRate) = updateExchangeRate();\n        accrue();\n\n        uint256 allCollateralShare;\n        uint256 allBorrowAmount;\n        uint256 allBorrowPart;\n        Rebase memory _totalBorrow = totalBorrow;\n        Rebase memory bentoBoxTotals = bentoBox.totals(collateral);\n        for (uint256 i = 0; i < users.length; i++) {\n            address user = users[i];\n            if (!_isSolvent(user, open, _exchangeRate)) {\n                uint256 borrowPart;\n                {\n                    uint256 availableBorrowPart = userBorrowPart[user];\n                    borrowPart = maxBorrowParts[i] > availableBorrowPart ? availableBorrowPart : maxBorrowParts[i];\n                    userBorrowPart[user] = availableBorrowPart.sub(borrowPart);\n                }\n                uint256 borrowAmount = _totalBorrow.toElastic(borrowPart, false);\n                uint256 collateralShare =\n                    bentoBoxTotals.toBase(\n                        borrowAmount.mul(LIQUIDATION_MULTIPLIER).mul(_exchangeRate) /\n                            (LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION),\n                        false\n                    );\n\n                userCollateralShare[user] = userCollateralShare[user].sub(collateralShare);\n                emit LogRemoveCollateral(user, swapper == ISwapper(0) ? to : address(swapper), collateralShare);\n                emit LogRepay(swapper == ISwapper(0) ? msg.sender : address(swapper), user, borrowAmount, borrowPart);\n\n                // Keep totals\n                allCollateralShare = allCollateralShare.add(collateralShare);\n                allBorrowAmount = allBorrowAmount.add(borrowAmount);\n                allBorrowPart = allBorrowPart.add(borrowPart);\n            }\n        }\n        require(allBorrowAmount != 0, \"KashiPair: all are solvent\");\n        _totalBorrow.elastic = _totalBorrow.elastic.sub(allBorrowAmount.to128());\n        _totalBorrow.base = _totalBorrow.base.sub(allBorrowPart.to128());\n        totalBorrow = _totalBorrow;\n        totalCollateralShare = totalCollateralShare.sub(allCollateralShare);\n\n        uint256 allBorrowShare = bentoBox.toShare(asset, allBorrowAmount, true);\n\n        if (!open) {\n            // Closed liquidation using a pre-approved swapper for the benefit of the LPs\n            require(masterContract.swappers(swapper), \"KashiPair: Invalid swapper\");\n\n            // Swaps the users' collateral for the borrowed asset\n            bentoBox.transfer(collateral, address(this), address(swapper), allCollateralShare);\n            swapper.swap(collateral, asset, address(this), allBorrowShare, allCollateralShare);\n\n            uint256 returnedShare = bentoBox.balanceOf(asset, address(this)).sub(uint256(totalAsset.elastic));\n            uint256 extraShare = returnedShare.sub(allBorrowShare);\n            uint256 feeShare = extraShare.mul(PROTOCOL_FEE) / PROTOCOL_FEE_DIVISOR; // % of profit goes to fee\n            // solhint-disable-next-line reentrancy\n            bentoBox.transfer(asset, address(this), masterContract.feeTo(), feeShare);\n            totalAsset.elastic = totalAsset.elastic.add(returnedShare.sub(feeShare).to128());\n            emit LogAddAsset(address(swapper), address(this), extraShare.sub(feeShare), 0);\n        } else {\n            // Swap using a swapper freely chosen by the caller\n            // Open (flash) liquidation: get proceeds first and provide the borrow after\n            bentoBox.transfer(collateral, address(this), swapper == ISwapper(0) ? to : address(swapper), allCollateralShare);\n            if (swapper != ISwapper(0)) {\n                swapper.swap(collateral, asset, msg.sender, allBorrowShare, allCollateralShare);\n            }\n\n            bentoBox.transfer(asset, msg.sender, address(this), allBorrowShare);\n            totalAsset.elastic = totalAsset.elastic.add(allBorrowShare.to128());\n        }\n    }\n\n    /// @notice Withdraws the fees accumulated.\n    function withdrawFees() public {\n        accrue();\n        address _feeTo = masterContract.feeTo();\n        uint256 _feesEarnedFraction = accrueInfo.feesEarnedFraction;\n        balanceOf[_feeTo] = balanceOf[_feeTo].add(_feesEarnedFraction);\n        emit Transfer(address(0), _feeTo, _feesEarnedFraction);\n        accrueInfo.feesEarnedFraction = 0;\n\n        emit LogWithdrawFees(_feeTo, _feesEarnedFraction);\n    }\n\n    /// @notice Used to register and enable or disable swapper contracts used in closed liquidations.\n    /// MasterContract Only Admin function.\n    /// @param swapper The address of the swapper contract that conforms to `ISwapper`.\n    /// @param enable True to enable the swapper. To disable use False.\n    function setSwapper(ISwapper swapper, bool enable) public onlyOwner {\n        swappers[swapper] = enable;\n    }\n\n    /// @notice Sets the beneficiary of fees accrued in liquidations.\n    /// MasterContract Only Admin function.\n    /// @param newFeeTo The address of the receiver.\n    function setFeeTo(address newFeeTo) public onlyOwner {\n        feeTo = newFeeTo;\n        emit LogFeeTo(newFeeTo);\n    }\n}\n"
        }
    },
    "settings": {
        "optimizer": {
            "enabled": true,
            "runs": 350
        },
        "outputSelection": {
            "*": {
                "*": ["abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers"],
                "": ["ast"]
            }
        }
    }
}
