Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
LendPool
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ILendPoolLoan} from "../interfaces/ILendPoolLoan.sol"; import {ILendPool} from "../interfaces/ILendPool.sol"; import {ILendPoolAddressesProvider} from "../interfaces/ILendPoolAddressesProvider.sol"; import {IUToken} from "../interfaces/IUToken.sol"; import {ICryptoPunksMarket} from "../interfaces/ICryptoPunksMarket.sol"; import {Errors} from "../libraries/helpers/Errors.sol"; import {GenericLogic} from "../libraries/logic/GenericLogic.sol"; import {ReserveLogic} from "../libraries/logic/ReserveLogic.sol"; import {NftLogic} from "../libraries/logic/NftLogic.sol"; import {ValidationLogic} from "../libraries/logic/ValidationLogic.sol"; import {SupplyLogic} from "../libraries/logic/SupplyLogic.sol"; import {BorrowLogic} from "../libraries/logic/BorrowLogic.sol"; import {LiquidateLogic} from "../libraries/logic/LiquidateLogic.sol"; import {ReserveConfiguration} from "../libraries/configuration/ReserveConfiguration.sol"; import {NftConfiguration} from "../libraries/configuration/NftConfiguration.sol"; import {DataTypes} from "../libraries/types/DataTypes.sol"; import {LendPoolStorage} from "./LendPoolStorage.sol"; import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import {IERC721ReceiverUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; /** * @title LendPool contract * @dev Main point of interaction with an Unlockd protocol's market * - Users can: * # Deposit * # Withdraw * # Borrow * # Repay * # Auction * # Liquidate * - To be covered by a proxy contract, owned by the LendPoolAddressesProvider of the specific market * - All admin functions are callable by the LendPoolConfigurator contract defined also in the * LendPoolAddressesProvider * @author BendDao; Forked and edited by Unlockd **/ // !!! For Upgradable: DO NOT ADJUST Inheritance Order !!! contract LendPool is Initializable, ILendPool, ContextUpgradeable, IERC721ReceiverUpgradeable, LendPoolStorage { using SafeERC20Upgradeable for IERC20Upgradeable; using SafeERC20 for IERC20; using ReserveLogic for DataTypes.ReserveData; using NftLogic for DataTypes.NftData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using NftConfiguration for DataTypes.NftConfigurationMap; /*////////////////////////////////////////////////////////////// CONSTANT VARIABLES //////////////////////////////////////////////////////////////*/ bytes32 public constant ADDRESS_ID_WETH_GATEWAY = keccak256("WETH_GATEWAY"); bytes32 public constant ADDRESS_ID_PUNK_GATEWAY = keccak256("PUNK_GATEWAY"); bytes32 public constant ADDRESS_ID_PUNKS = keccak256("PUNKS"); bytes32 public constant ADDRESS_ID_WPUNKS = keccak256("WPUNKS"); bytes32 public constant ADDRESS_ID_RESERVOIR_ADAPTER = keccak256("RESERVOIR_ADAPTER"); /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } modifier whenNotPaused() { _whenNotPaused(); _; } modifier onlyLendPoolConfigurator() { _onlyLendPoolConfigurator(); _; } modifier onlyLendPoolLiquidatorOrGateway() { _onlyLendPoolLiquidatorOrGateway(); _; } modifier onlyPoolAdmin() { require(_addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); _; } /** * @notice Revert if called by any account other than the rescuer. */ modifier onlyRescuer() { require(_msgSender() == _rescuer, "Rescuable: caller is not the rescuer"); _; } modifier onlyHolder(address nftAsset, uint256 nftTokenId) { if (nftAsset == _addressesProvider.getAddress(ADDRESS_ID_PUNKS)) { require( _msgSender() == ICryptoPunksMarket(nftAsset).punkIndexToAddress(nftTokenId), Errors.LP_CALLER_NOT_NFT_HOLDER ); } else { require( _msgSender() == IERC721Upgradeable(nftAsset).ownerOf(nftTokenId) || _msgSender() == IERC721Upgradeable(_nfts[nftAsset].uNftAddress).ownerOf(nftTokenId), Errors.LP_CALLER_NOT_NFT_HOLDER ); } _; } modifier onlyCollection(address nftAsset) { DataTypes.NftData memory data = _nfts[nftAsset]; if (nftAsset == _addressesProvider.getAddress(ADDRESS_ID_PUNKS)) { data = _nfts[_addressesProvider.getAddress(ADDRESS_ID_WPUNKS)]; } require(data.uNftAddress != address(0), Errors.LP_COLLECTION_NOT_SUPPORTED); _; } modifier onlyReservoirAdapter() { require( msg.sender == _addressesProvider.getAddress(ADDRESS_ID_RESERVOIR_ADAPTER), Errors.LP_CALLER_NOT_RESERVOIR_OR_DEBT_MARKET ); _; } /*////////////////////////////////////////////////////////////// MODIFIER FUNCTIONS //////////////////////////////////////////////////////////////*/ function _onlyLendPoolConfigurator() internal view { require(_addressesProvider.getLendPoolConfigurator() == _msgSender(), Errors.LP_CALLER_NOT_LEND_POOL_CONFIGURATOR); } function _onlyLendPoolLiquidatorOrGateway() internal view { require( _addressesProvider.getLendPoolLiquidator() == _msgSender() || _addressesProvider.getAddress(ADDRESS_ID_WETH_GATEWAY) == _msgSender() || _addressesProvider.getAddress(ADDRESS_ID_PUNK_GATEWAY) == _msgSender(), Errors.LP_CALLER_NOT_LEND_POOL_LIQUIDATOR_NOR_GATEWAY ); } function _whenNotPaused() internal view { require(!_paused, Errors.LP_IS_PAUSED); } /*////////////////////////////////////////////////////////////// INITIALIZERS //////////////////////////////////////////////////////////////*/ /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} /** * @dev Function is invoked by the proxy contract when the LendPool contract is added to the * LendPoolAddressesProvider of the market. * - Caching the address of the LendPoolAddressesProvider in order to reduce gas consumption * on subsequent operations * @param provider The address of the LendPoolAddressesProvider **/ function initialize(ILendPoolAddressesProvider provider) public initializer { require(address(provider) != address(0), Errors.INVALID_ZERO_ADDRESS); _maxNumberOfReserves = 32; _maxNumberOfNfts = 255; _liquidateFeePercentage = 250; _addressesProvider = provider; } /*////////////////////////////////////////////////////////////// Fallback and Receive Functions //////////////////////////////////////////////////////////////*/ function onERC721Received(address, address, uint256, bytes memory) external pure override returns (bytes4) { return IERC721ReceiverUpgradeable.onERC721Received.selector; } receive() external payable {} /*////////////////////////////////////////////////////////////// RESCUERS //////////////////////////////////////////////////////////////*/ /** * @notice Rescue tokens and ETH locked up in this contract. * @param tokenContract ERC20 token contract address * @param to Recipient address * @param amount Amount to withdraw */ function rescue( IERC20 tokenContract, address to, uint256 amount, bool rescueETH ) external override nonReentrant onlyRescuer { if (rescueETH) { (bool sent, ) = to.call{value: amount}(""); require(sent, "Failed to send Ether"); } else { tokenContract.safeTransfer(to, amount); } } /** * @notice Rescue NFTs locked up in this contract. * @param nftAsset ERC721 asset contract address * @param tokenId ERC721 token id * @param to Recipient address */ function rescueNFT( IERC721Upgradeable nftAsset, uint256 tokenId, address to ) external override nonReentrant onlyRescuer { nftAsset.safeTransferFrom(address(this), to, tokenId); } /** * @notice Assign the rescuer role to a given address. * @param newRescuer New rescuer's address */ function updateRescuer(address newRescuer) external override onlyLendPoolConfigurator { require(newRescuer != address(0), "Rescuable: new rescuer is the zero address"); _rescuer = newRescuer; emit RescuerChanged(newRescuer); } /** * @notice Returns current rescuer * @return Rescuer's address */ function rescuer() external view override returns (address) { return _rescuer; } /*////////////////////////////////////////////////////////////// RESERVES INITIALIZERS & UPDATERS //////////////////////////////////////////////////////////////*/ /** * @dev Initializes a reserve, activating it, assigning an uToken and nft loan and an * interest rate strategy * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param uTokenAddress The address of the uToken that will be assigned to the reserve * @param debtTokenAddress The address of the debtToken that will be assigned to the reserve * @param interestRateAddress The address of the interest rate strategy contract **/ function initReserve( address asset, address uTokenAddress, address debtTokenAddress, address interestRateAddress ) external override onlyLendPoolConfigurator { require(AddressUpgradeable.isContract(asset), Errors.LP_NOT_CONTRACT); require( uTokenAddress != address(0) && debtTokenAddress != address(0) && interestRateAddress != address(0), Errors.INVALID_ZERO_ADDRESS ); _reserves[asset].init(uTokenAddress, debtTokenAddress, interestRateAddress); _addReserveToList(asset); } /** * @dev Initializes a nft, activating it, assigning nft loan and an * interest rate strategy * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the nft * @param uNftAddress the address of the UNFT regarding the chosen asset **/ function initNft(address asset, address uNftAddress) external override onlyLendPoolConfigurator { require(AddressUpgradeable.isContract(asset), Errors.LP_NOT_CONTRACT); require(uNftAddress != address(0), Errors.INVALID_ZERO_ADDRESS); _nfts[asset].init(uNftAddress); _addNftToList(asset); require(_addressesProvider.getLendPoolLoan() != address(0), Errors.LPC_INVALIED_LOAN_ADDRESS); IERC721Upgradeable(asset).setApprovalForAll(_addressesProvider.getLendPoolLoan(), true); ILendPoolLoan(_addressesProvider.getLendPoolLoan()).initNft(asset, uNftAddress); } /** * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ function updateReserveState(address reserve) external override { DataTypes.ReserveData storage reserveData = _reserves[reserve]; require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); reserveData.updateState(); } /** * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate * @param reserve The address of the reserve to be updated **/ function updateReserveInterestRates(address reserve) external override { DataTypes.ReserveData storage reserveData = _reserves[reserve]; address uTokenAddress = reserveData.uTokenAddress; require(uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); reserveData.updateInterestRates(reserve, uTokenAddress, 0, 0); } /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying uTokens. * - E.g. User deposits 100 USDC and gets in return 100 uusdc * @param asset The address of the underlying asset to deposit * @param amount The amount to be deposited * @param onBehalfOf The address that will receive the uTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of uTokens * is a different wallet * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function deposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external override nonReentrant whenNotPaused { SupplyLogic.executeDeposit( _reserves, DataTypes.ExecuteDepositParams({ initiator: _msgSender(), asset: asset, amount: amount, onBehalfOf: onBehalfOf, referralCode: referralCode }) ); } /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent uTokens owned * E.g. User has 100 uusdc, calls withdraw() and receives 100 USDC, burning the 100 uusdc * @param asset The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole uToken balance * @param to Address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet * @return The final amount withdrawn **/ function withdraw( address asset, uint256 amount, address to ) external override nonReentrant whenNotPaused returns (uint256) { return SupplyLogic.executeWithdraw( _reserves, DataTypes.ExecuteWithdrawParams({initiator: _msgSender(), asset: asset, amount: amount, to: to}) ); } /** * @dev Allows users to borrow a specific `amount` of the reserve underlying asset * - E.g. User borrows 100 USDC, receiving the 100 USDC in his wallet * and lock collateral asset in contract * @param asset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param nftAsset The address of the underlying nft used as collateral * @param nftTokenId The token ID of the underlying nft used as collateral * @param onBehalfOf Address of the user who will receive the loan. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function borrow( address asset, uint256 amount, address nftAsset, uint256 nftTokenId, address onBehalfOf, uint16 referralCode ) external override nonReentrant whenNotPaused { BorrowLogic.executeBorrow( _addressesProvider, _reserves, _nfts, _nftConfig, DataTypes.ExecuteBorrowParams({ initiator: _msgSender(), asset: asset, amount: amount, nftAsset: nftAsset, nftTokenId: nftTokenId, onBehalfOf: onBehalfOf, referralCode: referralCode }) ); } /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent loan owned * - E.g. User repays 100 USDC, burning loan and receives collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param amount The amount to repay **/ function repay( address nftAsset, uint256 nftTokenId, uint256 amount ) external override nonReentrant whenNotPaused returns (uint256, bool) { return BorrowLogic.executeRepay( _addressesProvider, _reserves, _nfts, _nftConfig, DataTypes.ExecuteRepayParams({ initiator: _msgSender(), nftAsset: nftAsset, nftTokenId: nftTokenId, amount: amount }) ); } /** * @dev Function to auction a non-healthy position collateral-wise * - The bidder want to buy collateral asset of the user getting liquidated * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param bidPrice The bid price of the bidder want to buy underlying NFT * @param onBehalfOf Address of the user who will get the underlying NFT, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of NFT * is a different wallet **/ function auction( address nftAsset, uint256 nftTokenId, uint256 bidPrice, address onBehalfOf ) external override nonReentrant whenNotPaused { LiquidateLogic.executeAuction( _addressesProvider, _reserves, _nfts, _nftConfig, _buildLendPoolVars(), DataTypes.ExecuteAuctionParams({ initiator: _msgSender(), nftAsset: nftAsset, nftTokenId: nftTokenId, bidPrice: bidPrice, onBehalfOf: onBehalfOf, auctionDurationConfigFee: _auctionDurationConfigFee, bidDelta: _bidDelta }) ); } /** * @dev Function to buyout a non-healthy position collateral-wise * - The bidder want to buy collateral asset of the user getting liquidated * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param buyoutAmount The buyout price of the underlying NFT * @param onBehalfOf Address of the user who will get the underlying NFT, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of NFT * is a different wallet **/ function buyout( address nftAsset, uint256 nftTokenId, uint256 buyoutAmount, address onBehalfOf ) external override nonReentrant whenNotPaused { LiquidateLogic.executeBuyout( _addressesProvider, _reserves, _nfts, _nftConfig, DataTypes.ExecuteBuyoutParams({ initiator: _msgSender(), nftAsset: nftAsset, nftTokenId: nftTokenId, amount: buyoutAmount, onBehalfOf: onBehalfOf }) ); } /** * @notice Redeem a NFT loan which state is in Auction * - E.g. User repays 100 USDC, burning loan and receives collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param amount The amount to repay the debt * @param bidFine The amount of bid fine **/ function redeem( address nftAsset, uint256 nftTokenId, uint256 amount, uint256 bidFine ) external override nonReentrant whenNotPaused returns (uint256) { return LiquidateLogic.executeRedeem( _addressesProvider, _reserves, _nfts, _nftConfig, _buildLendPoolVars(), DataTypes.ExecuteRedeemParams({ initiator: _msgSender(), nftAsset: nftAsset, nftTokenId: nftTokenId, amount: amount, bidFine: bidFine, safeHealthFactor: _safeHealthFactor }) ); } /** * @dev Function to liquidate a non-healthy position collateral-wise * - The caller (liquidator) buy collateral asset of the user getting liquidated, and receives * the collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral **/ function liquidate( address nftAsset, uint256 nftTokenId, uint256 amount ) external override nonReentrant whenNotPaused returns (uint256) { return LiquidateLogic.executeLiquidate( _addressesProvider, _reserves, _nfts, _nftConfig, _buildLendPoolVars(), DataTypes.ExecuteLiquidateParams({ initiator: _msgSender(), nftAsset: nftAsset, nftTokenId: nftTokenId, amount: amount }) ); } function approveValuation( address nftAsset, uint256 nftTokenId ) external payable override onlyHolder(nftAsset, nftTokenId) onlyCollection(nftAsset) whenNotPaused { require(_configFee == msg.value, Errors.LP_MSG_VALUE_DIFFERENT_FROM_CONFIG_FEE); emit ValuationApproved(_msgSender(), nftAsset, nftTokenId); } function transferBidAmount( address reserveAsset, address bidder, uint256 bidAmount ) external override onlyReservoirAdapter { IERC20(reserveAsset).safeTransfer(bidder, bidAmount); } /*////////////////////////////////////////////////////////////// INTERNALS //////////////////////////////////////////////////////////////*/ function _addReserveToList(address asset) internal { uint256 reservesCount = _reservesCount; require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; if (!reserveAlreadyAdded) { _reserves[asset].id = uint8(reservesCount); _reservesList[reservesCount] = asset; _reservesCount = reservesCount + 1; } } function _addNftToList(address asset) internal { uint256 nftsCount = _nftsCount; require(nftsCount < _maxNumberOfNfts, Errors.LP_NO_MORE_NFTS_ALLOWED); bool nftAlreadyAdded = _nfts[asset].id != 0 || _nftsList[0] == asset; if (!nftAlreadyAdded) { _nfts[asset].id = uint8(nftsCount); _nftsList[nftsCount] = asset; _nftsCount = nftsCount + 1; } } function _buildLendPoolVars() internal view returns (DataTypes.ExecuteLendPoolStates memory) { return DataTypes.ExecuteLendPoolStates({pauseStartTime: _pauseStartTime, pauseDurationTime: _pauseDurationTime}); } /*////////////////////////////////////////////////////////////// GETTERS & SETTERS //////////////////////////////////////////////////////////////*/ /** * @dev Returns the cached LendPoolAddressesProvider connected to this contract **/ function getAddressesProvider() external view override returns (ILendPoolAddressesProvider) { return _addressesProvider; } /** * @dev Returns the normalized income of the reserve * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome(address asset) external view override returns (uint256) { return _reserves[asset].getNormalizedIncome(); } /** * @dev Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt(address asset) external view override returns (uint256) { return _reserves[asset].getNormalizedDebt(); } /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state of the reserve **/ function getReserveData(address asset) public view override returns (DataTypes.ReserveData memory) { return _reserves[asset]; } /** * @dev Returns the state and configuration of the nft * @param asset The address of the underlying asset of the nft * @return The state of the nft **/ function getNftData(address asset) external view override returns (DataTypes.NftData memory) { return _nfts[asset]; } /** * @dev Returns the configuration of the nft asset * @param asset The address of the underlying asset of the nft * @param tokenId NFT asset ID * @return The configuration of the nft asset **/ function getNftAssetConfig( address asset, uint256 tokenId ) external view override returns (DataTypes.NftConfigurationMap memory) { return _nftConfig[asset][tokenId]; } /** * @dev Returns the loan data of the NFT * @param nftAsset The address of the NFT * @param reserveAsset The address of the Reserve * @return totalCollateralInETH the total collateral in ETH of the NFT * @return totalCollateralInReserve the total collateral in Reserve of the NFT * @return availableBorrowsInETH the borrowing power in ETH of the NFT * @return availableBorrowsInReserve the borrowing power in Reserve of the NFT * @return ltv the loan to value of the user * @return liquidationThreshold the liquidation threshold of the NFT * @return liquidationBonus the liquidation bonus of the NFT **/ function getNftCollateralData( address nftAsset, uint256 nftTokenId, address reserveAsset ) external view override returns ( uint256 totalCollateralInETH, uint256 totalCollateralInReserve, uint256 availableBorrowsInETH, uint256 availableBorrowsInReserve, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus ) { DataTypes.NftConfigurationMap storage nftConfig = _nftConfig[nftAsset][nftTokenId]; DataTypes.ReserveData storage reserveData = _reserves[reserveAsset]; (ltv, liquidationThreshold, liquidationBonus) = nftConfig.getCollateralParams(); (totalCollateralInETH, totalCollateralInReserve) = GenericLogic.calculateNftCollateralData( reserveAsset, reserveData, nftAsset, nftTokenId, _addressesProvider.getReserveOracle(), _addressesProvider.getNFTOracle() ); availableBorrowsInETH = GenericLogic.calculateAvailableBorrows(totalCollateralInETH, 0, ltv); availableBorrowsInReserve = GenericLogic.calculateAvailableBorrows(totalCollateralInReserve, 0, ltv); } /** * @dev Returns the debt data of the NFT * @param nftAsset The address of the NFT * @param nftTokenId The token id of the NFT * @return loanId the loan id of the NFT * @return reserveAsset the address of the Reserve * @return totalCollateral the total power of the NFT * @return totalDebt the total debt of the NFT * @return availableBorrows the borrowing power left of the NFT * @return healthFactor the current health factor of the NFT **/ function getNftDebtData( address nftAsset, uint256 nftTokenId ) external view override returns ( uint256 loanId, address reserveAsset, uint256 totalCollateral, uint256 totalDebt, uint256 availableBorrows, uint256 healthFactor ) { DataTypes.NftConfigurationMap storage nftConfig = _nftConfig[nftAsset][nftTokenId]; (uint256 ltv, uint256 liquidationThreshold, ) = nftConfig.getCollateralParams(); loanId = ILendPoolLoan(_addressesProvider.getLendPoolLoan()).getCollateralLoanId(nftAsset, nftTokenId); if (loanId == 0) { return (0, address(0), 0, 0, 0, 0); } DataTypes.LoanData memory loan = ILendPoolLoan(_addressesProvider.getLendPoolLoan()).getLoan(loanId); reserveAsset = loan.reserveAsset; DataTypes.ReserveData storage reserveData = _reserves[reserveAsset]; (, totalCollateral) = GenericLogic.calculateNftCollateralData( reserveAsset, reserveData, nftAsset, nftTokenId, _addressesProvider.getReserveOracle(), _addressesProvider.getNFTOracle() ); (, totalDebt) = ILendPoolLoan(_addressesProvider.getLendPoolLoan()).getLoanReserveBorrowAmount(loanId); availableBorrows = GenericLogic.calculateAvailableBorrows(totalCollateral, totalDebt, ltv); if (loan.state == DataTypes.LoanState.Active) { healthFactor = GenericLogic.calculateHealthFactorFromBalances(totalCollateral, totalDebt, liquidationThreshold); } } /** * @dev Returns the auction data of the NFT * @param nftAsset The address of the NFT * @param nftTokenId The token id of the NFT * @return loanId the loan id of the NFT * @return bidderAddress the highest bidder address of the loan * @return bidPrice the highest bid price in Reserve of the loan * @return bidBorrowAmount the borrow amount in Reserve of the loan * @return bidFine the penalty fine of the loan **/ function getNftAuctionData( address nftAsset, uint256 nftTokenId ) external view override returns (uint256 loanId, address bidderAddress, uint256 bidPrice, uint256 bidBorrowAmount, uint256 bidFine) { DataTypes.NftConfigurationMap storage nftConfig = _nftConfig[nftAsset][nftTokenId]; ILendPoolLoan poolLoan = ILendPoolLoan(_addressesProvider.getLendPoolLoan()); loanId = poolLoan.getCollateralLoanId(nftAsset, nftTokenId); if (loanId != 0) { DataTypes.LoanData memory loan = ILendPoolLoan(_addressesProvider.getLendPoolLoan()).getLoan(loanId); DataTypes.ReserveData storage reserveData = _reserves[loan.reserveAsset]; bidderAddress = loan.bidderAddress; bidPrice = loan.bidPrice; bidBorrowAmount = loan.bidBorrowAmount; (, bidFine) = GenericLogic.calculateLoanBidFine( loan.reserveAsset, reserveData, nftAsset, nftConfig, loan, address(poolLoan), _addressesProvider.getReserveOracle() ); } } /** * @dev Validates and finalizes an uToken transfer * - Only callable by the overlying uToken of the `asset` * @param asset The address of the underlying asset of the uToken * @param from The user from which the uToken are transferred */ function finalizeTransfer( address asset, address from, address, uint256, uint256, uint256 ) external view override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; require(_msgSender() == reserve.uTokenAddress, Errors.LP_CALLER_MUST_BE_AN_UTOKEN); ValidationLogic.validateTransfer(from, reserve); } /** * @dev Returns the list of the initialized reserves **/ function getReservesList() external view override returns (address[] memory) { address[] memory _activeReserves = new address[](_reservesCount); for (uint256 i; i != _reservesCount; ) { _activeReserves[i] = _reservesList[i]; unchecked { ++i; } } return _activeReserves; } /** * @dev Returns the list of the initialized nfts **/ function getNftsList() external view override returns (address[] memory) { address[] memory _activeNfts = new address[](_nftsCount); for (uint256 i; i != _nftsCount; ) { _activeNfts[i] = _nftsList[i]; unchecked { i = i + 1; } } return _activeNfts; } /** * @dev Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getReserveConfiguration( address asset ) external view override returns (DataTypes.ReserveConfigurationMap memory) { return _reserves[asset].configuration; } /** * @dev Sets the configuration bitmap of the reserve as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param configuration The new configuration bitmap **/ function setReserveConfiguration(address asset, uint256 configuration) external override onlyLendPoolConfigurator { _reserves[asset].configuration.data = configuration; emit ReserveConfigurationChanged(asset, configuration); } /** * @dev Returns the configuration of the NFT * @param asset The address of the asset of the NFT * @return The configuration of the NFT **/ function getNftConfiguration(address asset) external view override returns (DataTypes.NftConfigurationMap memory) { return _nfts[asset].configuration; } /** * @dev Sets the configuration bitmap of the NFT as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the asset of the NFT * @param configuration The new configuration bitmap **/ function setNftConfiguration(address asset, uint256 configuration) external override onlyLendPoolConfigurator { _nfts[asset].configuration.data = configuration; emit NftConfigurationChanged(asset, configuration); } function getNftConfigByTokenId( address asset, uint256 nftTokenId ) external view override returns (DataTypes.NftConfigurationMap memory) { return _nftConfig[asset][nftTokenId]; } /** * @dev Sets the configuration bitmap of the NFT as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the asset of the NFT * @param nftTokenId the tokenId of the asset * @param configuration The new configuration bitmap **/ function setNftConfigByTokenId( address asset, uint256 nftTokenId, uint256 configuration ) external override onlyLendPoolConfigurator { _nftConfig[asset][nftTokenId].data = configuration; emit NftConfigurationByIdChanged(asset, nftTokenId, configuration); } /** * @dev Returns if the LendPool is paused */ function paused() external view override returns (bool) { return _paused; } /** * @dev Set the _pause state of the pool * - Only callable by the LendPoolConfigurator contract * @param val `true` to pause the pool, `false` to un-pause it */ function setPause(bool val) external override onlyLendPoolConfigurator { if (_paused != val) { _paused = val; if (_paused) { _pauseStartTime = block.timestamp; emit Paused(); } else { _pauseDurationTime = block.timestamp - _pauseStartTime; emit Unpaused(); } } } function setPausedTime(uint256 startTime, uint256 durationTime) external override onlyLendPoolConfigurator { _pauseStartTime = startTime; _pauseDurationTime = durationTime; emit PausedTimeUpdated(startTime, durationTime); } function getPausedTime() external view override returns (uint256, uint256) { return (_pauseStartTime, _pauseDurationTime); } /** * @dev Sets the max number of reserves in the protocol * @param val the value to set the max number of reserves **/ function setMaxNumberOfReserves(uint256 val) external override onlyLendPoolConfigurator { require(val <= 255, Errors.LP_INVALID_OVERFLOW_VALUE); //Sanity check to avoid overflows in `_addReserveToList` _maxNumberOfReserves = val; } /** * @dev Returns the maximum number of reserves supported to be listed in this LendPool */ function getMaxNumberOfReserves() external view override returns (uint256) { return _maxNumberOfReserves; } /** * @dev Sets the max number of NFTs in the protocol * @param val the value to set the max number of NFTs **/ function setMaxNumberOfNfts(uint256 val) external override onlyLendPoolConfigurator { require(val <= 255, Errors.LP_INVALID_OVERFLOW_VALUE); //Sanity check to avoid overflows in `_addNftToList` _maxNumberOfNfts = val; } /** * @dev Returns the maximum number of nfts supported to be listed in this LendPool */ function getMaxNumberOfNfts() external view override returns (uint256) { return _maxNumberOfNfts; } function setLiquidateFeePercentage(uint256 percentage) external override onlyLendPoolConfigurator { _liquidateFeePercentage = percentage; } /** * @dev Returns the liquidate fee percentage */ function getLiquidateFeePercentage() external view override returns (uint256) { return _liquidateFeePercentage; } /** * @dev Sets the max timeframe between NFT config triggers and borrows * @param timeframe the number of seconds for the timeframe **/ function setTimeframe(uint256 timeframe) external override onlyLendPoolConfigurator { _timeframe = timeframe; } /** * @dev Returns the max timeframe between NFT config triggers and borrows **/ function getTimeframe() external view override returns (uint256) { return _timeframe; } /** * @dev Sets configFee amount to be charged for ConfigureNFTAsColleteral * @param configFee the number of seconds for the timeframe **/ function setConfigFee(uint256 configFee) external override onlyLendPoolConfigurator { _configFee = configFee; } /** * @dev Returns the configFee amount **/ function getConfigFee() external view override returns (uint256) { return _configFee; } /** * @dev sets the fee to be charged on first bid on nft * @param auctionDurationConfigFee the amount to charge to the user **/ function setAuctionDurationConfigFee(uint256 auctionDurationConfigFee) external override onlyLendPoolConfigurator { _auctionDurationConfigFee = auctionDurationConfigFee; } /** * @dev Returns the auctionDurationConfigFee amount **/ function getAuctionDurationConfigFee() public view override returns (uint256) { return _auctionDurationConfigFee; } /** * @dev sets the bidDelta percentage - debt compounded + fees. * @param bidDelta the amount to charge to the user **/ function setBidDelta(uint256 bidDelta) external override onlyLendPoolConfigurator { _bidDelta = bidDelta; } /** * @dev Returns the bidDelta percentage - debt compounded + fees. **/ function getBidDelta() public view override returns (uint256) { return _bidDelta; } /** * @notice Update the safe health factor value for redeems * @param newSafeHealthFactor New safe health factor value */ function updateSafeHealthFactor(uint256 newSafeHealthFactor) external override onlyPoolAdmin { require(newSafeHealthFactor != 0, Errors.LP_INVALID_SAFE_HEALTH_FACTOR); _safeHealthFactor = newSafeHealthFactor; emit SafeHealthFactorUpdated(newSafeHealthFactor); } /** * @notice Returns current safe health factor * @return The safe health factor value */ function getSafeHealthFactor() external view override returns (uint256) { return _safeHealthFactor; } /** * @dev Updates the address of the interest rate strategy contract * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param rateAddress The address of the interest rate strategy contract **/ function setReserveInterestRateAddress( address asset, address rateAddress ) external override onlyLendPoolConfigurator { require(asset != address(0) && rateAddress != address(0), Errors.INVALID_ZERO_ADDRESS); _reserves[asset].interestRateAddress = rateAddress; emit ReserveInterestRateAddressChanged(asset, rateAddress); } /** * @dev Sets the max supply and token ID for a given asset * @param asset The address to set the data * @param maxSupply The max supply value * @param maxTokenId The max token ID value **/ function setNftMaxSupplyAndTokenId( address asset, uint256 maxSupply, uint256 maxTokenId ) external override onlyLendPoolConfigurator { _nfts[asset].maxSupply = maxSupply; _nfts[asset].maxTokenId = maxTokenId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol) pragma solidity ^0.8.0; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() initializer {} * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { // If the contract is initializing we ignore whether _initialized is set in order to support multiple // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the // contract may have been reentered. require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} modifier, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Enumerable.sol) pragma solidity ^0.8.0; import "../IERC721Upgradeable.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721EnumerableUpgradeable is IERC721Upgradeable { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721Upgradeable.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721MetadataUpgradeable is IERC721Upgradeable { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (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 IERC721ReceiverUpgradeable { /** * @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 `IERC721.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721Upgradeable is IERC165Upgradeable { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { __Context_init_unchained(); } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library CountersUpgradeable { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /** * @title IDebtToken * @author BendDao; Forked and edited by Unlockd * @notice Defines the basic interface for a debt token. **/ interface ICryptoPunksMarket { function punkIndexToAddress(uint256 index) external view returns (address); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {DataTypes} from "../libraries/types/DataTypes.sol"; import {CountersUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; interface IDebtMarket { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted on initialization to share location of dependent notes * @param pool The address of the associated lend pool */ event Initialized(address indexed pool); /** * @dev Emitted when a debt listing is created with a fixed price * @param debtId The debt listing identifier * @param debtor The owner of the debt listing * @param nftAsset The address of the underlying NFT used as collateral * @param tokenId The token id of the underlying NFT used as collateral * @param sellType The type of sell ( Fixed price or Auction ) * @param state The state of the actual debt offer ( New,Active,Sold,Canceled ) * @param sellPrice The price for to sell the debt * @param reserveAsset The asset from the reserve * @param debtAmount The total debt value */ event DebtListingCreated( uint256 debtId, address debtor, address indexed nftAsset, uint256 indexed tokenId, DataTypes.DebtMarketType indexed sellType, DataTypes.DebtMarketState state, uint256 sellPrice, address reserveAsset, uint256 debtAmount, uint256 auctionEndTimestamp, uint256 startBiddingPrice ); /** * @dev Emitted when a debt with auction listing is created * @param debtId The debt listing identifier * @param debtor The owner of the debt listing * @param nftAsset The address of the underlying NFT used as collateral * @param tokenId The token id of the underlying NFT used as collateral * @param sellType The type of sell ( Fixed price or Auction ) * @param state The state of the actual debt offer ( New,Active,Sold,Canceled ) * @param sellPrice The price for to sell the debt * @param reserveAsset The asset from the reserve * @param debtAmount The total debt value */ event DebtAuctionCreated( uint256 debtId, address debtor, address indexed nftAsset, uint256 indexed tokenId, DataTypes.DebtMarketType indexed sellType, DataTypes.DebtMarketState state, uint256 sellPrice, address reserveAsset, uint256 debtAmount ); /** * @dev Emitted when a debt listing is canceled * @param onBehalfOf Address of the user who will receive * @param debtId The debt listing identifier * @param marketListing The object of the debt * @param totalByCollection Total debts listings by collection from the actual debtId collection * @param totalByUserAndCollection Total debts listings by user from the actual debtId user */ event DebtListingCanceled( address indexed onBehalfOf, uint256 indexed debtId, DataTypes.DebtMarketListing marketListing, uint256 totalByCollection, uint256 totalByUserAndCollection ); /** * @dev Emitted when a bid is placed on a debt listing with auction * @param bidderAddress Address of the last bidder * @param reserveAsset The asset from the reserve * @param nftAsset The address of the underlying NFT used as collateral * @param tokenId The token id of the underlying NFT used as collateral * @param debtId The debt listing identifier * @param bidPrice Amount that bidder spend on the bid */ event BidPlaced( address bidderAddress, address reserveAsset, address indexed nftAsset, uint256 indexed tokenId, uint256 debtId, uint256 bidPrice ); /** * @dev Emitted when a debt is bought * @param from Address of owner of the debt * @param to Buyer address * @param debtId The debt listing identifier */ event DebtSold(address indexed from, address indexed to, uint256 indexed debtId); /** * @dev Emitted when a debt is claimed * @param from Address of owner of the debt * @param to Claimer address * @param debtId The debt listing identifier */ event DebtClaimed(address indexed from, address indexed to, uint256 indexed debtId); /** * @dev Emited when a new address is authorized to cancel debt listings * @param authorizedAddress Address to authorize */ event AuthorizedAddressChanged(address indexed authorizedAddress, bool isAuthorized); /** * @dev Emitted when the pause is triggered. */ event Paused(); /** * @dev Emitted when the pause is lifted. */ event Unpaused(); /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ function createDebtListing( address nftAsset, uint256 tokenId, uint256 sellPrice, address onBehalfOf, uint256 startBiddingPrice, uint256 auctionEndTimestamp ) external; function cancelDebtListing(address nftAsset, uint256 tokenId) external; function buy(address nftAsset, uint256 tokenId, address onBehalfOf, uint256 amount) external; function bid(address nftAsset, uint256 tokenId, uint256 bidPrice, address onBehalfOf) external; function claim(address nftAsset, uint256 tokenId, address onBehalfOf) external; /*////////////////////////////////////////////////////////////// GETTERS & SETTERS //////////////////////////////////////////////////////////////*/ function getDebtId(address nftAsset, uint256 tokenId) external view returns (uint256); function getDebt(uint256 debtId) external view returns (DataTypes.DebtMarketListing memory sellDebt); function getDebtIdTracker() external view returns (CountersUpgradeable.Counter memory); function setDeltaBidPercent(uint256 value) external; function setAuthorizedAddress(address newAuthorizedAddress, bool val) external; function paused() external view returns (bool); function setPause(bool val) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ILendPoolAddressesProvider} from "../interfaces/ILendPoolAddressesProvider.sol"; import {IIncentivesController} from "./IIncentivesController.sol"; import {IScaledBalanceToken} from "./IScaledBalanceToken.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; /** * @title IDebtToken * @author BendDao; Forked and edited by Unlockd * @notice Defines the basic interface for a debt token. **/ interface IDebtToken is IScaledBalanceToken, IERC20Upgradeable, IERC20MetadataUpgradeable { /** * @dev Emitted when a debt token is initialized * @param underlyingAsset The address of the underlying asset * @param pool The address of the associated lend pool * @param incentivesController The address of the incentives controller * @param debtTokenDecimals the decimals of the debt token * @param debtTokenName the name of the debt token * @param debtTokenSymbol the symbol of the debt token **/ event Initialized( address indexed underlyingAsset, address indexed pool, address incentivesController, uint8 debtTokenDecimals, string debtTokenName, string debtTokenSymbol ); /** * @dev Initializes the debt token. * @param addressProvider The address of the lend pool * @param underlyingAsset The address of the underlying asset * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's * @param debtTokenName The name of the token * @param debtTokenSymbol The symbol of the token */ function initialize( ILendPoolAddressesProvider addressProvider, address underlyingAsset, uint8 debtTokenDecimals, string memory debtTokenName, string memory debtTokenSymbol ) external; /** * @dev Emitted after the mint action * @param from The address performing the mint * @param value The amount to be minted * @param index The last index of the reserve **/ event Mint(address indexed from, uint256 value, uint256 index); /** * @dev Emitted after setting of addresses as debt token managers * @param debtTokenManagers the addresses to be updated * @param flag `true` to set addresses as managers, `false` otherwise **/ event TokenManagersUpdated(address[] indexed debtTokenManagers, bool flag); /** * @dev Mints debt token to the `user` address * @param user The address receiving the borrowed underlying * @param onBehalfOf The beneficiary of the mint * @param amount The amount of debt being minted * @param index The variable debt index of the reserve * @return `true` if the the previous balance of the user is 0 **/ function mint(address user, address onBehalfOf, uint256 amount, uint256 index) external returns (bool); /** * @dev Emitted when variable debt is burnt * @param user The user which debt has been burned * @param amount The amount of debt being burned * @param index The index of the user **/ event Burn(address indexed user, uint256 amount, uint256 index); /** * @dev Burns user variable debt * @param user The user which debt is burnt * @param amount The amount to be burnt * @param index The variable debt index of the reserve **/ function burn(address user, uint256 amount, uint256 index) external; /** * @dev Returns the address of the incentives controller contract **/ function getIncentivesController() external view returns (IIncentivesController); /** * @dev delegates borrowing power to a user on the specific debt token * @param delegatee the address receiving the delegated borrowing power * @param amount the maximum amount being delegated. Delegation will still * respect the liquidation constraints (even if delegated, a delegatee cannot * force a delegator HF to go below 1) **/ function approveDelegation(address delegatee, uint256 amount) external; /** * @dev returns the borrow allowance of the user * @param fromUser The user to giving allowance * @param toUser The user to give allowance to * @return the current allowance of toUser **/ function borrowAllowance(address fromUser, address toUser) external view returns (uint256); /** * @dev Updates allowed addresses to be debt token managers * @param debtTokenManagers array with addresses to be updated * @param flag `true` to set as manager, `false` to unset as manager **/ function updateTokenManagers(address[] calldata debtTokenManagers, bool flag) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; interface IIncentivesController { /** * @dev Called by the corresponding asset on any update that affects the rewards distribution * @param asset The address of the user * @param totalSupply The total supply of the asset in the lending pool * @param userBalance The balance of the user of the asset in the lending pool **/ function handleAction( address asset, uint256 totalSupply, uint256 userBalance ) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /** * @title IInterestRate interface * @dev Interface for the calculation of the interest rates * @author BendDao; Forked and edited by Unlockd */ interface IInterestRate { /** * @dev Get the variable borrow rate * @return the base variable borrow rate **/ function baseVariableBorrowRate() external view returns (uint256); /** * @dev Get the maximum variable borrow rate * @return the maximum variable borrow rate **/ function getMaxVariableBorrowRate() external view returns (uint256); /** * @dev Calculates the interest rates depending on the reserve's state and configurations * @param reserve The address of the reserve * @param availableLiquidity The available liquidity for the reserve * @param totalVariableDebt The total borrowed from the reserve at a variable rate * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market **/ function calculateInterestRates( address reserve, uint256 availableLiquidity, uint256 totalVariableDebt, uint256 reserveFactor ) external view returns (uint256, uint256); /** * @dev Calculates the interest rates depending on the reserve's state and configurations * @param reserve The address of the reserve * @param uToken The uToken address * @param liquidityAdded The liquidity added during the operation * @param liquidityTaken The liquidity taken during the operation * @param totalVariableDebt The total borrowed from the reserve at a variable rate * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market **/ function calculateInterestRates( address reserve, address uToken, uint256 liquidityAdded, uint256 liquidityTaken, uint256 totalVariableDebt, uint256 reserveFactor ) external view returns (uint256 liquidityRate, uint256 variableBorrowRate); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ILendPoolAddressesProvider} from "./ILendPoolAddressesProvider.sol"; import {IUToken} from "./IUToken.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import {DataTypes} from "../libraries/types/DataTypes.sol"; interface ILendPool { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted when _rescuer is modified in the LendPool * @param newRescuer The address of the new rescuer **/ event RescuerChanged(address indexed newRescuer); /** * @dev Emitted on deposit() * @param user The address initiating the deposit * @param amount The amount deposited * @param reserve The address of the underlying asset of the reserve * @param onBehalfOf The beneficiary of the deposit, receiving the uTokens * @param referral The referral code used **/ event Deposit( address user, address indexed reserve, uint256 amount, address indexed onBehalfOf, uint16 indexed referral ); /** * @dev Emitted on withdraw() * @param user The address initiating the withdrawal, owner of uTokens * @param reserve The address of the underlyng asset being withdrawn * @param amount The amount to be withdrawn * @param to Address that will receive the underlying **/ event Withdraw(address indexed user, address indexed reserve, uint256 amount, address indexed to); /** * @dev Emitted on borrow() when loan needs to be opened * @param user The address of the user initiating the borrow(), receiving the funds * @param reserve The address of the underlying asset being borrowed * @param amount The amount borrowed out * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param onBehalfOf The address that will be getting the loan * @param referral The referral code used * @param nftConfigFee an estimated gas cost fee for configuring the NFT **/ event Borrow( address user, address indexed reserve, uint256 amount, address nftAsset, uint256 nftTokenId, address indexed onBehalfOf, uint256 borrowRate, uint256 loanId, uint16 indexed referral, uint256 nftConfigFee ); /** * @dev Emitted on repay() * @param user The address of the user initiating the repay(), providing the funds * @param reserve The address of the underlying asset of the reserve * @param amount The amount repaid * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param borrower The beneficiary of the repayment, getting his debt reduced * @param loanId The loan ID of the NFT loans **/ event Repay( address user, address indexed reserve, uint256 amount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /** * @dev Emitted when a borrower's loan is auctioned. * @param user The address of the user initiating the auction * @param reserve The address of the underlying asset of the reserve * @param bidPrice The price of the underlying reserve given by the bidder * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param onBehalfOf The address that will be getting the NFT * @param loanId The loan ID of the NFT loans **/ event Auction( address user, address indexed reserve, uint256 bidPrice, address indexed nftAsset, uint256 nftTokenId, address onBehalfOf, address indexed borrower, uint256 loanId ); /** * @dev Emitted on redeem() * @param user The address of the user initiating the redeem(), providing the funds * @param reserve The address of the underlying asset of the reserve * @param borrowAmount The borrow amount repaid * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param loanId The loan ID of the NFT loans **/ event Redeem( address user, address indexed reserve, uint256 borrowAmount, uint256 fineAmount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /** * @dev Emitted when a borrower's loan is liquidated. * @param user The address of the user initiating the auction * @param reserve The address of the underlying asset of the reserve * @param repayAmount The amount of reserve repaid by the liquidator * @param remainAmount The amount of reserve received by the borrower * @param loanId The loan ID of the NFT loans **/ event Liquidate( address user, address indexed reserve, uint256 repayAmount, uint256 remainAmount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /** * @dev Emitted when an NFT is purchased via Buyout. * @param user The address of the user initiating the Buyout * @param reserve The address of the underlying asset of the reserve * @param buyoutAmount The amount of reserve paid by the buyer * @param borrowAmount The loan borrowed amount * @param nftAsset The amount of reserve received by the borrower * @param nftTokenId The token id of the underlying NFT used as collateral * @param borrower The loan borrower address * @param onBehalfOf The receiver of the underlying NFT * @param loanId The loan ID of the NFT loans **/ event Buyout( address user, address indexed reserve, uint256 buyoutAmount, uint256 borrowAmount, address indexed nftAsset, uint256 nftTokenId, address borrower, address onBehalfOf, uint256 indexed loanId ); /** * @dev Emitted when an NFT configuration is triggered. * @param user The NFT holder * @param nftAsset The NFT collection address * @param nftTokenId The NFT token Id **/ event ValuationApproved(address indexed user, address indexed nftAsset, uint256 indexed nftTokenId); /** * @dev Emitted when the pause is triggered. */ event Paused(); /** * @dev Emitted when the pause is lifted. */ event Unpaused(); /** * @dev Emitted when the pause time is updated. */ event PausedTimeUpdated(uint256 startTime, uint256 durationTime); /** * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, * the event will actually be fired by the LendPool contract. The event is therefore replicated here so it * gets added to the LendPool ABI * @param reserve The address of the underlying asset of the reserve * @param liquidityRate The new liquidity rate * @param variableBorrowRate The new variable borrow rate * @param liquidityIndex The new liquidity index * @param variableBorrowIndex The new variable borrow index **/ event ReserveDataUpdated( address indexed reserve, uint256 liquidityRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** @dev Emitted after the address of the interest rate strategy contract has been updated */ event ReserveInterestRateAddressChanged(address indexed asset, address indexed rateAddress); /** @dev Emitted after setting the configuration bitmap of the reserve as a whole */ event ReserveConfigurationChanged(address indexed asset, uint256 configuration); /** @dev Emitted after setting the configuration bitmap of the NFT collection as a whole */ event NftConfigurationChanged(address indexed asset, uint256 configuration); /** @dev Emitted after setting the configuration bitmap of the NFT as a whole */ event NftConfigurationByIdChanged(address indexed asset, uint256 indexed nftTokenId, uint256 configuration); /** @dev Emitted after setting the new safe health factor value for redeems */ event SafeHealthFactorUpdated(uint256 indexed newSafeHealthFactor); /*////////////////////////////////////////////////////////////// RESCUERS //////////////////////////////////////////////////////////////*/ /** * @notice Returns current rescuer * @return Rescuer's address */ function rescuer() external view returns (address); /** * @notice Assigns the rescuer role to a given address. * @param newRescuer New rescuer's address */ function updateRescuer(address newRescuer) external; /** * @notice Rescue tokens or ETH locked up in this contract. * @param tokenContract ERC20 token contract address * @param to Recipient address * @param amount Amount to withdraw * @param rescueETH bool to know if we want to rescue ETH or other token */ function rescue(IERC20 tokenContract, address to, uint256 amount, bool rescueETH) external; /** * @notice Rescue NFTs locked up in this contract. * @param nftAsset ERC721 asset contract address * @param tokenId ERC721 token id * @param to Recipient address */ function rescueNFT(IERC721Upgradeable nftAsset, uint256 tokenId, address to) external; /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying uTokens. * - E.g. User deposits 100 USDC and gets in return 100 uusdc * @param reserve The address of the underlying asset to deposit * @param amount The amount to be deposited * @param onBehalfOf The address that will receive the uTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of uTokens * is a different wallet * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function deposit(address reserve, uint256 amount, address onBehalfOf, uint16 referralCode) external; /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent uTokens owned * E.g. User has 100 uusdc, calls withdraw() and receives 100 USDC, burning the 100 uusdc * @param reserve The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole uToken balance * @param to Address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet * @return The final amount withdrawn **/ function withdraw(address reserve, uint256 amount, address to) external returns (uint256); /** * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower * already deposited enough collateral * - E.g. User borrows 100 USDC, receiving the 100 USDC in his wallet * and lock collateral asset in contract * @param reserveAsset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param onBehalfOf Address of the user who will receive the loan. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator * if he has been given credit delegation allowance * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function borrow( address reserveAsset, uint256 amount, address nftAsset, uint256 nftTokenId, address onBehalfOf, uint16 referralCode ) external; /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent loan owned * - E.g. User repays 100 USDC, burning loan and receives collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param amount The amount to repay * @return The final amount repaid, loan is burned or not **/ function repay(address nftAsset, uint256 nftTokenId, uint256 amount) external returns (uint256, bool); /** * @dev Function to auction a non-healthy position collateral-wise * - The caller (liquidator) want to buy collateral asset of the user getting liquidated * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param bidPrice The bid price of the liquidator want to buy the underlying NFT * @param onBehalfOf Address of the user who will get the underlying NFT, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of NFT * is a different wallet **/ function auction(address nftAsset, uint256 nftTokenId, uint256 bidPrice, address onBehalfOf) external; /** * @dev Function to buyout a non-healthy position collateral-wise * - The bidder want to buy collateral asset of the user getting liquidated * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param buyoutAmount The buyout price of the underlying NFT * @param onBehalfOf Address of the user who will get the underlying NFT, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of NFT * is a different wallet **/ function buyout(address nftAsset, uint256 nftTokenId, uint256 buyoutAmount, address onBehalfOf) external; /** * @notice Redeem a NFT loan which state is in Auction * - E.g. User repays 100 USDC, burning loan and receives collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral * @param amount The amount to repay the debt * @param bidFine The amount of bid fine **/ function redeem(address nftAsset, uint256 nftTokenId, uint256 amount, uint256 bidFine) external returns (uint256); /** * @dev Function to liquidate a non-healthy position collateral-wise * - The caller (liquidator) buy collateral asset of the user getting liquidated, and receives * the collateral asset * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral **/ function liquidate(address nftAsset, uint256 nftTokenId, uint256 amount) external returns (uint256); /** * @dev Approves valuation of an NFT for a user * @dev Just the NFT holder can trigger the configuration * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token ID of the underlying NFT used as collateral **/ function approveValuation(address nftAsset, uint256 nftTokenId) external payable; /** * @dev Validates and finalizes an uToken transfer * - Only callable by the overlying uToken of the `asset` * @param asset The address of the underlying asset of the uToken * @param from The user from which the uTokens are transferred * @param to The user receiving the uTokens * @param amount The amount being transferred/withdrawn * @param balanceFromBefore The uToken balance of the `from` user before the transfer * @param balanceToBefore The uToken balance of the `to` user before the transfer */ function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromBefore, uint256 balanceToBefore ) external view; /** * @dev Initializes a reserve, activating it, assigning an uToken and nft loan and an * interest rate strategy * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param uTokenAddress The address of the uToken that will be assigned to the reserve * @param debtTokenAddress The address of the debtToken that will be assigned to the reserve * @param interestRateAddress The address of the interest rate strategy contract **/ function initReserve( address asset, address uTokenAddress, address debtTokenAddress, address interestRateAddress ) external; /** * @dev Initializes a nft, activating it, assigning nft loan and an * interest rate strategy * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the nft **/ function initNft(address asset, address uNftAddress) external; /** * @dev Transfer the last bid amount to the bidder * @param reserveAsset address of the reserver asset (WETH) * @param bidder the bidder address * @param bidAmount the bid amount */ function transferBidAmount(address reserveAsset, address bidder, uint256 bidAmount) external; /*////////////////////////////////////////////////////////////// GETTERS & SETTERS //////////////////////////////////////////////////////////////*/ /** * @dev Returns the cached LendPoolAddressesProvider connected to this contract **/ function getAddressesProvider() external view returns (ILendPoolAddressesProvider); /** * @dev Returns the normalized income normalized income of the reserve * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome(address asset) external view returns (uint256); /** * @dev Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state of the reserve **/ function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); /** * @dev Returns the list of the initialized reserves * @return the list of initialized reserves **/ function getReservesList() external view returns (address[] memory); /** * @dev Returns the state and configuration of the nft * @param asset The address of the underlying asset of the nft * @return The status of the nft **/ function getNftData(address asset) external view returns (DataTypes.NftData memory); /** * @dev Returns the configuration of the nft asset * @param asset The address of the underlying asset of the nft * @param tokenId NFT asset ID * @return The configuration of the nft asset **/ function getNftAssetConfig( address asset, uint256 tokenId ) external view returns (DataTypes.NftConfigurationMap memory); /** * @dev Returns the loan data of the NFT * @param nftAsset The address of the NFT * @param reserveAsset The address of the Reserve * @return totalCollateralInETH the total collateral in ETH of the NFT * @return totalCollateralInReserve the total collateral in Reserve of the NFT * @return availableBorrowsInETH the borrowing power in ETH of the NFT * @return availableBorrowsInReserve the borrowing power in Reserve of the NFT * @return ltv the loan to value of the user * @return liquidationThreshold the liquidation threshold of the NFT * @return liquidationBonus the liquidation bonus of the NFT **/ function getNftCollateralData( address nftAsset, uint256 nftTokenId, address reserveAsset ) external view returns ( uint256 totalCollateralInETH, uint256 totalCollateralInReserve, uint256 availableBorrowsInETH, uint256 availableBorrowsInReserve, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus ); /** * @dev Returns the debt data of the NFT * @param nftAsset The address of the NFT * @param nftTokenId The token id of the NFT * @return loanId the loan id of the NFT * @return reserveAsset the address of the Reserve * @return totalCollateral the total power of the NFT * @return totalDebt the total debt of the NFT * @return availableBorrows the borrowing power left of the NFT * @return healthFactor the current health factor of the NFT **/ function getNftDebtData( address nftAsset, uint256 nftTokenId ) external view returns ( uint256 loanId, address reserveAsset, uint256 totalCollateral, uint256 totalDebt, uint256 availableBorrows, uint256 healthFactor ); /** * @dev Returns the auction data of the NFT * @param nftAsset The address of the NFT * @param nftTokenId The token id of the NFT * @return loanId the loan id of the NFT * @return bidderAddress the highest bidder address of the loan * @return bidPrice the highest bid price in Reserve of the loan * @return bidBorrowAmount the borrow amount in Reserve of the loan * @return bidFine the penalty fine of the loan **/ function getNftAuctionData( address nftAsset, uint256 nftTokenId ) external view returns (uint256 loanId, address bidderAddress, uint256 bidPrice, uint256 bidBorrowAmount, uint256 bidFine); /** * @dev Returns the list of nft addresses in the protocol **/ function getNftsList() external view returns (address[] memory); /** * @dev Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getReserveConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory); /** * @dev Sets the configuration bitmap of the reserve as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param configuration The new configuration bitmap **/ function setReserveConfiguration(address asset, uint256 configuration) external; /** * @dev Returns the configuration of the NFT * @param asset The address of the asset of the NFT * @return The configuration of the NFT **/ function getNftConfiguration(address asset) external view returns (DataTypes.NftConfigurationMap memory); /** * @dev Sets the configuration bitmap of the NFT as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the asset of the NFT * @param configuration The new configuration bitmap **/ function setNftConfiguration(address asset, uint256 configuration) external; /** * @dev Returns the configuration of the NFT * @param asset The address of the asset of the NFT * @param tokenId the Token Id of the NFT * @return The configuration of the NFT **/ function getNftConfigByTokenId( address asset, uint256 tokenId ) external view returns (DataTypes.NftConfigurationMap memory); /** * @dev Sets the configuration bitmap of the NFT as a whole * - Only callable by the LendPoolConfigurator contract * @param asset The address of the asset of the NFT * @param nftTokenId the NFT tokenId * @param configuration The new configuration bitmap **/ function setNftConfigByTokenId(address asset, uint256 nftTokenId, uint256 configuration) external; /** * @dev Returns if the LendPool is paused */ function paused() external view returns (bool); /** * @dev Set the _pause state of a reserve * - Only callable by the LendPool contract * @param val `true` to pause the reserve, `false` to un-pause it */ function setPause(bool val) external; /** * @dev Returns the _pause time of a reserve */ function getPausedTime() external view returns (uint256, uint256); /** * @dev Set the _pause state of the auctions * @param startTime when it will start to pause * @param durationTime how long it will pause */ function setPausedTime(uint256 startTime, uint256 durationTime) external; /** * @dev Returns the bidDelta percentage - debt compounded + fees. **/ function getBidDelta() external view returns (uint256); /** * @dev sets the bidDelta percentage - debt compounded + fees. * @param bidDelta the amount to charge to the user **/ function setBidDelta(uint256 bidDelta) external; /** * @dev Returns the max timeframe between NFT config triggers and borrows **/ function getTimeframe() external view returns (uint256); /** * @dev Sets the max timeframe between NFT config triggers and borrows * @param timeframe the number of seconds for the timeframe **/ function setTimeframe(uint256 timeframe) external; /** * @dev Returns the configFee amount **/ function getConfigFee() external view returns (uint256); /** * @dev sets the fee for configuringNFTAsCollateral * @param configFee the amount to charge to the user **/ function setConfigFee(uint256 configFee) external; /** * @dev Returns the auctionDurationConfigFee amount **/ function getAuctionDurationConfigFee() external view returns (uint256); /** * @dev sets the fee to be charged on first bid on nft * @param auctionDurationConfigFee the amount to charge to the user **/ function setAuctionDurationConfigFee(uint256 auctionDurationConfigFee) external; /** * @dev Returns the maximum number of reserves supported to be listed in this LendPool */ function getMaxNumberOfReserves() external view returns (uint256); /** * @dev Sets the max number of reserves in the protocol * @param val the value to set the max number of reserves **/ function setMaxNumberOfReserves(uint256 val) external; /** * @notice Returns current safe health factor * @return The safe health factor value */ function getSafeHealthFactor() external view returns (uint256); /** * @notice Update the safe health factor value for redeems * @param newSafeHealthFactor New safe health factor value */ function updateSafeHealthFactor(uint256 newSafeHealthFactor) external; /** * @dev Returns the maximum number of nfts supported to be listed in this LendPool */ function getMaxNumberOfNfts() external view returns (uint256); /** * @dev Sets the max number of NFTs in the protocol * @param val the value to set the max number of NFTs **/ function setMaxNumberOfNfts(uint256 val) external; /** * @dev Returns the fee percentage for liquidations **/ function getLiquidateFeePercentage() external view returns (uint256); /** * @dev Sets the fee percentage for liquidations * @param percentage the fee percentage to be set **/ function setLiquidateFeePercentage(uint256 percentage) external; /** * @dev Updates the address of the interest rate strategy contract * - Only callable by the LendPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param rateAddress The address of the interest rate strategy contract **/ function setReserveInterestRateAddress(address asset, address rateAddress) external; /** * @dev Sets the max supply and token ID for a given asset * @param asset The address to set the data * @param maxSupply The max supply value * @param maxTokenId The max token ID value **/ function setNftMaxSupplyAndTokenId(address asset, uint256 maxSupply, uint256 maxTokenId) external; /** * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ function updateReserveState(address reserve) external; /** * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate * @param reserve The address of the reserve to be updated **/ function updateReserveInterestRates(address reserve) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /** * @title LendPoolAddressesProvider contract * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles * - Acting also as factory of proxies and admin of those, so with right to change its implementations * - Owned by the Unlockd Governance * @author BendDao; Forked and edited by Unlockd **/ interface ILendPoolAddressesProvider { event MarketIdSet(string newMarketId); event LendPoolUpdated(address indexed newAddress, bytes encodedCallData); event ConfigurationAdminUpdated(address indexed newAddress); event EmergencyAdminUpdated(address indexed newAddress); event LendPoolConfiguratorUpdated(address indexed newAddress, bytes encodedCallData); event ReserveOracleUpdated(address indexed newAddress); event NftOracleUpdated(address indexed newAddress); event LendPoolLoanUpdated(address indexed newAddress, bytes encodedCallData); event ProxyCreated(bytes32 id, address indexed newAddress); event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy, bytes encodedCallData); event UNFTRegistryUpdated(address indexed newAddress); event IncentivesControllerUpdated(address indexed newAddress); event UIDataProviderUpdated(address indexed newAddress); event UnlockdDataProviderUpdated(address indexed newAddress); event WalletBalanceProviderUpdated(address indexed newAddress); event LendPoolLiquidatorUpdated(address indexed newAddress); event LtvManagerUpdated(address indexed newAddress); /** * @dev Returns the id of the Unlockd market to which this contracts points to * @return The market id **/ function getMarketId() external view returns (string memory); /** * @dev Allows to set the market which this LendPoolAddressesProvider represents * @param marketId The market id */ function setMarketId(string calldata marketId) external; /** * @dev Sets an address for an id replacing the address saved in the addresses map * IMPORTANT Use this function carefully, as it will do a hard replacement * @param id The id * @param newAddress The address to set */ function setAddress(bytes32 id, address newAddress) external; /** * @dev General function to update the implementation of a proxy registered with * certain `id`. If there is no proxy registered, it will instantiate one and * set as implementation the `implementationAddress` * IMPORTANT Use this function carefully, only for ids that don't have an explicit * setter function, in order to avoid unexpected consequences * @param id The id * @param impl The address of the new implementation */ function setAddressAsProxy(bytes32 id, address impl, bytes memory encodedCallData) external; /** * @dev Returns an address by id * @return The address */ function getAddress(bytes32 id) external view returns (address); /** * @dev Returns the address of the LendPool proxy * @return The LendPool proxy address **/ function getLendPool() external view returns (address); /** * @dev Updates the implementation of the LendPool, or creates the proxy * setting the new `pool` implementation on the first time calling it * @param pool The new LendPool implementation * @param encodedCallData calldata to execute **/ function setLendPoolImpl(address pool, bytes memory encodedCallData) external; /** * @dev Returns the address of the LendPoolConfigurator proxy * @return The LendPoolConfigurator proxy address **/ function getLendPoolConfigurator() external view returns (address); /** * @dev Updates the implementation of the LendPoolConfigurator, or creates the proxy * setting the new `configurator` implementation on the first time calling it * @param configurator The new LendPoolConfigurator implementation * @param encodedCallData calldata to execute **/ function setLendPoolConfiguratorImpl(address configurator, bytes memory encodedCallData) external; /** * @dev returns the address of the LendPool admin * @return the LendPoolAdmin address **/ function getPoolAdmin() external view returns (address); /** * @dev sets the address of the LendPool admin * @param admin the LendPoolAdmin address **/ function setPoolAdmin(address admin) external; /** * @dev returns the address of the emergency admin * @return the EmergencyAdmin address **/ function getEmergencyAdmin() external view returns (address); /** * @dev sets the address of the emergency admin * @param admin the EmergencyAdmin address **/ function setEmergencyAdmin(address admin) external; /** * @dev returns the address of the reserve oracle * @return the ReserveOracle address **/ function getReserveOracle() external view returns (address); /** * @dev sets the address of the reserve oracle * @param reserveOracle the ReserveOracle address **/ function setReserveOracle(address reserveOracle) external; /** * @dev returns the address of the NFT oracle * @return the NFTOracle address **/ function getNFTOracle() external view returns (address); /** * @dev sets the address of the NFT oracle * @param nftOracle the NFTOracle address **/ function setNFTOracle(address nftOracle) external; /** * @dev returns the address of the lendpool loan * @return the LendPoolLoan address **/ function getLendPoolLoan() external view returns (address); /** * @dev sets the address of the lendpool loan * @param loan the LendPoolLoan address * @param encodedCallData calldata to execute **/ function setLendPoolLoanImpl(address loan, bytes memory encodedCallData) external; /** * @dev returns the address of the UNFT Registry * @return the UNFTRegistry address **/ function getUNFTRegistry() external view returns (address); /** * @dev sets the address of the UNFT registry * @param factory the UNFTRegistry address **/ function setUNFTRegistry(address factory) external; /** * @dev returns the address of the incentives controller * @return the IncentivesController address **/ function getIncentivesController() external view returns (address); /** * @dev sets the address of the incentives controller * @param controller the IncentivesController address **/ function setIncentivesController(address controller) external; /** * @dev returns the address of the UI data provider * @return the UIDataProvider address **/ function getUIDataProvider() external view returns (address); /** * @dev sets the address of the UI data provider * @param provider the UIDataProvider address **/ function setUIDataProvider(address provider) external; /** * @dev returns the address of the Unlockd data provider * @return the UnlockdDataProvider address **/ function getUnlockdDataProvider() external view returns (address); /** * @dev sets the address of the Unlockd data provider * @param provider the UnlockdDataProvider address **/ function setUnlockdDataProvider(address provider) external; /** * @dev returns the address of the wallet balance provider * @return the WalletBalanceProvider address **/ function getWalletBalanceProvider() external view returns (address); /** * @dev sets the address of the wallet balance provider * @param provider the WalletBalanceProvider address **/ function setWalletBalanceProvider(address provider) external; /** * @dev returns the address of the LendPool liquidator contract **/ function getLendPoolLiquidator() external view returns (address); /** * @dev sets the address of the LendPool liquidator contract * @param liquidator the LendPool liquidator address **/ function setLendPoolLiquidator(address liquidator) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {DataTypes} from "../libraries/types/DataTypes.sol"; import {CountersUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; interface ILendPoolLoan { /** * @dev Emitted on initialization to share location of dependent notes * @param pool The address of the associated lend pool */ event Initialized(address indexed pool); /** * @dev Emitted when a loan is created * @param user The address initiating the action */ event LoanCreated( address indexed user, address indexed onBehalfOf, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amount, uint256 borrowIndex ); /** * @dev Emitted when a loan is updated * @param user The address initiating the action */ event LoanUpdated( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amountAdded, uint256 amountTaken, uint256 borrowIndex ); /** * @dev Emitted when a loan is repaid by the borrower * @param user The address initiating the action */ event LoanRepaid( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amount, uint256 borrowIndex ); /** * @dev Emitted when a loan is auction by the liquidator * @param user The address initiating the action */ event LoanAuctioned( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, uint256 amount, uint256 borrowIndex, address bidder, uint256 price, address previousBidder, uint256 previousPrice ); /** * @dev Emitted when a loan is bought out * @param loanId The loanId that was bought out */ event LoanBoughtOut( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, uint256 bidBorrowAmount, uint256 borrowIndex, uint256 buyoutAmount ); /** * @dev Emitted when a loan is redeemed * @param user The address initiating the action */ event LoanRedeemed( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amountTaken, uint256 borrowIndex ); /** * @dev Emitted when a loan is liquidate by the liquidator * @param user The address initiating the action */ event LoanLiquidated( address indexed user, uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amount, uint256 borrowIndex ); /** * @dev Emitted when a loan is liquidated in an external market */ event LoanLiquidatedMarket( uint256 indexed loanId, address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 amount, uint256 borrowIndex ); function initNft(address nftAsset, address uNftAddress) external; /** * @dev Create store a loan object with some params * @param initiator The address of the user initiating the borrow * @param onBehalfOf The address receiving the loan * @param nftAsset The address of the underlying NFT asset * @param nftTokenId The token Id of the underlying NFT asset * @param uNftAddress The address of the uNFT token * @param reserveAsset The address of the underlying reserve asset * @param amount The loan amount * @param borrowIndex The index to get the scaled loan amount */ function createLoan( address initiator, address onBehalfOf, address nftAsset, uint256 nftTokenId, address uNftAddress, address reserveAsset, uint256 amount, uint256 borrowIndex ) external returns (uint256); /** * @dev Update the given loan with some params * * Requirements: * - The caller must be a holder of the loan * - The loan must be in state Active * @param initiator The address of the user updating the loan * @param loanId The loan ID * @param amountAdded The amount added to the loan * @param amountTaken The amount taken from the loan * @param borrowIndex The index to get the scaled loan amount */ function updateLoan( address initiator, uint256 loanId, uint256 amountAdded, uint256 amountTaken, uint256 borrowIndex ) external; /** * @dev Repay the given loan * * Requirements: * - The caller must be a holder of the loan * - The caller must send in principal + interest * - The loan must be in state Active * * @param initiator The address of the user initiating the repay * @param loanId The loan getting burned * @param uNftAddress The address of uNFT * @param amount The amount repaid * @param borrowIndex The index to get the scaled loan amount */ function repayLoan( address initiator, uint256 loanId, address uNftAddress, uint256 amount, uint256 borrowIndex ) external; /** * @dev Auction the given loan * * Requirements: * - The price must be greater than current highest price * - The loan must be in state Active or Auction * * @param initiator The address of the user initiating the auction * @param loanId The loan getting auctioned * @param bidPrice The bid price of this auction */ function auctionLoan( address initiator, uint256 loanId, address onBehalfOf, uint256 bidPrice, uint256 borrowAmount, uint256 borrowIndex ) external; /** * @dev Buyout the given loan * * Requirements: * - The price has to be the valuation price of the nft * - The loan must be in state Active or Auction */ function buyoutLoan( address initiator, uint256 loanId, address uNftAddress, uint256 borrowAmount, uint256 borrowIndex, uint256 buyoutAmount ) external; /** * @dev Redeem the given loan with some params * * Requirements: * - The caller must be a holder of the loan * - The loan must be in state Auction * @param initiator The address of the user initiating the borrow * @param loanId The loan getting redeemed * @param amountTaken The taken amount * @param borrowIndex The index to get the scaled loan amount */ function redeemLoan(address initiator, uint256 loanId, uint256 amountTaken, uint256 borrowIndex) external; /** * @dev Liquidate the given loan * * Requirements: * - The caller must send in principal + interest * - The loan must be in state Active * * @param initiator The address of the user initiating the auction * @param loanId The loan getting burned * @param uNftAddress The address of uNFT * @param borrowAmount The borrow amount * @param borrowIndex The index to get the scaled loan amount */ function liquidateLoan( address initiator, uint256 loanId, address uNftAddress, uint256 borrowAmount, uint256 borrowIndex ) external; /** * @dev Liquidate the given loan on an external market * @param loanId The loan getting burned * @param uNftAddress The address of the underlying uNft * @param borrowAmount Amount borrowed in the loan * @param borrowIndex The reserve index */ function liquidateLoanMarket(uint256 loanId, address uNftAddress, uint256 borrowAmount, uint256 borrowIndex) external; /** * @dev Updates the `_marketAdapters` mapping, setting the params to * valid/unvalid adapters through the `flag` parameter * @param adapters The adapters addresses to be updated * @param flag `true` to set addresses as valid adapters, `false` otherwise */ function updateMarketAdapters(address[] calldata adapters, bool flag) external; /** * @dev returns the borrower of a specific loan * param loanId the loan to get the borrower from */ function borrowerOf(uint256 loanId) external view returns (address); /** * @dev returns the loan corresponding to a specific NFT * param nftAsset the underlying NFT asset * param tokenId the underlying token ID for the NFT */ function getCollateralLoanId(address nftAsset, uint256 nftTokenId) external view returns (uint256); /** * @dev returns the loan corresponding to a specific loan Id * param loanId the loan Id */ function getLoan(uint256 loanId) external view returns (DataTypes.LoanData memory loanData); /** * @dev returns the collateral and reserve corresponding to a specific loan * param loanId the loan Id */ function getLoanCollateralAndReserve( uint256 loanId ) external view returns (address nftAsset, uint256 nftTokenId, address reserveAsset, uint256 scaledAmount); /** * @dev returns the reserve and borrow __scaled__ amount corresponding to a specific loan * param loanId the loan Id */ function getLoanReserveBorrowScaledAmount(uint256 loanId) external view returns (address, uint256); /** * @dev returns the reserve and borrow amount corresponding to a specific loan * param loanId the loan Id */ function getLoanReserveBorrowAmount(uint256 loanId) external view returns (address, uint256); function getLoanHighestBid(uint256 loanId) external view returns (address, uint256); /** * @dev returns the collateral amount for a given NFT * param nftAsset the underlying NFT asset */ function getNftCollateralAmount(address nftAsset) external view returns (uint256); /** * @dev returns the collateral amount for a given NFT and a specific user * param user the user * param nftAsset the underlying NFT asset */ function getUserNftCollateralAmount(address user, address nftAsset) external view returns (uint256); /** * @dev returns the counter tracker for all the loan ID's in the protocol */ function getLoanIdTracker() external view returns (CountersUpgradeable.Counter memory); function reMintUNFT(address nftAsset, uint256 tokenId, address oldOnBehalfOf, address newOnBehalfOf) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; interface ILockeyManager { /** * @dev sets the discount percentage that the lockey holders can get on buyouts * @param discountPercentage the percentage lockey holders will have */ function setLockeyDiscountPercentage(uint256 discountPercentage) external; /** * @dev Returns the lockeys discount percentage **/ function getLockeyDiscountPercentage() external view returns (uint256); function getLockeyDiscountPercentageOnDebtMarket() external view returns (uint256); function setLockeyDiscountPercentageOnDebtMarket(uint256 discountPercentage) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /************ @title INFTOracleGetter interface @notice Interface for getting NFT price oracle.*/ interface INFTOracleGetter { /* CAUTION: Price uint is ETH based (WEI, 18 decimals) */ /*********** @dev returns the asset price in ETH @param assetContract the underlying NFT asset @param tokenId the underlying NFT token Id */ function getNFTPrice(address assetContract, uint256 tokenId) external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /************ @title IReserveOracleGetter interface @notice Interface for getting Reserve price oracle.*/ interface IReserveOracleGetter { /* CAUTION: Price uint is ETH based (WEI, 18 decimals) */ /*********** @dev returns the asset price in ETH */ function getAssetPrice(address asset) external view returns (uint256); // get twap price depending on _period function getTwapPrice(address _priceFeedKey, uint256 _interval) external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; interface IScaledBalanceToken { /** * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the * updated stored balance divided by the reserve's liquidity index at the moment of the update * @param user The user whose balance is calculated * @return The scaled balance of the user **/ function scaledBalanceOf(address user) external view returns (uint256); /** * @dev Returns the scaled balance of the user and the scaled total supply. * @param user The address of the user * @return The scaled balance of the user * @return The scaled balance and the scaled total supply **/ function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); /** * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) * @return The scaled total supply **/ function scaledTotalSupply() external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ILendPoolAddressesProvider} from "./ILendPoolAddressesProvider.sol"; import {IIncentivesController} from "./IIncentivesController.sol"; import {IScaledBalanceToken} from "./IScaledBalanceToken.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; interface IUToken is IScaledBalanceToken, IERC20Upgradeable, IERC20MetadataUpgradeable { /** * @dev Emitted when an uToken is initialized * @param underlyingAsset The address of the underlying asset * @param pool The address of the associated lending pool * @param treasury The address of the treasury * @param incentivesController The address of the incentives controller for this uToken **/ event Initialized( address indexed underlyingAsset, address indexed pool, address treasury, address incentivesController ); /** * @dev Initializes the bToken * @param addressProvider The address of the address provider where this bToken will be used * @param treasury The address of the Unlockd treasury, receiving the fees on this bToken * @param underlyingAsset The address of the underlying asset of this bToken * @param uTokenDecimals The amount of token decimals * @param uTokenName The name of the token * @param uTokenSymbol The token symbol */ function initialize( ILendPoolAddressesProvider addressProvider, address treasury, address underlyingAsset, uint8 uTokenDecimals, string calldata uTokenName, string calldata uTokenSymbol ) external; /** * @dev Emitted after the mint action * @param from The address performing the mint * @param value The amount being * @param index The new liquidity index of the reserve **/ event Mint(address indexed from, uint256 value, uint256 index); /** * @dev Emitted after setting of addresses as managers * @param managers the managers to be updated * @param flag `true` to set addresses as managers, `false` otherwise **/ event UTokenManagersUpdated(address[] indexed managers, bool flag); /** * @dev Mints `amount` uTokens to `user` * @param user The address receiving the minted tokens * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve * @return `true` if the the previous balance of the user was 0 */ function mint(address user, uint256 amount, uint256 index) external returns (bool); /** * @dev Emitted after uTokens are burned * @param from The owner of the uTokens, getting them burned * @param target The address that will receive the underlying * @param value The amount being burned * @param index The new liquidity index of the reserve **/ event Burn(address indexed from, address indexed target, uint256 value, uint256 index); /** * @dev Emitted during the transfer action * @param from The user whose tokens are being transferred * @param to The recipient * @param value The amount being transferred * @param index The new liquidity index of the reserve **/ event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index); /** * @dev Emitted when treasury address is updated in utoken * @param _newTreasuryAddress The new treasury address **/ event TreasuryAddressUpdated(address indexed _newTreasuryAddress); /** @dev Emitted after sweeping liquidity from the uToken to deposit it to external lending protocol * @param uToken The uToken swept * @param underlyingAsset The underlying asset from the uToken * @param amount The amount deposited to the lending protocol */ event UTokenSwept(address indexed uToken, address indexed underlyingAsset, uint256 indexed amount); /** * @dev Takes reserve liquidity from uToken and deposits it to external lening protocol **/ function sweepUToken() external; /** * @dev Burns uTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` * @param user The owner of the uTokens, getting them burned * @param receiverOfUnderlying The address that will receive the underlying * @param amount The amount being burned * @param index The new liquidity index of the reserve **/ function burn(address user, address receiverOfUnderlying, uint256 amount, uint256 index) external; /** * @dev Mints uTokens to the reserve treasury * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve */ function mintToTreasury(uint256 amount, uint256 index) external; /** * @dev Deposits `amount` to the lending protocol currently active * @param amount The amount of tokens to deposit */ function depositReserves(uint256 amount) external; /** * @dev Withdraws `amount` from the lending protocol currently active * @param amount The amount of tokens to withdraw */ function withdrawReserves(uint256 amount) external returns (uint256); /** * @dev Transfers the underlying asset to `target`. Used by the LendPool to transfer * assets in borrow() and withdraw() * @param user The recipient of the underlying * @param amount The amount getting transferred * @return The amount transferred **/ function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); /** * @dev Returns the scaled balance of the user and the scaled total supply. * @return The available liquidity in reserve **/ function getAvailableLiquidity() external view returns (uint256); /** * @dev Returns the address of the incentives controller contract **/ function getIncentivesController() external view returns (IIncentivesController); /** * @dev Returns the address of the underlying asset of this uToken **/ function UNDERLYING_ASSET_ADDRESS() external view returns (address); /** * @dev Returns the address of the treasury set to this uToken **/ function RESERVE_TREASURY_ADDRESS() external view returns (address); /** * @dev Sets the address of the treasury to this uToken **/ function setTreasuryAddress(address treasury) external; /** * @dev Updates the uToken manager addresses **/ function updateUTokenManagers(address[] calldata managers, bool flag) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; /** * @title NftConfiguration library * @author BendDao; Forked and edited by Unlockd * @notice Implements the bitmap logic to handle the NFT configuration */ library NftConfiguration { uint256 constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore uint256 constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore uint256 constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 constant REDEEM_DURATION_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant AUCTION_DURATION_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant REDEEM_FINE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant REDEEM_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant MIN_BIDFINE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant CONFIG_TIMESTAMP_MASK = 0xFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; uint256 constant IS_ACTIVE_START_BIT_POSITION = 56; uint256 constant IS_FROZEN_START_BIT_POSITION = 57; uint256 constant REDEEM_DURATION_START_BIT_POSITION = 64; uint256 constant AUCTION_DURATION_START_BIT_POSITION = 80; uint256 constant REDEEM_FINE_START_BIT_POSITION = 96; uint256 constant REDEEM_THRESHOLD_START_BIT_POSITION = 112; uint256 constant MIN_BIDFINE_START_BIT_POSITION = 128; uint256 constant CONFIG_TIMESTAMP_START_BIT_POSITION = 144; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; uint256 constant MAX_VALID_REDEEM_DURATION = 65535; uint256 constant MAX_VALID_AUCTION_DURATION = 65535; uint256 constant MAX_VALID_REDEEM_FINE = 65535; uint256 constant MAX_VALID_REDEEM_THRESHOLD = 65535; uint256 constant MAX_VALID_MIN_BIDFINE = 65535; uint256 constant MAX_VALID_CONFIG_TIMESTAMP = 4294967295; /** * @dev Sets the Loan to Value of the NFT * @param self The NFT configuration * @param ltv the new ltv **/ function setLtv(DataTypes.NftConfigurationMap memory self, uint256 ltv) internal pure { require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV); self.data = (self.data & LTV_MASK) | ltv; } /** * @dev Gets the Loan to Value of the NFT * @param self The NFT configuration * @return The loan to value **/ function getLtv(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return self.data & ~LTV_MASK; } /** * @dev Sets the liquidation threshold of the NFT * @param self The NFT configuration * @param threshold The new liquidation threshold **/ function setLiquidationThreshold(DataTypes.NftConfigurationMap memory self, uint256 threshold) internal pure { require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD); self.data = (self.data & LIQUIDATION_THRESHOLD_MASK) | (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); } /** * @dev Gets the liquidation threshold of the NFT * @param self The NFT configuration * @return The liquidation threshold **/ function getLiquidationThreshold(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } /** * @dev Sets the liquidation bonus of the NFT * @param self The NFT configuration * @param bonus The new liquidation bonus **/ function setLiquidationBonus(DataTypes.NftConfigurationMap memory self, uint256 bonus) internal pure { require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS); self.data = (self.data & LIQUIDATION_BONUS_MASK) | (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); } /** * @dev Gets the liquidation bonus of the NFT * @param self The NFT configuration * @return The liquidation bonus **/ function getLiquidationBonus(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; } /** * @dev Sets the active state of the NFT * @param self The NFT configuration * @param active The active state **/ function setActive(DataTypes.NftConfigurationMap memory self, bool active) internal pure { self.data = (self.data & ACTIVE_MASK) | (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); } /** * @dev Gets the active state of the NFT * @param self The NFT configuration * @return The active state **/ function getActive(DataTypes.NftConfigurationMap storage self) internal view returns (bool) { return (self.data & ~ACTIVE_MASK) != 0; } /** * @dev Sets the frozen state of the NFT * @param self The NFT configuration * @param frozen The frozen state **/ function setFrozen(DataTypes.NftConfigurationMap memory self, bool frozen) internal pure { self.data = (self.data & FROZEN_MASK) | (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); } /** * @dev Gets the frozen state of the NFT * @param self The NFT configuration * @return The frozen state **/ function getFrozen(DataTypes.NftConfigurationMap storage self) internal view returns (bool) { return (self.data & ~FROZEN_MASK) != 0; } /** * @dev Sets the redeem duration of the NFT * @param self The NFT configuration * @param redeemDuration The redeem duration **/ function setRedeemDuration(DataTypes.NftConfigurationMap memory self, uint256 redeemDuration) internal pure { require(redeemDuration <= MAX_VALID_REDEEM_DURATION, Errors.RC_INVALID_REDEEM_DURATION); self.data = (self.data & REDEEM_DURATION_MASK) | (redeemDuration << REDEEM_DURATION_START_BIT_POSITION); } /** * @dev Gets the redeem duration of the NFT * @param self The NFT configuration * @return The redeem duration **/ function getRedeemDuration(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~REDEEM_DURATION_MASK) >> REDEEM_DURATION_START_BIT_POSITION; } /** * @dev Sets the auction duration of the NFT * @param self The NFT configuration * @param auctionDuration The auction duration **/ function setAuctionDuration(DataTypes.NftConfigurationMap memory self, uint256 auctionDuration) internal pure { require(auctionDuration <= MAX_VALID_AUCTION_DURATION, Errors.RC_INVALID_AUCTION_DURATION); self.data = (self.data & AUCTION_DURATION_MASK) | (auctionDuration << AUCTION_DURATION_START_BIT_POSITION); } /** * @dev Gets the auction duration of the NFT * @param self The NFT configuration * @return The auction duration **/ function getAuctionDuration(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~AUCTION_DURATION_MASK) >> AUCTION_DURATION_START_BIT_POSITION; } /** * @dev Sets the redeem fine of the NFT * @param self The NFT configuration * @param redeemFine The redeem duration **/ function setRedeemFine(DataTypes.NftConfigurationMap memory self, uint256 redeemFine) internal pure { require(redeemFine <= MAX_VALID_REDEEM_FINE, Errors.RC_INVALID_REDEEM_FINE); self.data = (self.data & REDEEM_FINE_MASK) | (redeemFine << REDEEM_FINE_START_BIT_POSITION); } /** * @dev Gets the redeem fine of the NFT * @param self The NFT configuration * @return The redeem fine **/ function getRedeemFine(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~REDEEM_FINE_MASK) >> REDEEM_FINE_START_BIT_POSITION; } /** * @dev Sets the redeem threshold of the NFT * @param self The NFT configuration * @param redeemThreshold The redeem duration **/ function setRedeemThreshold(DataTypes.NftConfigurationMap memory self, uint256 redeemThreshold) internal pure { require(redeemThreshold <= MAX_VALID_REDEEM_THRESHOLD, Errors.RC_INVALID_REDEEM_THRESHOLD); self.data = (self.data & REDEEM_THRESHOLD_MASK) | (redeemThreshold << REDEEM_THRESHOLD_START_BIT_POSITION); } /** * @dev Gets the redeem threshold of the NFT * @param self The NFT configuration * @return The redeem threshold **/ function getRedeemThreshold(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~REDEEM_THRESHOLD_MASK) >> REDEEM_THRESHOLD_START_BIT_POSITION; } /** * @dev Sets the min & max threshold of the NFT * @param self The NFT configuration * @param minBidFine The min bid fine **/ function setMinBidFine(DataTypes.NftConfigurationMap memory self, uint256 minBidFine) internal pure { require(minBidFine <= MAX_VALID_MIN_BIDFINE, Errors.RC_INVALID_MIN_BID_FINE); self.data = (self.data & MIN_BIDFINE_MASK) | (minBidFine << MIN_BIDFINE_START_BIT_POSITION); } /** * @dev Gets the min bid fine of the NFT * @param self The NFT configuration * @return The min bid fine **/ function getMinBidFine(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return ((self.data & ~MIN_BIDFINE_MASK) >> MIN_BIDFINE_START_BIT_POSITION); } /** * @dev Sets the timestamp when the NFTconfig was triggered * @param self The NFT configuration * @param configTimestamp The config timestamp **/ function setConfigTimestamp(DataTypes.NftConfigurationMap memory self, uint256 configTimestamp) internal pure { require(configTimestamp <= MAX_VALID_CONFIG_TIMESTAMP, Errors.RC_INVALID_MAX_CONFIG_TIMESTAMP); self.data = (self.data & CONFIG_TIMESTAMP_MASK) | (configTimestamp << CONFIG_TIMESTAMP_START_BIT_POSITION); } /** * @dev Gets the timestamp when the NFTconfig was triggered * @param self The NFT configuration * @return The config timestamp **/ function getConfigTimestamp(DataTypes.NftConfigurationMap storage self) internal view returns (uint256) { return ((self.data & ~CONFIG_TIMESTAMP_MASK) >> CONFIG_TIMESTAMP_START_BIT_POSITION); } /** * @dev Gets the configuration flags of the NFT * @param self The NFT configuration * @return The state flags representing active, frozen **/ function getFlags(DataTypes.NftConfigurationMap storage self) internal view returns (bool, bool) { uint256 dataLocal = self.data; return ((dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0); } /** * @dev Gets the configuration flags of the NFT from a memory object * @param self The NFT configuration * @return The state flags representing active, frozen **/ function getFlagsMemory(DataTypes.NftConfigurationMap memory self) internal pure returns (bool, bool) { return ((self.data & ~ACTIVE_MASK) != 0, (self.data & ~FROZEN_MASK) != 0); } /** * @dev Gets the collateral configuration paramters of the NFT * @param self The NFT configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus **/ function getCollateralParams( DataTypes.NftConfigurationMap storage self ) internal view returns (uint256, uint256, uint256) { uint256 dataLocal = self.data; return ( dataLocal & ~LTV_MASK, (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION ); } /** * @dev Gets the auction configuration paramters of the NFT * @param self The NFT configuration * @return The state params representing redeem duration, auction duration, redeem fine **/ function getAuctionParams( DataTypes.NftConfigurationMap storage self ) internal view returns (uint256, uint256, uint256, uint256) { uint256 dataLocal = self.data; return ( (dataLocal & ~REDEEM_DURATION_MASK) >> REDEEM_DURATION_START_BIT_POSITION, (dataLocal & ~AUCTION_DURATION_MASK) >> AUCTION_DURATION_START_BIT_POSITION, (dataLocal & ~REDEEM_FINE_MASK) >> REDEEM_FINE_START_BIT_POSITION, (dataLocal & ~REDEEM_THRESHOLD_MASK) >> REDEEM_THRESHOLD_START_BIT_POSITION ); } /** * @dev Gets the collateral configuration paramters of the NFT from a memory object * @param self The NFT configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus **/ function getCollateralParamsMemory( DataTypes.NftConfigurationMap memory self ) internal pure returns (uint256, uint256, uint256) { return ( self.data & ~LTV_MASK, (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION ); } /** * @dev Gets the auction configuration paramters of the NFT from a memory object * @param self The NFT configuration * @return The state params representing redeem duration, auction duration, redeem fine **/ function getAuctionParamsMemory( DataTypes.NftConfigurationMap memory self ) internal pure returns (uint256, uint256, uint256, uint256) { return ( (self.data & ~REDEEM_DURATION_MASK) >> REDEEM_DURATION_START_BIT_POSITION, (self.data & ~AUCTION_DURATION_MASK) >> AUCTION_DURATION_START_BIT_POSITION, (self.data & ~REDEEM_FINE_MASK) >> REDEEM_FINE_START_BIT_POSITION, (self.data & ~REDEEM_THRESHOLD_MASK) >> REDEEM_THRESHOLD_START_BIT_POSITION ); } /** * @dev Gets the min & max bid fine of the NFT * @param self The NFT configuration * @return The min & max bid fine **/ function getMinBidFineMemory(DataTypes.NftConfigurationMap memory self) internal pure returns (uint256) { return ((self.data & ~MIN_BIDFINE_MASK) >> MIN_BIDFINE_START_BIT_POSITION); } /** * @dev Gets the timestamp the NFT was configured * @param self The NFT configuration * @return The timestamp value **/ function getConfigTimestampMemory(DataTypes.NftConfigurationMap memory self) internal pure returns (uint256) { return ((self.data & ~CONFIG_TIMESTAMP_MASK) >> CONFIG_TIMESTAMP_START_BIT_POSITION); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; /** * @title ReserveConfiguration library * @author BendDao; Forked and edited by Unlockd * @notice Implements the bitmap logic to handle the reserve configuration */ library ReserveConfiguration { uint256 constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore uint256 constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore uint256 constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore uint256 constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48; uint256 constant IS_ACTIVE_START_BIT_POSITION = 56; uint256 constant IS_FROZEN_START_BIT_POSITION = 57; uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; uint256 constant MAX_VALID_DECIMALS = 255; uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; /** * @dev Sets the Loan to Value of the reserve * @param self The reserve configuration * @param ltv the new ltv **/ function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure { require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV); self.data = (self.data & LTV_MASK) | ltv; } /** * @dev Gets the Loan to Value of the reserve * @param self The reserve configuration * @return The loan to value **/ function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return self.data & ~LTV_MASK; } /** * @dev Sets the liquidation threshold of the reserve * @param self The reserve configuration * @param threshold The new liquidation threshold **/ function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold) internal pure { require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD); self.data = (self.data & LIQUIDATION_THRESHOLD_MASK) | (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); } /** * @dev Gets the liquidation threshold of the reserve * @param self The reserve configuration * @return The liquidation threshold **/ function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } /** * @dev Sets the liquidation bonus of the reserve * @param self The reserve configuration * @param bonus The new liquidation bonus **/ function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) internal pure { require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS); self.data = (self.data & LIQUIDATION_BONUS_MASK) | (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); } /** * @dev Gets the liquidation bonus of the reserve * @param self The reserve configuration * @return The liquidation bonus **/ function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; } /** * @dev Sets the decimals of the underlying asset of the reserve * @param self The reserve configuration * @param decimals The decimals **/ function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) internal pure { require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS); self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION); } /** * @dev Gets the decimals of the underlying asset of the reserve * @param self The reserve configuration * @return The decimals of the asset **/ function getDecimals(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; } /** * @dev Sets the active state of the reserve * @param self The reserve configuration * @param active The active state **/ function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure { self.data = (self.data & ACTIVE_MASK) | (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); } /** * @dev Gets the active state of the reserve * @param self The reserve configuration * @return The active state **/ function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~ACTIVE_MASK) != 0; } /** * @dev Sets the frozen state of the reserve * @param self The reserve configuration * @param frozen The frozen state **/ function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure { self.data = (self.data & FROZEN_MASK) | (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); } /** * @dev Gets the frozen state of the reserve * @param self The reserve configuration * @return The frozen state **/ function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~FROZEN_MASK) != 0; } /** * @dev Enables or disables borrowing on the reserve * @param self The reserve configuration * @param enabled True if the borrowing needs to be enabled, false otherwise **/ function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) internal pure { self.data = (self.data & BORROWING_MASK) | (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION); } /** * @dev Gets the borrowing state of the reserve * @param self The reserve configuration * @return The borrowing state **/ function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~BORROWING_MASK) != 0; } /** * @dev Enables or disables stable rate borrowing on the reserve * @param self The reserve configuration * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise **/ function setStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) internal pure { self.data = (self.data & STABLE_BORROWING_MASK) | (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION); } /** * @dev Gets the stable rate borrowing state of the reserve * @param self The reserve configuration * @return The stable rate borrowing state **/ function getStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~STABLE_BORROWING_MASK) != 0; } /** * @dev Sets the reserve factor of the reserve * @param self The reserve configuration * @param reserveFactor The reserve factor **/ function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor) internal pure { require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR); self.data = (self.data & RESERVE_FACTOR_MASK) | (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION); } /** * @dev Gets the reserve factor of the reserve * @param self The reserve configuration * @return The reserve factor **/ function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; } /** * @dev Gets the configuration flags of the reserve * @param self The reserve configuration * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled **/ function getFlags(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool, bool, bool, bool) { uint256 dataLocal = self.data; return ( (dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, (dataLocal & ~STABLE_BORROWING_MASK) != 0 ); } /** * @dev Gets the configuration paramters of the reserve * @param self The reserve configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals **/ function getParams( DataTypes.ReserveConfigurationMap storage self ) internal view returns (uint256, uint256, uint256, uint256, uint256) { uint256 dataLocal = self.data; return ( dataLocal & ~LTV_MASK, (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION ); } /** * @dev Gets the configuration paramters of the reserve from a memory object * @param self The reserve configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals **/ function getParamsMemory( DataTypes.ReserveConfigurationMap memory self ) internal pure returns (uint256, uint256, uint256, uint256, uint256) { return ( self.data & ~LTV_MASK, (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION ); } /** * @dev Gets the configuration flags of the reserve from a memory object * @param self The reserve configuration * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled **/ function getFlagsMemory( DataTypes.ReserveConfigurationMap memory self ) internal pure returns (bool, bool, bool, bool) { return ( (self.data & ~ACTIVE_MASK) != 0, (self.data & ~FROZEN_MASK) != 0, (self.data & ~BORROWING_MASK) != 0, (self.data & ~STABLE_BORROWING_MASK) != 0 ); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; /** * @title Errors library * @author BendDao; Forked and edited by Unlockd * @notice Defines the error messages emitted by the different contracts of the Unlockd protocol */ library Errors { enum ReturnCode { SUCCESS, FAILED } string public constant SUCCESS = "0"; //common errors string public constant CALLER_NOT_POOL_ADMIN = "100"; // 'The caller must be the pool admin' string public constant CALLER_NOT_ADDRESS_PROVIDER = "101"; string public constant INVALID_FROM_BALANCE_AFTER_TRANSFER = "102"; string public constant INVALID_TO_BALANCE_AFTER_TRANSFER = "103"; string public constant CALLER_NOT_ONBEHALFOF_OR_IN_WHITELIST = "104"; string public constant CALLER_NOT_POOL_LIQUIDATOR = "105"; string public constant INVALID_ZERO_ADDRESS = "106"; string public constant CALLER_NOT_LTV_MANAGER = "107"; string public constant CALLER_NOT_PRICE_MANAGER = "108"; string public constant CALLER_NOT_UTOKEN_MANAGER = "109"; //math library errors string public constant MATH_MULTIPLICATION_OVERFLOW = "200"; string public constant MATH_ADDITION_OVERFLOW = "201"; string public constant MATH_DIVISION_BY_ZERO = "202"; //validation & check errors string public constant VL_INVALID_AMOUNT = "301"; // 'Amount must be greater than 0' string public constant VL_NO_ACTIVE_RESERVE = "302"; // 'Action requires an active reserve' string public constant VL_RESERVE_FROZEN = "303"; // 'Action cannot be performed because the reserve is frozen' string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = "304"; // 'User cannot withdraw more than the available balance' string public constant VL_BORROWING_NOT_ENABLED = "305"; // 'Borrowing is not enabled' string public constant VL_COLLATERAL_BALANCE_IS_0 = "306"; // 'The collateral balance is 0' string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = "307"; // 'Health factor is lesser than the liquidation threshold' string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = "308"; // 'There is not enough collateral to cover a new borrow' string public constant VL_NO_DEBT_OF_SELECTED_TYPE = "309"; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt' string public constant VL_NO_ACTIVE_NFT = "310"; string public constant VL_NFT_FROZEN = "311"; string public constant VL_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = "312"; // 'User did not borrow the specified currency' string public constant VL_INVALID_HEALTH_FACTOR = "313"; string public constant VL_INVALID_ONBEHALFOF_ADDRESS = "314"; string public constant VL_INVALID_TARGET_ADDRESS = "315"; string public constant VL_INVALID_RESERVE_ADDRESS = "316"; string public constant VL_SPECIFIED_LOAN_NOT_BORROWED_BY_USER = "317"; string public constant VL_SPECIFIED_RESERVE_NOT_BORROWED_BY_USER = "318"; string public constant VL_HEALTH_FACTOR_HIGHER_THAN_LIQUIDATION_THRESHOLD = "319"; string public constant VL_TIMEFRAME_EXCEEDED = "320"; string public constant VL_VALUE_EXCEED_TREASURY_BALANCE = "321"; //lend pool errors string public constant LP_CALLER_NOT_LEND_POOL_CONFIGURATOR = "400"; // 'The caller of the function is not the lending pool configurator' string public constant LP_IS_PAUSED = "401"; // 'Pool is paused' string public constant LP_NO_MORE_RESERVES_ALLOWED = "402"; string public constant LP_NOT_CONTRACT = "403"; string public constant LP_BORROW_NOT_EXCEED_LIQUIDATION_THRESHOLD = "404"; string public constant LP_BORROW_IS_EXCEED_LIQUIDATION_PRICE = "405"; string public constant LP_NO_MORE_NFTS_ALLOWED = "406"; string public constant LP_INVALID_USER_NFT_AMOUNT = "407"; string public constant LP_INCONSISTENT_PARAMS = "408"; string public constant LP_NFT_IS_NOT_USED_AS_COLLATERAL = "409"; string public constant LP_CALLER_MUST_BE_AN_UTOKEN = "410"; string public constant LP_INVALID_NFT_AMOUNT = "411"; string public constant LP_NFT_HAS_USED_AS_COLLATERAL = "412"; string public constant LP_DELEGATE_CALL_FAILED = "413"; string public constant LP_AMOUNT_LESS_THAN_EXTRA_DEBT = "414"; string public constant LP_AMOUNT_LESS_THAN_REDEEM_THRESHOLD = "415"; string public constant LP_AMOUNT_GREATER_THAN_MAX_REPAY = "416"; string public constant LP_NFT_TOKEN_ID_EXCEED_MAX_LIMIT = "417"; string public constant LP_NFT_SUPPLY_NUM_EXCEED_MAX_LIMIT = "418"; string public constant LP_CALLER_NOT_LEND_POOL_LIQUIDATOR_NOR_GATEWAY = "419"; string public constant LP_CONSECUTIVE_BIDS_NOT_ALLOWED = "420"; string public constant LP_INVALID_OVERFLOW_VALUE = "421"; string public constant LP_CALLER_NOT_NFT_HOLDER = "422"; string public constant LP_NFT_NOT_ALLOWED_TO_SELL = "423"; string public constant LP_RESERVES_WITHOUT_ENOUGH_LIQUIDITY = "424"; string public constant LP_COLLECTION_NOT_SUPPORTED = "425"; string public constant LP_MSG_VALUE_DIFFERENT_FROM_CONFIG_FEE = "426"; string public constant LP_INVALID_SAFE_HEALTH_FACTOR = "427"; string public constant LP_AMOUNT_LESS_THAN_DEBT = "428"; string public constant LP_AMOUNT_DIFFERENT_FROM_REQUIRED_BUYOUT_PRICE = "429"; string public constant LP_CALLER_NOT_DEBT_TOKEN_MANAGER = "430"; string public constant LP_CALLER_NOT_RESERVOIR_OR_DEBT_MARKET = "431"; //lend pool loan errors string public constant LPL_CLAIM_HASNT_STARTED_YET = "479"; string public constant LPL_INVALID_LOAN_STATE = "480"; string public constant LPL_INVALID_LOAN_AMOUNT = "481"; string public constant LPL_INVALID_TAKEN_AMOUNT = "482"; string public constant LPL_AMOUNT_OVERFLOW = "483"; string public constant LPL_BID_PRICE_LESS_THAN_DEBT_PRICE = "484"; string public constant LPL_BID_PRICE_LESS_THAN_HIGHEST_PRICE = "485"; string public constant LPL_BID_REDEEM_DURATION_HAS_END = "486"; string public constant LPL_BID_USER_NOT_SAME = "487"; string public constant LPL_BID_REPAY_AMOUNT_NOT_ENOUGH = "488"; string public constant LPL_BID_AUCTION_DURATION_HAS_END = "489"; string public constant LPL_BID_AUCTION_DURATION_NOT_END = "490"; string public constant LPL_BID_PRICE_LESS_THAN_BORROW = "491"; string public constant LPL_INVALID_BIDDER_ADDRESS = "492"; string public constant LPL_AMOUNT_LESS_THAN_BID_FINE = "493"; string public constant LPL_INVALID_BID_FINE = "494"; string public constant LPL_BID_PRICE_LESS_THAN_MIN_BID_REQUIRED = "495"; string public constant LPL_BID_NOT_BUYOUT_PRICE = "496"; string public constant LPL_BUYOUT_DURATION_HAS_END = "497"; string public constant LPL_BUYOUT_PRICE_LESS_THAN_BORROW = "498"; string public constant LPL_CALLER_MUST_BE_MARKET_ADAPTER = "499"; //common token errors string public constant CT_CALLER_MUST_BE_LEND_POOL = "500"; // 'The caller of this function must be a lending pool' string public constant CT_INVALID_MINT_AMOUNT = "501"; //invalid amount to mint string public constant CT_INVALID_BURN_AMOUNT = "502"; //invalid amount to burn string public constant CT_BORROW_ALLOWANCE_NOT_ENOUGH = "503"; string public constant CT_CALLER_MUST_BE_DEBT_MARKET = "504"; // 'The caller of this function must be a debt market' //reserve logic errors string public constant RL_RESERVE_ALREADY_INITIALIZED = "601"; // 'Reserve has already been initialized' string public constant RL_LIQUIDITY_INDEX_OVERFLOW = "602"; // Liquidity index overflows uint128 string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = "603"; // Variable borrow index overflows uint128 string public constant RL_LIQUIDITY_RATE_OVERFLOW = "604"; // Liquidity rate overflows uint128 string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = "605"; // Variable borrow rate overflows uint128 //configure errors string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = "700"; // 'The liquidity of the reserve needs to be 0' string public constant LPC_INVALID_CONFIGURATION = "701"; // 'Invalid risk parameters for the reserve' string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = "702"; // 'The caller must be the emergency admin' string public constant LPC_INVALID_UNFT_ADDRESS = "703"; string public constant LPC_INVALIED_LOAN_ADDRESS = "704"; string public constant LPC_NFT_LIQUIDITY_NOT_0 = "705"; string public constant LPC_PARAMS_MISMATCH = "706"; // NFT assets & token ids mismatch string public constant LPC_FEE_PERCENTAGE_TOO_HIGH = "707"; string public constant LPC_INVALID_LTVMANAGER_ADDRESS = "708"; string public constant LPC_INCONSISTENT_PARAMS = "709"; string public constant LPC_INVALID_SAFE_HEALTH_FACTOR = "710"; //reserve config errors string public constant RC_INVALID_LTV = "730"; string public constant RC_INVALID_LIQ_THRESHOLD = "731"; string public constant RC_INVALID_LIQ_BONUS = "732"; string public constant RC_INVALID_DECIMALS = "733"; string public constant RC_INVALID_RESERVE_FACTOR = "734"; string public constant RC_INVALID_REDEEM_DURATION = "735"; string public constant RC_INVALID_AUCTION_DURATION = "736"; string public constant RC_INVALID_REDEEM_FINE = "737"; string public constant RC_INVALID_REDEEM_THRESHOLD = "738"; string public constant RC_INVALID_MIN_BID_FINE = "739"; string public constant RC_INVALID_MAX_BID_FINE = "740"; string public constant RC_INVALID_MAX_CONFIG_TIMESTAMP = "741"; //address provider erros string public constant LPAPR_PROVIDER_NOT_REGISTERED = "760"; // 'Provider is not registered' string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = "761"; //NFTOracleErrors string public constant NFTO_INVALID_PRICEM_ADDRESS = "900"; //Debt Market string public constant DM_CALLER_NOT_THE_OWNER = "1000"; string public constant DM_DEBT_SHOULD_EXIST = "1001"; string public constant DM_INVALID_AMOUNT = "1002"; string public constant DM_FAIL_ON_SEND_ETH = "1003"; string public constant DM_DEBT_SHOULD_NOT_BE_SOLD = "1004"; string public constant DM_DEBT_ALREADY_EXIST = "1005"; string public constant DM_LOAN_SHOULD_EXIST = "1006"; string public constant DM_AUCTION_ALREADY_ENDED = "1007"; string public constant DM_BID_PRICE_HIGHER_THAN_SELL_PRICE = "1008"; string public constant DM_BID_PRICE_LESS_THAN_PREVIOUS_BID = "1009"; string public constant DM_INVALID_SELL_TYPE = "1010"; string public constant DM_AUCTION_NOT_ALREADY_ENDED = "1011"; string public constant DM_INVALID_CLAIM_RECEIVER = "1012"; string public constant DM_AMOUNT_DIFFERENT_FROM_SELL_PRICE = "1013"; string public constant DM_BID_PRICE_LESS_THAN_MIN_BID_PRICE = "1014"; string public constant DM_BORROWED_AMOUNT_DIVERGED = "1015"; string public constant DM_INVALID_AUTHORIZED_ADDRESS = "1016"; string public constant DM_CALLER_NOT_THE_OWNER_OR_AUTHORIZED = "1017"; string public constant DM_INVALID_DELTA_BID_PERCENT = "1018"; string public constant DM_IS_PAUSED = "1019"; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {IUToken} from "../../interfaces/IUToken.sol"; import {IDebtToken} from "../../interfaces/IDebtToken.sol"; import {IInterestRate} from "../../interfaces/IInterestRate.sol"; import {ILendPoolAddressesProvider} from "../../interfaces/ILendPoolAddressesProvider.sol"; import {IReserveOracleGetter} from "../../interfaces/IReserveOracleGetter.sol"; import {INFTOracleGetter} from "../../interfaces/INFTOracleGetter.sol"; import {ILendPoolLoan} from "../../interfaces/ILendPoolLoan.sol"; import {IDebtMarket} from "../../interfaces/IDebtMarket.sol"; import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol"; import {MathUtils} from "../math/MathUtils.sol"; import {WadRayMath} from "../math/WadRayMath.sol"; import {PercentageMath} from "../math/PercentageMath.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; import {GenericLogic} from "./GenericLogic.sol"; import {ValidationLogic} from "./ValidationLogic.sol"; /** * @title BorrowLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements the logic to borrow feature */ library BorrowLogic { using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; using ReserveLogic for DataTypes.ReserveData; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted on borrow() when loan needs to be opened * @param user The address of the user initiating the borrow(), receiving the funds * @param reserve The address of the underlying asset being borrowed * @param amount The amount borrowed out * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param onBehalfOf The address that will be getting the loan * @param referral The referral code used **/ event Borrow( address user, address indexed reserve, uint256 amount, address nftAsset, uint256 nftTokenId, address indexed onBehalfOf, uint256 borrowRate, uint256 loanId, uint16 indexed referral ); /** * @dev Emitted on repay() * @param user The address of the user initiating the repay(), providing the funds * @param reserve The address of the underlying asset of the reserve * @param amount The amount repaid * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param borrower The beneficiary of the repayment, getting his debt reduced * @param loanId The loan ID of the NFT loans **/ event Repay( address user, address indexed reserve, uint256 amount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /*////////////////////////////////////////////////////////////// Structs //////////////////////////////////////////////////////////////*/ struct ExecuteBorrowLocalVars { address initiator; uint256 ltv; uint256 liquidationThreshold; uint256 liquidationBonus; uint256 loanId; address reserveOracle; address nftOracle; address loanAddress; uint256 totalSupply; } struct RepayLocalVars { address initiator; address poolLoan; address onBehalfOf; uint256 loanId; bool isUpdate; uint256 borrowAmount; uint256 repayAmount; } /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Implements the borrow feature. Through `borrow()`, users borrow assets from the protocol. * @dev Emits the `Borrow()` event. * @param addressesProvider The addresses provider * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param params The additional parameters needed to execute the borrow function */ function executeBorrow( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteBorrowParams memory params ) external { _borrow(addressesProvider, reservesData, nftsData, nftsConfig, params); } /** * @notice Implements the repay feature. Through `repay()`, users repay assets to the protocol. * @dev Emits the `Repay()` event. * @param reservesData The state of all the reserves * @param nftsData The state of nfts * @param params The additional parameters needed to execute the repay function */ function executeRepay( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteRepayParams memory params ) external returns (uint256, bool) { return _repay(addressesProvider, reservesData, nftsData, nftsConfig, params); } /*////////////////////////////////////////////////////////////// INTERNALS //////////////////////////////////////////////////////////////*/ /** * @notice Implements the borrow feature. Through `_borrow()`, users borrow assets from the protocol. * @dev Emits the `Borrow()` event. * @param addressesProvider The addresses provider * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param params The additional parameters needed to execute the borrow function */ function _borrow( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteBorrowParams memory params ) internal { require(params.onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS); ExecuteBorrowLocalVars memory vars; vars.initiator = params.initiator; DataTypes.ReserveData storage reserveData = reservesData[params.asset]; DataTypes.NftData storage nftData = nftsData[params.nftAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[params.nftAsset][params.nftTokenId]; // update state MUST BEFORE get borrow amount which is depent on latest borrow index reserveData.updateState(); // Convert asset amount to ETH vars.reserveOracle = addressesProvider.getReserveOracle(); vars.nftOracle = addressesProvider.getNFTOracle(); vars.loanAddress = addressesProvider.getLendPoolLoan(); address debtMarket = addressesProvider.getAddress(keccak256("DEBT_MARKET")); vars.loanId = ILendPoolLoan(vars.loanAddress).getCollateralLoanId(params.nftAsset, params.nftTokenId); ValidationLogic.validateBorrow( params, reserveData, nftData, nftConfig, vars.loanAddress, vars.loanId, vars.reserveOracle, vars.nftOracle ); address uToken = reserveData.uTokenAddress; require(IUToken(uToken).getAvailableLiquidity() >= params.amount, Errors.LP_RESERVES_WITHOUT_ENOUGH_LIQUIDITY); if (vars.loanId == 0) { IERC721Upgradeable(params.nftAsset).safeTransferFrom(vars.initiator, address(this), params.nftTokenId); vars.loanId = ILendPoolLoan(vars.loanAddress).createLoan( vars.initiator, params.onBehalfOf, params.nftAsset, params.nftTokenId, nftData.uNftAddress, params.asset, params.amount, reserveData.variableBorrowIndex ); } else { ILendPoolLoan(vars.loanAddress).updateLoan( vars.initiator, vars.loanId, params.amount, 0, reserveData.variableBorrowIndex ); uint256 debtId = IDebtMarket(debtMarket).getDebtId(params.nftAsset, params.nftTokenId); if (debtId != 0) { IDebtMarket(debtMarket).cancelDebtListing(params.nftAsset, params.nftTokenId); } } IDebtToken(reserveData.debtTokenAddress).mint( vars.initiator, params.onBehalfOf, params.amount, reserveData.variableBorrowIndex ); // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(params.asset, uToken, 0, params.amount); // Withdraw amount from external lending protocol uint256 value = IUToken(uToken).withdrawReserves(params.amount); // Transfer underlying to user IUToken(uToken).transferUnderlyingTo(vars.initiator, value); emit Borrow( vars.initiator, params.asset, value, params.nftAsset, params.nftTokenId, params.onBehalfOf, reserveData.currentVariableBorrowRate, vars.loanId, params.referralCode ); } /** * @notice Implements the repay feature. Through `repay()`, users repay assets to the protocol. * @dev Emits the `Repay()` event. * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param params The additional parameters needed to execute the repay function */ function _repay( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteRepayParams memory params ) internal returns (uint256, bool) { RepayLocalVars memory vars; vars.initiator = params.initiator; vars.poolLoan = addressesProvider.getLendPoolLoan(); vars.loanId = ILendPoolLoan(vars.poolLoan).getCollateralLoanId(params.nftAsset, params.nftTokenId); require(vars.loanId != 0, Errors.LP_NFT_IS_NOT_USED_AS_COLLATERAL); DataTypes.LoanData memory loanData = ILendPoolLoan(vars.poolLoan).getLoan(vars.loanId); DataTypes.ReserveData storage reserveData = reservesData[loanData.reserveAsset]; DataTypes.NftData storage nftData = nftsData[loanData.nftAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[params.nftAsset][params.nftTokenId]; // update state MUST BEFORE get borrow amount which is depent on latest borrow index reserveData.updateState(); (, vars.borrowAmount) = ILendPoolLoan(vars.poolLoan).getLoanReserveBorrowAmount(vars.loanId); ValidationLogic.validateRepay(reserveData, nftData, nftConfig, loanData, params.amount, vars.borrowAmount); vars.repayAmount = vars.borrowAmount; vars.isUpdate = false; if (params.amount < vars.repayAmount) { vars.isUpdate = true; vars.repayAmount = params.amount; } // Cancel debt listing if exist address debtMarket = addressesProvider.getAddress(keccak256("DEBT_MARKET")); uint256 debtId = IDebtMarket(debtMarket).getDebtId(params.nftAsset, params.nftTokenId); if (debtId != 0) { IDebtMarket(debtMarket).cancelDebtListing(params.nftAsset, params.nftTokenId); } if (vars.isUpdate) { ILendPoolLoan(vars.poolLoan).updateLoan( vars.initiator, vars.loanId, 0, vars.repayAmount, reserveData.variableBorrowIndex ); } else { ILendPoolLoan(vars.poolLoan).repayLoan( vars.initiator, vars.loanId, nftData.uNftAddress, vars.repayAmount, reserveData.variableBorrowIndex ); } IDebtToken(reserveData.debtTokenAddress).burn(loanData.borrower, vars.repayAmount, reserveData.variableBorrowIndex); address uToken = reserveData.uTokenAddress; // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(loanData.reserveAsset, uToken, vars.repayAmount, 0); // transfer repay amount to uToken IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom(vars.initiator, uToken, vars.repayAmount); // Deposit amount repaid to external lending protocol IUToken(uToken).depositReserves(vars.repayAmount); // transfer erc721 to borrower if (!vars.isUpdate) { IERC721Upgradeable(loanData.nftAsset).safeTransferFrom(address(this), loanData.borrower, params.nftTokenId); } emit Repay( vars.initiator, loanData.reserveAsset, vars.repayAmount, loanData.nftAsset, loanData.nftTokenId, loanData.borrower, vars.loanId ); return (vars.repayAmount, !vars.isUpdate); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ILendPoolLoan} from "../../interfaces/ILendPoolLoan.sol"; import {IReserveOracleGetter} from "../../interfaces/IReserveOracleGetter.sol"; import {INFTOracleGetter} from "../../interfaces/INFTOracleGetter.sol"; import {WadRayMath} from "../math/WadRayMath.sol"; import {PercentageMath} from "../math/PercentageMath.sol"; import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol"; import {NftConfiguration} from "../configuration/NftConfiguration.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; /** * @title GenericLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements protocol-level logic to calculate and validate the state of a user */ library GenericLogic { using ReserveLogic for DataTypes.ReserveData; using WadRayMath for uint256; using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using NftConfiguration for DataTypes.NftConfigurationMap; /*////////////////////////////////////////////////////////////// GENERAL VARIABLES //////////////////////////////////////////////////////////////*/ uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; /*////////////////////////////////////////////////////////////// Structs //////////////////////////////////////////////////////////////*/ struct CalculateLoanDataVars { uint256 reserveUnitPrice; uint256 reserveUnit; uint256 reserveDecimals; uint256 healthFactor; uint256 totalCollateralInETH; uint256 totalCollateralInReserve; uint256 totalDebtInETH; uint256 totalDebtInReserve; uint256 nftLtv; uint256 nftLiquidationThreshold; address nftAsset; uint256 nftTokenId; uint256 nftUnitPrice; uint256 minRedeemValue; } struct CalcLiquidatePriceLocalVars { uint256 ltv; uint256 liquidationThreshold; uint256 liquidationBonus; uint256 nftPriceInETH; uint256 nftPriceInReserve; uint256 reserveDecimals; uint256 reservePriceInETH; uint256 thresholdPrice; uint256 liquidatePrice; uint256 borrowAmount; } struct CalcLoanBidFineLocalVars { uint256 reserveDecimals; uint256 reservePriceInETH; uint256 baseBidFineInReserve; uint256 minBidFinePct; uint256 minBidFineInReserve; uint256 bidFineInReserve; uint256 debtAmount; } /*////////////////////////////////////////////////////////////// INTERNALS //////////////////////////////////////////////////////////////*/ /** * @dev Calculates the nft loan data. * this includes the total collateral/borrow balances in Reserve, * the Loan To Value, the Liquidation Ratio, and the Health factor. * @param reserveAddress the underlying asset of the reserve * @param reserveData Data of the reserve * @param nftAddress the underlying NFT asset * @param nftTokenId the token Id for the NFT * @param nftConfig Config of the nft by tokenId * @param loanAddress The loan address * @param loanId The loan identifier * @param reserveOracle The price oracle address of reserve * @param nftOracle The price oracle address of nft * @return The total collateral and total debt of the loan in Reserve, the ltv, liquidation threshold and the HF **/ function calculateLoanData( address reserveAddress, DataTypes.ReserveData storage reserveData, address nftAddress, uint256 nftTokenId, DataTypes.NftConfigurationMap storage nftConfig, address loanAddress, uint256 loanId, address reserveOracle, address nftOracle ) internal view returns (uint256, uint256, uint256) { CalculateLoanDataVars memory vars; (vars.nftLtv, vars.nftLiquidationThreshold, ) = nftConfig.getCollateralParams(); // calculate total borrow balance for the loan if (loanId != 0) { (vars.totalDebtInETH, vars.totalDebtInReserve) = calculateNftDebtData( reserveAddress, reserveData, loanAddress, loanId, reserveOracle ); } // calculate total collateral balance for the nft (vars.totalCollateralInETH, vars.totalCollateralInReserve) = calculateNftCollateralData( reserveAddress, reserveData, nftAddress, nftTokenId, reserveOracle, nftOracle ); // calculate health by borrow and collateral vars.healthFactor = calculateHealthFactorFromBalances( vars.totalCollateralInReserve, vars.totalDebtInReserve, vars.nftLiquidationThreshold ); return (vars.totalCollateralInReserve, vars.totalDebtInReserve, vars.healthFactor); } /** * @dev Calculates the nft debt data. * this includes the total collateral/borrow balances in Reserve, * the Loan To Value, the Liquidation Ratio, and the Health factor. * @param reserveAddress the underlying asset of the reserve * @param reserveData Data of the reserve * @param loanAddress The loan address * @param loanId The loan identifier * @param reserveOracle The price oracle address of reserve * @return The total debt in ETH and the total debt in the Reserve **/ function calculateNftDebtData( address reserveAddress, DataTypes.ReserveData storage reserveData, address loanAddress, uint256 loanId, address reserveOracle ) internal view returns (uint256, uint256) { CalculateLoanDataVars memory vars; // all asset price has converted to ETH based, unit is in WEI (18 decimals) vars.reserveDecimals = reserveData.configuration.getDecimals(); vars.reserveUnit = 10 ** vars.reserveDecimals; vars.reserveUnitPrice = IReserveOracleGetter(reserveOracle).getAssetPrice(reserveAddress); (, vars.totalDebtInReserve) = ILendPoolLoan(loanAddress).getLoanReserveBorrowAmount(loanId); vars.totalDebtInETH = (vars.totalDebtInReserve * vars.reserveUnitPrice) / vars.reserveUnit; return (vars.totalDebtInETH, vars.totalDebtInReserve); } /** * @dev Calculates the nft collateral data. * this includes the total collateral/borrow balances in Reserve, * the Loan To Value, the Liquidation Ratio, and the Health factor. * @param reserveAddress the underlying asset of the reserve * @param reserveData Data of the reserve * @param nftAddress The underlying NFT asset * @param nftTokenId The underlying NFT token Id * @param reserveOracle The price oracle address of reserve * @param nftOracle The nft price oracle address * @return The total debt in ETH and the total debt in the Reserve **/ function calculateNftCollateralData( address reserveAddress, DataTypes.ReserveData storage reserveData, address nftAddress, uint256 nftTokenId, address reserveOracle, address nftOracle ) internal view returns (uint256, uint256) { reserveData; CalculateLoanDataVars memory vars; // calculate total collateral balance for the nft // all asset price has converted to ETH based, unit is in WEI (18 decimals) vars.nftUnitPrice = INFTOracleGetter(nftOracle).getNFTPrice(nftAddress, nftTokenId); vars.totalCollateralInETH = vars.nftUnitPrice; if (reserveAddress != address(0)) { vars.reserveDecimals = reserveData.configuration.getDecimals(); vars.reserveUnit = 10 ** vars.reserveDecimals; vars.reserveUnitPrice = IReserveOracleGetter(reserveOracle).getAssetPrice(reserveAddress); vars.totalCollateralInReserve = (vars.totalCollateralInETH * vars.reserveUnit) / vars.reserveUnitPrice; } return (vars.totalCollateralInETH, vars.totalCollateralInReserve); } /** * @dev Calculates the optimal min redeem value * @param borrowAmount The debt * @param nftAddress The nft address * @param nftTokenId The token id * @param nftOracle The nft oracle address * @param liquidationThreshold The liquidation threshold * @param safeHealthFactor The safe health factor value * @return The health factor calculated from the balances provided **/ function calculateOptimalMinRedeemValue( uint256 borrowAmount, address nftAddress, uint256 nftTokenId, address nftOracle, uint256 liquidationThreshold, uint256 safeHealthFactor ) internal view returns (uint256) { CalculateLoanDataVars memory vars; vars.nftUnitPrice = INFTOracleGetter(nftOracle).getNFTPrice(nftAddress, nftTokenId); vars.minRedeemValue = borrowAmount - ((vars.nftUnitPrice.percentMul(liquidationThreshold)).wadDiv(safeHealthFactor)); if (vars.nftUnitPrice < vars.minRedeemValue) { return vars.nftUnitPrice; } return vars.minRedeemValue; } function calculateOptimalMaxRedeemValue(uint256 debt, uint256 minRedeem) internal pure returns (uint256) { return debt - ((debt - minRedeem).wadDiv(2 ether)); } /** * @dev Calculates the health factor from the corresponding balances * @param totalCollateral The total collateral * @param totalDebt The total debt * @param liquidationThreshold The avg liquidation threshold * @return The health factor calculated from the balances provided **/ function calculateHealthFactorFromBalances( uint256 totalCollateral, uint256 totalDebt, uint256 liquidationThreshold ) internal pure returns (uint256) { if (totalDebt == 0) return type(uint256).max; return (totalCollateral.percentMul(liquidationThreshold)).wadDiv(totalDebt); } /** * @dev Calculates the equivalent amount that an user can borrow, depending on the available collateral and the * average Loan To Value * @param totalCollateral The total collateral * @param totalDebt The total borrow balance * @param ltv The average loan to value * @return the amount available to borrow for the user **/ function calculateAvailableBorrows( uint256 totalCollateral, uint256 totalDebt, uint256 ltv ) internal pure returns (uint256) { uint256 availableBorrows = totalCollateral.percentMul(ltv); if (availableBorrows < totalDebt) { return 0; } availableBorrows = availableBorrows - totalDebt; return availableBorrows; } /** * @dev Calculates the loan liquidation price * @param loanId the loan Id * @param reserveAsset The underlying asset of the reserve * @param reserveData the reserve data * @param nftAsset the underlying NFT asset * @param nftTokenId the NFT token Id * @param nftConfig The NFT data * @param poolLoan The pool loan address * @param reserveOracle The price oracle address of reserve * @param nftOracle The price oracle address of nft * @return The borrow amount, threshold price and liquidation price **/ function calculateLoanLiquidatePrice( uint256 loanId, address reserveAsset, DataTypes.ReserveData storage reserveData, address nftAsset, uint256 nftTokenId, DataTypes.NftConfigurationMap storage nftConfig, address poolLoan, address reserveOracle, address nftOracle ) internal view returns (uint256, uint256, uint256) { CalcLiquidatePriceLocalVars memory vars; /* * 0 CR LH 100 * |___________________|___________________|___________________| * < Borrowing with Interest < * CR: Callteral Ratio; * LH: Liquidate Threshold; * Liquidate Trigger: Borrowing with Interest > thresholdPrice; * Liquidate Price: (100% - BonusRatio) * NFT Price; */ vars.reserveDecimals = reserveData.configuration.getDecimals(); (, vars.borrowAmount) = ILendPoolLoan(poolLoan).getLoanReserveBorrowAmount(loanId); (vars.ltv, vars.liquidationThreshold, vars.liquidationBonus) = nftConfig.getCollateralParams(); vars.nftPriceInETH = INFTOracleGetter(nftOracle).getNFTPrice(nftAsset, nftTokenId); vars.reservePriceInETH = IReserveOracleGetter(reserveOracle).getAssetPrice(reserveAsset); vars.nftPriceInReserve = ((10 ** vars.reserveDecimals) * vars.nftPriceInETH) / vars.reservePriceInETH; vars.thresholdPrice = vars.nftPriceInReserve.percentMul(vars.liquidationThreshold); vars.liquidatePrice = vars.nftPriceInReserve.percentMul(PercentageMath.PERCENTAGE_FACTOR - vars.liquidationBonus); return (vars.borrowAmount, vars.thresholdPrice, vars.liquidatePrice); } function calculateLoanBidFine( address reserveAsset, DataTypes.ReserveData storage reserveData, address nftAsset, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData, address poolLoan, address reserveOracle ) internal view returns (uint256, uint256) { nftAsset; if (loanData.bidPrice == 0) { return (0, 0); } CalcLoanBidFineLocalVars memory vars; vars.reserveDecimals = reserveData.configuration.getDecimals(); vars.reservePriceInETH = IReserveOracleGetter(reserveOracle).getAssetPrice(reserveAsset); vars.baseBidFineInReserve = (1 ether * 10 ** vars.reserveDecimals) / vars.reservePriceInETH; vars.minBidFinePct = nftConfig.getMinBidFine(); vars.minBidFineInReserve = vars.baseBidFineInReserve.percentMul(vars.minBidFinePct); (, vars.debtAmount) = ILendPoolLoan(poolLoan).getLoanReserveBorrowAmount(loanData.loanId); vars.bidFineInReserve = vars.debtAmount.percentMul(nftConfig.getRedeemFine()); if (vars.bidFineInReserve < vars.minBidFineInReserve) { vars.bidFineInReserve = vars.minBidFineInReserve; } return (vars.minBidFineInReserve, vars.bidFineInReserve); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {IUToken} from "../../interfaces/IUToken.sol"; import {IDebtToken} from "../../interfaces/IDebtToken.sol"; import {IInterestRate} from "../../interfaces/IInterestRate.sol"; import {ILendPoolAddressesProvider} from "../../interfaces/ILendPoolAddressesProvider.sol"; import {IReserveOracleGetter} from "../../interfaces/IReserveOracleGetter.sol"; import {INFTOracleGetter} from "../../interfaces/INFTOracleGetter.sol"; import {ILendPoolLoan} from "../../interfaces/ILendPoolLoan.sol"; import {ILendPool} from "../../interfaces/ILendPool.sol"; import {ILockeyManager} from "../../interfaces/ILockeyManager.sol"; import {IDebtMarket} from "../../interfaces/IDebtMarket.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; import {GenericLogic} from "./GenericLogic.sol"; import {ValidationLogic} from "./ValidationLogic.sol"; import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol"; import {NftConfiguration} from "../configuration/NftConfiguration.sol"; import {PercentageMath} from "../math/PercentageMath.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import {IERC721MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol"; /** * @title LiquidateLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements the logic to liquidate feature */ library LiquidateLogic { using PercentageMath for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; using ReserveLogic for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using NftConfiguration for DataTypes.NftConfigurationMap; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted when a borrower's loan is auctioned. * @param user The address of the user initiating the auction * @param reserve The address of the underlying asset of the reserve * @param bidPrice The price of the underlying reserve given by the bidder * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param onBehalfOf The address that will be getting the NFT * @param loanId The loan ID of the NFT loans **/ event Auction( address user, address indexed reserve, uint256 bidPrice, address indexed nftAsset, uint256 nftTokenId, address onBehalfOf, address indexed borrower, uint256 loanId ); /** * @dev Emitted on redeem() * @param user The address of the user initiating the redeem(), providing the funds * @param reserve The address of the underlying asset of the reserve * @param borrowAmount The borrow amount repaid * @param nftAsset The address of the underlying NFT used as collateral * @param nftTokenId The token id of the underlying NFT used as collateral * @param loanId The loan ID of the NFT loans **/ event Redeem( address user, address indexed reserve, uint256 borrowAmount, uint256 fineAmount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /** * @dev Emitted when a borrower's loan is liquidated. * @param user The address of the user initiating the auction * @param reserve The address of the underlying asset of the reserve * @param repayAmount The amount of reserve repaid by the liquidator * @param remainAmount The amount of reserve received by the borrower * @param loanId The loan ID of the NFT loans **/ event Liquidate( address user, address indexed reserve, uint256 repayAmount, uint256 remainAmount, address indexed nftAsset, uint256 nftTokenId, address indexed borrower, uint256 loanId ); /** * @dev Emitted when an NFT is purchased via Buyout. * @param user The address of the user initiating the Buyout * @param reserve The address of the underlying asset of the reserve * @param buyoutAmount The amount of reserve paid by the buyer * @param borrowAmount The loan borrowed amount * @param nftAsset The amount of reserve received by the borrower * @param nftTokenId The token id of the underlying NFT used as collateral * @param borrower The loan borrower address * @param onBehalfOf The receiver of the underlying NFT * @param loanId The loan ID of the NFT loans **/ event Buyout( address user, address indexed reserve, uint256 buyoutAmount, uint256 borrowAmount, address indexed nftAsset, uint256 nftTokenId, address borrower, address onBehalfOf, uint256 indexed loanId ); /*////////////////////////////////////////////////////////////// Structs //////////////////////////////////////////////////////////////*/ struct AuctionLocalVars { address loanAddress; address reserveOracle; address nftOracle; address initiator; uint256 loanId; uint256 thresholdPrice; uint256 liquidatePrice; uint256 borrowAmount; uint256 auctionEndTimestamp; uint256 minBidDelta; uint256 extraAuctionDuration; } struct RedeemLocalVars { address initiator; address poolLoan; address reserveOracle; address nftOracle; address uToken; address debtMarket; uint256 loanId; uint256 borrowAmount; uint256 repayAmount; uint256 minRepayAmount; uint256 maxRepayAmount; uint256 bidFine; uint256 redeemEndTimestamp; uint256 minBidFinePct; uint256 minBidFine; uint256 extraRedeemDuration; uint256 liquidationThreshold; } struct LiquidateLocalVars { address initiator; address poolLoan; address reserveOracle; address nftOracle; address uToken; address debtMarket; uint256 loanId; uint256 borrowAmount; uint256 extraDebtAmount; uint256 remainAmount; uint256 auctionEndTimestamp; uint256 extraAuctionDuration; } struct BuyoutLocalVars { address initiator; address onBehalfOf; address poolLoan; address reserveOracle; address nftOracle; address debtMarket; uint256 loanId; uint256 borrowAmount; uint256 remainAmount; uint256 bidFine; uint256 nftPrice; address lockeysCollection; address lockeyManagerAddress; } /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Implements the auction feature. Through `auction()`, users auction assets in the protocol. * @dev Emits the `Auction()` event. * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param poolStates The state of the lend pool * @param params The additional parameters needed to execute the auction function */ function executeAuction( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteLendPoolStates memory poolStates, DataTypes.ExecuteAuctionParams memory params ) external { require(params.onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS); AuctionLocalVars memory vars; vars.initiator = params.initiator; vars.loanAddress = addressesProvider.getLendPoolLoan(); vars.reserveOracle = addressesProvider.getReserveOracle(); vars.nftOracle = addressesProvider.getNFTOracle(); vars.loanId = ILendPoolLoan(vars.loanAddress).getCollateralLoanId(params.nftAsset, params.nftTokenId); require(vars.loanId != 0, Errors.LP_NFT_IS_NOT_USED_AS_COLLATERAL); DataTypes.LoanData memory loanData = ILendPoolLoan(vars.loanAddress).getLoan(vars.loanId); //Initiator can not bid for same onBehalfOf address, as the new auction would be the same as the currently existing auction //created by them previously. Nevertheless, it is possible for the initiator to bid for a different `onBehalfOf` address, //as the new bidderAddress will be different. require(params.onBehalfOf != loanData.bidderAddress, Errors.LP_CONSECUTIVE_BIDS_NOT_ALLOWED); DataTypes.ReserveData storage reserveData = reservesData[loanData.reserveAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[loanData.nftAsset][loanData.nftTokenId]; DataTypes.NftData storage nftData = nftsData[loanData.nftAsset]; ValidationLogic.validateAuction(reserveData, nftData, nftConfig, loanData, params.bidPrice); // update state MUST BEFORE get borrow amount which is depent on latest borrow index reserveData.updateState(); (vars.borrowAmount, vars.thresholdPrice, ) = GenericLogic.calculateLoanLiquidatePrice( vars.loanId, loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.loanAddress, vars.reserveOracle, vars.nftOracle ); // first time bid need to burn debt tokens and transfer reserve to uTokens if (loanData.state == DataTypes.LoanState.Active) { // loan's accumulated debt must exceed threshold (heath factor below 1.0) require(vars.borrowAmount > vars.thresholdPrice, Errors.LP_BORROW_NOT_EXCEED_LIQUIDATION_THRESHOLD); // bid price must greater than borrow amount + delta (predicted compounded debt || fees) require( params.bidPrice >= vars.borrowAmount.percentMul(params.bidDelta), Errors.LPL_BID_PRICE_LESS_THAN_DEBT_PRICE ); // bid price must greater than borrow amount plus timestamp configuration fee require( params.bidPrice >= (vars.borrowAmount + params.auctionDurationConfigFee), Errors.LPL_BID_PRICE_LESS_THAN_MIN_BID_REQUIRED ); } else { // bid price must greater than borrow debt require(params.bidPrice >= vars.borrowAmount, Errors.LPL_BID_PRICE_LESS_THAN_BORROW); if ((poolStates.pauseDurationTime > 0) && (loanData.bidStartTimestamp <= poolStates.pauseStartTime)) { vars.extraAuctionDuration = poolStates.pauseDurationTime; } vars.auctionEndTimestamp = loanData.bidStartTimestamp + vars.extraAuctionDuration + (nftConfig.getAuctionDuration() * 1 minutes); require(block.timestamp <= vars.auctionEndTimestamp, Errors.LPL_BID_AUCTION_DURATION_HAS_END); // bid price must greater than highest bid + delta vars.minBidDelta = vars.borrowAmount.percentMul(PercentageMath.ONE_PERCENT); require(params.bidPrice >= (loanData.bidPrice + vars.minBidDelta), Errors.LPL_BID_PRICE_LESS_THAN_HIGHEST_PRICE); } ILendPoolLoan(vars.loanAddress).auctionLoan( vars.initiator, vars.loanId, params.onBehalfOf, params.bidPrice, vars.borrowAmount, reserveData.variableBorrowIndex ); // lock highest bidder bid price amount to lend pool IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom(vars.initiator, address(this), params.bidPrice); // transfer (return back) last bid price amount to previous bidder from lend pool if (loanData.bidderAddress != address(0)) { IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.bidderAddress, loanData.bidPrice); } // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(loanData.reserveAsset, reserveData.uTokenAddress, 0, 0); emit Auction( vars.initiator, loanData.reserveAsset, params.bidPrice, params.nftAsset, params.nftTokenId, params.onBehalfOf, loanData.borrower, vars.loanId ); } /** * @notice Implements the redeem feature. Through `redeem()`, users redeem assets in the protocol. * @dev Emits the `Redeem()` event. * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param poolStates The state of the lend pool * @param params The additional parameters needed to execute the redeem function */ function executeRedeem( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteLendPoolStates memory poolStates, DataTypes.ExecuteRedeemParams memory params ) external returns (uint256) { RedeemLocalVars memory vars; vars.initiator = params.initiator; vars.poolLoan = addressesProvider.getLendPoolLoan(); vars.reserveOracle = addressesProvider.getReserveOracle(); vars.nftOracle = addressesProvider.getNFTOracle(); vars.loanId = ILendPoolLoan(vars.poolLoan).getCollateralLoanId(params.nftAsset, params.nftTokenId); require(vars.loanId != 0, Errors.LP_NFT_IS_NOT_USED_AS_COLLATERAL); DataTypes.LoanData memory loanData = ILendPoolLoan(vars.poolLoan).getLoan(vars.loanId); DataTypes.ReserveData storage reserveData = reservesData[loanData.reserveAsset]; DataTypes.NftData storage nftData = nftsData[loanData.nftAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[loanData.nftAsset][loanData.nftTokenId]; ValidationLogic.validateRedeem(reserveData, nftData, nftConfig, loanData, params.amount); if ((poolStates.pauseDurationTime > 0) && (loanData.bidStartTimestamp <= poolStates.pauseStartTime)) { vars.extraRedeemDuration = poolStates.pauseDurationTime; } vars.redeemEndTimestamp = (loanData.bidStartTimestamp + vars.extraRedeemDuration + nftConfig.getRedeemDuration() * 1 minutes); require(block.timestamp <= vars.redeemEndTimestamp, Errors.LPL_BID_REDEEM_DURATION_HAS_END); // update state MUST BEFORE get borrow amount which is depent on latest borrow index reserveData.updateState(); (vars.borrowAmount, , ) = GenericLogic.calculateLoanLiquidatePrice( vars.loanId, loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.poolLoan, vars.reserveOracle, vars.nftOracle ); // check bid fine in min & max range (, vars.bidFine) = GenericLogic.calculateLoanBidFine( loanData.reserveAsset, reserveData, loanData.nftAsset, nftConfig, loanData, vars.poolLoan, vars.reserveOracle ); // check bid fine is enough require(vars.bidFine <= params.bidFine, Errors.LPL_INVALID_BID_FINE); vars.repayAmount = params.amount; // check the minimum debt repay amount, use liquidation threshold in config (, vars.liquidationThreshold, ) = nftConfig.getCollateralParams(); vars.minRepayAmount = GenericLogic.calculateOptimalMinRedeemValue( vars.borrowAmount, params.nftAsset, params.nftTokenId, vars.nftOracle, vars.liquidationThreshold, params.safeHealthFactor ); require(vars.repayAmount >= vars.minRepayAmount, Errors.LP_AMOUNT_LESS_THAN_REDEEM_THRESHOLD); // check the maxinmum debt repay amount vars.maxRepayAmount = GenericLogic.calculateOptimalMaxRedeemValue(vars.borrowAmount, vars.minRepayAmount); require(vars.repayAmount <= vars.maxRepayAmount, Errors.LP_AMOUNT_GREATER_THAN_MAX_REPAY); ILendPoolLoan(vars.poolLoan).redeemLoan( vars.initiator, vars.loanId, vars.repayAmount, reserveData.variableBorrowIndex ); IDebtToken(reserveData.debtTokenAddress).burn(loanData.borrower, vars.repayAmount, reserveData.variableBorrowIndex); // Cancel debt listing if exist vars.debtMarket = addressesProvider.getAddress(keccak256("DEBT_MARKET")); if (IDebtMarket(vars.debtMarket).getDebtId(loanData.nftAsset, loanData.nftTokenId) != 0) { IDebtMarket(vars.debtMarket).cancelDebtListing(loanData.nftAsset, loanData.nftTokenId); } vars.uToken = reserveData.uTokenAddress; // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(loanData.reserveAsset, vars.uToken, vars.repayAmount, 0); // transfer repay amount from borrower to uToken IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom(vars.initiator, vars.uToken, vars.repayAmount); // Deposit amount redeemed to lending protocol IUToken(vars.uToken).depositReserves(vars.repayAmount); if (loanData.bidderAddress != address(0)) { // transfer (return back) last bid price amount from lend pool to bidder IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.bidderAddress, loanData.bidPrice); // transfer bid penalty fine amount from borrower to the first bidder IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom( vars.initiator, loanData.firstBidderAddress, vars.bidFine ); } emit Redeem( vars.initiator, loanData.reserveAsset, vars.repayAmount, vars.bidFine, loanData.nftAsset, loanData.nftTokenId, loanData.borrower, vars.loanId ); return (vars.repayAmount + vars.bidFine); } /** * @notice Implements the liquidate feature. Through `liquidate()`, users liquidate assets in the protocol. * @dev Emits the `Liquidate()` event. * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param nftsConfig The state of the nft by tokenId * @param poolStates The state of the lend pool * @param params The additional parameters needed to execute the liquidate function */ function executeLiquidate( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteLendPoolStates memory poolStates, DataTypes.ExecuteLiquidateParams memory params ) external returns (uint256) { LiquidateLocalVars memory vars; vars.initiator = params.initiator; vars.poolLoan = addressesProvider.getLendPoolLoan(); vars.reserveOracle = addressesProvider.getReserveOracle(); vars.nftOracle = addressesProvider.getNFTOracle(); vars.loanId = ILendPoolLoan(vars.poolLoan).getCollateralLoanId(params.nftAsset, params.nftTokenId); require(vars.loanId != 0, Errors.LP_NFT_IS_NOT_USED_AS_COLLATERAL); DataTypes.LoanData memory loanData = ILendPoolLoan(vars.poolLoan).getLoan(vars.loanId); DataTypes.ReserveData storage reserveData = reservesData[loanData.reserveAsset]; DataTypes.NftData storage nftData = nftsData[loanData.nftAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[loanData.nftAsset][loanData.nftTokenId]; ValidationLogic.validateLiquidate(reserveData, nftData, nftConfig, loanData); // If pool paused after bidding start, add pool pausing time as extra auction duration if ((poolStates.pauseDurationTime > 0) && (loanData.bidStartTimestamp <= poolStates.pauseStartTime)) { vars.extraAuctionDuration = poolStates.pauseDurationTime; } vars.auctionEndTimestamp = loanData.bidStartTimestamp + vars.extraAuctionDuration + (nftConfig.getAuctionDuration() * 1 minutes); require(block.timestamp > vars.auctionEndTimestamp + 20 minutes, Errors.LPL_CLAIM_HASNT_STARTED_YET); // update state MUST BEFORE get borrow amount which is depent on latest borrow index reserveData.updateState(); (vars.borrowAmount, , ) = GenericLogic.calculateLoanLiquidatePrice( vars.loanId, loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.poolLoan, vars.reserveOracle, vars.nftOracle ); // Last bid price can not cover borrow amount if (loanData.bidPrice < vars.borrowAmount) { vars.extraDebtAmount = vars.borrowAmount - loanData.bidPrice; require(params.amount >= vars.extraDebtAmount, Errors.LP_AMOUNT_LESS_THAN_EXTRA_DEBT); } if (loanData.bidPrice > vars.borrowAmount) { vars.remainAmount = loanData.bidPrice - vars.borrowAmount; } ILendPoolLoan(vars.poolLoan).liquidateLoan( loanData.bidderAddress, vars.loanId, nftData.uNftAddress, vars.borrowAmount, reserveData.variableBorrowIndex ); IDebtToken(reserveData.debtTokenAddress).burn( loanData.borrower, vars.borrowAmount, reserveData.variableBorrowIndex ); // Cancel debt listing if exist vars.debtMarket = addressesProvider.getAddress(keccak256("DEBT_MARKET")); if (IDebtMarket(vars.debtMarket).getDebtId(loanData.nftAsset, loanData.nftTokenId) != 0) { IDebtMarket(vars.debtMarket).cancelDebtListing(loanData.nftAsset, loanData.nftTokenId); } vars.uToken = reserveData.uTokenAddress; // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(loanData.reserveAsset, vars.uToken, vars.borrowAmount, 0); // transfer extra borrow amount from liquidator to lend pool if (vars.extraDebtAmount > 0) { IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom(vars.initiator, address(this), vars.extraDebtAmount); } // transfer borrow amount from lend pool to uToken, repay debt IERC20Upgradeable(loanData.reserveAsset).safeTransfer(vars.uToken, vars.borrowAmount); // Deposit amount from debt repaid to lending protocol IUToken(vars.uToken).depositReserves(vars.borrowAmount); // transfer remain amount to borrower if (vars.remainAmount > 0) { IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.borrower, vars.remainAmount); } // transfer erc721 to bidder. //avoid DoS by transferring NFT to a malicious contract that reverts on ERC721 receive (bool success, ) = address(loanData.nftAsset).call( abi.encodeWithSignature( "safeTransferFrom(address,address,uint256)", address(this), loanData.bidderAddress, params.nftTokenId ) ); //If transfer was made to a malicious contract, send NFT to treasury if (!success) IERC721Upgradeable(loanData.nftAsset).safeTransferFrom( address(this), IUToken(vars.uToken).RESERVE_TREASURY_ADDRESS(), params.nftTokenId ); emit Liquidate( vars.initiator, loanData.reserveAsset, vars.borrowAmount, vars.remainAmount, loanData.nftAsset, loanData.nftTokenId, loanData.borrower, vars.loanId ); return (vars.extraDebtAmount); } /** * @notice Implements the buyout feature. Through `buyout()`, users buy NFTs currently deposited in the protocol. * @dev Emits the `Buyout()` event. * @param reservesData The state of all the reserves * @param nftsData The state of all the nfts * @param nftsConfig The state of the nft by tokenId * @param params The additional parameters needed to execute the buyout function */ function executeBuyout( ILendPoolAddressesProvider addressesProvider, mapping(address => DataTypes.ReserveData) storage reservesData, mapping(address => DataTypes.NftData) storage nftsData, mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) storage nftsConfig, DataTypes.ExecuteBuyoutParams memory params ) external { require(params.onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS); BuyoutLocalVars memory vars; vars.initiator = params.initiator; vars.onBehalfOf = params.onBehalfOf; vars.poolLoan = addressesProvider.getLendPoolLoan(); vars.reserveOracle = addressesProvider.getReserveOracle(); vars.nftOracle = addressesProvider.getNFTOracle(); vars.lockeysCollection = addressesProvider.getAddress(keccak256("LOCKEY_COLLECTION")); vars.lockeyManagerAddress = addressesProvider.getAddress(keccak256("LOCKEY_MANAGER")); vars.loanId = ILendPoolLoan(vars.poolLoan).getCollateralLoanId(params.nftAsset, params.nftTokenId); require(vars.loanId != 0, Errors.LP_NFT_IS_NOT_USED_AS_COLLATERAL); DataTypes.LoanData memory loanData = ILendPoolLoan(vars.poolLoan).getLoan(vars.loanId); DataTypes.ReserveData storage reserveData = reservesData[loanData.reserveAsset]; DataTypes.NftData storage nftData = nftsData[loanData.nftAsset]; DataTypes.NftConfigurationMap storage nftConfig = nftsConfig[loanData.nftAsset][loanData.nftTokenId]; vars.nftPrice = INFTOracleGetter(vars.nftOracle).getNFTPrice(loanData.nftAsset, loanData.nftTokenId); // Check for health factor (, , uint256 healthFactor) = GenericLogic.calculateLoanData( loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.poolLoan, vars.loanId, vars.reserveOracle, vars.nftOracle ); //Loan must be unhealthy in order to get liquidated require( healthFactor <= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); (vars.borrowAmount, , ) = GenericLogic.calculateLoanLiquidatePrice( vars.loanId, loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.poolLoan, vars.reserveOracle, vars.nftOracle ); (, vars.bidFine) = GenericLogic.calculateLoanBidFine( loanData.reserveAsset, reserveData, loanData.nftAsset, nftConfig, loanData, vars.poolLoan, vars.reserveOracle ); ValidationLogic.validateBuyout(reserveData, nftData, nftConfig, loanData); require(params.amount > vars.borrowAmount, Errors.LP_AMOUNT_LESS_THAN_DEBT); // If the user is a lockey holder, he gets a discount if (IERC721Upgradeable(vars.lockeysCollection).balanceOf(vars.onBehalfOf) != 0) { require( params.amount == vars.nftPrice.percentMul(ILockeyManager(vars.lockeyManagerAddress).getLockeyDiscountPercentage()), Errors.LP_AMOUNT_DIFFERENT_FROM_REQUIRED_BUYOUT_PRICE ); } else { require(params.amount == vars.nftPrice, Errors.LP_AMOUNT_DIFFERENT_FROM_REQUIRED_BUYOUT_PRICE); } reserveData.updateState(); (vars.borrowAmount, , ) = GenericLogic.calculateLoanLiquidatePrice( vars.loanId, loanData.reserveAsset, reserveData, loanData.nftAsset, loanData.nftTokenId, nftConfig, vars.poolLoan, vars.reserveOracle, vars.nftOracle ); ILendPoolLoan(vars.poolLoan).buyoutLoan( loanData.bidderAddress, vars.loanId, nftData.uNftAddress, vars.borrowAmount, reserveData.variableBorrowIndex, params.amount ); IDebtToken(reserveData.debtTokenAddress).burn( loanData.borrower, vars.borrowAmount, reserveData.variableBorrowIndex ); // Cancel debt listing if exist vars.debtMarket = addressesProvider.getAddress(keccak256("DEBT_MARKET")); if (IDebtMarket(vars.debtMarket).getDebtId(loanData.nftAsset, loanData.nftTokenId) != 0) { IDebtMarket(vars.debtMarket).cancelDebtListing(loanData.nftAsset, loanData.nftTokenId); } // update interest rate according latest borrow amount (utilizaton) reserveData.updateInterestRates(loanData.reserveAsset, reserveData.uTokenAddress, vars.borrowAmount, 0); //Transfer buyout amount to lendpool IERC20Upgradeable(loanData.reserveAsset).safeTransferFrom(vars.initiator, address(this), params.amount); // transfer borrow amount from lend pool to uToken, repay debt IERC20Upgradeable(loanData.reserveAsset).safeTransfer(reserveData.uTokenAddress, vars.borrowAmount); // Deposit amount from debt repaid to lending protocol IUToken(reserveData.uTokenAddress).depositReserves(vars.borrowAmount); vars.remainAmount = params.amount - vars.borrowAmount; // In case the NFT has bids, transfer (give back) bid amount to bidder, and send incentive fine to first bidder if (loanData.bidderAddress != address(0)) { IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.bidderAddress, loanData.bidPrice); // Remaining amount can cover the bid fine. if (vars.remainAmount >= vars.bidFine) { vars.remainAmount -= vars.bidFine; IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.firstBidderAddress, vars.bidFine); } else if (vars.remainAmount > 0) { // Remaining amount can not cover the bid fine, but it is greater than 0 IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.firstBidderAddress, vars.remainAmount); delete vars.remainAmount; } } // transfer remain amount to borrower if (vars.remainAmount != 0) { IERC20Upgradeable(loanData.reserveAsset).safeTransfer(loanData.borrower, vars.remainAmount); } // transfer erc721 to buyer. //avoid DoS by transferring NFT to a malicious contract that reverts on ERC721 receive (bool success, ) = address(loanData.nftAsset).call( abi.encodeWithSignature( "safeTransferFrom(address,address,uint256)", address(this), vars.onBehalfOf, params.nftTokenId ) ); //If transfer was made to a malicious contract, send NFT to treasury if (!success) IERC721Upgradeable(loanData.nftAsset).safeTransferFrom( address(this), IUToken(reserveData.uTokenAddress).RESERVE_TREASURY_ADDRESS(), params.nftTokenId ); { emit Buyout( vars.initiator, loanData.reserveAsset, params.amount, vars.borrowAmount, loanData.nftAsset, loanData.nftTokenId, loanData.borrower, vars.onBehalfOf, vars.loanId ); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; /** * @title NftLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements the logic to update the nft state */ library NftLogic { /** * @dev Initializes a nft * @param nft The nft object * @param uNftAddress The address of the uNFT contract **/ function init(DataTypes.NftData storage nft, address uNftAddress) external { require(nft.uNftAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED); nft.uNftAddress = uNftAddress; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {IUToken} from "../../interfaces/IUToken.sol"; import {IDebtToken} from "../../interfaces/IDebtToken.sol"; import {IInterestRate} from "../../interfaces/IInterestRate.sol"; import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol"; import {MathUtils} from "../math/MathUtils.sol"; import {WadRayMath} from "../math/WadRayMath.sol"; import {PercentageMath} from "../math/PercentageMath.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; /** * @title ReserveLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements the logic to update the reserves state */ library ReserveLogic { using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; using ReserveLogic for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted when the state of a reserve is updated * @param asset The address of the underlying asset of the reserve * @param liquidityRate The new liquidity rate * @param variableBorrowRate The new variable borrow rate * @param liquidityIndex The new liquidity index * @param variableBorrowIndex The new variable borrow index **/ event ReserveDataUpdated( address indexed asset, uint256 liquidityRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /*////////////////////////////////////////////////////////////// STRUCTS //////////////////////////////////////////////////////////////*/ struct UpdateInterestRatesLocalVars { uint256 availableLiquidity; uint256 newLiquidityRate; uint256 newVariableRate; uint256 totalVariableDebt; } struct MintToTreasuryLocalVars { uint256 currentVariableDebt; uint256 previousVariableDebt; uint256 totalDebtAccrued; uint256 amountToMint; uint256 reserveFactor; } /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @dev Initializes a reserve * @param reserve The reserve object * @param uTokenAddress The address of the overlying uToken contract * @param debtTokenAddress The address of the overlying debtToken contract * @param interestRateAddress The address of the interest rate strategy contract **/ function init( DataTypes.ReserveData storage reserve, address uTokenAddress, address debtTokenAddress, address interestRateAddress ) external { require(reserve.uTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED); reserve.liquidityIndex = uint128(WadRayMath.ray()); reserve.variableBorrowIndex = uint128(WadRayMath.ray()); reserve.uTokenAddress = uTokenAddress; reserve.debtTokenAddress = debtTokenAddress; reserve.interestRateAddress = interestRateAddress; } /*////////////////////////////////////////////////////////////// INTERNALS //////////////////////////////////////////////////////////////*/ /** * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ function updateState(DataTypes.ReserveData storage reserve) internal { uint256 scaledVariableDebt = IDebtToken(reserve.debtTokenAddress).scaledTotalSupply(); uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; uint256 previousLiquidityIndex = reserve.liquidityIndex; uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes( reserve, scaledVariableDebt, previousLiquidityIndex, previousVariableBorrowIndex, lastUpdatedTimestamp ); _mintToTreasury( reserve, scaledVariableDebt, previousVariableBorrowIndex, newLiquidityIndex, newVariableBorrowIndex, lastUpdatedTimestamp ); } /** * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. * @param reserve The reserve object * @param totalLiquidity The total liquidity available in the reserve * @param amount The amount to accomulate **/ function cumulateToLiquidityIndex( DataTypes.ReserveData storage reserve, uint256 totalLiquidity, uint256 amount ) internal { uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay()); uint256 result = amountToLiquidityRatio + (WadRayMath.ray()); result = result.rayMul(reserve.liquidityIndex); require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); reserve.liquidityIndex = uint128(result); } /** * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate * @param reserve The address of the reserve to be updated * @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action * @param liquidityTaken The amount of liquidity taken from the protocol (withdraw or borrow) **/ function updateInterestRates( DataTypes.ReserveData storage reserve, address reserveAddress, address uTokenAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { UpdateInterestRatesLocalVars memory vars; //calculates the total variable debt locally using the scaled borrow amount instead //of borrow amount(), as it's noticeably cheaper. Also, the index has been //updated by the previous updateState() call vars.totalVariableDebt = IDebtToken(reserve.debtTokenAddress).scaledTotalSupply().rayMul( reserve.variableBorrowIndex ); (vars.newLiquidityRate, vars.newVariableRate) = IInterestRate(reserve.interestRateAddress).calculateInterestRates( reserveAddress, uTokenAddress, liquidityAdded, liquidityTaken, vars.totalVariableDebt, reserve.configuration.getReserveFactor() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW); reserve.currentLiquidityRate = uint128(vars.newLiquidityRate); reserve.currentVariableBorrowRate = uint128(vars.newVariableRate); emit ReserveDataUpdated( reserveAddress, vars.newLiquidityRate, vars.newVariableRate, reserve.liquidityIndex, reserve.variableBorrowIndex ); } /** * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * specific asset. * @param reserve The reserve reserve to be updated * @param scaledVariableDebt The current scaled total variable debt * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest * @param newLiquidityIndex The new liquidity index * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest **/ function _mintToTreasury( DataTypes.ReserveData storage reserve, uint256 scaledVariableDebt, uint256 previousVariableBorrowIndex, uint256 newLiquidityIndex, uint256 newVariableBorrowIndex, uint40 timestamp ) internal { timestamp; MintToTreasuryLocalVars memory vars; vars.reserveFactor = reserve.configuration.getReserveFactor(); if (vars.reserveFactor == 0) { return; } //calculate the last principal variable debt vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); //calculate the new total supply after accumulation of the index vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars.currentVariableDebt - (vars.previousVariableDebt); vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { IUToken(reserve.uTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex); } } /** * @dev Updates the reserve indexes and the timestamp of the update * @param reserve The reserve reserve to be updated * @param scaledVariableDebt The scaled variable debt * @param liquidityIndex The last stored liquidity index * @param variableBorrowIndex The last stored variable borrow index **/ function _updateIndexes( DataTypes.ReserveData storage reserve, uint256 scaledVariableDebt, uint256 liquidityIndex, uint256 variableBorrowIndex, uint40 timestamp ) internal returns (uint256, uint256) { uint256 currentLiquidityRate = reserve.currentLiquidityRate; uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; //only cumulating if there is any income being produced if (currentLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); reserve.liquidityIndex = uint128(newLiquidityIndex); //as the liquidity rate might come only from stable rate loans, we need to ensure //that there is actual variable debt before accumulating if (scaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( reserve.currentVariableBorrowRate, timestamp ); newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); require(newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW); reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); } } //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); return (newLiquidityIndex, newVariableBorrowIndex); } /*////////////////////////////////////////////////////////////// INTERNAL GETTERS //////////////////////////////////////////////////////////////*/ /** * @dev Returns the ongoing normalized income for the reserve * A value of 1e27 means there is no income. As time passes, the income is accrued * A value of 2*1e27 means for each unit of asset one unit of income has been accrued * @param reserve The reserve object * @return the normalized income. expressed in ray **/ function getNormalizedIncome(DataTypes.ReserveData storage reserve) internal view returns (uint256) { uint40 timestamp = reserve.lastUpdateTimestamp; //solium-disable-next-line if (timestamp == uint40(block.timestamp)) { //if the index was updated in the same block, no need to perform any calculation return reserve.liquidityIndex; } uint256 cumulated = MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul( reserve.liquidityIndex ); return cumulated; } /** * @dev Returns the ongoing normalized variable debt for the reserve * A value of 1e27 means there is no debt. As time passes, the income is accrued * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated * @param reserve The reserve object * @return The normalized variable debt. expressed in ray **/ function getNormalizedDebt(DataTypes.ReserveData storage reserve) internal view returns (uint256) { uint40 timestamp = reserve.lastUpdateTimestamp; //solium-disable-next-line if (timestamp == uint40(block.timestamp)) { //if the index was updated in the same block, no need to perform any calculation return reserve.variableBorrowIndex; } uint256 cumulated = MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul( reserve.variableBorrowIndex ); return cumulated; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {IUToken} from "../../interfaces/IUToken.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; import {ValidationLogic} from "./ValidationLogic.sol"; /** * @title SupplyLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements the logic to supply feature */ library SupplyLogic { using SafeERC20Upgradeable for IERC20Upgradeable; using ReserveLogic for DataTypes.ReserveData; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @dev Emitted on deposit() * @param user The address initiating the deposit * @param amount The amount deposited * @param reserve The address of the underlying asset of the reserve * @param onBehalfOf The beneficiary of the deposit, receiving the uTokens * @param referral The referral code used **/ event Deposit( address user, address indexed reserve, uint256 amount, address indexed onBehalfOf, uint16 indexed referral ); /** * @dev Emitted on withdraw() * @param user The address initiating the withdrawal, owner of uTokens * @param reserve The address of the underlyng asset being withdrawn * @param amount The amount to be withdrawn * @param to Address that will receive the underlying **/ event Withdraw(address indexed user, address indexed reserve, uint256 amount, address indexed to); /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Implements the supply feature. Through `deposit()`, users deposit assets to the protocol. * @dev Emits the `Deposit()` event. * @param reservesData The state of all the reserves * @param params The additional parameters needed to execute the deposit function */ function executeDeposit( mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.ExecuteDepositParams memory params ) external { require(params.onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS); DataTypes.ReserveData storage reserve = reservesData[params.asset]; address uToken = reserve.uTokenAddress; require(uToken != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); ValidationLogic.validateDeposit(reserve, params.amount); reserve.updateState(); reserve.updateInterestRates(params.asset, uToken, params.amount, 0); IERC20Upgradeable(params.asset).safeTransferFrom(params.initiator, uToken, params.amount); IUToken(uToken).mint(params.onBehalfOf, params.amount, reserve.liquidityIndex); // Deposit amount to external lending protocol IUToken(uToken).depositReserves(params.amount); emit Deposit(params.initiator, params.asset, params.amount, params.onBehalfOf, params.referralCode); } /** * @notice Implements the withdraw feature. Through `withdraw()`, users withdraw assets from the protocol. * @dev Emits the `Withdraw()` event. * @param reservesData The state of all the reserves * @param params The additional parameters needed to execute the withdraw function */ function executeWithdraw( mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.ExecuteWithdrawParams memory params ) external returns (uint256) { require(params.to != address(0), Errors.VL_INVALID_TARGET_ADDRESS); DataTypes.ReserveData storage reserve = reservesData[params.asset]; address uToken = reserve.uTokenAddress; require(uToken != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); uint256 userBalance = IUToken(uToken).balanceOf(params.initiator); uint256 amountToWithdraw = params.amount; if (params.amount == type(uint256).max) { amountToWithdraw = userBalance; } ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance, uToken); reserve.updateState(); reserve.updateInterestRates(params.asset, uToken, 0, amountToWithdraw); // Withdraw amount from external lending protocol IUToken(uToken).withdrawReserves(amountToWithdraw); IUToken(uToken).burn(params.initiator, params.to, amountToWithdraw, reserve.liquidityIndex); emit Withdraw(params.initiator, params.asset, amountToWithdraw, params.to); return amountToWithdraw; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {ReserveLogic} from "./ReserveLogic.sol"; import {GenericLogic} from "./GenericLogic.sol"; import {WadRayMath} from "../math/WadRayMath.sol"; import {PercentageMath} from "../math/PercentageMath.sol"; import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol"; import {NftConfiguration} from "../configuration/NftConfiguration.sol"; import {Errors} from "../helpers/Errors.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {IInterestRate} from "../../interfaces/IInterestRate.sol"; import {ILendPoolLoan} from "../../interfaces/ILendPoolLoan.sol"; import {ILendPool} from "../../interfaces/ILendPool.sol"; import {IUToken} from "../../interfaces/IUToken.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; /** * @title ValidationLogic library * @author BendDao; Forked and edited by Unlockd * @notice Implements functions to validate the different actions of the protocol */ library ValidationLogic { using ReserveLogic for DataTypes.ReserveData; using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using NftConfiguration for DataTypes.NftConfigurationMap; /*////////////////////////////////////////////////////////////// STRUCTS //////////////////////////////////////////////////////////////*/ struct ValidateBorrowLocalVars { uint256 currentLtv; uint256 currentLiquidationThreshold; uint256 amountOfCollateralNeeded; uint256 userCollateralBalance; uint256 userBorrowBalance; uint256 availableLiquidity; uint256 healthFactor; bool isActive; bool isFrozen; bool borrowingEnabled; bool stableRateBorrowingEnabled; bool nftIsActive; bool nftIsFrozen; address loanReserveAsset; address loanBorrower; } /*////////////////////////////////////////////////////////////// MAIN LOGIC //////////////////////////////////////////////////////////////*/ /** * @dev Validates a deposit action * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isFrozen, Errors.VL_RESERVE_FROZEN); } /** * @dev Validates a withdraw action * @param reserveData The reserve state * @param amount The amount to be withdrawn * @param userBalance The balance of the user */ function validateWithdraw( DataTypes.ReserveData storage reserveData, uint256 amount, uint256 userBalance, address uToken ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); (bool isActive, , , ) = reserveData.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); // Case where there is not enough liquidity to cover user's withdrawal uint256 availableLiquidity = IUToken(uToken).getAvailableLiquidity(); require(amount <= availableLiquidity, Errors.LP_RESERVES_WITHOUT_ENOUGH_LIQUIDITY); } /** * @dev Validates a borrow action * @param reserveData The reserve state from which the user is borrowing * @param nftData The state of the user for the specific nft */ function validateBorrow( DataTypes.ExecuteBorrowParams memory params, DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, address loanAddress, uint256 loanId, address reserveOracle, address nftOracle ) external view { ValidateBorrowLocalVars memory vars; require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(params.amount > 0, Errors.VL_INVALID_AMOUNT); if (loanId != 0) { DataTypes.LoanData memory loanData = ILendPoolLoan(loanAddress).getLoan(loanId); require(loanData.state == DataTypes.LoanState.Active, Errors.LPL_INVALID_LOAN_STATE); require(params.asset == loanData.reserveAsset, Errors.VL_SPECIFIED_RESERVE_NOT_BORROWED_BY_USER); require(params.onBehalfOf == loanData.borrower, Errors.VL_SPECIFIED_LOAN_NOT_BORROWED_BY_USER); } (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserveData .configuration .getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED); (vars.nftIsActive, vars.nftIsFrozen) = nftData.configuration.getFlags(); require(vars.nftIsActive, Errors.VL_NO_ACTIVE_NFT); require(!vars.nftIsFrozen, Errors.VL_NFT_FROZEN); /** * @dev additional check for individual asset */ (vars.nftIsActive, vars.nftIsFrozen) = nftConfig.getFlags(); require(vars.nftIsActive, Errors.VL_NO_ACTIVE_NFT); require(!vars.nftIsFrozen, Errors.VL_NFT_FROZEN); require( (nftConfig.getConfigTimestamp() + ILendPool(address(this)).getTimeframe()) >= block.timestamp, Errors.VL_TIMEFRAME_EXCEEDED ); (vars.currentLtv, vars.currentLiquidationThreshold, ) = nftConfig.getCollateralParams(); (vars.userCollateralBalance, vars.userBorrowBalance, vars.healthFactor) = GenericLogic.calculateLoanData( params.asset, reserveData, params.nftAsset, params.nftTokenId, nftConfig, loanAddress, loanId, reserveOracle, nftOracle ); require(vars.userCollateralBalance != 0, Errors.VL_COLLATERAL_BALANCE_IS_0); require( vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); //add the current already borrowed amount to the amount requested to calculate the total collateral needed. //LTV is calculated in percentage vars.amountOfCollateralNeeded = (vars.userBorrowBalance + params.amount).percentDiv(vars.currentLtv); require(vars.amountOfCollateralNeeded <= vars.userCollateralBalance, Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW); } /** * @dev Validates a repay action * @param reserveData The reserve state from which the user is repaying * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) * @param borrowAmount The borrow balance of the user */ function validateRepay( DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData, uint256 amountSent, uint256 borrowAmount ) external view { require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); require(nftData.configuration.getActive(), Errors.VL_NO_ACTIVE_NFT); /** * @dev additional check for individual asset */ require(nftConfig.getActive(), Errors.VL_NO_ACTIVE_NFT); require(amountSent > 0, Errors.VL_INVALID_AMOUNT); require(borrowAmount > 0, Errors.VL_NO_DEBT_OF_SELECTED_TYPE); require(loanData.state == DataTypes.LoanState.Active, Errors.LPL_INVALID_LOAN_STATE); } /** * @dev Validates a redeem action * @param reserveData The reserve state * @param nftData The nft state */ function validateRedeem( DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData, uint256 amount ) external view { require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); require(nftData.configuration.getActive(), Errors.VL_NO_ACTIVE_NFT); /** * @dev additional check for individual asset */ require(nftConfig.getActive(), Errors.VL_NO_ACTIVE_NFT); require(loanData.state == DataTypes.LoanState.Auction, Errors.LPL_INVALID_LOAN_STATE); require(amount > 0, Errors.VL_INVALID_AMOUNT); } /*////////////////////////////////////////////////////////////// INTERNALS //////////////////////////////////////////////////////////////*/ /** * @dev Validates the auction action * @param reserveData The reserve data of the principal * @param nftData The nft data of the underlying nft * @param bidPrice Total variable debt balance of the user **/ function validateAuction( DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData, uint256 bidPrice ) internal view { require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); require(nftData.configuration.getActive(), Errors.VL_NO_ACTIVE_NFT); /** * @dev additional check for individual asset */ require(nftConfig.getActive(), Errors.VL_NO_ACTIVE_NFT); require( loanData.state == DataTypes.LoanState.Active || loanData.state == DataTypes.LoanState.Auction, Errors.LPL_INVALID_LOAN_STATE ); require(bidPrice > 0, Errors.VL_INVALID_AMOUNT); } /** * @dev Validates the liquidation action * @param reserveData The reserve data of the principal * @param nftData The data of the underlying NFT * @param loanData The loan data of the underlying NFT **/ function validateBuyout( DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData ) internal view { require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); require(nftData.configuration.getActive(), Errors.VL_NO_ACTIVE_NFT); /** * @dev additional check for individual asset */ require(nftConfig.getActive(), Errors.VL_NO_ACTIVE_NFT); require( loanData.state == DataTypes.LoanState.Active || loanData.state == DataTypes.LoanState.Auction, Errors.LPL_INVALID_LOAN_STATE ); } /** * @dev Validates the liquidation action * @param reserveData The reserve data of the principal * @param nftData The data of the underlying NFT * @param loanData The loan data of the underlying NFT **/ function validateLiquidate( DataTypes.ReserveData storage reserveData, DataTypes.NftData storage nftData, DataTypes.NftConfigurationMap storage nftConfig, DataTypes.LoanData memory loanData ) internal view { require(nftData.uNftAddress != address(0), Errors.LPC_INVALID_UNFT_ADDRESS); require(reserveData.uTokenAddress != address(0), Errors.VL_INVALID_RESERVE_ADDRESS); require(reserveData.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE); require(nftData.configuration.getActive(), Errors.VL_NO_ACTIVE_NFT); /** * @dev additional check for individual asset */ require(nftConfig.getActive(), Errors.VL_NO_ACTIVE_NFT); require(loanData.state == DataTypes.LoanState.Auction, Errors.LPL_INVALID_LOAN_STATE); } /** * @dev Validates an uToken transfer * @param from The user from which the uTokens are being transferred * @param reserveData The state of the reserve */ function validateTransfer(address from, DataTypes.ReserveData storage reserveData) internal pure { from; reserveData; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {WadRayMath} from "./WadRayMath.sol"; library MathUtils { using WadRayMath for uint256; /// @dev Ignoring leap years uint256 internal constant SECONDS_PER_YEAR = 365 days; /** * @dev Function to calculate the interest accumulated using a linear interest rate formula * @param rate The interest rate, in ray * @param lastUpdateTimestamp The timestamp of the last update of the interest * @return The interest rate linearly accumulated during the timeDelta, in ray **/ function calculateLinearInterest(uint256 rate, uint40 lastUpdateTimestamp) internal view returns (uint256) { //solium-disable-next-line uint256 timeDifference = block.timestamp - (uint256(lastUpdateTimestamp)); return ((rate * (timeDifference)) / SECONDS_PER_YEAR) + (WadRayMath.ray()); } /** * @dev Function to calculate the interest using a compounded interest rate formula * To avoid expensive exponentiation, the calculation is performed using a binomial approximation: * * (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3... * * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods * * @param rate The interest rate, in ray * @param lastUpdateTimestamp The timestamp of the last update of the interest * @return The interest rate compounded during the timeDelta, in ray **/ function calculateCompoundedInterest( uint256 rate, uint40 lastUpdateTimestamp, uint256 currentTimestamp ) internal pure returns (uint256) { //solium-disable-next-line uint256 exp = currentTimestamp - (uint256(lastUpdateTimestamp)); if (exp == 0) { return WadRayMath.ray(); } uint256 expMinusOne = exp - 1; uint256 expMinusTwo = exp > 2 ? exp - 2 : 0; uint256 ratePerSecond = rate / SECONDS_PER_YEAR; uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond); uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond); uint256 secondTerm = (exp * (expMinusOne) * (basePowerTwo)) / 2; uint256 thirdTerm = (exp * (expMinusOne) * (expMinusTwo) * (basePowerThree)) / 6; return WadRayMath.ray() + (ratePerSecond * (exp)) + (secondTerm) + (thirdTerm); } /** * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp * @param rate The interest rate (in ray) * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated **/ function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp) internal view returns (uint256) { return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {Errors} from "../helpers/Errors.sol"; /** * @title PercentageMath library * @author BendDao; Forked and edited by Unlockd * @notice Provides functions to perform percentage calculations * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR * @dev Operations are rounded half up **/ library PercentageMath { uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2; uint256 constant ONE_PERCENT = 1e2; //100, 1% uint256 constant TEN_PERCENT = 1e3; //1000, 10% uint256 constant ONE_THOUSANDTH_PERCENT = 1e1; //10, 0.1% uint256 constant ONE_TEN_THOUSANDTH_PERCENT = 1; //1, 0.01% /** * @dev Executes a percentage multiplication * @param value The value of which the percentage needs to be calculated * @param percentage The percentage of the value to be calculated * @return The percentage of value **/ function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) { if (value == 0 || percentage == 0) { return 0; } require(value <= (type(uint256).max - HALF_PERCENT) / percentage, Errors.MATH_MULTIPLICATION_OVERFLOW); return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR; } /** * @dev Executes a percentage division * @param value The value of which the percentage needs to be calculated * @param percentage The percentage of the value to be calculated * @return The value divided the percentage **/ function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) { require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfPercentage = percentage / 2; require(value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR, Errors.MATH_MULTIPLICATION_OVERFLOW); return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {Errors} from "../helpers/Errors.sol"; /** * @title WadRayMath library * @author BendDao; Forked and edited by Unlockd * @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) **/ library WadRayMath { uint256 internal constant WAD = 1e18; uint256 internal constant HALF_WAD = WAD / 2; uint256 internal constant RAY = 1e27; uint256 internal constant HALF_RAY = RAY / 2; uint256 internal constant WAD_RAY_RATIO = 1e9; /** * @return One ray, 1e27 **/ function ray() internal pure returns (uint256) { return RAY; } /** * @return One wad, 1e18 **/ function wad() internal pure returns (uint256) { return WAD; } /** * @return Half ray, 1e27/2 **/ function halfRay() internal pure returns (uint256) { return HALF_RAY; } /** * @return Half ray, 1e18/2 **/ function halfWad() internal pure returns (uint256) { return HALF_WAD; } /** * @dev Multiplies two wad, rounding half up to the nearest wad * @param a Wad * @param b Wad * @return The result of a*b, in wad **/ function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } require(a <= (type(uint256).max - HALF_WAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * b + HALF_WAD) / WAD; } /** * @dev Divides two wad, rounding half up to the nearest wad * @param a Wad * @param b Wad * @return The result of a/b, in wad **/ function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfB = b / 2; require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * WAD + halfB) / b; } /** * @dev Multiplies two ray, rounding half up to the nearest ray * @param a Ray * @param b Ray * @return The result of a*b, in ray **/ function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } require(a <= (type(uint256).max - HALF_RAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * b + HALF_RAY) / RAY; } /** * @dev Divides two ray, rounding half up to the nearest ray * @param a Ray * @param b Ray * @return The result of a/b, in ray **/ function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfB = b / 2; require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * RAY + halfB) / b; } /** * @dev Casts ray down to wad * @param a Ray * @return a casted to wad, rounded half up to the nearest wad **/ function rayToWad(uint256 a) internal pure returns (uint256) { uint256 halfRatio = WAD_RAY_RATIO / 2; uint256 result = halfRatio + a; require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW); return result / WAD_RAY_RATIO; } /** * @dev Converts wad up to ray * @param a Wad * @return a converted in ray **/ function wadToRay(uint256 a) internal pure returns (uint256) { uint256 result = a * WAD_RAY_RATIO; require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW); return result; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; library DataTypes { struct ReserveData { //stores the reserve configuration ReserveConfigurationMap configuration; //the liquidity index. Expressed in ray uint128 liquidityIndex; //variable borrow index. Expressed in ray uint128 variableBorrowIndex; //the current supply rate. Expressed in ray uint128 currentLiquidityRate; //the current variable borrow rate. Expressed in ray uint128 currentVariableBorrowRate; uint40 lastUpdateTimestamp; //tokens addresses address uTokenAddress; address debtTokenAddress; //address of the interest rate strategy address interestRateAddress; //the id of the reserve. Represents the position in the list of the active reserves uint8 id; } struct NftData { //stores the nft configuration NftConfigurationMap configuration; //address of the uNFT contract address uNftAddress; //the id of the nft. Represents the position in the list of the active nfts uint8 id; uint256 maxSupply; uint256 maxTokenId; } struct ReserveConfigurationMap { //bit 0-15: LTV //bit 16-31: Liq. threshold //bit 32-47: Liq. bonus //bit 48-55: Decimals //bit 56: Reserve is active //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 59: stable rate borrowing enabled //bit 60-63: reserved //bit 64-79: reserve factor uint256 data; } struct NftConfigurationMap { //bit 0-15: LTV //bit 16-31: Liq. threshold //bit 32-47: Liq. bonus //bit 56: NFT is active //bit 57: NFT is frozen //bit 64-71: Redeem duration //bit 72-79: Auction duration //bit 80-95: Redeem fine //bit 96-111: Redeem threshold //bit 112-127: Min bid fine //bit 128-159: Timestamp Config uint256 data; } /** * @dev Enum describing the current state of a loan * State change flow: * Created -> Active -> Repaid * -> Auction -> Defaulted */ enum LoanState { // We need a default that is not 'Created' - this is the zero value None, // The loan data is stored, but not initiated yet. Created, // The loan has been initialized, funds have been delivered to the borrower and the collateral is held. Active, // The loan is in auction, higest price liquidator will got chance to claim it. Auction, // The loan has been repaid, and the collateral has been returned to the borrower. This is a terminal state. Repaid, // The loan was delinquent and collateral claimed by the liquidator. This is a terminal state. Defaulted } struct LoanData { //the id of the nft loan uint256 loanId; //the current state of the loan LoanState state; //address of borrower address borrower; //address of nft asset token address nftAsset; //the id of nft token uint256 nftTokenId; //address of reserve asset token address reserveAsset; //scaled borrow amount. Expressed in ray uint256 scaledAmount; //start time of first bid time uint256 bidStartTimestamp; //bidder address of higest bid address bidderAddress; //price of higest bid uint256 bidPrice; //borrow amount of loan uint256 bidBorrowAmount; //bidder address of first bid address firstBidderAddress; } struct ExecuteDepositParams { address initiator; address asset; uint256 amount; address onBehalfOf; uint16 referralCode; } struct ExecuteWithdrawParams { address initiator; address asset; uint256 amount; address to; } struct ExecuteBorrowParams { address initiator; address asset; uint256 amount; address nftAsset; uint256 nftTokenId; address onBehalfOf; uint16 referralCode; } struct ExecuteRepayParams { address initiator; address nftAsset; uint256 nftTokenId; uint256 amount; } struct ExecuteAuctionParams { address initiator; address nftAsset; uint256 nftTokenId; uint256 bidPrice; address onBehalfOf; uint256 auctionDurationConfigFee; uint256 bidDelta; } struct ExecuteRedeemParams { address initiator; address nftAsset; uint256 nftTokenId; uint256 amount; uint256 bidFine; uint256 safeHealthFactor; } struct ExecuteLiquidateParams { address initiator; address nftAsset; uint256 nftTokenId; uint256 amount; } struct ExecuteBuyoutParams { address initiator; address nftAsset; uint256 nftTokenId; uint256 amount; address onBehalfOf; } struct ExecuteLiquidateMarketsParams { address nftAsset; uint256 nftTokenId; uint256 liquidateFeePercentage; uint256 amountOutMin; } struct ExecuteLendPoolStates { uint256 pauseStartTime; uint256 pauseDurationTime; } struct ExecuteYearnParams { address underlyingAsset; uint256 amount; } enum DebtMarketType { FixedPrice, //0 Auction, //1 Mixed //2 } enum DebtMarketState { //No bids New, //Exist bids Active, //Is sold Sold, Canceled } struct DebtMarketListing { uint256 debtId; address debtor; address nftAsset; uint256 tokenId; DebtMarketType sellType; DebtMarketState state; uint256 sellPrice; address reserveAsset; uint256 scaledAmount; address bidderAddress; uint256 bidPrice; uint256 auctionEndTimestamp; uint256 startBiddingPrice; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.8.4; import {DataTypes} from "../libraries/types/DataTypes.sol"; import {ReserveLogic} from "../libraries/logic/ReserveLogic.sol"; import {NftLogic} from "../libraries/logic/NftLogic.sol"; import {ILendPoolAddressesProvider} from "../interfaces/ILendPoolAddressesProvider.sol"; contract LendPoolStorage { using ReserveLogic for DataTypes.ReserveData; using NftLogic for DataTypes.NftData; ILendPoolAddressesProvider internal _addressesProvider; uint256 internal _reservesCount; uint256 internal _maxNumberOfReserves; uint256 internal _nftsCount; uint256 internal _maxNumberOfNfts; uint256 internal constant _NOT_ENTERED = 0; uint256 internal constant _ENTERED = 1; uint256 internal _status; uint256 internal _pauseStartTime; uint256 internal _pauseDurationTime; uint256 internal _liquidateFeePercentage; uint256 internal _timeframe; uint256 internal _configFee; bool internal _paused; mapping(address => DataTypes.ReserveData) internal _reserves; mapping(uint256 => address) internal _reservesList; mapping(address => DataTypes.NftData) internal _nfts; mapping(address => mapping(uint256 => DataTypes.NftConfigurationMap)) internal _nftConfig; mapping(uint256 => address) internal _nftsList; /* * @dev Markets supported for each NFT * @param address -> the collection address * @param uint8 -> market id (0 for NFTX, 1 for SudoSwap) * @param bool -> whether it is supported in the corresponding market or not */ mapping(address => mapping(uint8 => bool)) public _isMarketSupported; mapping(address => address[2]) internal _sudoswapPairs; uint256 internal _auctionDurationConfigFee; address internal _rescuer; uint256 internal _safeHealthFactor; uint256 internal _bidDelta; // For upgradable, add one new variable above, minus 1 at here uint256[49] private __gap; }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "istanbul", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/libraries/logic/BorrowLogic.sol": { "BorrowLogic": "0xea3770c20762b5930d05d2409310ff2e77d350d1" }, "contracts/libraries/logic/LiquidateLogic.sol": { "LiquidateLogic": "0x62fd7b5ab5003d9287e807a4e72c7651344f474b" }, "contracts/libraries/logic/NftLogic.sol": { "NftLogic": "0xd531824320bcf6cb5f55a139028fc3aa03b554b4" }, "contracts/libraries/logic/ReserveLogic.sol": { "ReserveLogic": "0x7d739ba7ed9f62552ffb5e674b58badebaf3c2c4" }, "contracts/libraries/logic/SupplyLogic.sol": { "SupplyLogic": "0x520f28b17bc9f9758d2bac914de4b166a6a5ba85" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidPrice","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"Auction","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"referral","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"nftConfigFee","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyoutAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"Buyout","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint16","name":"referral","type":"uint16"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"NftConfigurationByIdChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"NftConfigurationChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"durationTime","type":"uint256"}],"name":"PausedTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fineAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newRescuer","type":"address"}],"name":"RescuerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"ReserveConfigurationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidityRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"variableBorrowRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidityIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"variableBorrowIndex","type":"uint256"}],"name":"ReserveDataUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"rateAddress","type":"address"}],"name":"ReserveInterestRateAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"newSafeHealthFactor","type":"uint256"}],"name":"SafeHealthFactorUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"nftAsset","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"ValuationApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"ADDRESS_ID_PUNKS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADDRESS_ID_PUNK_GATEWAY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADDRESS_ID_RESERVOIR_ADAPTER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADDRESS_ID_WETH_GATEWAY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADDRESS_ID_WPUNKS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint8","name":"","type":"uint8"}],"name":"_isMarketSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"approveValuation","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"bidPrice","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"auction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"buyoutAmount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"buyout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"finalizeTransfer","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAddressesProvider","outputs":[{"internalType":"contract ILendPoolAddressesProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAuctionDurationConfigFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBidDelta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfigFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidateFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxNumberOfNfts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxNumberOfReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getNftAssetConfig","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.NftConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"getNftAuctionData","outputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"bidderAddress","type":"address"},{"internalType":"uint256","name":"bidPrice","type":"uint256"},{"internalType":"uint256","name":"bidBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"bidFine","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"address","name":"reserveAsset","type":"address"}],"name":"getNftCollateralData","outputs":[{"internalType":"uint256","name":"totalCollateralInETH","type":"uint256"},{"internalType":"uint256","name":"totalCollateralInReserve","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsInETH","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsInReserve","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"liquidationThreshold","type":"uint256"},{"internalType":"uint256","name":"liquidationBonus","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"getNftConfigByTokenId","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.NftConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getNftConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.NftConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getNftData","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.NftConfigurationMap","name":"configuration","type":"tuple"},{"internalType":"address","name":"uNftAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"maxTokenId","type":"uint256"}],"internalType":"struct DataTypes.NftData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"getNftDebtData","outputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"reserveAsset","type":"address"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"availableBorrows","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNftsList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPausedTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.ReserveConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveData","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.ReserveConfigurationMap","name":"configuration","type":"tuple"},{"internalType":"uint128","name":"liquidityIndex","type":"uint128"},{"internalType":"uint128","name":"variableBorrowIndex","type":"uint128"},{"internalType":"uint128","name":"currentLiquidityRate","type":"uint128"},{"internalType":"uint128","name":"currentVariableBorrowRate","type":"uint128"},{"internalType":"uint40","name":"lastUpdateTimestamp","type":"uint40"},{"internalType":"address","name":"uTokenAddress","type":"address"},{"internalType":"address","name":"debtTokenAddress","type":"address"},{"internalType":"address","name":"interestRateAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"}],"internalType":"struct DataTypes.ReserveData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveNormalizedIncome","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveNormalizedVariableDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReservesList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSafeHealthFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimeframe","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"uNftAddress","type":"address"}],"name":"initNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"uTokenAddress","type":"address"},{"internalType":"address","name":"debtTokenAddress","type":"address"},{"internalType":"address","name":"interestRateAddress","type":"address"}],"name":"initReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILendPoolAddressesProvider","name":"provider","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"bidFine","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenContract","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"rescueETH","type":"bool"}],"name":"rescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721Upgradeable","name":"nftAsset","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"rescueNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescuer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionDurationConfigFee","type":"uint256"}],"name":"setAuctionDurationConfigFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidDelta","type":"uint256"}],"name":"setBidDelta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"configFee","type":"uint256"}],"name":"setConfigFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"setLiquidateFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMaxNumberOfNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMaxNumberOfReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"setNftConfigByTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"setNftConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"maxTokenId","type":"uint256"}],"name":"setNftMaxSupplyAndTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"val","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"durationTime","type":"uint256"}],"name":"setPausedTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"setReserveConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"rateAddress","type":"address"}],"name":"setReserveInterestRateAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timeframe","type":"uint256"}],"name":"setTimeframe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reserveAsset","type":"address"},{"internalType":"address","name":"bidder","type":"address"},{"internalType":"uint256","name":"bidAmount","type":"uint256"}],"name":"transferBidAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRescuer","type":"address"}],"name":"updateRescuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reserve","type":"address"}],"name":"updateReserveInterestRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reserve","type":"address"}],"name":"updateReserveState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSafeHealthFactor","type":"uint256"}],"name":"updateSafeHealthFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60806040523480156200001157600080fd5b50600054610100900460ff166200002f5760005460ff161562000039565b62000039620000de565b620000a15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600054610100900460ff16158015620000c4576000805461ffff19166101011790555b8015620000d7576000805461ff00191690555b5062000102565b6000620000f630620000fc60201b62003bc91760201c565b15905090565b3b151590565b615b1e80620001126000396000f3fe6080604052600436106103dd5760003560e01c80638e15df28116101fd578063d80a958d11610118578063e8eda9df116100ab578063ec765d3d1161007a578063ec765d3d14610edf578063f2d5e6c714610f34578063f3a266e914610f68578063fdff6f2614610f88578063fe65acfe14610fa857600080fd5b8063e8eda9df14610e5f578063ea2092f314610e7f578063ea97cb7e14610e9f578063eaa7109314610ebf57600080fd5b8063dd90ff38116100e7578063dd90ff3814610dc7578063df651d7114610ddc578063e5bceca514610dfc578063e84f272914610e4c57600080fd5b8063d80a958d14610d42578063da01c4a514610d57578063da5d9ad014610d92578063db78f21614610da757600080fd5b8063bb35c10111610190578063d15e00531161015f578063d15e005314610cb9578063d1946dbc14610cd9578063d4a9e0c914610cee578063d5ed393314610d2257600080fd5b8063bb35c10114610c4f578063bedb86fb14610c64578063c345246014610c84578063c4d66de814610c9957600080fd5b8063a4c0166b116101cc578063a4c0166b14610bfa578063acdb8f1214610b25578063b15e3d4514610c1a578063b6529aee14610c2f57600080fd5b80638e15df2814610b255780638fa9b5a014610b7e5780638fc4218814610b9e57806391aa19f414610bc657600080fd5b8063498c7b89116102f857806374affc3f1161028b57806383c8afd71161025a57806383c8afd714610a41578063873e4dab14610a6157806387c32dec14610a815780638bd2567714610ad05780638cd2e0c714610af057600080fd5b806374affc3f146108e157806377bdc0c3146109015780637ecc33b114610a0157806383b1555f14610a2157600080fd5b806369328dec116102c757806369328dec1461085f5780636b25c8351461087f57806373b43825146108a1578063746c35a2146108c157600080fd5b8063498c7b891461079d5780635c975abb146107bd5780635fc526ff146107e15780636283d5701461083f57600080fd5b80632e8d1a441161037057806338a631831161033f57806338a631831461070b57806339a368811461073d57806342bbd6531461075d57806343f0f7331461077d57600080fd5b80632e8d1a44146105745780632f923ff71461058957806335ea6a75146105a9578063386497fd146106eb57600080fd5b8063159d6365116103ac578063159d6365146104a957806317c8cfe4146104dd5780632ab60045146104ff5780632dbbc9d91461051f57600080fd5b80630710285c146103e957806308ac08b91461041c5780630f863ef014610431578063150b7a021461046557600080fd5b366103e457005b600080fd5b3480156103f557600080fd5b50610409610404366004615372565b610fc6565b6040519081526020015b60405180910390f35b34801561042857600080fd5b50603554610409565b34801561043d57600080fd5b506104097fc4cecc127857f85f2ddb3e712184d3431e2c982794df368953a6a7ddef5db13581565b34801561047157600080fd5b50610490610480366004615162565b630a85bd0160e11b949350505050565b6040516001600160e01b03199091168152602001610413565b3480156104b557600080fd5b506104097f8c6c451a3c78b1a61d300b75cea4e998723915c4ab1ff7888c30dbbfcaf39efb81565b3480156104e957600080fd5b506104fd6104f8366004615372565b611164565b005b34801561050b57600080fd5b506104fd61051a366004614ff3565b6111cb565b34801561052b57600080fd5b5061053f61053a366004615276565b611286565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610413565b34801561058057600080fd5b50604854610409565b34801561059557600080fd5b506104fd6105a43660046155e4565b61142d565b3480156105b557600080fd5b506106de6105c4366004614ff3565b604080516101608101825260006101408201818152825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152506001600160a01b039081166000908152603f602090815260409182902082516101608101845281546101408201908152815260018201546001600160801b0380821694830194909452600160801b9081900484169482019490945260028201548084166060830152939093049091166080830152600381015464ffffffffff811660a0840152600160281b9004831660c08301526004810154831660e083015260050154918216610100820152600160a01b90910460ff1661012082015290565b60405161041391906157cb565b3480156106f757600080fd5b50610409610706366004614ff3565b61147c565b34801561071757600080fd5b506047546001600160a01b03165b6040516001600160a01b039091168152602001610413565b34801561074957600080fd5b506104fd610758366004615590565b6114a3565b34801561076957600080fd5b506104fd610778366004614ff3565b6115d0565b34801561078957600080fd5b506104fd61079836600461521e565b61168e565b3480156107a957600080fd5b506104fd6107b8366004615590565b6116ef565b3480156107c957600080fd5b50603e5460ff165b6040519015158152602001610413565b3480156107ed57600080fd5b506108306107fc366004614ff3565b60408051602080820183526000918290526001600160a01b03939093168152603f8352819020815192830190915254815290565b60405190518152602001610413565b34801561084b57600080fd5b506104fd61085a366004614ff3565b6116fc565b34801561086b57600080fd5b5061040961087a366004615276565b6117b2565b34801561088b57600080fd5b50610894611884565b6040516104139190615644565b3480156108ad57600080fd5b506104fd6108bc366004615488565b61194c565b3480156108cd57600080fd5b506104fd6108dc366004615590565b611a6c565b3480156108ed57600080fd5b506104fd6108fc366004615590565b611ab6565b34801561090d57600080fd5b506109b761091c366004614ff3565b6040805160c081018252600060a08201818152825260208201819052918101829052606081018290526080810191909152506001600160a01b03908116600090815260416020908152604091829020825160c081018452815460a082019081528152600182015494851692810192909252600160a01b90930460ff169181019190915260028201546060820152600390910154608082015290565b604051610413919081515181526020808301516001600160a01b03169082015260408083015160ff1690820152606080830151908201526080918201519181019190915260a00190565b348015610a0d57600080fd5b506104fd610a1c366004615590565b611ac3565b348015610a2d57600080fd5b506104fd610a3c36600461502b565b611ad0565b348015610a4d57600080fd5b506104fd610a5c36600461521e565b611b8d565b348015610a6d57600080fd5b506104fd610a7c36600461502b565b611be2565b348015610a8d57600080fd5b50610830610a9c366004614ff3565b60408051602080820183526000918290526001600160a01b0393909316815260418352819020815192830190915254815290565b348015610adc57600080fd5b506104fd610aeb366004615063565b611f9c565b348015610afc57600080fd5b50610b10610b0b366004615372565b6120f2565b60408051928352901515602083015201610413565b348015610b3157600080fd5b50610830610b4036600461521e565b60408051602080820183526000918290526001600160a01b039490941681526042845281812092815291835290819020815192830190915254815290565b348015610b8a57600080fd5b506104fd610b993660046153a6565b612252565b348015610baa57600080fd5b50603954603a5460408051928352602083019190915201610413565b348015610bd257600080fd5b506104097f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b081565b348015610c0657600080fd5b506104fd610c153660046153a6565b61239f565b348015610c2657600080fd5b50603c54610409565b348015610c3b57600080fd5b506104fd610c4a366004615307565b612531565b348015610c5b57600080fd5b50604954610409565b348015610c7057600080fd5b506104fd610c7f366004615450565b612655565b348015610c9057600080fd5b50603d54610409565b348015610ca557600080fd5b506104fd610cb4366004614ff3565b6126f3565b348015610cc557600080fd5b50610409610cd4366004614ff3565b612818565b348015610ce557600080fd5b50610894612839565b348015610cfa57600080fd5b506104097f7c34be048fc583adad09a0b6b5c3870d1f149431761f9dfbae926873c7917bf981565b348015610d2e57600080fd5b506104fd610d3d3660046150be565b6128fb565b348015610d4e57600080fd5b50603b54610409565b348015610d6357600080fd5b506107d1610d7236600461541e565b604460209081526000928352604080842090915290825290205460ff1681565b348015610d9e57600080fd5b50604654610409565b348015610db357600080fd5b506104fd610dc2366004615372565b61297c565b348015610dd357600080fd5b50603754610409565b348015610de857600080fd5b506104fd610df7366004615276565b6129ab565b348015610e0857600080fd5b50610e1c610e1736600461521e565b612a78565b604080519586526001600160a01b039094166020860152928401919091526060830152608082015260a001610413565b6104fd610e5a36600461521e565b612d8f565b348015610e6b57600080fd5b506104fd610e7a3660046152b7565b61337b565b348015610e8b57600080fd5b50610409610e9a3660046153e4565b613466565b348015610eab57600080fd5b506104fd610eba366004615590565b613638565b348015610ecb57600080fd5b506104fd610eda366004615122565b613645565b348015610eeb57600080fd5b50610eff610efa36600461521e565b613743565b604080519687526001600160a01b039095166020870152938501929092526060840152608083015260a082015260c001610413565b348015610f4057600080fd5b506104097f2fd61ae66f2d61d8f037eb4f0b42e4cdcefa98eda491ab1715169a30e5551a7e81565b348015610f7457600080fd5b506104fd610f83366004615590565b613b72565b348015610f9457600080fd5b506104fd610fa3366004615590565b613b7f565b348015610fb457600080fd5b506033546001600160a01b0316610725565b600060016038541415610ff45760405162461bcd60e51b8152600401610feb90615794565b60405180910390fd5b6001603855611001613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b906348e9e573906001600160a01b0316603f6041604261105d604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b604051806080016040528061106f3390565b6001600160a01b0390811682528d811660208084019190915260408084018f905260609384018e905280516001600160e01b031960e08d901b16815299831660048b015260248a019890985260448901969096526064880194909452825160848801529184015160a48701528051831660c48701529283015190911660e4850152918101516101048401520151610124820152610144015b60206040518083038186803b15801561111f57600080fd5b505af4158015611133573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115791906155a8565b6000603855949350505050565b61116c613c0e565b6001600160a01b038316600081815260426020908152604080832086845282529182902084905590518381528492917f8401056e4ede1adeae18047abd742b6020c12a4b1b9ecb484f38922975ce6801910160405180910390a3505050565b6111d3613c0e565b6001600160a01b03811661123c5760405162461bcd60e51b815260206004820152602a60248201527f526573637561626c653a206e6577207265736375657220697320746865207a65604482015269726f206164647265737360b01b6064820152608401610feb565b604780546001600160a01b0319166001600160a01b0383169081179091556040517fe475e580d85111348e40d8ca33cfdd74c30fe1655c2d8537a13abc10065ffa5a90600090a250565b6001600160a01b03838116600090815260426020908152604080832086845282528083209385168352603f825282208354929384938493849384938493849391929161ffff80831692601081901c8216921c168095508196508297505050506113fd8a828e8e603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b15801561133a57600080fd5b505afa15801561134e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611372919061500f565b603360009054906101000a90046001600160a01b03166001600160a01b03166391aefd4c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156113c057600080fd5b505afa1580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f8919061500f565b613cce565b909950975061140e89600087613ec1565b965061141c88600087613ec1565b955050509397509397509397909450565b611435613c0e565b6039829055603a81905560408051838152602081018390527fd897a722b1c0a957941f99a13c0ea24d7d4ffafe0953658f68f49e13ccba5c5a910160405180910390a15050565b6001600160a01b0381166000908152603f6020526040812061149d90613ef7565b92915050565b603354604080516315d9b46f60e31b8152905133926001600160a01b03169163aecda378916004808301926020929190829003018186803b1580156114e757600080fd5b505afa1580156114fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151f919061500f565b6001600160a01b0316146040518060400160405280600381526020016203130360ec1b815250906115635760405162461bcd60e51b8152600401610feb919061571d565b5060408051808201909152600381526234323760e81b60208201528161159c5760405162461bcd60e51b8152600401610feb919061571d565b50604881905560405181907f9368516ff3437214c191f209fa4cf4af19c06ca2f2f36ff5fe76a6597daaeac390600090a250565b6001600160a01b038082166000908152603f602090815260409182902060038082015484518086019095529084526219989b60e91b9284019290925292600160281b9091041690816116355760405162461bcd60e51b8152600401610feb919061571d565b508154600160381b1615156040518060400160405280600381526020016219981960e91b8152509061167a5760405162461bcd60e51b8152600401610feb919061571d565b50611689828483600080613f6a565b505050565b611696613c0e565b6001600160a01b0382166000818152603f602052604090819020839055517fd304f6d1bc9e508077b6f059b9f83857a34d0b072986667f9092e9f2698506e1906116e39084815260200190565b60405180910390a25050565b6116f7613c0e565b604955565b6001600160a01b038082166000908152603f602090815260409182902060038082015484518086019095529084526219989b60e91b9284019290925292600160281b9091041661175f5760405162461bcd60e51b8152600401610feb919061571d565b508054600160381b1615156040518060400160405280600381526020016219981960e91b815250906117a45760405162461bcd60e51b8152600401610feb919061571d565b506117ae81614214565b5050565b6000600160385414156117d75760405162461bcd60e51b8152600401610feb90615794565b60016038556117e4613bcf565b73520f28b17bc9f9758d2bac914de4b166a6a5ba8563dc947ee2603f60405180608001604052806118123390565b6001600160a01b03908116825289811660208084019190915260408084018b905289831660609485015280516001600160e01b031960e089901b16815260048101969096528451831660248701529084015182166044860152830151606485015291015116608482015260a401611107565b6060600060365467ffffffffffffffff8111156118b157634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156118da578160200160208202803683370190505b50905060005b60365481146119465760008181526043602052604090205482516001600160a01b039091169083908390811061192657634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101526001016118e0565b50919050565b6001603854141561196f5760405162461bcd60e51b8152600401610feb90615794565b60016038556047546001600160a01b0316336001600160a01b0316146119a75760405162461bcd60e51b8152600401610feb90615750565b8015611a4d576000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146119fa576040519150601f19603f3d011682016040523d82523d6000602084013e6119ff565b606091505b5050905080611a475760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610feb565b50611a61565b611a616001600160a01b03851684846142e9565b505060006038555050565b611a74613c0e565b60408051808201909152600381526234323160e81b602082015260ff821115611ab05760405162461bcd60e51b8152600401610feb919061571d565b50603555565b611abe613c0e565b603b55565b611acb613c0e565b604655565b611ad8613c0e565b6001600160a01b03821615801590611af857506001600160a01b03811615155b6040518060400160405280600381526020016218981b60e91b81525090611b325760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038281166000818152603f602052604080822060050180546001600160a01b0319169486169485179055517f195838d540de3dc858973066767082af33599dde9e7a8ddfe1cd40bba6487fcc9190a35050565b611b95613c0e565b6001600160a01b03821660008181526041602052604090819020839055517f0e5cd7e52259b05b0b3cd175800318f114be4e1bfcf716ab6f940e608a122ab3906116e39084815260200190565b611bea613c0e565b60408051808201909152600381526234303360e81b6020820152823b611c235760405162461bcd60e51b8152600401610feb919061571d565b5060408051808201909152600381526218981b60e91b60208201526001600160a01b038216611c655760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b03828116600090815260416020526040908190209051631c67bc0160e31b81526004810191909152908216602482015273d531824320bcf6cb5f55a139028fc3aa03b554b49063e33de0089060440160006040518083038186803b158015611cd457600080fd5b505af4158015611ce8573d6000803e3d6000fd5b50505050611cf58261433b565b6033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b158015611d3a57600080fd5b505afa158015611d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d72919061500f565b6001600160a01b03161415604051806040016040528060038152602001620dcc0d60ea1b81525090611db75760405162461bcd60e51b8152600401610feb919061571d565b50816001600160a01b031663a22cb465603360009054906101000a90046001600160a01b03166001600160a01b03166335e6e4d06040518163ffffffff1660e01b815260040160206040518083038186803b158015611e1557600080fd5b505afa158015611e29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4d919061500f565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260016024820152604401600060405180830381600087803b158015611e9557600080fd5b505af1158015611ea9573d6000803e3d6000fd5b50505050603360009054906101000a90046001600160a01b03166001600160a01b03166335e6e4d06040518163ffffffff1660e01b815260040160206040518083038186803b158015611efb57600080fd5b505afa158015611f0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f33919061500f565b60405163873e4dab60e01b81526001600160a01b0384811660048301528381166024830152919091169063873e4dab90604401600060405180830381600087803b158015611f8057600080fd5b505af1158015611f94573d6000803e3d6000fd5b505050505050565b611fa4613c0e565b60408051808201909152600381526234303360e81b6020820152843b611fdd5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b03831615801590611ffe57506001600160a01b03821615155b801561201257506001600160a01b03811615155b6040518060400160405280600381526020016218981b60e91b8152509061204c5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038481166000908152603f6020526040908190209051636047157f60e01b81526004810191909152848216602482015283821660448201529082166064820152737d739ba7ed9f62552ffb5e674b58badebaf3c2c490636047157f9060840160006040518083038186803b1580156120cb57600080fd5b505af41580156120df573d6000803e3d6000fd5b505050506120ec84614451565b50505050565b600080600160385414156121185760405162461bcd60e51b8152600401610feb90615794565b6001603855612125613bcf565b73ea3770c20762b5930d05d2409310ff2e77d350d163b65e773c603360009054906101000a90046001600160a01b0316603f60416042604051806080016040528061216d3390565b6001600160a01b0390811682528d811660208084019190915260408084018f905260609384018e905280516001600160e01b031960e08c901b16815298831660048a01526024890197909752604488019590955260648701939093528151831660848701529281015190911660a48501529182015160c4840152015160e482015261010401604080518083038186803b15801561220957600080fd5b505af415801561221d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224191906155c0565b600060385590969095509350505050565b600160385414156122755760405162461bcd60e51b8152600401610feb90615794565b6001603855612282613bcf565b7362fd7b5ab5003d9287e807a4e72c7651344f474b630255d390603360009054906101000a90046001600160a01b0316603f604160426040518060a001604052806122ca3390565b6001600160a01b0390811682528c811660208084019190915260408084018e905260608085018e90528c841660809586015281516001600160e01b031960e08d901b16815299841660048b015260248a01989098526044890196909652606488019490945282518116608488015292820151831660a48701529281015160c48601529283015160e485015291015116610104820152610124015b60006040518083038186803b15801561237c57600080fd5b505af4158015612390573d6000803e3d6000fd5b50506000603855505050505050565b600160385414156123c25760405162461bcd60e51b8152600401610feb90615794565b60016038556123cf613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b9063200f7ab5906001600160a01b0316603f6041604261242b604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b6040518060e0016040528061243d3390565b6001600160a01b031681526020018c6001600160a01b031681526020018b81526020018a8152602001896001600160a01b0316815260200160465481526020016049548152506040518763ffffffff1660e01b8152600401612364969594939291906001600160a01b038781168252602080830188905260408301879052606083018690528451608084015284015160a08301526101a08201908084511660c08401528060208501511660e084015260408401516101008401526060840151610120840152806080850151166101408401525060a083015161016083015260c0830151610180830152979650505050505050565b600160385414156125545760405162461bcd60e51b8152600401610feb90615794565b6001603855612561613bcf565b73ea3770c20762b5930d05d2409310ff2e77d350d163f2b6dd87603360009054906101000a90046001600160a01b0316603f604160426040518060e001604052806125a93390565b6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b6001600160a01b031681526020018a8152602001896001600160a01b031681526020018861ffff168152506040518663ffffffff1660e01b8152600401612618959493929190615691565b60006040518083038186803b15801561263057600080fd5b505af4158015612644573d6000803e3d6000fd5b505060006038555050505050505050565b61265d613c0e565b603e5460ff161515811515146126f057603e805460ff191682151590811790915560ff16156126b657426039556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a150565b6039546126c39042615a56565b603a556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a15b50565b600054610100900460ff1661270e5760005460ff1615612712565b303b155b6127755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610feb565b600054610100900460ff16158015612797576000805461ffff19166101011790555b60408051808201909152600381526218981b60e91b60208201526001600160a01b0383166127d85760405162461bcd60e51b8152600401610feb919061571d565b50602060355560ff60375560fa603b55603380546001600160a01b0319166001600160a01b03841617905580156117ae576000805461ff00191690555050565b6001600160a01b0381166000908152603f6020526040812061149d90614562565b6060600060345467ffffffffffffffff81111561286657634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561288f578160200160208202803683370190505b50905060005b60345481146119465760008181526040602081905290205482516001600160a01b03909116908390839081106128db57634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390921660209283029190910190910152600101612895565b612903613bcf565b6001600160a01b038681166000908152603f6020526040902060038101549091600160281b90910416336001600160a01b0316146040518060400160405280600381526020016203431360ec1b815250906129715760405162461bcd60e51b8152600401610feb919061571d565b505b50505050505050565b612984613c0e565b6001600160a01b039092166000908152604160205260409020600281019190915560030155565b600160385414156129ce5760405162461bcd60e51b8152600401610feb90615794565b60016038556047546001600160a01b0316336001600160a01b031614612a065760405162461bcd60e51b8152600401610feb90615750565b604051632142170760e11b81523060048201526001600160a01b038281166024830152604482018490528416906342842e0e90606401600060405180830381600087803b158015612a5657600080fd5b505af1158015612a6a573d6000803e3d6000fd5b505060006038555050505050565b6001600160a01b0380831660009081526042602090815260408083208584528252808320603354825163035e6e4d60e41b815292519495869586958695869594869416926335e6e4d092600480840193919291829003018186803b158015612adf57600080fd5b505afa158015612af3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b17919061500f565b60405163058dcda760e21b81526001600160a01b038b81166004830152602482018b905291925090821690631637369c9060440160206040518083038186803b158015612b6357600080fd5b505afa158015612b77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9b91906155a8565b96508615612d83576033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b158015612be857600080fd5b505afa158015612bfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c20919061500f565b6001600160a01b031663504006ca896040518263ffffffff1660e01b8152600401612c4d91815260200190565b6101806040518083038186803b158015612c6657600080fd5b505afa158015612c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9e91906154cf565b90506000603f60008360a001516001600160a01b03166001600160a01b031681526020019081526020016000209050816101000151975081610120015196508161014001519550612d7d8260a00151828d878688603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4057600080fd5b505afa158015612d54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d78919061500f565b6145b7565b95505050505b50509295509295909350565b6033546040516321f8a72160e01b81527f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b06004820152839183916001600160a01b03909116906321f8a7219060240160206040518083038186803b158015612df657600080fd5b505afa158015612e0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e2e919061500f565b6001600160a01b0316826001600160a01b03161415612f1357604051630b02f02d60e31b8152600481018290526001600160a01b0383169063581781689060240160206040518083038186803b158015612e8757600080fd5b505afa158015612e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebf919061500f565b6001600160a01b0316336001600160a01b031614604051806040016040528060038152602001621a191960e91b81525090612f0d5760405162461bcd60e51b8152600401610feb919061571d565b50613084565b6040516331a9108f60e11b8152600481018290526001600160a01b03831690636352211e9060240160206040518083038186803b158015612f5357600080fd5b505afa158015612f67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8b919061500f565b6001600160a01b0316336001600160a01b0316148061304857506001600160a01b03828116600090815260416020526040908190206001015490516331a9108f60e11b815260048101849052911690636352211e9060240160206040518083038186803b158015612ffb57600080fd5b505afa15801561300f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613033919061500f565b6001600160a01b0316336001600160a01b0316145b604051806040016040528060038152602001621a191960e91b815250906130825760405162461bcd60e51b8152600401610feb919061571d565b505b6001600160a01b03848116600090815260416020908152604091829020825160c081018452815460a082019081528152600182015480861693820193909352600160a01b90920460ff16828401526002810154606083015260030154608082015260335491516321f8a72160e01b81527f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b0600482015287939192909116906321f8a7219060240160206040518083038186803b15801561314357600080fd5b505afa158015613157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317b919061500f565b6001600160a01b0316826001600160a01b0316141561329d576033546040516321f8a72160e01b81527fc4cecc127857f85f2ddb3e712184d3431e2c982794df368953a6a7ddef5db13560048201526041916000916001600160a01b03909116906321f8a7219060240160206040518083038186803b1580156131fd57600080fd5b505afa158015613211573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613235919061500f565b6001600160a01b0390811682526020808301939093526040918201600020825160c081018452815460a082019081528152600182015492831694810194909452600160a01b90910460ff16918301919091526002810154606083015260030154608082015290505b60006001600160a01b031681602001516001600160a01b031614156040518060400160405280600381526020016234323560e81b815250906132f25760405162461bcd60e51b8152600401610feb919061571d565b506132fb613bcf565b34603d5414604051806040016040528060038152602001621a191b60e91b8152509061333a5760405162461bcd60e51b8152600401610feb919061571d565b5060405185906001600160a01b0388169033907f9b17da8a37b9aa9a3c85708ff3ad811dc736697b43dc09952dc2e30123e3f1c590600090a4505050505050565b6001603854141561339e5760405162461bcd60e51b8152600401610feb90615794565b60016038556133ab613bcf565b73520f28b17bc9f9758d2bac914de4b166a6a5ba8563eef7aa6a603f6040518060a001604052806133d93390565b6001600160a01b03908116825289811660208084019190915260408084018b905289831660608086019190915261ffff808b1660809687015282516001600160e01b031960e08b901b1681526004810198909852865185166024890152928601518416604488015290850151606487015284015190911660848501529101511660a482015260c401612364565b60006001603854141561348b5760405162461bcd60e51b8152600401610feb90615794565b6001603855613498613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b906395e4a103906001600160a01b0316603f604160426134f4604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b6040518060c001604052806135063390565b6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b81526020018a81526020016048548152506040518763ffffffff1660e01b81526004016135da969594939291906001600160a01b038781168252602080830188905260408301879052606083018690528451608084015284015160a08301526101808201908351811660c084015260208401511660e083015260408301516101008301526060830151610120830152608083015161014083015260a0909201516101609091015295945050505050565b60206040518083038186803b1580156135f257600080fd5b505af4158015613606573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362a91906155a8565b600060385595945050505050565b613640613c0e565b603c55565b6033546040516321f8a72160e01b81527f2fd61ae66f2d61d8f037eb4f0b42e4cdcefa98eda491ab1715169a30e5551a7e60048201526001600160a01b03909116906321f8a7219060240160206040518083038186803b1580156136a857600080fd5b505afa1580156136bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136e0919061500f565b6001600160a01b0316336001600160a01b0316146040518060400160405280600381526020016234333160e81b8152509061372e5760405162461bcd60e51b8152600401610feb919061571d565b506116896001600160a01b03841683836142e9565b6001600160a01b03828116600090815260426020908152604080832085845282528083208054603354835163035e6e4d60e41b81529351959687968796879687968796909561ffff8083169660109390931c16949116926335e6e4d09260048082019391829003018186803b1580156137bb57600080fd5b505afa1580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f3919061500f565b60405163058dcda760e21b81526001600160a01b038d81166004830152602482018d90529190911690631637369c9060440160206040518083038186803b15801561383d57600080fd5b505afa158015613851573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061387591906155a8565b98508861389957600080600080600080985098509850985098509850505050613b68565b6033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b1580156138de57600080fd5b505afa1580156138f2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613916919061500f565b6001600160a01b031663504006ca8b6040518263ffffffff1660e01b815260040161394391815260200190565b6101806040518083038186803b15801561395c57600080fd5b505afa158015613970573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399491906154cf565b90508060a0015198506000603f60008b6001600160a01b03166001600160a01b031681526020019081526020016000209050613a1b8a828f8f603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b15801561133a57600080fd5b6033546040805163035e6e4d60e41b81529051929c506001600160a01b0390911692506335e6e4d0916004808301926020929190829003018186803b158015613a6357600080fd5b505afa158015613a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9b919061500f565b6001600160a01b03166357e4bfce8c6040518263ffffffff1660e01b8152600401613ac891815260200190565b604080518083038186803b158015613adf57600080fd5b505afa158015613af3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b179190615249565b9850613b269050898986613ec1565b9650600282602001516005811115613b4e57634e487b7160e01b600052602160045260246000fd5b1415613b6257613b5f8989856147bf565b95505b50505050505b9295509295509295565b613b7a613c0e565b603d55565b613b87613c0e565b60408051808201909152600381526234323160e81b602082015260ff821115613bc35760405162461bcd60e51b8152600401610feb919061571d565b50603755565b3b151590565b603e5460408051808201909152600381526234303160e81b60208201529060ff16156126f05760405162461bcd60e51b8152600401610feb919061571d565b60335460408051630de81b1d60e21b8152905133926001600160a01b0316916337a06c74916004808301926020929190829003018186803b158015613c5257600080fd5b505afa158015613c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8a919061500f565b6001600160a01b0316146040518060400160405280600381526020016203430360ec1b815250906126f05760405162461bcd60e51b8152600401610feb919061571d565b600080613d4c604051806101c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b60405163bc24179360e01b81526001600160a01b0388811660048301526024820188905285169063bc2417939060440160206040518083038186803b158015613d9457600080fd5b505afa158015613da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcc91906155a8565b610180820181905260808201526001600160a01b03891615613ea757875460301c60ff1660408201819052613e0290600a61598f565b602082015260405163b3596f0760e01b81526001600160a01b038a8116600483015286169063b3596f079060240160206040518083038186803b158015613e4857600080fd5b505afa158015613e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e8091906155a8565b80825260208201516080830151613e979190615a37565b613ea1919061592c565b60a08201525b80608001518160a001519250925050965096945050505050565b600080613ece85846147e3565b905083811015613ee2576000915050613ef0565b613eec8482615a56565b9150505b9392505050565b600381015460009064ffffffffff908116904216811415613f2b57505060010154600160801b90046001600160801b031690565b60018301546002840154600091613f62916001600160801b03600160801b92839004811692613f5c9204168561488b565b90614898565b949350505050565b613f956040518060800160405280600081526020016000815260200160008152602001600081525090565b60018601546004808801546040805163b1bf962d60e01b8152905161402c94600160801b90046001600160801b0316936001600160a01b039093169263b1bf962d92808201926020929091829003018186803b158015613ff457600080fd5b505afa158015614008573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f5c91906155a8565b60608201819052600587015487546001600160a01b03909116916381c8c97291889188918891889160401c61ffff166040516001600160e01b031960e089901b1681526001600160a01b03968716600482015295909416602486015260448501929092526064840152608483015260a482015260c401604080518083038186803b1580156140b957600080fd5b505afa1580156140cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140f19190615605565b6040808401919091526020808401839052815180830190925260038252620d8c0d60ea1b90820152906001600160801b0310156141415760405162461bcd60e51b8152600401610feb919061571d565b506040808201518151808301909252600382526236303560e81b60208301526001600160801b0310156141875760405162461bcd60e51b8152600401610feb919061571d565b506020818101516040808401516001600160801b03808416600160801b82841681029190911760028d015560018c0154845195865295850192909252848116848401529304909216606082015290516001600160a01b038716917f4063a2df84b66bb796eb32622851d833e57b2c4292900c18f963af8808b13e35919081900360800190a2505050505050565b60008160040160009054906101000a90046001600160a01b03166001600160a01b031663b1bf962d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561426657600080fd5b505afa15801561427a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061429e91906155a8565b600183015460038401549192506001600160801b03600160801b820481169291169064ffffffffff166000806142d78787868887614936565b91509150612973878787858588614a95565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611689908490614ba9565b6036546037546040805180820190915260038152621a181b60e91b602082015290821061437b5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038216600090815260416020526040812060010154600160a01b900460ff161515806143e457506000805260436020527f8872e3e321618d759d961376a09ede5795255e81a37cc4a6812c4ae68bf2f7cd546001600160a01b038481169116145b905080611689576001600160a01b03831660008181526041602090815260408083206001908101805460ff60a01b1916600160a01b60ff8a1602179055868452604390925290912080546001600160a01b031916909217909155614449908390615914565b603655505050565b6034546035546040805180820190915260038152621a181960e91b60208201529082106144915760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b0382166000908152603f6020526040812060050154600160a01b900460ff161515806144fa57506000805260406020527f72d0927ca0fe30cbe8022c9a81259da3fa697099c3a44d94726bf134bd7668ca546001600160a01b038481169116145b905080611689576001600160a01b0383166000818152603f60209081526040808320600501805460ff60a01b1916600160a01b60ff89160217905585835290819052902080546001600160a01b031916909117905561455a826001615914565b603455505050565b600381015460009064ffffffffff90811690421681141561458f575050600101546001600160801b031690565b60018301546002840154600091613f62916001600160801b0391821691613f5c911685614c7b565b600080846101200151600014156145d3575060009050806147b3565b6146136040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b885460301c60ff16815260405163b3596f0760e01b81526001600160a01b038b8116600483015285169063b3596f079060240160206040518083038186803b15801561465e57600080fd5b505afa158015614672573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061469691906155a8565b6020820181905281516146aa90600a61598f565b6146bc90670de0b6b3a7640000615a37565b6146c6919061592c565b6040820152865460801c61ffff166060820181905260408201516146e9916147e3565b60808201528551604051632bf25fe760e11b815260048101919091526001600160a01b038616906357e4bfce90602401604080518083038186803b15801561473057600080fd5b505afa158015614744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147689190615249565b60c08301525086546147869060601c61ffff1660c0830151906147e3565b60a08201819052608082015111156147a357608081015160a08201525b80608001518160a0015192509250505b97509795505050505050565b6000826147cf5750600019613ef0565b613f62836147dd86856147e3565b90614cc1565b60008215806147f0575081155b156147fd5750600061149d565b8161480b600261271061592c565b61481790600019615a56565b614821919061592c565b8311156040518060400160405280600381526020016203230360ec1b8152509061485e5760405162461bcd60e51b8152600401610feb919061571d565b5061271061486d60028261592c565b6148778486615a37565b6148819190615914565b613ef0919061592c565b6000613ef0838342614d91565b60008215806148a5575081155b156148b25750600061149d565b816148ca60026b033b2e3c9fd0803ce800000061592c565b6148d690600019615a56565b6148e0919061592c565b8311156040518060400160405280600381526020016203230360ec1b8152509061491d5760405162461bcd60e51b8152600401610feb919061571d565b506b033b2e3c9fd0803ce800000061486d60028261592c565b600285015460009081906001600160801b031685858215614a6f57600061495d8488614c7b565b9050614969818a614898565b6040805180820190915260038152621b181960e91b60208201529093506001600160801b038411156149ae5760405162461bcd60e51b8152600401610feb919061571d565b5060018b0180546fffffffffffffffffffffffffffffffff19166001600160801b0385161790558915614a6d5760028b01546000906149fd90600160801b90046001600160801b03168961488b565b9050614a09818a614898565b60408051808201909152600381526236303360e81b60208201529093506001600160801b03841115614a4e5760405162461bcd60e51b8152600401610feb919061571d565b505060018b0180546001600160801b03808516600160801b0291161790555b505b600399909901805464ffffffffff19164264ffffffffff16179055989650505050505050565b614ac76040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b865460401c61ffff1660808201819052614ae15750611f94565b614aeb8686614898565b6020820152614afa8684614898565b8082526020820151614b0b91615a56565b604082018190526080820151614b2191906147e3565b60608201819052156129735760038701546060820151604051637df5bd3b60e01b8152600481019190915260248101869052600160281b9091046001600160a01b031690637df5bd3b90604401600060405180830381600087803b158015614b8857600080fd5b505af1158015614b9c573d6000803e3d6000fd5b5050505050505050505050565b6000614bfe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614eb79092919063ffffffff16565b8051909150156116895780806020019051810190614c1c919061546c565b6116895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610feb565b600080614c8f64ffffffffff841642615a56565b90506b033b2e3c9fd0803ce80000006301e13380614cad8387615a37565b614cb7919061592c565b613f629190615914565b60408051808201909152600381526219181960e91b602082015260009082614cfc5760405162461bcd60e51b8152600401610feb919061571d565b506000614d0a60028461592c565b9050670de0b6b3a7640000614d2182600019615a56565b614d2b919061592c565b8411156040518060400160405280600381526020016203230360ec1b81525090614d685760405162461bcd60e51b8152600401610feb919061571d565b508281614d7d670de0b6b3a764000087615a37565b614d879190615914565b613f62919061592c565b600080614da564ffffffffff851684615a56565b905080614dc1576b033b2e3c9fd0803ce8000000915050613ef0565b6000614dce600183615a56565b9050600060028311614de1576000614dec565b614dec600284615a56565b90506000614dfe6301e133808961592c565b90506000614e0c8280614898565b90506000614e1a8284614898565b90506000600283614e2b888a615a37565b614e359190615a37565b614e3f919061592c565b9050600060068387614e518a8c615a37565b614e5b9190615a37565b614e659190615a37565b614e6f919061592c565b90508082614e7d8a88615a37565b614e93906b033b2e3c9fd0803ce8000000615914565b614e9d9190615914565b614ea79190615914565b9c9b505050505050505050505050565b6060613f62848460008585843b614f105760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610feb565b600080866001600160a01b03168587604051614f2c9190615628565b60006040518083038185875af1925050503d8060008114614f69576040519150601f19603f3d011682016040523d82523d6000602084013e614f6e565b606091505b5091509150614f7e828286614f89565b979650505050505050565b60608315614f98575081613ef0565b825115614fa85782518084602001fd5b8160405162461bcd60e51b8152600401610feb919061571d565b8051614fcd81615ac5565b919050565b805160068110614fcd57600080fd5b803561ffff81168114614fcd57600080fd5b600060208284031215615004578081fd5b8135613ef081615ac5565b600060208284031215615020578081fd5b8151613ef081615ac5565b6000806040838503121561503d578081fd5b823561504881615ac5565b9150602083013561505881615ac5565b809150509250929050565b60008060008060808587031215615078578182fd5b843561508381615ac5565b9350602085013561509381615ac5565b925060408501356150a381615ac5565b915060608501356150b381615ac5565b939692955090935050565b60008060008060008060c087890312156150d6578182fd5b86356150e181615ac5565b955060208701356150f181615ac5565b9450604087013561510181615ac5565b959894975094956060810135955060808101359460a0909101359350915050565b600080600060608486031215615136578283fd5b833561514181615ac5565b9250602084013561515181615ac5565b929592945050506040919091013590565b60008060008060808587031215615177578384fd5b843561518281615ac5565b935060208581013561519381615ac5565b935060408601359250606086013567ffffffffffffffff808211156151b6578384fd5b818801915088601f8301126151c9578384fd5b8135818111156151db576151db615aaf565b6151ed601f8201601f191685016158e3565b91508082528984828501011115615202578485fd5b8084840185840137810190920192909252939692955090935050565b60008060408385031215615230578182fd5b823561523b81615ac5565b946020939093013593505050565b6000806040838503121561525b578182fd5b825161526681615ac5565b6020939093015192949293505050565b60008060006060848603121561528a578081fd5b833561529581615ac5565b92506020840135915060408401356152ac81615ac5565b809150509250925092565b600080600080608085870312156152cc578182fd5b84356152d781615ac5565b93506020850135925060408501356152ee81615ac5565b91506152fc60608601614fe1565b905092959194509250565b60008060008060008060c0878903121561531f578384fd5b863561532a81615ac5565b955060208701359450604087013561534181615ac5565b935060608701359250608087013561535881615ac5565b915061536660a08801614fe1565b90509295509295509295565b600080600060608486031215615386578081fd5b833561539181615ac5565b95602085013595506040909401359392505050565b600080600080608085870312156153bb578182fd5b84356153c681615ac5565b9350602085013592506040850135915060608501356150b381615ac5565b600080600080608085870312156153f9578182fd5b843561540481615ac5565b966020860135965060408601359560600135945092505050565b60008060408385031215615430578182fd5b823561543b81615ac5565b9150602083013560ff81168114615058578182fd5b600060208284031215615461578081fd5b8135613ef081615ada565b60006020828403121561547d578081fd5b8151613ef081615ada565b6000806000806080858703121561549d578182fd5b84356154a881615ac5565b935060208501356154b881615ac5565b92506040850135915060608501356150b381615ada565b600061018082840312156154e1578081fd5b6154e96158b9565b825181526154f960208401614fd2565b602082015261550a60408401614fc2565b604082015261551b60608401614fc2565b60608201526080830151608082015261553660a08401614fc2565b60a082015260c083015160c082015260e083015160e082015261010061555d818501614fc2565b9082015261012083810151908201526101408084015190820152610160615585818501614fc2565b908201529392505050565b6000602082840312156155a1578081fd5b5035919050565b6000602082840312156155b9578081fd5b5051919050565b600080604083850312156155d2578182fd5b82519150602083015161505881615ada565b600080604083850312156155f6578182fd5b50508035926020909101359150565b60008060408385031215615617578182fd5b505080516020909101519092909150565b6000825161563a818460208701615a6d565b9190910192915050565b6020808252825182820181905260009190848201906040850190845b818110156156855783516001600160a01b031683529284019291840191600101615660565b50909695505050505050565b60006101608201905060018060a01b0380881683528660208401528560408401528460608401528084511660808401528060208501511660a0840152604084015160c08401528060608501511660e084015260808401516101008401528060a0850151166101208401525060c083015161571261014084018261ffff169052565b509695505050505050565b602081526000825180602084015261573c816040850160208701615a6d565b601f01601f19169190910160400192915050565b60208082526024908201527f526573637561626c653a2063616c6c6572206973206e6f74207468652072657360408201526331bab2b960e11b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b8151518152610140810160208301516157ef60208401826001600160801b03169052565b50604083015161580a60408401826001600160801b03169052565b50606083015161582560608401826001600160801b03169052565b50608083015161584060808401826001600160801b03169052565b5060a083015161585960a084018264ffffffffff169052565b5060c083015161587460c08401826001600160a01b03169052565b5060e083015161588f60e08401826001600160a01b03169052565b50610100838101516001600160a01b0316908301526101209283015160ff16929091019190915290565b604051610180810167ffffffffffffffff811182821017156158dd576158dd615aaf565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561590c5761590c615aaf565b604052919050565b6000821982111561592757615927615a99565b500190565b60008261594757634e487b7160e01b81526012600452602481fd5b500490565b600181815b8085111561598757816000190482111561596d5761596d615a99565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b6000613ef083836000826159a55750600161149d565b816159b25750600061149d565b81600181146159c857600281146159d2576159ee565b600191505061149d565b60ff8411156159e3576159e3615a99565b50506001821b61149d565b5060208310610133831016604e8410600b8410161715615a11575081810a61149d565b615a1b838361594c565b8060001904821115615a2f57615a2f615a99565b029392505050565b6000816000190483118215151615615a5157615a51615a99565b500290565b600082821015615a6857615a68615a99565b500390565b60005b83811015615a88578181015183820152602001615a70565b838111156120ec5750506000910152565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146126f057600080fd5b80151581146126f057600080fdfea2646970667358221220a8340cc866554b23c0da3b5d28397c1a69228d4be1d8d70ab9cb636f5978c48864736f6c63430008040033
Deployed Bytecode
0x6080604052600436106103dd5760003560e01c80638e15df28116101fd578063d80a958d11610118578063e8eda9df116100ab578063ec765d3d1161007a578063ec765d3d14610edf578063f2d5e6c714610f34578063f3a266e914610f68578063fdff6f2614610f88578063fe65acfe14610fa857600080fd5b8063e8eda9df14610e5f578063ea2092f314610e7f578063ea97cb7e14610e9f578063eaa7109314610ebf57600080fd5b8063dd90ff38116100e7578063dd90ff3814610dc7578063df651d7114610ddc578063e5bceca514610dfc578063e84f272914610e4c57600080fd5b8063d80a958d14610d42578063da01c4a514610d57578063da5d9ad014610d92578063db78f21614610da757600080fd5b8063bb35c10111610190578063d15e00531161015f578063d15e005314610cb9578063d1946dbc14610cd9578063d4a9e0c914610cee578063d5ed393314610d2257600080fd5b8063bb35c10114610c4f578063bedb86fb14610c64578063c345246014610c84578063c4d66de814610c9957600080fd5b8063a4c0166b116101cc578063a4c0166b14610bfa578063acdb8f1214610b25578063b15e3d4514610c1a578063b6529aee14610c2f57600080fd5b80638e15df2814610b255780638fa9b5a014610b7e5780638fc4218814610b9e57806391aa19f414610bc657600080fd5b8063498c7b89116102f857806374affc3f1161028b57806383c8afd71161025a57806383c8afd714610a41578063873e4dab14610a6157806387c32dec14610a815780638bd2567714610ad05780638cd2e0c714610af057600080fd5b806374affc3f146108e157806377bdc0c3146109015780637ecc33b114610a0157806383b1555f14610a2157600080fd5b806369328dec116102c757806369328dec1461085f5780636b25c8351461087f57806373b43825146108a1578063746c35a2146108c157600080fd5b8063498c7b891461079d5780635c975abb146107bd5780635fc526ff146107e15780636283d5701461083f57600080fd5b80632e8d1a441161037057806338a631831161033f57806338a631831461070b57806339a368811461073d57806342bbd6531461075d57806343f0f7331461077d57600080fd5b80632e8d1a44146105745780632f923ff71461058957806335ea6a75146105a9578063386497fd146106eb57600080fd5b8063159d6365116103ac578063159d6365146104a957806317c8cfe4146104dd5780632ab60045146104ff5780632dbbc9d91461051f57600080fd5b80630710285c146103e957806308ac08b91461041c5780630f863ef014610431578063150b7a021461046557600080fd5b366103e457005b600080fd5b3480156103f557600080fd5b50610409610404366004615372565b610fc6565b6040519081526020015b60405180910390f35b34801561042857600080fd5b50603554610409565b34801561043d57600080fd5b506104097fc4cecc127857f85f2ddb3e712184d3431e2c982794df368953a6a7ddef5db13581565b34801561047157600080fd5b50610490610480366004615162565b630a85bd0160e11b949350505050565b6040516001600160e01b03199091168152602001610413565b3480156104b557600080fd5b506104097f8c6c451a3c78b1a61d300b75cea4e998723915c4ab1ff7888c30dbbfcaf39efb81565b3480156104e957600080fd5b506104fd6104f8366004615372565b611164565b005b34801561050b57600080fd5b506104fd61051a366004614ff3565b6111cb565b34801561052b57600080fd5b5061053f61053a366004615276565b611286565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610413565b34801561058057600080fd5b50604854610409565b34801561059557600080fd5b506104fd6105a43660046155e4565b61142d565b3480156105b557600080fd5b506106de6105c4366004614ff3565b604080516101608101825260006101408201818152825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152506001600160a01b039081166000908152603f602090815260409182902082516101608101845281546101408201908152815260018201546001600160801b0380821694830194909452600160801b9081900484169482019490945260028201548084166060830152939093049091166080830152600381015464ffffffffff811660a0840152600160281b9004831660c08301526004810154831660e083015260050154918216610100820152600160a01b90910460ff1661012082015290565b60405161041391906157cb565b3480156106f757600080fd5b50610409610706366004614ff3565b61147c565b34801561071757600080fd5b506047546001600160a01b03165b6040516001600160a01b039091168152602001610413565b34801561074957600080fd5b506104fd610758366004615590565b6114a3565b34801561076957600080fd5b506104fd610778366004614ff3565b6115d0565b34801561078957600080fd5b506104fd61079836600461521e565b61168e565b3480156107a957600080fd5b506104fd6107b8366004615590565b6116ef565b3480156107c957600080fd5b50603e5460ff165b6040519015158152602001610413565b3480156107ed57600080fd5b506108306107fc366004614ff3565b60408051602080820183526000918290526001600160a01b03939093168152603f8352819020815192830190915254815290565b60405190518152602001610413565b34801561084b57600080fd5b506104fd61085a366004614ff3565b6116fc565b34801561086b57600080fd5b5061040961087a366004615276565b6117b2565b34801561088b57600080fd5b50610894611884565b6040516104139190615644565b3480156108ad57600080fd5b506104fd6108bc366004615488565b61194c565b3480156108cd57600080fd5b506104fd6108dc366004615590565b611a6c565b3480156108ed57600080fd5b506104fd6108fc366004615590565b611ab6565b34801561090d57600080fd5b506109b761091c366004614ff3565b6040805160c081018252600060a08201818152825260208201819052918101829052606081018290526080810191909152506001600160a01b03908116600090815260416020908152604091829020825160c081018452815460a082019081528152600182015494851692810192909252600160a01b90930460ff169181019190915260028201546060820152600390910154608082015290565b604051610413919081515181526020808301516001600160a01b03169082015260408083015160ff1690820152606080830151908201526080918201519181019190915260a00190565b348015610a0d57600080fd5b506104fd610a1c366004615590565b611ac3565b348015610a2d57600080fd5b506104fd610a3c36600461502b565b611ad0565b348015610a4d57600080fd5b506104fd610a5c36600461521e565b611b8d565b348015610a6d57600080fd5b506104fd610a7c36600461502b565b611be2565b348015610a8d57600080fd5b50610830610a9c366004614ff3565b60408051602080820183526000918290526001600160a01b0393909316815260418352819020815192830190915254815290565b348015610adc57600080fd5b506104fd610aeb366004615063565b611f9c565b348015610afc57600080fd5b50610b10610b0b366004615372565b6120f2565b60408051928352901515602083015201610413565b348015610b3157600080fd5b50610830610b4036600461521e565b60408051602080820183526000918290526001600160a01b039490941681526042845281812092815291835290819020815192830190915254815290565b348015610b8a57600080fd5b506104fd610b993660046153a6565b612252565b348015610baa57600080fd5b50603954603a5460408051928352602083019190915201610413565b348015610bd257600080fd5b506104097f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b081565b348015610c0657600080fd5b506104fd610c153660046153a6565b61239f565b348015610c2657600080fd5b50603c54610409565b348015610c3b57600080fd5b506104fd610c4a366004615307565b612531565b348015610c5b57600080fd5b50604954610409565b348015610c7057600080fd5b506104fd610c7f366004615450565b612655565b348015610c9057600080fd5b50603d54610409565b348015610ca557600080fd5b506104fd610cb4366004614ff3565b6126f3565b348015610cc557600080fd5b50610409610cd4366004614ff3565b612818565b348015610ce557600080fd5b50610894612839565b348015610cfa57600080fd5b506104097f7c34be048fc583adad09a0b6b5c3870d1f149431761f9dfbae926873c7917bf981565b348015610d2e57600080fd5b506104fd610d3d3660046150be565b6128fb565b348015610d4e57600080fd5b50603b54610409565b348015610d6357600080fd5b506107d1610d7236600461541e565b604460209081526000928352604080842090915290825290205460ff1681565b348015610d9e57600080fd5b50604654610409565b348015610db357600080fd5b506104fd610dc2366004615372565b61297c565b348015610dd357600080fd5b50603754610409565b348015610de857600080fd5b506104fd610df7366004615276565b6129ab565b348015610e0857600080fd5b50610e1c610e1736600461521e565b612a78565b604080519586526001600160a01b039094166020860152928401919091526060830152608082015260a001610413565b6104fd610e5a36600461521e565b612d8f565b348015610e6b57600080fd5b506104fd610e7a3660046152b7565b61337b565b348015610e8b57600080fd5b50610409610e9a3660046153e4565b613466565b348015610eab57600080fd5b506104fd610eba366004615590565b613638565b348015610ecb57600080fd5b506104fd610eda366004615122565b613645565b348015610eeb57600080fd5b50610eff610efa36600461521e565b613743565b604080519687526001600160a01b039095166020870152938501929092526060840152608083015260a082015260c001610413565b348015610f4057600080fd5b506104097f2fd61ae66f2d61d8f037eb4f0b42e4cdcefa98eda491ab1715169a30e5551a7e81565b348015610f7457600080fd5b506104fd610f83366004615590565b613b72565b348015610f9457600080fd5b506104fd610fa3366004615590565b613b7f565b348015610fb457600080fd5b506033546001600160a01b0316610725565b600060016038541415610ff45760405162461bcd60e51b8152600401610feb90615794565b60405180910390fd5b6001603855611001613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b906348e9e573906001600160a01b0316603f6041604261105d604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b604051806080016040528061106f3390565b6001600160a01b0390811682528d811660208084019190915260408084018f905260609384018e905280516001600160e01b031960e08d901b16815299831660048b015260248a019890985260448901969096526064880194909452825160848801529184015160a48701528051831660c48701529283015190911660e4850152918101516101048401520151610124820152610144015b60206040518083038186803b15801561111f57600080fd5b505af4158015611133573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115791906155a8565b6000603855949350505050565b61116c613c0e565b6001600160a01b038316600081815260426020908152604080832086845282529182902084905590518381528492917f8401056e4ede1adeae18047abd742b6020c12a4b1b9ecb484f38922975ce6801910160405180910390a3505050565b6111d3613c0e565b6001600160a01b03811661123c5760405162461bcd60e51b815260206004820152602a60248201527f526573637561626c653a206e6577207265736375657220697320746865207a65604482015269726f206164647265737360b01b6064820152608401610feb565b604780546001600160a01b0319166001600160a01b0383169081179091556040517fe475e580d85111348e40d8ca33cfdd74c30fe1655c2d8537a13abc10065ffa5a90600090a250565b6001600160a01b03838116600090815260426020908152604080832086845282528083209385168352603f825282208354929384938493849384938493849391929161ffff80831692601081901c8216921c168095508196508297505050506113fd8a828e8e603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b15801561133a57600080fd5b505afa15801561134e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611372919061500f565b603360009054906101000a90046001600160a01b03166001600160a01b03166391aefd4c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156113c057600080fd5b505afa1580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f8919061500f565b613cce565b909950975061140e89600087613ec1565b965061141c88600087613ec1565b955050509397509397509397909450565b611435613c0e565b6039829055603a81905560408051838152602081018390527fd897a722b1c0a957941f99a13c0ea24d7d4ffafe0953658f68f49e13ccba5c5a910160405180910390a15050565b6001600160a01b0381166000908152603f6020526040812061149d90613ef7565b92915050565b603354604080516315d9b46f60e31b8152905133926001600160a01b03169163aecda378916004808301926020929190829003018186803b1580156114e757600080fd5b505afa1580156114fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151f919061500f565b6001600160a01b0316146040518060400160405280600381526020016203130360ec1b815250906115635760405162461bcd60e51b8152600401610feb919061571d565b5060408051808201909152600381526234323760e81b60208201528161159c5760405162461bcd60e51b8152600401610feb919061571d565b50604881905560405181907f9368516ff3437214c191f209fa4cf4af19c06ca2f2f36ff5fe76a6597daaeac390600090a250565b6001600160a01b038082166000908152603f602090815260409182902060038082015484518086019095529084526219989b60e91b9284019290925292600160281b9091041690816116355760405162461bcd60e51b8152600401610feb919061571d565b508154600160381b1615156040518060400160405280600381526020016219981960e91b8152509061167a5760405162461bcd60e51b8152600401610feb919061571d565b50611689828483600080613f6a565b505050565b611696613c0e565b6001600160a01b0382166000818152603f602052604090819020839055517fd304f6d1bc9e508077b6f059b9f83857a34d0b072986667f9092e9f2698506e1906116e39084815260200190565b60405180910390a25050565b6116f7613c0e565b604955565b6001600160a01b038082166000908152603f602090815260409182902060038082015484518086019095529084526219989b60e91b9284019290925292600160281b9091041661175f5760405162461bcd60e51b8152600401610feb919061571d565b508054600160381b1615156040518060400160405280600381526020016219981960e91b815250906117a45760405162461bcd60e51b8152600401610feb919061571d565b506117ae81614214565b5050565b6000600160385414156117d75760405162461bcd60e51b8152600401610feb90615794565b60016038556117e4613bcf565b73520f28b17bc9f9758d2bac914de4b166a6a5ba8563dc947ee2603f60405180608001604052806118123390565b6001600160a01b03908116825289811660208084019190915260408084018b905289831660609485015280516001600160e01b031960e089901b16815260048101969096528451831660248701529084015182166044860152830151606485015291015116608482015260a401611107565b6060600060365467ffffffffffffffff8111156118b157634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156118da578160200160208202803683370190505b50905060005b60365481146119465760008181526043602052604090205482516001600160a01b039091169083908390811061192657634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101526001016118e0565b50919050565b6001603854141561196f5760405162461bcd60e51b8152600401610feb90615794565b60016038556047546001600160a01b0316336001600160a01b0316146119a75760405162461bcd60e51b8152600401610feb90615750565b8015611a4d576000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146119fa576040519150601f19603f3d011682016040523d82523d6000602084013e6119ff565b606091505b5050905080611a475760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610feb565b50611a61565b611a616001600160a01b03851684846142e9565b505060006038555050565b611a74613c0e565b60408051808201909152600381526234323160e81b602082015260ff821115611ab05760405162461bcd60e51b8152600401610feb919061571d565b50603555565b611abe613c0e565b603b55565b611acb613c0e565b604655565b611ad8613c0e565b6001600160a01b03821615801590611af857506001600160a01b03811615155b6040518060400160405280600381526020016218981b60e91b81525090611b325760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038281166000818152603f602052604080822060050180546001600160a01b0319169486169485179055517f195838d540de3dc858973066767082af33599dde9e7a8ddfe1cd40bba6487fcc9190a35050565b611b95613c0e565b6001600160a01b03821660008181526041602052604090819020839055517f0e5cd7e52259b05b0b3cd175800318f114be4e1bfcf716ab6f940e608a122ab3906116e39084815260200190565b611bea613c0e565b60408051808201909152600381526234303360e81b6020820152823b611c235760405162461bcd60e51b8152600401610feb919061571d565b5060408051808201909152600381526218981b60e91b60208201526001600160a01b038216611c655760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b03828116600090815260416020526040908190209051631c67bc0160e31b81526004810191909152908216602482015273d531824320bcf6cb5f55a139028fc3aa03b554b49063e33de0089060440160006040518083038186803b158015611cd457600080fd5b505af4158015611ce8573d6000803e3d6000fd5b50505050611cf58261433b565b6033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b158015611d3a57600080fd5b505afa158015611d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d72919061500f565b6001600160a01b03161415604051806040016040528060038152602001620dcc0d60ea1b81525090611db75760405162461bcd60e51b8152600401610feb919061571d565b50816001600160a01b031663a22cb465603360009054906101000a90046001600160a01b03166001600160a01b03166335e6e4d06040518163ffffffff1660e01b815260040160206040518083038186803b158015611e1557600080fd5b505afa158015611e29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4d919061500f565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260016024820152604401600060405180830381600087803b158015611e9557600080fd5b505af1158015611ea9573d6000803e3d6000fd5b50505050603360009054906101000a90046001600160a01b03166001600160a01b03166335e6e4d06040518163ffffffff1660e01b815260040160206040518083038186803b158015611efb57600080fd5b505afa158015611f0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f33919061500f565b60405163873e4dab60e01b81526001600160a01b0384811660048301528381166024830152919091169063873e4dab90604401600060405180830381600087803b158015611f8057600080fd5b505af1158015611f94573d6000803e3d6000fd5b505050505050565b611fa4613c0e565b60408051808201909152600381526234303360e81b6020820152843b611fdd5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b03831615801590611ffe57506001600160a01b03821615155b801561201257506001600160a01b03811615155b6040518060400160405280600381526020016218981b60e91b8152509061204c5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038481166000908152603f6020526040908190209051636047157f60e01b81526004810191909152848216602482015283821660448201529082166064820152737d739ba7ed9f62552ffb5e674b58badebaf3c2c490636047157f9060840160006040518083038186803b1580156120cb57600080fd5b505af41580156120df573d6000803e3d6000fd5b505050506120ec84614451565b50505050565b600080600160385414156121185760405162461bcd60e51b8152600401610feb90615794565b6001603855612125613bcf565b73ea3770c20762b5930d05d2409310ff2e77d350d163b65e773c603360009054906101000a90046001600160a01b0316603f60416042604051806080016040528061216d3390565b6001600160a01b0390811682528d811660208084019190915260408084018f905260609384018e905280516001600160e01b031960e08c901b16815298831660048a01526024890197909752604488019590955260648701939093528151831660848701529281015190911660a48501529182015160c4840152015160e482015261010401604080518083038186803b15801561220957600080fd5b505af415801561221d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224191906155c0565b600060385590969095509350505050565b600160385414156122755760405162461bcd60e51b8152600401610feb90615794565b6001603855612282613bcf565b7362fd7b5ab5003d9287e807a4e72c7651344f474b630255d390603360009054906101000a90046001600160a01b0316603f604160426040518060a001604052806122ca3390565b6001600160a01b0390811682528c811660208084019190915260408084018e905260608085018e90528c841660809586015281516001600160e01b031960e08d901b16815299841660048b015260248a01989098526044890196909652606488019490945282518116608488015292820151831660a48701529281015160c48601529283015160e485015291015116610104820152610124015b60006040518083038186803b15801561237c57600080fd5b505af4158015612390573d6000803e3d6000fd5b50506000603855505050505050565b600160385414156123c25760405162461bcd60e51b8152600401610feb90615794565b60016038556123cf613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b9063200f7ab5906001600160a01b0316603f6041604261242b604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b6040518060e0016040528061243d3390565b6001600160a01b031681526020018c6001600160a01b031681526020018b81526020018a8152602001896001600160a01b0316815260200160465481526020016049548152506040518763ffffffff1660e01b8152600401612364969594939291906001600160a01b038781168252602080830188905260408301879052606083018690528451608084015284015160a08301526101a08201908084511660c08401528060208501511660e084015260408401516101008401526060840151610120840152806080850151166101408401525060a083015161016083015260c0830151610180830152979650505050505050565b600160385414156125545760405162461bcd60e51b8152600401610feb90615794565b6001603855612561613bcf565b73ea3770c20762b5930d05d2409310ff2e77d350d163f2b6dd87603360009054906101000a90046001600160a01b0316603f604160426040518060e001604052806125a93390565b6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b6001600160a01b031681526020018a8152602001896001600160a01b031681526020018861ffff168152506040518663ffffffff1660e01b8152600401612618959493929190615691565b60006040518083038186803b15801561263057600080fd5b505af4158015612644573d6000803e3d6000fd5b505060006038555050505050505050565b61265d613c0e565b603e5460ff161515811515146126f057603e805460ff191682151590811790915560ff16156126b657426039556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a150565b6039546126c39042615a56565b603a556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a15b50565b600054610100900460ff1661270e5760005460ff1615612712565b303b155b6127755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610feb565b600054610100900460ff16158015612797576000805461ffff19166101011790555b60408051808201909152600381526218981b60e91b60208201526001600160a01b0383166127d85760405162461bcd60e51b8152600401610feb919061571d565b50602060355560ff60375560fa603b55603380546001600160a01b0319166001600160a01b03841617905580156117ae576000805461ff00191690555050565b6001600160a01b0381166000908152603f6020526040812061149d90614562565b6060600060345467ffffffffffffffff81111561286657634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561288f578160200160208202803683370190505b50905060005b60345481146119465760008181526040602081905290205482516001600160a01b03909116908390839081106128db57634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390921660209283029190910190910152600101612895565b612903613bcf565b6001600160a01b038681166000908152603f6020526040902060038101549091600160281b90910416336001600160a01b0316146040518060400160405280600381526020016203431360ec1b815250906129715760405162461bcd60e51b8152600401610feb919061571d565b505b50505050505050565b612984613c0e565b6001600160a01b039092166000908152604160205260409020600281019190915560030155565b600160385414156129ce5760405162461bcd60e51b8152600401610feb90615794565b60016038556047546001600160a01b0316336001600160a01b031614612a065760405162461bcd60e51b8152600401610feb90615750565b604051632142170760e11b81523060048201526001600160a01b038281166024830152604482018490528416906342842e0e90606401600060405180830381600087803b158015612a5657600080fd5b505af1158015612a6a573d6000803e3d6000fd5b505060006038555050505050565b6001600160a01b0380831660009081526042602090815260408083208584528252808320603354825163035e6e4d60e41b815292519495869586958695869594869416926335e6e4d092600480840193919291829003018186803b158015612adf57600080fd5b505afa158015612af3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b17919061500f565b60405163058dcda760e21b81526001600160a01b038b81166004830152602482018b905291925090821690631637369c9060440160206040518083038186803b158015612b6357600080fd5b505afa158015612b77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9b91906155a8565b96508615612d83576033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b158015612be857600080fd5b505afa158015612bfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c20919061500f565b6001600160a01b031663504006ca896040518263ffffffff1660e01b8152600401612c4d91815260200190565b6101806040518083038186803b158015612c6657600080fd5b505afa158015612c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9e91906154cf565b90506000603f60008360a001516001600160a01b03166001600160a01b031681526020019081526020016000209050816101000151975081610120015196508161014001519550612d7d8260a00151828d878688603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4057600080fd5b505afa158015612d54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d78919061500f565b6145b7565b95505050505b50509295509295909350565b6033546040516321f8a72160e01b81527f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b06004820152839183916001600160a01b03909116906321f8a7219060240160206040518083038186803b158015612df657600080fd5b505afa158015612e0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e2e919061500f565b6001600160a01b0316826001600160a01b03161415612f1357604051630b02f02d60e31b8152600481018290526001600160a01b0383169063581781689060240160206040518083038186803b158015612e8757600080fd5b505afa158015612e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebf919061500f565b6001600160a01b0316336001600160a01b031614604051806040016040528060038152602001621a191960e91b81525090612f0d5760405162461bcd60e51b8152600401610feb919061571d565b50613084565b6040516331a9108f60e11b8152600481018290526001600160a01b03831690636352211e9060240160206040518083038186803b158015612f5357600080fd5b505afa158015612f67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8b919061500f565b6001600160a01b0316336001600160a01b0316148061304857506001600160a01b03828116600090815260416020526040908190206001015490516331a9108f60e11b815260048101849052911690636352211e9060240160206040518083038186803b158015612ffb57600080fd5b505afa15801561300f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613033919061500f565b6001600160a01b0316336001600160a01b0316145b604051806040016040528060038152602001621a191960e91b815250906130825760405162461bcd60e51b8152600401610feb919061571d565b505b6001600160a01b03848116600090815260416020908152604091829020825160c081018452815460a082019081528152600182015480861693820193909352600160a01b90920460ff16828401526002810154606083015260030154608082015260335491516321f8a72160e01b81527f8a93d180722d35e7bad6c51783a694c9bb7f479b8c0b2ffea77c18dead6877b0600482015287939192909116906321f8a7219060240160206040518083038186803b15801561314357600080fd5b505afa158015613157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317b919061500f565b6001600160a01b0316826001600160a01b0316141561329d576033546040516321f8a72160e01b81527fc4cecc127857f85f2ddb3e712184d3431e2c982794df368953a6a7ddef5db13560048201526041916000916001600160a01b03909116906321f8a7219060240160206040518083038186803b1580156131fd57600080fd5b505afa158015613211573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613235919061500f565b6001600160a01b0390811682526020808301939093526040918201600020825160c081018452815460a082019081528152600182015492831694810194909452600160a01b90910460ff16918301919091526002810154606083015260030154608082015290505b60006001600160a01b031681602001516001600160a01b031614156040518060400160405280600381526020016234323560e81b815250906132f25760405162461bcd60e51b8152600401610feb919061571d565b506132fb613bcf565b34603d5414604051806040016040528060038152602001621a191b60e91b8152509061333a5760405162461bcd60e51b8152600401610feb919061571d565b5060405185906001600160a01b0388169033907f9b17da8a37b9aa9a3c85708ff3ad811dc736697b43dc09952dc2e30123e3f1c590600090a4505050505050565b6001603854141561339e5760405162461bcd60e51b8152600401610feb90615794565b60016038556133ab613bcf565b73520f28b17bc9f9758d2bac914de4b166a6a5ba8563eef7aa6a603f6040518060a001604052806133d93390565b6001600160a01b03908116825289811660208084019190915260408084018b905289831660608086019190915261ffff808b1660809687015282516001600160e01b031960e08b901b1681526004810198909852865185166024890152928601518416604488015290850151606487015284015190911660848501529101511660a482015260c401612364565b60006001603854141561348b5760405162461bcd60e51b8152600401610feb90615794565b6001603855613498613bcf565b6033547362fd7b5ab5003d9287e807a4e72c7651344f474b906395e4a103906001600160a01b0316603f604160426134f4604080518082018252600080825260209182015281518083019092526039548252603a549082015290565b6040518060c001604052806135063390565b6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b81526020018a81526020016048548152506040518763ffffffff1660e01b81526004016135da969594939291906001600160a01b038781168252602080830188905260408301879052606083018690528451608084015284015160a08301526101808201908351811660c084015260208401511660e083015260408301516101008301526060830151610120830152608083015161014083015260a0909201516101609091015295945050505050565b60206040518083038186803b1580156135f257600080fd5b505af4158015613606573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362a91906155a8565b600060385595945050505050565b613640613c0e565b603c55565b6033546040516321f8a72160e01b81527f2fd61ae66f2d61d8f037eb4f0b42e4cdcefa98eda491ab1715169a30e5551a7e60048201526001600160a01b03909116906321f8a7219060240160206040518083038186803b1580156136a857600080fd5b505afa1580156136bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136e0919061500f565b6001600160a01b0316336001600160a01b0316146040518060400160405280600381526020016234333160e81b8152509061372e5760405162461bcd60e51b8152600401610feb919061571d565b506116896001600160a01b03841683836142e9565b6001600160a01b03828116600090815260426020908152604080832085845282528083208054603354835163035e6e4d60e41b81529351959687968796879687968796909561ffff8083169660109390931c16949116926335e6e4d09260048082019391829003018186803b1580156137bb57600080fd5b505afa1580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f3919061500f565b60405163058dcda760e21b81526001600160a01b038d81166004830152602482018d90529190911690631637369c9060440160206040518083038186803b15801561383d57600080fd5b505afa158015613851573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061387591906155a8565b98508861389957600080600080600080985098509850985098509850505050613b68565b6033546040805163035e6e4d60e41b815290516000926001600160a01b0316916335e6e4d0916004808301926020929190829003018186803b1580156138de57600080fd5b505afa1580156138f2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613916919061500f565b6001600160a01b031663504006ca8b6040518263ffffffff1660e01b815260040161394391815260200190565b6101806040518083038186803b15801561395c57600080fd5b505afa158015613970573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399491906154cf565b90508060a0015198506000603f60008b6001600160a01b03166001600160a01b031681526020019081526020016000209050613a1b8a828f8f603360009054906101000a90046001600160a01b03166001600160a01b031663d35d46e16040518163ffffffff1660e01b815260040160206040518083038186803b15801561133a57600080fd5b6033546040805163035e6e4d60e41b81529051929c506001600160a01b0390911692506335e6e4d0916004808301926020929190829003018186803b158015613a6357600080fd5b505afa158015613a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9b919061500f565b6001600160a01b03166357e4bfce8c6040518263ffffffff1660e01b8152600401613ac891815260200190565b604080518083038186803b158015613adf57600080fd5b505afa158015613af3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b179190615249565b9850613b269050898986613ec1565b9650600282602001516005811115613b4e57634e487b7160e01b600052602160045260246000fd5b1415613b6257613b5f8989856147bf565b95505b50505050505b9295509295509295565b613b7a613c0e565b603d55565b613b87613c0e565b60408051808201909152600381526234323160e81b602082015260ff821115613bc35760405162461bcd60e51b8152600401610feb919061571d565b50603755565b3b151590565b603e5460408051808201909152600381526234303160e81b60208201529060ff16156126f05760405162461bcd60e51b8152600401610feb919061571d565b60335460408051630de81b1d60e21b8152905133926001600160a01b0316916337a06c74916004808301926020929190829003018186803b158015613c5257600080fd5b505afa158015613c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8a919061500f565b6001600160a01b0316146040518060400160405280600381526020016203430360ec1b815250906126f05760405162461bcd60e51b8152600401610feb919061571d565b600080613d4c604051806101c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b60405163bc24179360e01b81526001600160a01b0388811660048301526024820188905285169063bc2417939060440160206040518083038186803b158015613d9457600080fd5b505afa158015613da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcc91906155a8565b610180820181905260808201526001600160a01b03891615613ea757875460301c60ff1660408201819052613e0290600a61598f565b602082015260405163b3596f0760e01b81526001600160a01b038a8116600483015286169063b3596f079060240160206040518083038186803b158015613e4857600080fd5b505afa158015613e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e8091906155a8565b80825260208201516080830151613e979190615a37565b613ea1919061592c565b60a08201525b80608001518160a001519250925050965096945050505050565b600080613ece85846147e3565b905083811015613ee2576000915050613ef0565b613eec8482615a56565b9150505b9392505050565b600381015460009064ffffffffff908116904216811415613f2b57505060010154600160801b90046001600160801b031690565b60018301546002840154600091613f62916001600160801b03600160801b92839004811692613f5c9204168561488b565b90614898565b949350505050565b613f956040518060800160405280600081526020016000815260200160008152602001600081525090565b60018601546004808801546040805163b1bf962d60e01b8152905161402c94600160801b90046001600160801b0316936001600160a01b039093169263b1bf962d92808201926020929091829003018186803b158015613ff457600080fd5b505afa158015614008573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f5c91906155a8565b60608201819052600587015487546001600160a01b03909116916381c8c97291889188918891889160401c61ffff166040516001600160e01b031960e089901b1681526001600160a01b03968716600482015295909416602486015260448501929092526064840152608483015260a482015260c401604080518083038186803b1580156140b957600080fd5b505afa1580156140cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140f19190615605565b6040808401919091526020808401839052815180830190925260038252620d8c0d60ea1b90820152906001600160801b0310156141415760405162461bcd60e51b8152600401610feb919061571d565b506040808201518151808301909252600382526236303560e81b60208301526001600160801b0310156141875760405162461bcd60e51b8152600401610feb919061571d565b506020818101516040808401516001600160801b03808416600160801b82841681029190911760028d015560018c0154845195865295850192909252848116848401529304909216606082015290516001600160a01b038716917f4063a2df84b66bb796eb32622851d833e57b2c4292900c18f963af8808b13e35919081900360800190a2505050505050565b60008160040160009054906101000a90046001600160a01b03166001600160a01b031663b1bf962d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561426657600080fd5b505afa15801561427a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061429e91906155a8565b600183015460038401549192506001600160801b03600160801b820481169291169064ffffffffff166000806142d78787868887614936565b91509150612973878787858588614a95565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611689908490614ba9565b6036546037546040805180820190915260038152621a181b60e91b602082015290821061437b5760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b038216600090815260416020526040812060010154600160a01b900460ff161515806143e457506000805260436020527f8872e3e321618d759d961376a09ede5795255e81a37cc4a6812c4ae68bf2f7cd546001600160a01b038481169116145b905080611689576001600160a01b03831660008181526041602090815260408083206001908101805460ff60a01b1916600160a01b60ff8a1602179055868452604390925290912080546001600160a01b031916909217909155614449908390615914565b603655505050565b6034546035546040805180820190915260038152621a181960e91b60208201529082106144915760405162461bcd60e51b8152600401610feb919061571d565b506001600160a01b0382166000908152603f6020526040812060050154600160a01b900460ff161515806144fa57506000805260406020527f72d0927ca0fe30cbe8022c9a81259da3fa697099c3a44d94726bf134bd7668ca546001600160a01b038481169116145b905080611689576001600160a01b0383166000818152603f60209081526040808320600501805460ff60a01b1916600160a01b60ff89160217905585835290819052902080546001600160a01b031916909117905561455a826001615914565b603455505050565b600381015460009064ffffffffff90811690421681141561458f575050600101546001600160801b031690565b60018301546002840154600091613f62916001600160801b0391821691613f5c911685614c7b565b600080846101200151600014156145d3575060009050806147b3565b6146136040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b885460301c60ff16815260405163b3596f0760e01b81526001600160a01b038b8116600483015285169063b3596f079060240160206040518083038186803b15801561465e57600080fd5b505afa158015614672573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061469691906155a8565b6020820181905281516146aa90600a61598f565b6146bc90670de0b6b3a7640000615a37565b6146c6919061592c565b6040820152865460801c61ffff166060820181905260408201516146e9916147e3565b60808201528551604051632bf25fe760e11b815260048101919091526001600160a01b038616906357e4bfce90602401604080518083038186803b15801561473057600080fd5b505afa158015614744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147689190615249565b60c08301525086546147869060601c61ffff1660c0830151906147e3565b60a08201819052608082015111156147a357608081015160a08201525b80608001518160a0015192509250505b97509795505050505050565b6000826147cf5750600019613ef0565b613f62836147dd86856147e3565b90614cc1565b60008215806147f0575081155b156147fd5750600061149d565b8161480b600261271061592c565b61481790600019615a56565b614821919061592c565b8311156040518060400160405280600381526020016203230360ec1b8152509061485e5760405162461bcd60e51b8152600401610feb919061571d565b5061271061486d60028261592c565b6148778486615a37565b6148819190615914565b613ef0919061592c565b6000613ef0838342614d91565b60008215806148a5575081155b156148b25750600061149d565b816148ca60026b033b2e3c9fd0803ce800000061592c565b6148d690600019615a56565b6148e0919061592c565b8311156040518060400160405280600381526020016203230360ec1b8152509061491d5760405162461bcd60e51b8152600401610feb919061571d565b506b033b2e3c9fd0803ce800000061486d60028261592c565b600285015460009081906001600160801b031685858215614a6f57600061495d8488614c7b565b9050614969818a614898565b6040805180820190915260038152621b181960e91b60208201529093506001600160801b038411156149ae5760405162461bcd60e51b8152600401610feb919061571d565b5060018b0180546fffffffffffffffffffffffffffffffff19166001600160801b0385161790558915614a6d5760028b01546000906149fd90600160801b90046001600160801b03168961488b565b9050614a09818a614898565b60408051808201909152600381526236303360e81b60208201529093506001600160801b03841115614a4e5760405162461bcd60e51b8152600401610feb919061571d565b505060018b0180546001600160801b03808516600160801b0291161790555b505b600399909901805464ffffffffff19164264ffffffffff16179055989650505050505050565b614ac76040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b865460401c61ffff1660808201819052614ae15750611f94565b614aeb8686614898565b6020820152614afa8684614898565b8082526020820151614b0b91615a56565b604082018190526080820151614b2191906147e3565b60608201819052156129735760038701546060820151604051637df5bd3b60e01b8152600481019190915260248101869052600160281b9091046001600160a01b031690637df5bd3b90604401600060405180830381600087803b158015614b8857600080fd5b505af1158015614b9c573d6000803e3d6000fd5b5050505050505050505050565b6000614bfe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614eb79092919063ffffffff16565b8051909150156116895780806020019051810190614c1c919061546c565b6116895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610feb565b600080614c8f64ffffffffff841642615a56565b90506b033b2e3c9fd0803ce80000006301e13380614cad8387615a37565b614cb7919061592c565b613f629190615914565b60408051808201909152600381526219181960e91b602082015260009082614cfc5760405162461bcd60e51b8152600401610feb919061571d565b506000614d0a60028461592c565b9050670de0b6b3a7640000614d2182600019615a56565b614d2b919061592c565b8411156040518060400160405280600381526020016203230360ec1b81525090614d685760405162461bcd60e51b8152600401610feb919061571d565b508281614d7d670de0b6b3a764000087615a37565b614d879190615914565b613f62919061592c565b600080614da564ffffffffff851684615a56565b905080614dc1576b033b2e3c9fd0803ce8000000915050613ef0565b6000614dce600183615a56565b9050600060028311614de1576000614dec565b614dec600284615a56565b90506000614dfe6301e133808961592c565b90506000614e0c8280614898565b90506000614e1a8284614898565b90506000600283614e2b888a615a37565b614e359190615a37565b614e3f919061592c565b9050600060068387614e518a8c615a37565b614e5b9190615a37565b614e659190615a37565b614e6f919061592c565b90508082614e7d8a88615a37565b614e93906b033b2e3c9fd0803ce8000000615914565b614e9d9190615914565b614ea79190615914565b9c9b505050505050505050505050565b6060613f62848460008585843b614f105760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610feb565b600080866001600160a01b03168587604051614f2c9190615628565b60006040518083038185875af1925050503d8060008114614f69576040519150601f19603f3d011682016040523d82523d6000602084013e614f6e565b606091505b5091509150614f7e828286614f89565b979650505050505050565b60608315614f98575081613ef0565b825115614fa85782518084602001fd5b8160405162461bcd60e51b8152600401610feb919061571d565b8051614fcd81615ac5565b919050565b805160068110614fcd57600080fd5b803561ffff81168114614fcd57600080fd5b600060208284031215615004578081fd5b8135613ef081615ac5565b600060208284031215615020578081fd5b8151613ef081615ac5565b6000806040838503121561503d578081fd5b823561504881615ac5565b9150602083013561505881615ac5565b809150509250929050565b60008060008060808587031215615078578182fd5b843561508381615ac5565b9350602085013561509381615ac5565b925060408501356150a381615ac5565b915060608501356150b381615ac5565b939692955090935050565b60008060008060008060c087890312156150d6578182fd5b86356150e181615ac5565b955060208701356150f181615ac5565b9450604087013561510181615ac5565b959894975094956060810135955060808101359460a0909101359350915050565b600080600060608486031215615136578283fd5b833561514181615ac5565b9250602084013561515181615ac5565b929592945050506040919091013590565b60008060008060808587031215615177578384fd5b843561518281615ac5565b935060208581013561519381615ac5565b935060408601359250606086013567ffffffffffffffff808211156151b6578384fd5b818801915088601f8301126151c9578384fd5b8135818111156151db576151db615aaf565b6151ed601f8201601f191685016158e3565b91508082528984828501011115615202578485fd5b8084840185840137810190920192909252939692955090935050565b60008060408385031215615230578182fd5b823561523b81615ac5565b946020939093013593505050565b6000806040838503121561525b578182fd5b825161526681615ac5565b6020939093015192949293505050565b60008060006060848603121561528a578081fd5b833561529581615ac5565b92506020840135915060408401356152ac81615ac5565b809150509250925092565b600080600080608085870312156152cc578182fd5b84356152d781615ac5565b93506020850135925060408501356152ee81615ac5565b91506152fc60608601614fe1565b905092959194509250565b60008060008060008060c0878903121561531f578384fd5b863561532a81615ac5565b955060208701359450604087013561534181615ac5565b935060608701359250608087013561535881615ac5565b915061536660a08801614fe1565b90509295509295509295565b600080600060608486031215615386578081fd5b833561539181615ac5565b95602085013595506040909401359392505050565b600080600080608085870312156153bb578182fd5b84356153c681615ac5565b9350602085013592506040850135915060608501356150b381615ac5565b600080600080608085870312156153f9578182fd5b843561540481615ac5565b966020860135965060408601359560600135945092505050565b60008060408385031215615430578182fd5b823561543b81615ac5565b9150602083013560ff81168114615058578182fd5b600060208284031215615461578081fd5b8135613ef081615ada565b60006020828403121561547d578081fd5b8151613ef081615ada565b6000806000806080858703121561549d578182fd5b84356154a881615ac5565b935060208501356154b881615ac5565b92506040850135915060608501356150b381615ada565b600061018082840312156154e1578081fd5b6154e96158b9565b825181526154f960208401614fd2565b602082015261550a60408401614fc2565b604082015261551b60608401614fc2565b60608201526080830151608082015261553660a08401614fc2565b60a082015260c083015160c082015260e083015160e082015261010061555d818501614fc2565b9082015261012083810151908201526101408084015190820152610160615585818501614fc2565b908201529392505050565b6000602082840312156155a1578081fd5b5035919050565b6000602082840312156155b9578081fd5b5051919050565b600080604083850312156155d2578182fd5b82519150602083015161505881615ada565b600080604083850312156155f6578182fd5b50508035926020909101359150565b60008060408385031215615617578182fd5b505080516020909101519092909150565b6000825161563a818460208701615a6d565b9190910192915050565b6020808252825182820181905260009190848201906040850190845b818110156156855783516001600160a01b031683529284019291840191600101615660565b50909695505050505050565b60006101608201905060018060a01b0380881683528660208401528560408401528460608401528084511660808401528060208501511660a0840152604084015160c08401528060608501511660e084015260808401516101008401528060a0850151166101208401525060c083015161571261014084018261ffff169052565b509695505050505050565b602081526000825180602084015261573c816040850160208701615a6d565b601f01601f19169190910160400192915050565b60208082526024908201527f526573637561626c653a2063616c6c6572206973206e6f74207468652072657360408201526331bab2b960e11b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b8151518152610140810160208301516157ef60208401826001600160801b03169052565b50604083015161580a60408401826001600160801b03169052565b50606083015161582560608401826001600160801b03169052565b50608083015161584060808401826001600160801b03169052565b5060a083015161585960a084018264ffffffffff169052565b5060c083015161587460c08401826001600160a01b03169052565b5060e083015161588f60e08401826001600160a01b03169052565b50610100838101516001600160a01b0316908301526101209283015160ff16929091019190915290565b604051610180810167ffffffffffffffff811182821017156158dd576158dd615aaf565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561590c5761590c615aaf565b604052919050565b6000821982111561592757615927615a99565b500190565b60008261594757634e487b7160e01b81526012600452602481fd5b500490565b600181815b8085111561598757816000190482111561596d5761596d615a99565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b6000613ef083836000826159a55750600161149d565b816159b25750600061149d565b81600181146159c857600281146159d2576159ee565b600191505061149d565b60ff8411156159e3576159e3615a99565b50506001821b61149d565b5060208310610133831016604e8410600b8410161715615a11575081810a61149d565b615a1b838361594c565b8060001904821115615a2f57615a2f615a99565b029392505050565b6000816000190483118215151615615a5157615a51615a99565b500290565b600082821015615a6857615a68615a99565b500390565b60005b83811015615a88578181015183820152602001615a70565b838111156120ec5750506000910152565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146126f057600080fd5b80151581146126f057600080fdfea2646970667358221220a8340cc866554b23c0da3b5d28397c1a69228d4be1d8d70ab9cb636f5978c48864736f6c63430008040033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.