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);
}