ETH Price: $3,317.54 (-8.79%)

Contract Diff Checker

Contract Name:
PoolManager

Contract Source Code:

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IAddressProvider} from 'src/interfaces/IAddressProvider.sol';
import {IACLManager} from 'src/interfaces/IACLManager.sol';

import {Constants} from 'src/libraries/helpers/Constants.sol';
import {Errors} from 'src/libraries/helpers/Errors.sol';
import {StorageSlot} from 'src/libraries/logic/StorageSlot.sol';
import {DataTypes} from 'src/libraries/types/DataTypes.sol';

import {Base} from 'src/base/Base.sol';
import {Proxy} from 'src/base/Proxy.sol';

/// @notice Main storage contract
contract PoolManager is Base {
  string public constant name = 'BendDAO Protocol V2';

  constructor(address provider_, address installerModule) {
    reentrancyLock = Constants.REENTRANCYLOCK__UNLOCKED;

    DataTypes.PoolStorage storage ps = StorageSlot.getPoolStorage();
    ps.addressProvider = provider_;
    ps.nextPoolId = Constants.INITIAL_POOL_ID;

    ps.wrappedNativeToken = IAddressProvider(ps.addressProvider).getWrappedNativeToken();
    require(ps.wrappedNativeToken != address(0), Errors.INVALID_ADDRESS);

    moduleLookup[Constants.MODULEID__INSTALLER] = installerModule;
    address installerProxy = _createProxy(Constants.MODULEID__INSTALLER);
    trustedSenders[installerProxy].moduleImpl = installerModule;
  }

  /// @notice Lookup the current implementation contract for a module
  /// @param moduleId Fixed constant that refers to a module type
  /// @return An internal address specifies the module's implementation code
  function moduleIdToImplementation(uint moduleId) external view returns (address) {
    return moduleLookup[moduleId];
  }

  /// @notice Lookup a proxy that can be used to interact with a module (only valid for single-proxy modules)
  /// @param moduleId Fixed constant that refers to a module type
  /// @return An address that should be cast to the appropriate module interface
  function moduleIdToProxy(uint moduleId) external view returns (address) {
    return proxyLookup[moduleId];
  }

  function dispatch() external payable reentrantOK {
    // only trusted proxy
    uint32 moduleId = trustedSenders[msg.sender].moduleId;
    address moduleImpl = trustedSenders[msg.sender].moduleImpl;

    require(moduleId != 0, Errors.PROXY_SENDER_NOT_TRUST);

    // multi proxy module
    if (moduleImpl == address(0)) moduleImpl = moduleLookup[moduleId];

    uint msgDataLength = msg.data.length;
    require(msgDataLength >= (4 + 4 + 20), Errors.PROXY_MSGDATA_TOO_SHORT);

    assembly {
      let payloadSize := sub(calldatasize(), 4)
      calldatacopy(0, 4, payloadSize)
      mstore(payloadSize, shl(96, caller()))

      let result := delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0)

      returndatacopy(0, 0, returndatasize())

      switch result
      case 0 {
        revert(0, returndatasize())
      }
      default {
        return(0, returndatasize())
      }
    }
  }

  receive() external payable {}

  modifier onlyPoolAdmin() {
    _onlyPoolAdmin();

    _;
  }

  function _onlyPoolAdmin() internal view {
    DataTypes.PoolStorage storage ps = StorageSlot.getPoolStorage();

    IACLManager aclManager = IACLManager(IAddressProvider(ps.addressProvider).getACLManager());
    require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
  }

  /* @notice only used when user transfer ETH to contract by mistake */
  function emergencyEtherTransfer(address to, uint256 amount) public onlyPoolAdmin {
    (bool success, ) = to.call{value: amount}(new bytes(0));
    require(success, Errors.ETH_TRANSFER_FAILED);
  }

  /* @notice only used when user transfer ETH to module contract by mistake */
  function emergencyProxyEtherTransfer(address proxyAddr, address to, uint256 amount) public onlyPoolAdmin {
    Proxy proxy = Proxy(payable(proxyAddr));
    proxy.emergencyEtherTransfer(to, amount);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

interface IAddressProvider {
  event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);

  event WrappedNativeTokenUpdated(address indexed oldAddress, address indexed newAddress);

  event TreasuryUpdated(address indexed oldAddress, address indexed newAddress);

  event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);

  event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);

  event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);

  event PoolManagerUpdated(address indexed oldAddress, address indexed newAddress);

  event YieldRegistryUpdated(address indexed oldAddress, address indexed newAddress);

  function getAddress(bytes32 id) external view returns (address);

  function setAddress(bytes32 id, address newAddress) external;

  function getWrappedNativeToken() external view returns (address);

  function setWrappedNativeToken(address newAddress) external;

  function getTreasury() external view returns (address);

  function setTreasury(address newAddress) external;

  function getACLAdmin() external view returns (address);

  function setACLAdmin(address newAddress) external;

  function getACLManager() external view returns (address);

  function setACLManager(address newAddress) external;

  function getPriceOracle() external view returns (address);

  function setPriceOracle(address newAddress) external;

  function getPoolManager() external view returns (address);

  function setPoolManager(address newAddress) external;

  function getPoolModuleImplementation(uint moduleId) external view returns (address);

  function getPoolModuleProxy(uint moduleId) external view returns (address);

  function getPoolModuleProxies(uint[] memory moduleIds) external view returns (address[] memory);

  function getYieldRegistry() external view returns (address);

  function setYieldRegistry(address newAddress) external;

  function getDelegateRegistryV2() external view returns (address);

  function setDelegateRegistryV2(address newAddress) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
 * @title IACLManager
 * @notice Defines the basic interface for the ACL Manager
 */
interface IACLManager {
  /**
   * @notice Returns the identifier of the PoolAdmin role
   * @return The id of the PoolAdmin role
   */
  function POOL_ADMIN_ROLE() external view returns (bytes32);

  /**
   * @notice Returns the identifier of the EmergencyAdmin role
   * @return The id of the EmergencyAdmin role
   */
  function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);

  /**
   * @notice Returns the identifier of the OracleAdmin role
   * @return The id of the OracleAdmin role
   */
  function ORACLE_ADMIN_ROLE() external view returns (bytes32);

  /**
   * @notice Adds a new admin as PoolAdmin
   * @param admin The address of the new admin
   */
  function addPoolAdmin(address admin) external;

  /**
   * @notice Removes an admin as PoolAdmin
   * @param admin The address of the admin to remove
   */
  function removePoolAdmin(address admin) external;

  /**
   * @notice Returns true if the address is PoolAdmin, false otherwise
   * @param admin The address to check
   * @return True if the given address is PoolAdmin, false otherwise
   */
  function isPoolAdmin(address admin) external view returns (bool);

  /**
   * @notice Adds a new admin as EmergencyAdmin
   * @param admin The address of the new admin
   */
  function addEmergencyAdmin(address admin) external;

  /**
   * @notice Removes an admin as EmergencyAdmin
   * @param admin The address of the admin to remove
   */
  function removeEmergencyAdmin(address admin) external;

  /**
   * @notice Returns true if the address is EmergencyAdmin, false otherwise
   * @param admin The address to check
   * @return True if the given address is EmergencyAdmin, false otherwise
   */
  function isEmergencyAdmin(address admin) external view returns (bool);

  /**
   * @notice Adds a new admin as OracleAdmin
   * @param admin The address of the new admin
   */
  function addOracleAdmin(address admin) external;

  /**
   * @notice Removes an admin as OracleAdmin
   * @param admin The address of the admin to remove
   */
  function removeOracleAdmin(address admin) external;

  /**
   * @notice Returns true if the address is OracleAdmin, false otherwise
   * @param admin The address to check
   * @return True if the given address is OracleAdmin, false otherwise
   */
  function isOracleAdmin(address admin) external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library Constants {
  // Universal
  address public constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

  // Implementation internals

  uint internal constant REENTRANCYLOCK__UNLOCKED = 1;
  uint internal constant REENTRANCYLOCK__LOCKED = 2;

  // Modules

  // Public single-proxy modules
  uint internal constant MODULEID__INSTALLER = 1;
  uint internal constant MODULEID__CONFIGURATOR = 2;
  uint internal constant MODULEID__BVAULT = 3;
  uint internal constant MODULEID__POOL_LENS = 4;
  uint internal constant MODULEID__FLASHLOAN = 5;
  uint internal constant MODULEID__YIELD = 6;
  uint internal constant MODULEID__CONFIGURATOR_POOL = 7;
  uint internal constant MODULEID__CROSS_LENDING = 11;
  uint internal constant MODULEID__CROSS_LIQUIDATION = 12;
  uint internal constant MODULEID__ISOLATE_LENDING = 21;
  uint internal constant MODULEID__ISOLATE_LIQUIDATION = 22;
  uint internal constant MODULEID__UI_POOL_LENS = 31;

  uint internal constant MAX_EXTERNAL_SINGLE_PROXY_MODULEID = 499_999;

  // Public multi-proxy modules
  // uint internal constant MODULEID__xxx = 500_000;

  uint internal constant MAX_EXTERNAL_MODULEID = 999_999;

  // Internal modules
  // uint internal constant MODULEID__xxx = 1_000_000;

  // Pool params
  uint32 public constant INITIAL_POOL_ID = 1;

  // Asset params
  uint16 public constant MAX_COLLATERAL_FACTOR = 10000;
  uint16 public constant MAX_LIQUIDATION_THRESHOLD = 10000;
  uint16 public constant MAX_LIQUIDATION_BONUS = 10000;
  uint16 public constant MAX_FEE_FACTOR = 10000;
  uint16 public constant MAX_REDEEM_THRESHOLD = 10000;
  uint16 public constant MAX_BIDFINE_FACTOR = 10000;
  uint16 public constant MAX_MIN_BIDFINE_FACTOR = 10000;
  uint40 public constant MAX_AUCTION_DUARATION = 7 days;
  uint40 public constant MAX_YIELD_CAP_FACTOR = 10000;

  uint16 public constant MAX_NUMBER_OF_ASSET = 256;

  uint8 public constant MAX_NUMBER_OF_GROUP = 4;
  uint8 public constant GROUP_ID_INVALID = 255;
  uint8 public constant GROUP_ID_YIELD = 0;
  uint8 public constant GROUP_ID_LEND_MIN = 1;
  uint8 public constant GROUP_ID_LEND_MAX = 3;

  // Asset type
  uint8 public constant ASSET_TYPE_ERC20 = 1;
  uint8 public constant ASSET_TYPE_ERC721 = 2;

  // Supply Mode
  uint8 public constant SUPPLY_MODE_CROSS = 1;
  uint8 public constant SUPPLY_MODE_ISOLATE = 2;

  // Asset Lock Flag
  uint16 public constant ASSET_LOCK_FLAG_CROSS = 0x0001; // not used
  uint16 public constant ASSET_LOCK_FLAG_ISOLATE = 0x0002; // not used
  uint16 public constant ASSET_LOCK_FLAG_YIELD = 0x0004;

  // Loan Status
  uint8 public constant LOAN_STATUS_ACTIVE = 1;
  uint8 public constant LOAN_STATUS_REPAID = 2;
  uint8 public constant LOAN_STATUS_AUCTION = 3;
  uint8 public constant LOAN_STATUS_DEFAULT = 4;

  /**
   * @dev Minimum health factor to consider a user position healthy
   * A value of 1e18 results in 1
   */
  uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;

  /**
   * @dev Default percentage of borrower's debt to be repaid in a liquidation.
   * @dev Percentage applied when the users health factor is above `CLOSE_FACTOR_HF_THRESHOLD`
   * Expressed in bps, a value of 0.5e4 results in 50.00%
   */
  uint256 internal constant DEFAULT_LIQUIDATION_CLOSE_FACTOR = 0.5e4;

  /**
   * @dev Maximum percentage of borrower's debt to be repaid in a liquidation
   * @dev Percentage applied when the users health factor is below `CLOSE_FACTOR_HF_THRESHOLD`
   * Expressed in bps, a value of 1e4 results in 100.00%
   */
  uint256 public constant MAX_LIQUIDATION_CLOSE_FACTOR = 1e4;

  /**
   * @dev This constant represents below which health factor value it is possible to liquidate
   * an amount of debt corresponding to `MAX_LIQUIDATION_CLOSE_FACTOR`.
   * A value of 0.95e18 results in 0.95
   */
  uint256 public constant CLOSE_FACTOR_HF_THRESHOLD = 0.95e18;

  uint256 public constant MAX_LIQUIDATION_ERC721_TOKEN_NUM = 1;

  // Yield Status
  uint8 public constant YIELD_STATUS_ACTIVE = 1;
  uint8 public constant YIELD_STATUS_UNSTAKE = 2;
  uint8 public constant YIELD_STATUS_CLAIM = 3;
  uint8 public constant YIELD_STATUS_REPAID = 4;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library Errors {
  string public constant OK = '0';
  string public constant EMPTY_ERROR = '1';
  string public constant ETH_TRANSFER_FAILED = '2';
  string public constant TOKEN_TRANSFER_FAILED = '3';
  string public constant MSG_VALUE_NOT_ZERO = '4';
  string public constant TOKEN_ALLOWANCE_INSUFFICIENT = '5';
  string public constant TOKEN_BALANCE_INSUFFICIENT = '6';

  string public constant REENTRANCY_ALREADY_LOCKED = '10';

  string public constant PROXY_INVALID_MODULE = '30';
  string public constant PROXY_INTERNAL_MODULE = '31';
  string public constant PROXY_SENDER_NOT_TRUST = '32';
  string public constant PROXY_MSGDATA_TOO_SHORT = '33';

  string public constant INVALID_AMOUNT = '100';
  string public constant INVALID_SCALED_AMOUNT = '101';
  string public constant INVALID_TRANSFER_AMOUNT = '102';
  string public constant INVALID_ADDRESS = '103';
  string public constant INVALID_FROM_ADDRESS = '104';
  string public constant INVALID_TO_ADDRESS = '105';
  string public constant INVALID_SUPPLY_MODE = '106';
  string public constant INVALID_ASSET_TYPE = '107';
  string public constant INVALID_POOL_ID = '108';
  string public constant INVALID_GROUP_ID = '109';
  string public constant INVALID_ASSET_ID = '110';
  string public constant INVALID_ASSET_DECIMALS = '111';
  string public constant INVALID_IRM_ADDRESS = '112';
  string public constant INVALID_CALLER = '113';
  string public constant INVALID_ID_LIST = '114';
  string public constant INVALID_COLLATERAL_AMOUNT = '115';
  string public constant INVALID_BORROW_AMOUNT = '116';
  string public constant INVALID_TOKEN_OWNER = '117';
  string public constant INVALID_YIELD_STAKER = '118';
  string public constant INCONSISTENT_PARAMS_LENGTH = '119';
  string public constant INVALID_LOAN_STATUS = '120';
  string public constant ARRAY_HAS_DUP_ELEMENT = '121';
  string public constant INVALID_ONBEHALF_ADDRESS = '122';
  string public constant SAME_ONBEHALF_ADDRESS = '123';
  string public constant INVALID_OPTIMAL_USAGE_RATIO = '124';
  string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '125';
  string public constant INVALID_MAX_RATE = '126';
  string public constant INVALID_RATE_MODEL = '127';

  string public constant ENUM_SET_ADD_FAILED = '150';
  string public constant ENUM_SET_REMOVE_FAILED = '151';

  string public constant ACL_ADMIN_CANNOT_BE_ZERO = '200';
  string public constant ACL_MANAGER_CANNOT_BE_ZERO = '201';
  string public constant CALLER_NOT_ORACLE_ADMIN = '202';
  string public constant CALLER_NOT_POOL_ADMIN = '203';
  string public constant CALLER_NOT_EMERGENCY_ADMIN = '204';
  string public constant OWNER_CANNOT_BE_ZERO = '205';
  string public constant INVALID_ASSET_PARAMS = '206';
  string public constant FLASH_LOAN_EXEC_FAILED = '207';
  string public constant TREASURY_CANNOT_BE_ZERO = '208';
  string public constant PRICE_ORACLE_CANNOT_BE_ZERO = '209';
  string public constant ADDR_PROVIDER_CANNOT_BE_ZERO = '210';
  string public constant SENDER_NOT_APPROVED = '211';
  string public constant SENDER_RECEIVER_NOT_SAME = '212';

  string public constant POOL_ALREADY_EXISTS = '300';
  string public constant POOL_NOT_EXISTS = '301';
  string public constant POOL_IS_PAUSED = '302';
  string public constant POOL_YIELD_ALREADY_ENABLE = '303';
  string public constant POOL_YIELD_NOT_ENABLE = '304';
  string public constant POOL_YIELD_IS_PAUSED = '305';

  string public constant GROUP_ALREADY_EXISTS = '320';
  string public constant GROUP_NOT_EXISTS = '321';
  string public constant GROUP_LIST_NOT_EMPTY = '322';
  string public constant GROUP_LIST_IS_EMPTY = '323';
  string public constant GROUP_NUMBER_EXCEED_MAX_LIMIT = '324';
  string public constant GROUP_USED_BY_ASSET = '325';

  string public constant ASSET_ALREADY_EXISTS = '340';
  string public constant ASSET_NOT_EXISTS = '341';
  string public constant ASSET_LIST_NOT_EMPTY = '342';
  string public constant ASSET_NUMBER_EXCEED_MAX_LIMIT = '343';
  string public constant ASSET_AGGREGATOR_NOT_EXIST = '344';
  string public constant ASSET_PRICE_IS_ZERO = '345';
  string public constant ASSET_TYPE_NOT_ERC20 = '346';
  string public constant ASSET_TYPE_NOT_ERC721 = '347';
  string public constant ASSET_NOT_ACTIVE = '348';
  string public constant ASSET_IS_PAUSED = '349';
  string public constant ASSET_IS_FROZEN = '350';
  string public constant ASSET_IS_BORROW_DISABLED = '351';
  string public constant ASSET_NOT_CROSS_MODE = '352';
  string public constant ASSET_NOT_ISOLATE_MODE = '353';
  string public constant ASSET_YIELD_ALREADY_ENABLE = '354';
  string public constant ASSET_YIELD_NOT_ENABLE = '355';
  string public constant ASSET_YIELD_IS_PAUSED = '356';
  string public constant ASSET_INSUFFICIENT_LIQUIDITY = '357';
  string public constant ASSET_INSUFFICIENT_BIDAMOUNT = '358';
  string public constant ASSET_ALREADY_LOCKED_IN_USE = '359';
  string public constant ASSET_SUPPLY_CAP_EXCEEDED = '360';
  string public constant ASSET_BORROW_CAP_EXCEEDED = '361';
  string public constant ASSET_IS_FLASHLOAN_DISABLED = '362';
  string public constant ASSET_SUPPLY_MODE_IS_SAME = '363';
  string public constant ASSET_TOKEN_ALREADY_EXISTS = '364';
  string public constant ASSET_LIQUIDITY_NOT_ZERO = '365';

  string public constant HEALTH_FACTOR_BELOW_LIQUIDATION_THRESHOLD = '400';
  string public constant HEALTH_FACTOR_NOT_BELOW_LIQUIDATION_THRESHOLD = '401';
  string public constant CROSS_SUPPLY_NOT_EMPTY = '402';
  string public constant ISOLATE_SUPPLY_NOT_EMPTY = '403';
  string public constant CROSS_BORROW_NOT_EMPTY = '404';
  string public constant ISOLATE_BORROW_NOT_EMPTY = '405';
  string public constant COLLATERAL_BALANCE_IS_ZERO = '406';
  string public constant BORROW_BALANCE_IS_ZERO = '407';
  string public constant LTV_VALIDATION_FAILED = '408';
  string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '409';
  string public constant LIQUIDATE_REPAY_DEBT_FAILED = '410';
  string public constant ORACLE_PRICE_IS_STALE = '411';
  string public constant LIQUIDATION_EXCEED_MAX_TOKEN_NUM = '412';
  string public constant USER_COLLATERAL_SUPPLY_ZERO = '413';
  string public constant ACTUAL_COLLATERAL_TO_LIQUIDATE_ZERO = '414';
  string public constant ACTUAL_DEBT_TO_LIQUIDATE_ZERO = '415';
  string public constant USER_DEBT_BORROWED_ZERO = '416';

  string public constant YIELD_EXCEED_ASSET_CAP_LIMIT = '500';
  string public constant YIELD_EXCEED_STAKER_CAP_LIMIT = '501';
  string public constant YIELD_TOKEN_ALREADY_LOCKED = '502';
  string public constant YIELD_ACCOUNT_NOT_EXIST = '503';
  string public constant YIELD_ACCOUNT_ALREADY_EXIST = '504';
  string public constant YIELD_REGISTRY_IS_NOT_AUTH = '505';
  string public constant YIELD_MANAGER_IS_NOT_AUTH = '506';
  string public constant YIELD_ACCOUNT_IMPL_ZERO = '507';
  string public constant YIELD_TOKEN_LOCKED_BY_OTHER = '508';

  string public constant ISOLATE_LOAN_ASSET_NOT_MATCH = '600';
  string public constant ISOLATE_LOAN_GROUP_NOT_MATCH = '601';
  string public constant ISOLATE_LOAN_OWNER_NOT_MATCH = '602';
  string public constant ISOLATE_BORROW_NOT_EXCEED_LIQUIDATION_THRESHOLD = '603';
  string public constant ISOLATE_BID_PRICE_LESS_THAN_BORROW = '604';
  string public constant ISOLATE_BID_PRICE_LESS_THAN_LIQUIDATION_PRICE = '605';
  string public constant ISOLATE_BID_PRICE_LESS_THAN_HIGHEST_PRICE = '606';
  string public constant ISOLATE_BID_AUCTION_DURATION_HAS_END = '607';
  string public constant ISOLATE_BID_AUCTION_DURATION_NOT_END = '608';
  string public constant ISOLATE_LOAN_BORROW_AMOUNT_NOT_COVER = '609';
  string public constant ISOLATE_LOAN_EXISTS = '610';
  string public constant ISOLATE_LOAN_BIDDER_NOT_SAME = '611';

  // Yield Staking, don't care about the ETH
  string public constant YIELD_ETH_NFT_NOT_ACTIVE = '1000';
  string public constant YIELD_ETH_POOL_NOT_SAME = '1001';
  string public constant YIELD_ETH_STATUS_NOT_ACTIVE = '1002';
  string public constant YIELD_ETH_STATUS_NOT_UNSTAKE = '1003';
  string public constant YIELD_ETH_NFT_ALREADY_USED = '1004';
  string public constant YIELD_ETH_NFT_NOT_USED_BY_ME = '1005';
  string public constant YIELD_ETH_EXCEED_MAX_BORROWABLE = '1006';
  string public constant YIELD_ETH_HEATH_FACTOR_TOO_LOW = '1007';
  string public constant YIELD_ETH_HEATH_FACTOR_TOO_HIGH = '1008';
  string public constant YIELD_ETH_EXCEED_MAX_FINE = '1009';
  string public constant YIELD_ETH_WITHDRAW_NOT_READY = '1010';
  string public constant YIELD_ETH_DEPOSIT_FAILED = '1011';
  string public constant YIELD_ETH_WITHDRAW_FAILED = '1012';
  string public constant YIELD_ETH_CLAIM_FAILED = '1013';
  string public constant YIELD_ETH_ACCOUNT_INSUFFICIENT = '1014';
  string public constant YIELD_ETH_LT_MIN_AMOUNT = '1015';
  string public constant YIELD_ETH_GT_MAX_AMOUNT = '1016';
  string public constant YIELD_ETH_NFT_LEVERAGE_FACTOR_ZERO = '1017';
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {DataTypes} from '../types/DataTypes.sol';

library StorageSlot {
  // keccak256(abi.encode(uint256(keccak256("benddao.storage.v2.pool")) - 1)) & ~bytes32(uint256(0xff));
  bytes32 constant STORAGE_POSITION_POOL = 0xce044ef5c897ad3fe9fcce02f9f2b7dc69de8685dee403b46b4b685baa720200;

  function getPoolStorage() internal pure returns (DataTypes.PoolStorage storage rs) {
    bytes32 position = STORAGE_POSITION_POOL;
    assembly {
      rs.slot := position
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {EnumerableSetUpgradeable} from '@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol';

library DataTypes {
  /****************************************************************************/
  /* Data Types for Pool Lending */
  struct PoolData {
    uint32 poolId;
    string name;
    bool isPaused;

    // group
    mapping(uint8 => bool) enabledGroups;
    EnumerableSetUpgradeable.UintSet groupList;

    // underlying asset to asset data
    mapping(address => AssetData) assetLookup;
    EnumerableSetUpgradeable.AddressSet assetList;

    // nft address -> nft id -> isolate loan
    mapping(address => mapping(uint256 => IsolateLoanData)) loanLookup;
    // account data
    mapping(address => AccountData) accountLookup;

    // yield
    bool isYieldEnabled;
    bool isYieldPaused;
    uint8 yieldGroup;
  }

  struct AccountData {
    EnumerableSetUpgradeable.AddressSet suppliedAssets;
    EnumerableSetUpgradeable.AddressSet borrowedAssets;
    // asset => operator => approved
    mapping(address => mapping(address => bool)) operatorAuthorizations;
  }

  struct GroupData {
    // config parameters
    address rateModel;

    // user state
    uint256 totalScaledCrossBorrow;
    mapping(address => uint256) userScaledCrossBorrow;
    uint256 totalScaledIsolateBorrow;
    mapping(address => uint256) userScaledIsolateBorrow;

    // interest state
    uint128 borrowRate;
    uint128 borrowIndex;
    uint8 groupId;
  }

  struct ERC721TokenData {
    address owner;
    uint8 supplyMode; // 0=cross margin, 1=isolate
    address lockerAddr;
  }

  struct YieldManagerData {
    uint256 yieldCap; // percentage, 500 -> 5%
  }

  struct AssetData {
    // config params
    address underlyingAsset;
    uint8 assetType; // See ASSET_TYPE_xxx
    uint8 underlyingDecimals; // only for ERC20
    uint8 classGroup;
    bool isActive;
    bool isFrozen;
    bool isPaused;
    bool isBorrowingEnabled;
    bool isFlashLoanEnabled;
    bool isYieldEnabled;
    bool isYieldPaused;
    uint16 feeFactor;
    uint16 collateralFactor;
    uint16 liquidationThreshold;
    uint16 liquidationBonus;
    uint16 redeemThreshold;
    uint16 bidFineFactor;
    uint16 minBidFineFactor;
    uint40 auctionDuration;
    uint256 supplyCap; // amount with token decimals, 100e18 -> 100
    uint256 borrowCap; // amount with token decimals
    uint256 yieldCap; // percentage, 500 -> 5%

    // group state
    mapping(uint8 => GroupData) groupLookup;
    EnumerableSetUpgradeable.UintSet groupList;

    // user state
    uint256 totalScaledCrossSupply; // total supplied balance in cross margin mode
    uint256 totalScaledIsolateSupply; // total supplied balance in isolate mode, only for ERC721
    uint256 availableLiquidity;
    uint256 totalBidAmout;
    mapping(address => uint256) userScaledCrossSupply; // user supplied balance in cross margin mode
    mapping(address => uint256) userScaledIsolateSupply; // user supplied balance in isolate mode, only for ERC721
    mapping(uint256 => ERC721TokenData) erc721TokenData; // token -> data, only for ERC721

    // asset interest state
    uint128 supplyRate;
    uint128 supplyIndex;
    uint256 accruedFee; // as treasury supplied balance in cross mode
    uint40 lastUpdateTimestamp;

    // yield state
    mapping(address => YieldManagerData) yieldManagerLookup;
  }

  struct IsolateLoanData {
    address reserveAsset;
    uint256 scaledAmount;
    uint8 reserveGroup;
    uint8 loanStatus;
    uint40 bidStartTimestamp;
    address firstBidder;
    address lastBidder;
    uint256 bidAmount;
  }

  /****************************************************************************/
  /* Data Types for Storage */
  struct PoolStorage {
    // common fileds
    address addressProvider;
    address wrappedNativeToken; // WETH

    // pool fields
    uint32 nextPoolId;
    mapping(uint32 => PoolData) poolLookup;
    EnumerableSetUpgradeable.UintSet poolList;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';
import {ERC721Holder} from '@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol';

import {Constants} from 'src/libraries/helpers/Constants.sol';
import {Events} from 'src/libraries/helpers/Events.sol';
import {Errors} from 'src/libraries/helpers/Errors.sol';

import {Storage} from 'src/base/Storage.sol';
import {Proxy} from 'src/base/Proxy.sol';

abstract contract Base is Storage, Pausable, ERC721Holder {
  constructor() {
    reentrancyLock = Constants.REENTRANCYLOCK__UNLOCKED;
  }

  // Modules

  function _createProxy(uint proxyModuleId) internal returns (address) {
    require(proxyModuleId != 0, Errors.PROXY_INVALID_MODULE);
    require(proxyModuleId <= Constants.MAX_EXTERNAL_MODULEID, Errors.PROXY_INTERNAL_MODULE);

    // If we've already created a proxy for a single-proxy module, just return it:

    if (proxyLookup[proxyModuleId] != address(0)) return proxyLookup[proxyModuleId];

    // Otherwise create a proxy:

    address proxyAddr = address(new Proxy());

    if (proxyModuleId <= Constants.MAX_EXTERNAL_SINGLE_PROXY_MODULEID) proxyLookup[proxyModuleId] = proxyAddr;

    trustedSenders[proxyAddr] = TrustedSenderInfo({moduleId: uint32(proxyModuleId), moduleImpl: address(0)});

    emit Events.ProxyCreated(proxyAddr, proxyModuleId);

    return proxyAddr;
  }

  function callInternalModule(uint moduleId, bytes memory input) internal returns (bytes memory) {
    (bool success, bytes memory result) = moduleLookup[moduleId].delegatecall(input);
    if (!success) revertBytes(result);
    return result;
  }

  // Modifiers

  modifier nonReentrant() {
    require(reentrancyLock == Constants.REENTRANCYLOCK__UNLOCKED, Errors.REENTRANCY_ALREADY_LOCKED);

    reentrancyLock = Constants.REENTRANCYLOCK__LOCKED;
    _;
    reentrancyLock = Constants.REENTRANCYLOCK__UNLOCKED;
  }

  modifier reentrantOK() {
    // documentation only
    _;
  }

  // Used to flag functions which do not modify storage, but do perform a delegate call
  // to a view function, which prohibits a standard view modifier. The flag is used to
  // patch state mutability in compiled ABIs and interfaces.
  modifier staticDelegate() {
    _;
  }

  // Error handling

  function revertBytes(bytes memory errMsg) internal pure {
    if (errMsg.length > 0) {
      assembly {
        revert(add(32, errMsg), mload(errMsg))
      }
    }

    revert(Errors.EMPTY_ERROR);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

contract Proxy {
  address immutable creator;

  constructor() {
    creator = msg.sender;
  }

  // External interface

  receive() external payable {}

  fallback() external payable {
    address creator_ = creator;
    uint value = msg.value;

    if (msg.sender == creator_) {
      assembly {
        mstore(0, 0)
        calldatacopy(31, 0, calldatasize())

        switch mload(0) // numTopics
        case 0 {
          log0(32, sub(calldatasize(), 1))
        }
        case 1 {
          log1(64, sub(calldatasize(), 33), mload(32))
        }
        case 2 {
          log2(96, sub(calldatasize(), 65), mload(32), mload(64))
        }
        case 3 {
          log3(128, sub(calldatasize(), 97), mload(32), mload(64), mload(96))
        }
        case 4 {
          log4(160, sub(calldatasize(), 129), mload(32), mload(64), mload(96), mload(128))
        }
        default {
          revert(0, 0)
        }

        return(0, 0)
      }
    } else {
      assembly {
        mstore(0, 0xe9c4a3ac00000000000000000000000000000000000000000000000000000000) // dispatch() selector
        calldatacopy(4, 0, calldatasize())
        mstore(add(4, calldatasize()), shl(96, caller()))

        let result := call(gas(), creator_, value, 0, add(24, calldatasize()), 0, 0)
        returndatacopy(0, 0, returndatasize())

        switch result
        case 0 {
          revert(0, returndatasize())
        }
        default {
          return(0, returndatasize())
        }
      }
    }
  }

  /* @notice only used when user transfer ETH to contract by mistake */
  function emergencyEtherTransfer(address to, uint256 amount) public {
    require(msg.sender == creator, 'Invalid caller');

    (bool success, ) = to.call{value: amount}(new bytes(0));
    require(success, 'ETH_TRANSFER_FAILED');
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library Events {
  // Modeuls Events
  event ProxyCreated(address indexed proxy, uint moduleId);
  event InstallerSetUpgradeAdmin(address indexed newUpgradeAdmin);
  event InstallerSetGovernorAdmin(address indexed newGovernorAdmin);
  event InstallerInstallModule(uint indexed moduleId, address indexed moduleImpl, bytes32 moduleGitCommit);

  /* Oracle Events */
  event AssetAggregatorUpdated(address indexed asset, address aggregator);
  event BendNFTOracleUpdated(address bendNFTOracle);

  /* Pool Events */
  event CreatePool(uint32 indexed poolId, string name);
  event DeletePool(uint32 indexed poolId);
  event SetPoolName(uint32 indexed poolId, string name);

  event AddPoolGroup(uint32 indexed poolId, uint8 groupId);
  event RemovePoolGroup(uint32 indexed poolId, uint8 groupId);

  event SetPoolPause(uint32 indexed poolId, bool isPause);
  event CollectFeeToTreasury(uint32 indexed poolId, address indexed asset, uint256 fee, uint256 index);

  event SetPoolYieldEnable(uint32 indexed poolId, bool isEnable);
  event SetPoolYieldPause(uint32 indexed poolId, bool isPause);

  /* Asset Events */
  event AssetInterestSupplyDataUpdated(
    uint32 indexed poolId,
    address indexed asset,
    uint256 supplyRate,
    uint256 supplyIndex
  );
  event AssetInterestBorrowDataUpdated(
    uint32 indexed poolId,
    address indexed asset,
    uint256 groupId,
    uint256 borrowRate,
    uint256 borrowIndex
  );

  event AddAsset(uint32 indexed poolId, address indexed asset, uint8 assetType);
  event RemoveAsset(uint32 indexed poolId, address indexed asset, uint8 assetType);

  event AddAssetGroup(uint32 indexed poolId, address indexed asset, uint8 groupId);
  event RemoveAssetGroup(uint32 indexed poolId, address indexed asset, uint8 groupId);

  event SetAssetActive(uint32 indexed poolId, address indexed asset, bool isActive);
  event SetAssetFrozen(uint32 indexed poolId, address indexed asset, bool isFrozen);
  event SetAssetPause(uint32 indexed poolId, address indexed asset, bool isPause);
  event SetAssetBorrowing(uint32 indexed poolId, address indexed asset, bool isEnable);
  event SetAssetFlashLoan(uint32 indexed poolId, address indexed asset, bool isEnable);
  event SetAssetSupplyCap(uint32 indexed poolId, address indexed asset, uint256 newCap);
  event SetAssetBorrowCap(uint32 indexed poolId, address indexed asset, uint256 newCap);
  event SetAssetClassGroup(uint32 indexed poolId, address indexed asset, uint8 groupId);
  event SetAssetCollateralParams(
    uint32 indexed poolId,
    address indexed asset,
    uint16 collateralFactor,
    uint16 liquidationThreshold,
    uint16 liquidationBonus
  );
  event SetAssetAuctionParams(
    uint32 indexed poolId,
    address indexed asset,
    uint16 redeemThreshold,
    uint16 bidFineFactor,
    uint16 minBidFineFactor,
    uint40 auctionDuration
  );
  event SetAssetProtocolFee(uint32 indexed poolId, address indexed asset, uint16 feeFactor);
  event SetAssetLendingRate(uint32 indexed poolId, address indexed asset, uint8 groupId, address rateModel);

  event SetAssetYieldEnable(uint32 indexed poolId, address indexed asset, bool isEnable);
  event SetAssetYieldPause(uint32 indexed poolId, address indexed asset, bool isPause);
  event SetAssetYieldCap(uint32 indexed poolId, address indexed asset, uint256 newCap);
  event SetAssetYieldRate(uint32 indexed poolId, address indexed asset, address rateModel);
  event SetManagerYieldCap(uint32 indexed poolId, address indexed staker, address indexed asset, uint256 newCap);

  /* Supply Events */
  event DepositERC20(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint256 amount,
    address onBehalf
  );
  event WithdrawERC20(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint256 amount,
    address onBehalf,
    address receiver
  );

  event DepositERC721(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint256[] tokenIds,
    uint8 supplyMode,
    address onBehalf
  );
  event WithdrawERC721(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint256[] tokenIds,
    uint8 supplyMode,
    address onBehalf,
    address receiver
  );

  event SetERC721SupplyMode(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint256[] tokenIds,
    uint8 supplyMode,
    address onBehalf
  );

  // Cross Lending Events
  event CrossBorrowERC20(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint8[] groups,
    uint256[] amounts,
    address onBehalf,
    address receiver
  );

  event CrossRepayERC20(
    address indexed sender,
    uint256 indexed poolId,
    address indexed asset,
    uint8[] groups,
    uint256[] amounts,
    address onBehalf
  );

  event CrossLiquidateERC20(
    address indexed liquidator,
    uint256 indexed poolId,
    address indexed user,
    address collateralAsset,
    address debtAsset,
    uint256 debtToCover,
    uint256 liquidatedCollateralAmount,
    bool supplyAsCollateral
  );

  event CrossLiquidateERC721(
    address indexed liquidator,
    uint256 indexed poolId,
    address indexed user,
    address collateralAsset,
    uint256[] liquidatedCollateralTokenIds,
    address debtAsset,
    uint256 liquidatedDebtAmount,
    bool supplyAsCollateral
  );

  // Isolate Lending Events
  event IsolateBorrow(
    address indexed sender,
    uint256 indexed poolId,
    address indexed nftAsset,
    uint256[] tokenIds,
    address debtAsset,
    uint256[] amounts,
    address onBehalf,
    address receiver
  );

  event IsolateRepay(
    address indexed sender,
    uint256 indexed poolId,
    address indexed nftAsset,
    uint256[] tokenIds,
    address debtAsset,
    uint256[] amounts,
    address onBehalf
  );

  event IsolateAuction(
    address indexed sender,
    uint256 indexed poolId,
    address indexed nftAsset,
    uint256[] tokenIds,
    address debtAsset,
    uint256[] bidAmounts
  );

  event IsolateRedeem(
    address indexed sender,
    uint256 indexed poolId,
    address indexed nftAsset,
    uint256[] tokenIds,
    address debtAsset,
    uint256[] redeemAmounts,
    uint256[] bidFines
  );

  event IsolateLiquidate(
    address indexed sender,
    uint256 indexed poolId,
    address indexed nftAsset,
    uint256[] tokenIds,
    address debtAsset,
    uint256[] extraAmounts,
    uint256[] remainAmounts,
    bool supplyAsCollateral
  );

  /* Yield Events */
  event YieldBorrowERC20(address indexed sender, uint256 indexed poolId, address indexed asset, uint256 amount);

  event YieldRepayERC20(address indexed sender, uint256 indexed poolId, address indexed asset, uint256 amount);

  // Misc Events
  event FlashLoanERC20(
    address indexed sender,
    uint32 indexed poolId,
    address[] assets,
    uint256[] amounts,
    address receiverAddress
  );

  event FlashLoanERC721(
    address indexed sender,
    uint32 indexed poolId,
    address[] nftAssets,
    uint256[] nftTokenIds,
    address receiverAddress
  );

  event SetAuthorization(
    address indexed sender,
    uint32 indexed poolId,
    address indexed asset,
    address operator,
    bool approved
  );
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {StorageSlot} from 'src/libraries/logic/StorageSlot.sol';
import {DataTypes} from 'src/libraries/types/DataTypes.sol';

abstract contract Storage {
  // Dispatcher and upgrades

  uint internal reentrancyLock;

  mapping(uint => address) moduleLookup; // moduleId => module implementation
  mapping(uint => address) proxyLookup; // moduleId => proxy address (only for single-proxy modules)

  struct TrustedSenderInfo {
    uint32 moduleId; // 0 = un-trusted
    address moduleImpl; // only non-zero for external single-proxy modules
  }

  mapping(address => TrustedSenderInfo) trustedSenders; // sender address => moduleId (0 = un-trusted)

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[46] private __gap;

  // Services

  function getPoolStorage() internal pure returns (DataTypes.PoolStorage storage rs) {
    return StorageSlot.getPoolStorage();
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):