Polygon Sponsored slots available. Book your slot here!
My Name Tag:
Not Available, login to update
Txn Hash |
Method
|
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xf8574168e5ef70e42c7cb4a3562fe32d97f03bcfa468e76ee40e95844cabb238 | Initialize | 29409832 | 355 days 14 hrs ago | 0xa1671bdf1d34283e47b024a01a9118ef4e7ef4ab | IN | Atlendis: Borrower Pools Implementation | 0 MATIC | 0.004349880001 | |
0x7872646efe8559aa951fde4aa95abbac3733c714754a5daf3504aea235004763 | 0x60806040 | 29238423 | 359 days 21 hrs ago | Atlendis: Deployer | IN | Create: BorrowerPools | 0 MATIC | 0.212709537365 |
[ Download CSV Export ]
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 Name:
BorrowerPools
Compiler Version
v0.8.9+commit.e5eed63a
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.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "./interfaces/IBorrowerPools.sol"; import "./extensions/AaveILendingPool.sol"; import "./lib/Errors.sol"; import "./lib/PoolLogic.sol"; import "./lib/Scaling.sol"; import "./lib/Types.sol"; import "./lib/Uint128WadRayMath.sol"; import "./PoolsController.sol"; contract BorrowerPools is PoolsController, IBorrowerPools { using PoolLogic for Types.Pool; using Scaling for uint128; using Uint128WadRayMath for uint128; function initialize(address governance) public initializer { _initialize(); if (governance == address(0)) { // Prevent setting governance to null account governance = _msgSender(); } _grantRole(DEFAULT_ADMIN_ROLE, governance); _grantRole(Roles.GOVERNANCE_ROLE, governance); _setRoleAdmin(Roles.BORROWER_ROLE, Roles.GOVERNANCE_ROLE); _setRoleAdmin(Roles.POSITION_ROLE, Roles.GOVERNANCE_ROLE); } // VIEW METHODS /** * @notice Returns the liquidity ratio of a given tick in a pool's order book. * The liquidity ratio is an accounting construct to deduce the accrued interest over time. * @param poolHash The identifier of the pool * @param rate The tick rate from which to extract the liquidity ratio * @return liquidityRatio The liquidity ratio of the given tick **/ function getTickLiquidityRatio(bytes32 poolHash, uint128 rate) public view override returns (uint128 liquidityRatio) { liquidityRatio = pools[poolHash].ticks[rate].atlendisLiquidityRatio; if (liquidityRatio == 0) { liquidityRatio = uint128(PoolLogic.RAY); } } /** * @notice Returns the repartition between bonds and deposits of the given tick. * @param poolHash The identifier of the pool * @param rate The tick rate from which to get data * @return adjustedTotalAmount Total amount of deposit in the tick, excluding * the pending amounts * @return adjustedRemainingAmount Amount of tokens in tick deposited with the * underlying yield provider that were deposited before bond issuance * @return bondsQuantity The quantity of bonds within the tick * @return adjustedPendingAmount Amount of deposit in tick deposited with the * underlying yield provider that were deposited after bond issuance * @return atlendisLiquidityRatio The liquidity ratio of the given tick * @return accruedFees The total fees claimable in the current tick, either from * yield provider interests or liquidity rewards accrual **/ function getTickAmounts(bytes32 poolHash, uint128 rate) public view override returns ( uint128 adjustedTotalAmount, uint128 adjustedRemainingAmount, uint128 bondsQuantity, uint128 adjustedPendingAmount, uint128 atlendisLiquidityRatio, uint128 accruedFees ) { Types.Tick storage tick = pools[poolHash].ticks[rate]; return ( tick.adjustedTotalAmount, tick.adjustedRemainingAmount, tick.bondsQuantity, tick.adjustedPendingAmount, tick.atlendisLiquidityRatio, tick.accruedFees ); } /** * @notice Returns the timestamp of the last fee distribution to the tick * @param pool The identifier of the pool pool * @param rate The tick rate from which to get data * @return lastFeeDistributionTimestamp Timestamp of the last fee's distribution to the tick **/ function getTickLastUpdate(string calldata pool, uint128 rate) public view override returns (uint128 lastFeeDistributionTimestamp) { Types.Tick storage tick = pools[keccak256(abi.encode(pool))].ticks[rate]; return tick.lastFeeDistributionTimestamp; } /** * @notice Returns the current state of the pool's parameters * @param poolHash The identifier of the pool * @return weightedAverageLendingRate The average deposit bidding rate in the order book * @return adjustedPendingDeposits Amount of tokens deposited after bond * issuance and currently on third party yield provider **/ function getPoolAggregates(bytes32 poolHash) external view override returns (uint128 weightedAverageLendingRate, uint128 adjustedPendingDeposits) { Types.Pool storage pool = pools[poolHash]; Types.PoolParameters storage parameters = pools[poolHash].parameters; adjustedPendingDeposits = 0; if (pool.state.currentMaturity == 0) { weightedAverageLendingRate = estimateLoanRate(pool.parameters.MAX_BORROWABLE_AMOUNT, poolHash); } else { uint128 amountWeightedRate = 0; uint128 totalAmount = 0; uint128 rate = parameters.MIN_RATE; for (rate; rate != parameters.MAX_RATE + parameters.RATE_SPACING; rate += parameters.RATE_SPACING) { amountWeightedRate += pool.ticks[rate].normalizedLoanedAmount.wadMul(rate); totalAmount += pool.ticks[rate].normalizedLoanedAmount; adjustedPendingDeposits += pool.ticks[rate].adjustedPendingAmount; } weightedAverageLendingRate = amountWeightedRate.wadDiv(totalAmount); } } /** * @notice Returns the current maturity of the pool * @param poolHash The identifier of the pool * @return poolCurrentMaturity The pool's current maturity **/ function getPoolMaturity(bytes32 poolHash) public view override returns (uint128 poolCurrentMaturity) { return pools[poolHash].state.currentMaturity; } /** * @notice Estimates the lending rate corresponding to the input amount, * depending on the current state of the pool * @param normalizedBorrowedAmount The amount to be borrowed from the pool * @param poolHash The identifier of the pool * @return estimatedRate The estimated loan rate for the current state of the pool **/ function estimateLoanRate(uint128 normalizedBorrowedAmount, bytes32 poolHash) public view override returns (uint128 estimatedRate) { Types.Pool storage pool = pools[poolHash]; Types.PoolParameters storage parameters = pool.parameters; if (pool.state.currentMaturity > 0 || pool.state.defaulted || pool.state.closed || !pool.state.active) { return 0; } if (normalizedBorrowedAmount > pool.parameters.MAX_BORROWABLE_AMOUNT) { normalizedBorrowedAmount = pool.parameters.MAX_BORROWABLE_AMOUNT; } uint128 yieldProviderLiquidityRatio = uint128( parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(parameters.UNDERLYING_TOKEN)) ); uint128 rate = pool.parameters.MIN_RATE; uint128 normalizedRemainingAmount = normalizedBorrowedAmount; uint128 amountWeightedRate = 0; for (rate; rate != parameters.MAX_RATE + parameters.RATE_SPACING; rate += parameters.RATE_SPACING) { (uint128 atlendisLiquidityRatio, , , ) = pool.peekFeesForTick(rate, yieldProviderLiquidityRatio); uint128 tickAmount = pool.ticks[rate].adjustedRemainingAmount.wadRayMul(atlendisLiquidityRatio); if (tickAmount < normalizedRemainingAmount) { normalizedRemainingAmount -= tickAmount; amountWeightedRate += tickAmount.wadMul(rate); } else { amountWeightedRate += normalizedRemainingAmount.wadMul(rate); normalizedRemainingAmount = 0; break; } } if (normalizedBorrowedAmount == normalizedRemainingAmount) { return 0; } estimatedRate = amountWeightedRate.wadDiv(normalizedBorrowedAmount - normalizedRemainingAmount); } /** * @notice Returns the token amount's repartition between bond quantity and normalized * deposited amount currently placed on third party yield provider * @param poolHash The identifier of the pool * @param rate Tick's rate * @param adjustedAmount Adjusted amount of tokens currently on third party yield provider * @param bondsIssuanceIndex The identifier of the borrow group * @return bondsQuantity Quantity of bonds held * @return normalizedDepositedAmount Amount of deposit currently on third party yield provider **/ function getAmountRepartition( bytes32 poolHash, uint128 rate, uint128 adjustedAmount, uint128 bondsIssuanceIndex ) public view override returns (uint128 bondsQuantity, uint128 normalizedDepositedAmount) { Types.Pool storage pool = pools[poolHash]; uint128 yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); if (bondsIssuanceIndex > pool.state.currentBondsIssuanceIndex) { return (0, adjustedAmount.wadRayMul(yieldProviderLiquidityRatio)); } uint128 adjustedDepositedAmount; (bondsQuantity, adjustedDepositedAmount) = pool.computeAmountRepartitionForTick( rate, adjustedAmount, bondsIssuanceIndex ); (uint128 atlendisLiquidityRatio, uint128 accruedFees, , ) = pool.peekFeesForTick(rate, yieldProviderLiquidityRatio); uint128 accruedFeesShare = pool.peekAccruedFeesShare(rate, adjustedDepositedAmount, accruedFees); normalizedDepositedAmount = adjustedDepositedAmount.wadRayMul(atlendisLiquidityRatio) + accruedFeesShare; } /** * @notice Returns the total amount a borrower has to repay to a pool. Includes borrowed * amount, late repay fees and protocol fees * @param poolHash The identifier of the pool * @return normalizedRepayAmount Total repay amount **/ function getRepayAmounts(bytes32 poolHash, bool earlyRepay) public view override returns ( uint128 normalizedRepayAmount, uint128 lateRepayFee, uint128 repaymentFees ) { uint128 preFeeRepayAmount = pools[poolHash].getRepayValue(earlyRepay); lateRepayFee = pools[poolHash].getLateRepayFeePerBond().wadMul(preFeeRepayAmount); repaymentFees = pools[poolHash].getRepaymentFees(preFeeRepayAmount + lateRepayFee); normalizedRepayAmount = preFeeRepayAmount + repaymentFees + lateRepayFee; } // LENDER METHODS /** * @notice Gets called within the Position.deposit() function and enables a lender to deposit assets * into a given pool's order book. The lender specifies a rate (price) at which it is willing to * lend out its assets (bid on the zero coupon bond). The full amount will initially be deposited * on the underlying yield provider until the borrower sells bonds at the specified rate. * @param normalizedAmount The amount of the given asset to deposit * @param rate The rate at which to bid for a bond * @param poolHash The identifier of the pool * @param underlyingToken Contract' address of the token to be deposited * @param sender The lender address who calls the deposit function on the Position * @return adjustedAmount Deposited amount adjusted with current liquidity index * @return bondsIssuanceIndex The identifier of the borrow group to which the deposit has been allocated **/ function deposit( uint128 rate, bytes32 poolHash, address underlyingToken, address sender, uint128 normalizedAmount ) public override whenNotPaused onlyRole(Roles.POSITION_ROLE) returns (uint128 adjustedAmount, uint128 bondsIssuanceIndex) { Types.Pool storage pool = pools[poolHash]; if (pool.state.defaulted) { revert Errors.BP_POOL_DEFAULTED(); } if (!pool.state.active) { revert Errors.BP_POOL_NOT_ACTIVE(); } if (pool.state.closed) { revert Errors.BP_POOL_CLOSED(); } if (underlyingToken != pool.parameters.UNDERLYING_TOKEN) { revert Errors.BP_UNMATCHED_TOKEN(); } if (rate < pool.parameters.MIN_RATE) { revert Errors.BP_OUT_OF_BOUND_MIN_RATE(); } if (rate > pool.parameters.MAX_RATE) { revert Errors.BP_OUT_OF_BOUND_MAX_RATE(); } if ((rate - pool.parameters.MIN_RATE) % pool.parameters.RATE_SPACING != 0) { revert Errors.BP_RATE_SPACING(); } adjustedAmount = 0; bondsIssuanceIndex = 0; (adjustedAmount, bondsIssuanceIndex) = pool.depositToTick(rate, normalizedAmount); pool.depositToYieldProvider(sender, normalizedAmount); } /** * @notice Gets called within the Position.withdraw() function and enables a lender to * evaluate the exact amount of tokens it is allowed to withdraw * @dev This method is meant to be used exclusively with the withdraw() method * Under certain circumstances, this method can return incorrect values, that would otherwise * be rejected by the checks made in the withdraw() method * @param poolHash The identifier of the pool * @param rate The rate the position is bidding for * @param adjustedAmount The amount of tokens in the position, adjusted to the deposit liquidity ratio * @param bondsIssuanceIndex An index determining deposit timing * @return adjustedAmountToWithdraw The amount of tokens to withdraw, adjuste for borrow pool use * @return depositedAmountToWithdraw The amount of tokens to withdraw, adjuste for position use * @return remainingBondsQuantity The quantity of bonds remaining within the position * @return bondsMaturity The maturity of bonds remaining within the position after withdraw **/ function getWithdrawAmounts( bytes32 poolHash, uint128 rate, uint128 adjustedAmount, uint128 bondsIssuanceIndex ) public view override returns ( uint128 adjustedAmountToWithdraw, uint128 depositedAmountToWithdraw, uint128 remainingBondsQuantity, uint128 bondsMaturity ) { Types.Pool storage pool = pools[poolHash]; if (!pool.state.active) { revert Errors.BP_POOL_NOT_ACTIVE(); } (remainingBondsQuantity, adjustedAmountToWithdraw) = pool.computeAmountRepartitionForTick( rate, adjustedAmount, bondsIssuanceIndex ); // return amount adapted to bond index depositedAmountToWithdraw = adjustedAmountToWithdraw.wadRayDiv( pool.getBondIssuanceMultiplierForTick(rate, bondsIssuanceIndex) ); bondsMaturity = pool.state.currentMaturity; } /** * @notice Gets called within the Position.withdraw() function and enables a lender to * withdraw assets that are deposited with the underlying yield provider * @param poolHash The identifier of the pool * @param rate The rate the position is bidding for * @param adjustedAmountToWithdraw The actual amount of tokens to withdraw from the position * @param bondsIssuanceIndex An index determining deposit timing * @param owner The address to which the withdrawns funds are sent * @return normalizedDepositedAmountToWithdraw Actual amount of tokens withdrawn and sent to the lender **/ function withdraw( bytes32 poolHash, uint128 rate, uint128 adjustedAmountToWithdraw, uint128 bondsIssuanceIndex, address owner ) public override whenNotPaused onlyRole(Roles.POSITION_ROLE) returns (uint128 normalizedDepositedAmountToWithdraw) { Types.Pool storage pool = pools[poolHash]; if (bondsIssuanceIndex > (pool.state.currentBondsIssuanceIndex + 1)) { revert Errors.BP_BOND_ISSUANCE_ID_TOO_HIGH(); } bool isPendingDeposit = bondsIssuanceIndex > pool.state.currentBondsIssuanceIndex; if ( !((!(isPendingDeposit) && pool.ticks[rate].adjustedRemainingAmount > 0) || (isPendingDeposit && pool.ticks[rate].adjustedPendingAmount > 0)) ) { revert Errors.BP_TARGET_BOND_ISSUANCE_INDEX_EMPTY(); } if (adjustedAmountToWithdraw <= 0) { revert Errors.BP_NO_DEPOSIT_TO_WITHDRAW(); } normalizedDepositedAmountToWithdraw = pool.withdrawDepositedAmountForTick( rate, adjustedAmountToWithdraw, bondsIssuanceIndex ); pool.parameters.YIELD_PROVIDER.withdraw( pool.parameters.UNDERLYING_TOKEN, normalizedDepositedAmountToWithdraw.scaleFromWad(pool.parameters.TOKEN_DECIMALS), owner ); } /** * @notice Gets called within Position.updateRate() and updates the order book ticks affected by the position * updating its rate. This is only possible as long as there are no bonds in the position, i.e the full * position currently lies with the yield provider * @param adjustedAmount The adjusted balance of tokens of the given position * @param poolHash The identifier of the pool * @param oldRate The current rate of the position * @param newRate The new rate of the position * @param oldBondsIssuanceIndex The identifier of the borrow group from the given position * @return newAdjustedAmount The updated amount of tokens of the position adjusted by the * new tick's global liquidity ratio * @return newBondsIssuanceIndex The new borrow group id to which the updated position is linked **/ function updateRate( uint128 adjustedAmount, bytes32 poolHash, uint128 oldRate, uint128 newRate, uint128 oldBondsIssuanceIndex ) public override whenNotPaused onlyRole(Roles.POSITION_ROLE) returns ( uint128 newAdjustedAmount, uint128 newBondsIssuanceIndex, uint128 normalizedAmount ) { Types.Pool storage pool = pools[poolHash]; if (pool.state.closed) { revert Errors.BP_POOL_CLOSED(); } // cannot update rate when being borrowed (uint128 bondsQuantity, ) = getAmountRepartition(poolHash, oldRate, adjustedAmount, oldBondsIssuanceIndex); if (bondsQuantity != 0) { revert Errors.BP_LOAN_ONGOING(); } if (newRate < pool.parameters.MIN_RATE) { revert Errors.BP_OUT_OF_BOUND_MIN_RATE(); } if (newRate > pool.parameters.MAX_RATE) { revert Errors.BP_OUT_OF_BOUND_MAX_RATE(); } if ((newRate - pool.parameters.MIN_RATE) % pool.parameters.RATE_SPACING != 0) { revert Errors.BP_RATE_SPACING(); } // input amount adapted to bond index uint128 adjustedBondIndexAmount = adjustedAmount.wadRayMul( pool.getBondIssuanceMultiplierForTick(oldRate, oldBondsIssuanceIndex) ); normalizedAmount = pool.withdrawDepositedAmountForTick(oldRate, adjustedBondIndexAmount, oldBondsIssuanceIndex); (newAdjustedAmount, newBondsIssuanceIndex) = pool.depositToTick(newRate, normalizedAmount); } // BORROWER METHODS /** * @notice Called by the borrower to sell bonds to the order book. * The affected ticks get updated according the amount of bonds sold. * @param to The address to which the borrowed funds should be sent. * @param loanAmount The total amount of the loan **/ function borrow(address to, uint128 loanAmount) external override whenNotPaused onlyRole(Roles.BORROWER_ROLE) { bytes32 poolHash = borrowerAuthorizedPools[_msgSender()]; Types.Pool storage pool = pools[poolHash]; if (pool.state.closed) { revert Errors.BP_POOL_CLOSED(); } if (pool.state.defaulted) { revert Errors.BP_POOL_DEFAULTED(); } if (pool.state.currentMaturity > 0 && (block.timestamp > pool.state.currentMaturity)) { revert Errors.BP_MULTIPLE_BORROW_AFTER_MATURITY(); } uint128 normalizedLoanAmount = loanAmount.scaleToWad(pool.parameters.TOKEN_DECIMALS); uint128 normalizedEstablishmentFee = normalizedLoanAmount.wadMul(pool.parameters.ESTABLISHMENT_FEE_RATE); uint128 normalizedBorrowedAmount = normalizedLoanAmount - normalizedEstablishmentFee; if (pool.state.normalizedBorrowedAmount + normalizedLoanAmount > pool.parameters.MAX_BORROWABLE_AMOUNT) { revert Errors.BP_BORROW_MAX_BORROWABLE_AMOUNT_EXCEEDED(); } if (block.timestamp < pool.state.nextLoanMinStart) { revert Errors.BP_BORROW_COOLDOWN_PERIOD_NOT_OVER(); } // collectFees should be called before changing pool global state as fee collection depends on it pool.collectFees(); if (normalizedLoanAmount > pool.state.normalizedAvailableDeposits) { revert Errors.BP_BORROW_OUT_OF_BOUND_AMOUNT(); } uint128 remainingAmount = normalizedLoanAmount; uint128 currentInterestRate = pool.state.lowerInterestRate - pool.parameters.RATE_SPACING; while (remainingAmount > 0 && currentInterestRate < pool.parameters.MAX_RATE) { currentInterestRate += pool.parameters.RATE_SPACING; if (pool.ticks[currentInterestRate].adjustedRemainingAmount > 0) { (uint128 bondsPurchasedQuantity, uint128 normalizedUsedAmountForPurchase) = pool .getBondsIssuanceParametersForTick(currentInterestRate, remainingAmount); pool.addBondsToTick(currentInterestRate, bondsPurchasedQuantity, normalizedUsedAmountForPurchase); remainingAmount -= normalizedUsedAmountForPurchase; } } if (remainingAmount != 0) { revert Errors.BP_BORROW_UNSUFFICIENT_BORROWABLE_AMOUNT_WITHIN_BRACKETS(); } if (pool.state.currentMaturity == 0) { pool.state.currentMaturity = uint128(block.timestamp + pool.parameters.LOAN_DURATION); emit Borrow(poolHash, normalizedBorrowedAmount, normalizedEstablishmentFee); } else { emit FurtherBorrow(poolHash, normalizedBorrowedAmount, normalizedEstablishmentFee); } protocolFees[poolHash] += normalizedEstablishmentFee; pool.state.normalizedBorrowedAmount += normalizedLoanAmount; pool.parameters.YIELD_PROVIDER.withdraw( pool.parameters.UNDERLYING_TOKEN, normalizedBorrowedAmount.scaleFromWad(pool.parameters.TOKEN_DECIMALS), to ); } /** * @notice Repays a currently outstanding bonds of the given pool. **/ function repay() external override whenNotPaused onlyRole(Roles.BORROWER_ROLE) { bytes32 poolHash = borrowerAuthorizedPools[_msgSender()]; Types.Pool storage pool = pools[poolHash]; if (pool.state.defaulted) { revert Errors.BP_POOL_DEFAULTED(); } if (pool.state.currentMaturity == 0) { revert Errors.BP_REPAY_NO_ACTIVE_LOAN(); } bool earlyRepay = pool.state.currentMaturity > block.timestamp; if (earlyRepay && !pool.parameters.EARLY_REPAY) { revert Errors.BP_EARLY_REPAY_NOT_ACTIVATED(); } // collectFees should be called before changing pool global state as fee collection depends on it pool.collectFees(); uint128 lateRepayFee; bool bondsIssuanceIndexAlreadyIncremented = false; uint128 normalizedRepayAmount; uint128 lateRepayFeePerBond = pool.getLateRepayFeePerBond(); for ( uint128 rate = pool.state.lowerInterestRate; rate <= pool.parameters.MAX_RATE; rate += pool.parameters.RATE_SPACING ) { (uint128 normalizedRepayAmountForTick, uint128 lateRepayFeeForTick) = pool.repayForTick( rate, lateRepayFeePerBond ); normalizedRepayAmount += normalizedRepayAmountForTick + lateRepayFeeForTick; lateRepayFee += lateRepayFeeForTick; bool indexIncremented = pool.includePendingDepositsForTick(rate, bondsIssuanceIndexAlreadyIncremented); bondsIssuanceIndexAlreadyIncremented = indexIncremented || bondsIssuanceIndexAlreadyIncremented; } uint128 repaymentFees = pool.getRepaymentFees(normalizedRepayAmount); normalizedRepayAmount += repaymentFees; pool.depositToYieldProvider(_msgSender(), normalizedRepayAmount); pool.state.nextLoanMinStart = uint128(block.timestamp) + pool.parameters.COOLDOWN_PERIOD; pool.state.bondsIssuedQuantity = 0; protocolFees[poolHash] += repaymentFees; pool.state.normalizedAvailableDeposits += normalizedRepayAmount; if (block.timestamp > (pool.state.currentMaturity + pool.parameters.REPAYMENT_PERIOD)) { emit LateRepay( poolHash, normalizedRepayAmount, lateRepayFee, repaymentFees, pool.state.normalizedAvailableDeposits, pool.state.nextLoanMinStart ); } else if (pool.state.currentMaturity > block.timestamp) { emit EarlyRepay( poolHash, normalizedRepayAmount, repaymentFees, pool.state.normalizedAvailableDeposits, pool.state.nextLoanMinStart ); } else { emit Repay( poolHash, normalizedRepayAmount, repaymentFees, pool.state.normalizedAvailableDeposits, pool.state.nextLoanMinStart ); } // set global data for next loan pool.state.currentMaturity = 0; pool.state.normalizedBorrowedAmount = 0; } /** * @notice Called by the borrower to top up liquidity rewards' reserve that * is distributed to liquidity providers at the pre-defined distribution rate. * @param amount Amount of tokens that will be add up to the pool's liquidity rewards reserve **/ function topUpLiquidityRewards(uint128 amount) external override whenNotPaused onlyRole(Roles.BORROWER_ROLE) { Types.Pool storage pool = pools[borrowerAuthorizedPools[_msgSender()]]; uint128 normalizedAmount = amount.scaleToWad(pool.parameters.TOKEN_DECIMALS); pool.depositToYieldProvider(_msgSender(), normalizedAmount); uint128 yieldProviderLiquidityRatio = pool.topUpLiquidityRewards(normalizedAmount); if ( !pool.state.active && pool.state.remainingAdjustedLiquidityRewardsReserve.wadRayMul(yieldProviderLiquidityRatio) >= pool.parameters.LIQUIDITY_REWARDS_ACTIVATION_THRESHOLD ) { pool.state.active = true; emit PoolActivated(pool.parameters.POOL_HASH); } emit TopUpLiquidityRewards(borrowerAuthorizedPools[_msgSender()], normalizedAmount); } // PUBLIC METHODS /** * @notice Collect yield provider fees as well as liquidity rewards for the target tick * @param poolHash The identifier of the pool **/ function collectFeesForTick(bytes32 poolHash, uint128 rate) external override whenNotPaused { Types.Pool storage pool = pools[poolHash]; pool.collectFees(rate); } /** * @notice Collect yield provider fees as well as liquidity rewards for the whole pool * Iterates over all pool initialized ticks * @param poolHash The identifier of the pool **/ function collectFees(bytes32 poolHash) external override whenNotPaused { Types.Pool storage pool = pools[poolHash]; pool.collectFees(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (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 proxied contracts do not make use of 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 (last updated v4.5.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControlUpgradeable.sol"; import "../utils/ContextUpgradeable.sol"; import "../utils/StringsUpgradeable.sol"; import "../utils/introspection/ERC165Upgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", StringsUpgradeable.toHexString(uint160(account), 20), " is missing role ", StringsUpgradeable.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "../extensions/AaveILendingPool.sol"; import "../lib/Types.sol"; /** * @title IBorrowerPools * @notice Used by the Position contract to pool lender positions in the borrowers order books * Used by the borrowers to manage their loans on their pools **/ interface IBorrowerPools { // EVENTS /** * @notice Emitted after a successful borrow * @param poolHash The identifier of the pool * @param normalizedBorrowedAmount The actual amount of tokens borrowed * @param establishmentFees Fees paid to the protocol at borrow time **/ event Borrow(bytes32 indexed poolHash, uint128 normalizedBorrowedAmount, uint128 establishmentFees); /** * @notice Emitted after a successful further borrow * @param poolHash The identifier of the pool * @param normalizedBorrowedAmount The actual amount of tokens borrowed * @param establishmentFees Fees paid to the protocol at borrow time **/ event FurtherBorrow(bytes32 indexed poolHash, uint128 normalizedBorrowedAmount, uint128 establishmentFees); /** * @notice Emitted after a successful repay * @param poolHash The identifier of the pool * @param normalizedRepayAmount The actual amount of tokens repaid * @param repaymentFee The amount of fee paid to the protocol at repay time * @param normalizedDepositsAfterRepay The actual amount of tokens deposited and available for next loan after repay * @param nextLoanMinStart The timestamp after which a new loan can be taken **/ event Repay( bytes32 indexed poolHash, uint128 normalizedRepayAmount, uint128 repaymentFee, uint128 normalizedDepositsAfterRepay, uint128 nextLoanMinStart ); /** * @notice Emitted after a successful early repay * @param poolHash The identifier of the pool * @param normalizedRepayAmount The actual amount of tokens repaid * @param repaymentFee The amount of fee paid to the protocol at repay time * @param normalizedDepositsAfterRepay The actual amount of tokens deposited and available for next loan after repay * @param nextLoanMinStart The timestamp after which a new loan can be taken **/ event EarlyRepay( bytes32 indexed poolHash, uint128 normalizedRepayAmount, uint128 repaymentFee, uint128 normalizedDepositsAfterRepay, uint128 nextLoanMinStart ); /** * @notice Emitted after a successful repay, made after the repayment period * Includes a late repay fee * @param poolHash The identifier of the pool * @param normalizedRepayAmount The actual amount of tokens repaid * @param lateRepayFee The amount of fee paid due to a late repayment * @param repaymentFee The amount of fee paid to the protocol at repay time * @param normalizedDepositsAfterRepay The actual amount of tokens deposited and available for next loan after repay * @param nextLoanMinStart The timestamp after which a new loan can be taken **/ event LateRepay( bytes32 indexed poolHash, uint128 normalizedRepayAmount, uint128 lateRepayFee, uint128 repaymentFee, uint128 normalizedDepositsAfterRepay, uint128 nextLoanMinStart ); /** * @notice Emitted after a borrower successfully deposits tokens in its pool liquidity rewards reserve * @param poolHash The identifier of the pool * @param normalizedAmount The actual amount of tokens deposited into the reserve **/ event TopUpLiquidityRewards(bytes32 poolHash, uint128 normalizedAmount); // The below events and enums are being used in the PoolLogic library // The same way that libraries don't have storage, they don't have an event log // Hence event logs will be saved in the calling contract // For the contract abi to reflect this and be used by offchain libraries, // we define these events and enums in the contract itself as well /** * @notice Emitted when a tick is initialized, i.e. when its first deposited in * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param atlendisLiquidityRatio The tick current liquidity index **/ event TickInitialized(bytes32 poolHash, uint128 rate, uint128 atlendisLiquidityRatio); /** * @notice Emitted after a deposit on a tick that was done during a loan * @param poolHash The identifier of the pool * @param rate The position bidding rate * @param adjustedPendingDeposit The amount of tokens deposited during a loan, adjusted to the current liquidity index **/ event TickLoanDeposit(bytes32 poolHash, uint128 rate, uint128 adjustedPendingDeposit); /** * @notice Emitted after a deposit on a tick that was done without an active loan * @param poolHash The identifier of the pool * @param rate The position bidding rate * @param adjustedAvailableDeposit The amount of tokens available to the borrower for its next loan * @param atlendisLiquidityRatio The tick current liquidity index **/ event TickNoLoanDeposit( bytes32 poolHash, uint128 rate, uint128 adjustedAvailableDeposit, uint128 atlendisLiquidityRatio ); /** * @notice Emitted when a borrow successfully impacts a tick * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param adjustedRemainingAmountReduction The amount of tokens left to borrow from other ticks * @param loanedAmount The amount borrowed from the tick * @param atlendisLiquidityRatio The tick current liquidity index * @param unborrowedRatio Proportion of ticks funds that were not borrowed **/ event TickBorrow( bytes32 poolHash, uint128 rate, uint128 adjustedRemainingAmountReduction, uint128 loanedAmount, uint128 atlendisLiquidityRatio, uint128 unborrowedRatio ); /** * @notice Emitted when a withdraw is done outside of a loan on the tick * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param adjustedAmountToWithdraw The amount of tokens to withdraw, adjusted to the tick liquidity index **/ event TickWithdrawPending(bytes32 poolHash, uint128 rate, uint128 adjustedAmountToWithdraw); /** * @notice Emitted when a withdraw is done during a loan on the tick * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param adjustedAmountToWithdraw The amount of tokens to withdraw, adjusted to the tick liquidity index * @param atlendisLiquidityRatio The tick current liquidity index * @param accruedFeesToWithdraw The amount of fees the position has a right to claim **/ event TickWithdrawRemaining( bytes32 poolHash, uint128 rate, uint128 adjustedAmountToWithdraw, uint128 atlendisLiquidityRatio, uint128 accruedFeesToWithdraw ); /** * @notice Emitted when pending amounts are merged with the rest of the pool during a repay * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param adjustedPendingAmount The amount of pending funds deposited with available funds **/ event TickPendingDeposit( bytes32 poolHash, uint128 rate, uint128 adjustedPendingAmount, bool poolBondIssuanceIndexIncremented ); /** * @notice Emitted when funds from a tick are repaid by the borrower * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param adjustedRemainingAmount The total amount of tokens available to the borrower for * its next loan, adjusted to the tick current liquidity index * @param atlendisLiquidityRatio The tick current liquidity index **/ event TickRepay(bytes32 poolHash, uint128 rate, uint128 adjustedRemainingAmount, uint128 atlendisLiquidityRatio); /** * @notice Emitted when liquidity rewards are distributed to a tick * @param poolHash The identifier of the pool * @param rate The tick's bidding rate * @param remainingLiquidityRewards the amount of liquidityRewards added to the tick * @param addedAccruedFees Increase in accrued fees for that tick **/ event CollectFeesForTick(bytes32 poolHash, uint128 rate, uint128 remainingLiquidityRewards, uint128 addedAccruedFees); // VIEW METHODS /** * @notice Returns the liquidity ratio of a given tick in a pool's order book. * The liquidity ratio is an accounting construct to deduce the accrued interest over time. * @param poolHash The identifier of the pool * @param rate The tick rate from which to extract the liquidity ratio * @return liquidityRatio The liquidity ratio of the given tick **/ function getTickLiquidityRatio(bytes32 poolHash, uint128 rate) external view returns (uint128 liquidityRatio); /** * @notice Returns the repartition between bonds and deposits of the given tick. * @param poolHash The identifier of the pool * @param rate The tick rate from which to get data * @return adjustedTotalAmount Total amount of deposit in the tick * @return adjustedRemainingAmount Amount of tokens in tick deposited with the * underlying yield provider that were deposited before bond issuance * @return bondsQuantity The quantity of bonds within the tick * @return adjustedPendingAmount Amount of deposit in tick deposited with the * underlying yield provider that were deposited after bond issuance * @return atlendisLiquidityRatio The liquidity ratio of the given tick * @return accruedFees The total fees claimable in the current tick, either from * yield provider interests or liquidity rewards accrual **/ function getTickAmounts(bytes32 poolHash, uint128 rate) external view returns ( uint128 adjustedTotalAmount, uint128 adjustedRemainingAmount, uint128 bondsQuantity, uint128 adjustedPendingAmount, uint128 atlendisLiquidityRatio, uint128 accruedFees ); /** * @notice Returns the timestamp of the last fee distribution to the tick * @param poolHash The identifier of the pool * @param rate The tick rate from which to get data * @return lastFeeDistributionTimestamp Timestamp of the last fee's distribution to the tick **/ function getTickLastUpdate(string calldata poolHash, uint128 rate) external view returns (uint128 lastFeeDistributionTimestamp); /** * @notice Returns the current state of the pool's parameters * @param poolHash The identifier of the pool * @return weightedAverageLendingRate The average deposit bidding rate in the order book * @return adjustedPendingDeposits Amount of tokens deposited after bond * issuance and currently on third party yield provider **/ function getPoolAggregates(bytes32 poolHash) external view returns (uint128 weightedAverageLendingRate, uint128 adjustedPendingDeposits); /** * @notice Returns the current maturity of the pool * @param poolHash The identifier of the pool * @return poolCurrentMaturity The pool's current maturity **/ function getPoolMaturity(bytes32 poolHash) external view returns (uint128 poolCurrentMaturity); /** * @notice Estimates the lending rate corresponding to the input amount, * depending on the current state of the pool * @param normalizedBorrowedAmount The amount to be borrowed from the pool * @param poolHash The identifier of the pool * @return estimatedRate The estimated loan rate for the current state of the pool **/ function estimateLoanRate(uint128 normalizedBorrowedAmount, bytes32 poolHash) external view returns (uint128 estimatedRate); /** * @notice Returns the token amount's repartition between bond quantity and normalized * deposited amount currently placed on third party yield provider * @param poolHash The identifier of the pool * @param rate Tick's rate * @param adjustedAmount Adjusted amount of tokens currently on third party yield provider * @param bondsIssuanceIndex The identifier of the borrow group * @return bondsQuantity Quantity of bonds held * @return normalizedDepositedAmount Amount of deposit currently on third party yield provider **/ function getAmountRepartition( bytes32 poolHash, uint128 rate, uint128 adjustedAmount, uint128 bondsIssuanceIndex ) external view returns (uint128 bondsQuantity, uint128 normalizedDepositedAmount); /** * @notice Returns the total amount a borrower has to repay to a pool. Includes borrowed * amount, late repay fees and protocol fees * @param poolHash The identifier of the pool * @param earlyRepay indicates if this is an early repay * @return normalizedRepayAmount Total repay amount * @return lateRepayFee Normalized amount to be paid to each bond in case of late repayment * @return repaymentFee Normalized fee amount paid to the protocol **/ function getRepayAmounts(bytes32 poolHash, bool earlyRepay) external view returns ( uint128 normalizedRepayAmount, uint128 lateRepayFee, uint128 repaymentFee ); // LENDER METHODS /** * @notice Gets called within the Position.deposit() function and enables a lender to deposit assets * into a given borrower's order book. The lender specifies a rate (price) at which it is willing to * lend out its assets (bid on the zero coupon bond). The full amount will initially be deposited * on the underlying yield provider until the borrower sells bonds at the specified rate. * @param normalizedAmount The amount of the given asset to deposit * @param rate The rate at which to bid for a bond * @param poolHash The identifier of the pool * @param underlyingToken Contract' address of the token to be deposited * @param sender The lender address who calls the deposit function on the Position * @return adjustedAmount Deposited amount adjusted with current liquidity index * @return bondsIssuanceIndex The identifier of the borrow group to which the deposit has been allocated **/ function deposit( uint128 rate, bytes32 poolHash, address underlyingToken, address sender, uint128 normalizedAmount ) external returns (uint128 adjustedAmount, uint128 bondsIssuanceIndex); /** * @notice Gets called within the Position.withdraw() function and enables a lender to * evaluate the exact amount of tokens it is allowed to withdraw * @dev This method is meant to be used exclusively with the withdraw() method * Under certain circumstances, this method can return incorrect values, that would otherwise * be rejected by the checks made in the withdraw() method * @param poolHash The identifier of the pool * @param rate The rate the position is bidding for * @param adjustedAmount The amount of tokens in the position, adjusted to the deposit liquidity ratio * @param bondsIssuanceIndex An index determining deposit timing * @return adjustedAmountToWithdraw The amount of tokens to withdraw, adjuste for borrow pool use * @return depositedAmountToWithdraw The amount of tokens to withdraw, adjuste for position use * @return remainingBondsQuantity The quantity of bonds remaining within the position * @return bondsMaturity The maturity of bonds remaining within the position after withdraw **/ function getWithdrawAmounts( bytes32 poolHash, uint128 rate, uint128 adjustedAmount, uint128 bondsIssuanceIndex ) external view returns ( uint128 adjustedAmountToWithdraw, uint128 depositedAmountToWithdraw, uint128 remainingBondsQuantity, uint128 bondsMaturity ); /** * @notice Gets called within the Position.withdraw() function and enables a lender to * withdraw assets that are deposited with the underlying yield provider * @param poolHash The identifier of the pool * @param rate The rate the position is bidding for * @param adjustedAmountToWithdraw The actual amount of tokens to withdraw from the position * @param bondsIssuanceIndex An index determining deposit timing * @param owner The address to which the withdrawns funds are sent * @return normalizedDepositedAmountToWithdraw Actual amount of tokens withdrawn and sent to the lender **/ function withdraw( bytes32 poolHash, uint128 rate, uint128 adjustedAmountToWithdraw, uint128 bondsIssuanceIndex, address owner ) external returns (uint128 normalizedDepositedAmountToWithdraw); /** * @notice Gets called within Position.updateRate() and updates the order book ticks affected by the position * updating its rate. This is only possible as long as there are no bonds in the position, i.e the full * position currently lies with the yield provider * @param adjustedAmount The adjusted balance of tokens of the given position * @param poolHash The identifier of the pool * @param oldRate The current rate of the position * @param newRate The new rate of the position * @param oldBondsIssuanceIndex The identifier of the borrow group from the given position * @return newAdjustedAmount The updated amount of tokens of the position adjusted by the * new tick's global liquidity ratio * @return newBondsIssuanceIndex The new borrow group id to which the updated position is linked **/ function updateRate( uint128 adjustedAmount, bytes32 poolHash, uint128 oldRate, uint128 newRate, uint128 oldBondsIssuanceIndex ) external returns ( uint128 newAdjustedAmount, uint128 newBondsIssuanceIndex, uint128 normalizedAmount ); // BORROWER METHODS /** * @notice Called by the borrower to sell bonds to the order book. * The affected ticks get updated according the amount of bonds sold. * @param to The address to which the borrowed funds should be sent. * @param loanAmount The total amount of the loan **/ function borrow(address to, uint128 loanAmount) external; /** * @notice Repays a currently outstanding bonds of the given borrower. **/ function repay() external; /** * @notice Called by the borrower to top up liquidity rewards' reserve that * is distributed to liquidity providers at the pre-defined distribution rate. * @param normalizedAmount Amount of tokens that will be add up to the borrower's liquidity rewards reserve **/ function topUpLiquidityRewards(uint128 normalizedAmount) external; // FEE COLLECTION /** * @notice Collect yield provider fees as well as liquidity rewards for the target tick * @param poolHash The identifier of the pool **/ function collectFeesForTick(bytes32 poolHash, uint128 rate) external; /** * @notice Collect yield provider fees as well as liquidity rewards for the whole pool * Iterates over all pool initialized ticks * @param poolHash The identifier of the pool **/ function collectFees(bytes32 poolHash) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; interface ILendingPool { /** * @dev Emitted on deposit() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the deposit * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens * @param amount The amount deposited * @param referral The referral code used **/ event Deposit( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referral ); /** * @dev Emitted on withdraw() * @param reserve The address of the underlyng asset being withdrawn * @param user The address initiating the withdrawal, owner of aTokens * @param to Address that will receive the underlying * @param amount The amount to be withdrawn **/ event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. * - E.g. User deposits 100 USDC and gets in return 100 aUSDC * @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 aTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens * 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; /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC * @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 aToken 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 returns (uint256); /** * @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); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; library Errors { // *** Contract Specific Errors *** // BorrowerPools error BP_BORROW_MAX_BORROWABLE_AMOUNT_EXCEEDED(); // "Amount borrowed is too big, exceeding borrowable capacity"; error BP_REPAY_NO_ACTIVE_LOAN(); // "No active loan to be repaid, action cannot be performed"; error BP_BORROW_UNSUFFICIENT_BORROWABLE_AMOUNT_WITHIN_BRACKETS(); // "Amount provided is greater than available amount within min rate and max rate brackets"; error BP_REPAY_AT_MATURITY_ONLY(); // "Maturity has not been reached yet, action cannot be performed"; error BP_BORROW_COOLDOWN_PERIOD_NOT_OVER(); // "Cooldown period after a repayment is not over"; error BP_MULTIPLE_BORROW_AFTER_MATURITY(); // "Cannot borrow again from pool after loan maturity"; error BP_POOL_NOT_ACTIVE(); // "Pool not active" error BP_POOL_DEFAULTED(); // "Pool defaulted" error BP_LOAN_ONGOING(); // "There's a loan ongoing, cannot update rate" error BP_BORROW_OUT_OF_BOUND_AMOUNT(); // "Amount provided is greater than available amount, action cannot be performed"; error BP_POOL_CLOSED(); // "Pool closed"; error BP_OUT_OF_BOUND_MIN_RATE(); // "Rate provided is lower than minimum rate of the pool"; error BP_OUT_OF_BOUND_MAX_RATE(); // "Rate provided is greater than maximum rate of the pool"; error BP_UNMATCHED_TOKEN(); // "Token/Asset provided does not match the underlying token of the pool"; error BP_RATE_SPACING(); // "Decimals of rate provided do not comply with rate spacing of the pool"; error BP_BOND_ISSUANCE_ID_TOO_HIGH(); // "Bond issuance id is too high"; error BP_NO_DEPOSIT_TO_WITHDRAW(); // "Deposited amount non-borrowed equals to zero"; error BP_TARGET_BOND_ISSUANCE_INDEX_EMPTY(); // "Target bond issuance index has no amount to withdraw"; error BP_EARLY_REPAY_NOT_ACTIVATED(); // "The early repay feature is not activated for this pool"; // PoolController error PC_POOL_NOT_ACTIVE(); // "Pool not active" error PC_POOL_DEFAULTED(); // "Pool defaulted" error PC_POOL_ALREADY_SET_FOR_BORROWER(); // "Targeted borrower is already set for another pool"; error PC_POOL_TOKEN_NOT_SUPPORTED(); // "Underlying token is not supported by the yield provider"; error PC_DISALLOW_UNMATCHED_BORROWER(); // "Revoking the wrong borrower as the provided borrower does not match the provided address"; error PC_RATE_SPACING_COMPLIANCE(); // "Provided rate must be compliant with rate spacing"; error PC_NO_ONGOING_LOAN(); // "Cannot default a pool that has no ongoing loan"; error PC_NOT_ENOUGH_PROTOCOL_FEES(); // "Not enough registered protocol fees to withdraw"; error PC_POOL_ALREADY_CLOSED(); // "Pool already closed"; error PC_ZERO_POOL(); // "Cannot make actions on the zero pool"; error PC_ZERO_ADDRESS(); // "Cannot make actions on the zero address"; error PC_REPAYMENT_PERIOD_ONGOING(); // "Cannot default pool while repayment period in ongoing" error PC_ESTABLISHMENT_FEES_TOO_HIGH(); // "Cannot set establishment fee over 100% of loan amount" error PC_BORROWER_ALREADY_AUTHORIZED(); // "Borrower already authorized on another pool" // PositionManager error POS_MGMT_ONLY_OWNER(); // "Only the owner of the position token can manage it (update rate, withdraw)"; error POS_POSITION_ONLY_IN_BONDS(); // "Cannot withdraw a position that's only in bonds"; error POS_ZERO_AMOUNT(); // "Cannot deposit zero amount"; error POS_TIMELOCK(); // "Cannot withdraw or update rate in the same block as deposit"; error POS_POSITION_DOES_NOT_EXIST(); // "Position does not exist"; error POS_POOL_DEFAULTED(); // "Pool defaulted"; error POS_ZERO_ADDRESS(); // "Cannot make actions on the zero address"; error POS_NOT_ALLOWED(); // "Transaction sender is not allowed to perform the target action"; // PositionDescriptor error POD_BAD_INPUT(); // "Input pool identifier does not correspond to input pool hash"; //*** Library Specific Errors *** // WadRayMath error MATH_MULTIPLICATION_OVERFLOW(); // "The multiplication would result in a overflow"; error MATH_ADDITION_OVERFLOW(); // "The addition would result in a overflow"; error MATH_DIVISION_BY_ZERO(); // "The division would result in a divzion by zero"; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {Rounding} from "./Rounding.sol"; import {Scaling} from "./Scaling.sol"; import {Uint128WadRayMath} from "./Uint128WadRayMath.sol"; import "./Types.sol"; import "./Errors.sol"; import "../extensions/AaveILendingPool.sol"; library PoolLogic { event PoolActivated(bytes32 poolHash); enum BalanceUpdateType { INCREASE, DECREASE } event TickInitialized(bytes32 borrower, uint128 rate, uint128 atlendisLiquidityRatio); event TickLoanDeposit(bytes32 borrower, uint128 rate, uint128 adjustedPendingDeposit); event TickNoLoanDeposit( bytes32 borrower, uint128 rate, uint128 adjustedPendingDeposit, uint128 atlendisLiquidityRatio ); event TickBorrow( bytes32 borrower, uint128 rate, uint128 adjustedRemainingAmountReduction, uint128 loanedAmount, uint128 atlendisLiquidityRatio, uint128 unborrowedRatio ); event TickWithdrawPending(bytes32 borrower, uint128 rate, uint128 adjustedAmountToWithdraw); event TickWithdrawRemaining( bytes32 borrower, uint128 rate, uint128 adjustedAmountToWithdraw, uint128 atlendisLiquidityRatio, uint128 accruedFeesToWithdraw ); event TickPendingDeposit( bytes32 borrower, uint128 rate, uint128 adjustedPendingAmount, bool poolBondIssuanceIndexIncremented ); event TopUpLiquidityRewards(bytes32 borrower, uint128 addedLiquidityRewards); event TickRepay(bytes32 borrower, uint128 rate, uint128 newAdjustedRemainingAmount, uint128 atlendisLiquidityRatio); event CollectFeesForTick(bytes32 borrower, uint128 rate, uint128 remainingLiquidityRewards, uint128 addedAccruedFees); using PoolLogic for Types.Pool; using Uint128WadRayMath for uint128; using Rounding for uint128; using Scaling for uint128; using SafeERC20Upgradeable for IERC20Upgradeable; uint256 public constant SECONDS_PER_YEAR = 365 days; uint256 public constant WAD = 1e18; uint256 public constant RAY = 1e27; /** * @dev Getter for the multiplier allowing a conversion between pending and deposited * amounts for the target bonds issuance index **/ function getBondIssuanceMultiplierForTick( Types.Pool storage pool, uint128 rate, uint128 bondsIssuanceIndex ) internal view returns (uint128 returnBondsIssuanceMultiplier) { Types.Tick storage tick = pool.ticks[rate]; returnBondsIssuanceMultiplier = tick.bondsIssuanceIndexMultiplier[bondsIssuanceIndex]; if (returnBondsIssuanceMultiplier == 0) { returnBondsIssuanceMultiplier = uint128(RAY); } } /** * @dev Get share of accumulated fees from stored current tick state **/ function getAccruedFeesShare( Types.Pool storage pool, uint128 rate, uint128 adjustedAmount ) internal view returns (uint128 accruedFeesShare) { Types.Tick storage tick = pool.ticks[rate]; accruedFeesShare = tick.accruedFees.wadMul(adjustedAmount).wadDiv(tick.adjustedRemainingAmount); } /** * @dev Get share of accumulated fees from estimated current tick state **/ function peekAccruedFeesShare( Types.Pool storage pool, uint128 rate, uint128 adjustedAmount, uint128 accruedFees ) public view returns (uint128 accruedFeesShare) { Types.Tick storage tick = pool.ticks[rate]; if (tick.adjustedRemainingAmount == 0) { return 0; } accruedFeesShare = accruedFees.wadMul(adjustedAmount).wadDiv(tick.adjustedRemainingAmount); } function getLateRepayFeePerBond(Types.Pool storage pool) public view returns (uint128 lateRepayFeePerBond) { uint256 lateRepaymentTimestamp = pool.state.currentMaturity + pool.parameters.REPAYMENT_PERIOD; if (block.timestamp > lateRepaymentTimestamp) { uint256 referenceTimestamp = pool.state.defaultTimestamp > 0 ? pool.state.defaultTimestamp : block.timestamp; lateRepayFeePerBond = uint128( uint256(referenceTimestamp - lateRepaymentTimestamp) * uint256(pool.parameters.LATE_REPAY_FEE_PER_BOND_RATE) ); } } function getRepaymentFees(Types.Pool storage pool, uint128 normalizedRepayAmount) public view returns (uint128 repaymentFees) { repaymentFees = (normalizedRepayAmount - pool.state.normalizedBorrowedAmount).wadMul( pool.parameters.REPAYMENT_FEE_RATE ); } /** * @dev The return value includes only notional and accrued interest, * it does not include any fees due for repay by the borrrower **/ function getRepayValue(Types.Pool storage pool, bool earlyRepay) public view returns (uint128 repayValue) { if (pool.state.currentMaturity == 0) { return 0; } if (!earlyRepay) { // Note: Despite being in the context of a none early repay we prevent underflow in case of wrong user input // and allow querying expected bonds quantity if loan is repaid at maturity if (block.timestamp <= pool.state.currentMaturity) { return pool.state.bondsIssuedQuantity; } } for ( uint128 rate = pool.state.lowerInterestRate; rate <= pool.parameters.MAX_RATE; rate += pool.parameters.RATE_SPACING ) { Types.Tick storage tick = pool.ticks[rate]; repayValue += getTimeValue(pool, tick.bondsQuantity, rate); } } function getTimeValue( Types.Pool storage pool, uint128 bondsQuantity, uint128 rate ) public view returns (uint128) { if (block.timestamp <= pool.state.currentMaturity) { return bondsQuantity.wadMul(getTickBondPrice(rate, uint128(pool.state.currentMaturity - block.timestamp))); } uint256 referenceTimestamp = uint128(block.timestamp); if (pool.state.defaultTimestamp > 0) { referenceTimestamp = pool.state.defaultTimestamp; } return bondsQuantity.wadDiv(getTickBondPrice(rate, uint128(referenceTimestamp - pool.state.currentMaturity))); } /** * @dev Deposit to a target tick * Updates tick data **/ function depositToTick( Types.Pool storage pool, uint128 rate, uint128 normalizedAmount ) public returns (uint128 adjustedAmount, uint128 returnBondsIssuanceIndex) { Types.Tick storage tick = pool.ticks[rate]; pool.collectFees(rate); // if there is an ongoing loan, the deposited amount goes to the pending // quantity and will be considered for next loan if (pool.state.currentMaturity > 0) { adjustedAmount = normalizedAmount.wadRayDiv(tick.yieldProviderLiquidityRatio); tick.adjustedPendingAmount += adjustedAmount; returnBondsIssuanceIndex = pool.state.currentBondsIssuanceIndex + 1; emit TickLoanDeposit(pool.parameters.POOL_HASH, rate, adjustedAmount); } // if there is no ongoing loan, the deposited amount goes to total and remaining // amount and can be borrowed instantaneously else { adjustedAmount = normalizedAmount.wadRayDiv(tick.atlendisLiquidityRatio); tick.adjustedTotalAmount += adjustedAmount; tick.adjustedRemainingAmount += adjustedAmount; returnBondsIssuanceIndex = pool.state.currentBondsIssuanceIndex; pool.state.normalizedAvailableDeposits += normalizedAmount; // return amount adapted to bond index adjustedAmount = adjustedAmount.wadRayDiv( pool.getBondIssuanceMultiplierForTick(rate, pool.state.currentBondsIssuanceIndex) ); emit TickNoLoanDeposit(pool.parameters.POOL_HASH, rate, adjustedAmount, tick.atlendisLiquidityRatio); } if ((pool.state.lowerInterestRate == 0) || (rate < pool.state.lowerInterestRate)) { pool.state.lowerInterestRate = rate; } } /** * @dev Computes the quantity of bonds purchased, and the equivalent adjusted deposit amount used for the issuance **/ function getBondsIssuanceParametersForTick( Types.Pool storage pool, uint128 rate, uint128 normalizedRemainingAmount ) public returns (uint128 bondsPurchasedQuantity, uint128 normalizedUsedAmount) { Types.Tick storage tick = pool.ticks[rate]; if (tick.adjustedRemainingAmount.wadRayMul(tick.atlendisLiquidityRatio) >= normalizedRemainingAmount) { normalizedUsedAmount = normalizedRemainingAmount; } else if ( tick.adjustedRemainingAmount.wadRayMul(tick.atlendisLiquidityRatio) + tick.accruedFees >= normalizedRemainingAmount ) { normalizedUsedAmount = normalizedRemainingAmount; tick.accruedFees -= normalizedRemainingAmount - tick.adjustedRemainingAmount.wadRayMul(tick.atlendisLiquidityRatio); } else { normalizedUsedAmount = tick.adjustedRemainingAmount.wadRayMul(tick.atlendisLiquidityRatio) + tick.accruedFees; tick.accruedFees = 0; } uint128 bondsPurchasePrice = getTickBondPrice( rate, pool.state.currentMaturity == 0 ? pool.parameters.LOAN_DURATION : pool.state.currentMaturity - uint128(block.timestamp) ); bondsPurchasedQuantity = normalizedUsedAmount.wadDiv(bondsPurchasePrice); } /** * @dev Makes all the state changes necessary to add bonds to a tick * Updates tick data and conversion data **/ function addBondsToTick( Types.Pool storage pool, uint128 rate, uint128 bondsIssuedQuantity, uint128 normalizedUsedAmountForPurchase ) public { Types.Tick storage tick = pool.ticks[rate]; // update global state for tick and pool tick.bondsQuantity += bondsIssuedQuantity; uint128 adjustedAmountForPurchase = normalizedUsedAmountForPurchase.wadRayDiv(tick.atlendisLiquidityRatio); if (adjustedAmountForPurchase > tick.adjustedRemainingAmount) { adjustedAmountForPurchase = tick.adjustedRemainingAmount; } tick.adjustedRemainingAmount -= adjustedAmountForPurchase; tick.normalizedLoanedAmount += normalizedUsedAmountForPurchase; // emit event with tick updates uint128 unborrowedRatio = tick.adjustedRemainingAmount.wadDiv(tick.adjustedTotalAmount); emit TickBorrow( pool.parameters.POOL_HASH, rate, adjustedAmountForPurchase, normalizedUsedAmountForPurchase, tick.atlendisLiquidityRatio, unborrowedRatio ); pool.state.bondsIssuedQuantity += bondsIssuedQuantity; pool.state.normalizedAvailableDeposits -= normalizedUsedAmountForPurchase; } /** * @dev Computes how the position is split between deposit and bonds **/ function computeAmountRepartitionForTick( Types.Pool storage pool, uint128 rate, uint128 adjustedAmount, uint128 bondsIssuanceIndex ) public view returns (uint128 bondsQuantity, uint128 adjustedDepositedAmount) { Types.Tick storage tick = pool.ticks[rate]; if (bondsIssuanceIndex > pool.state.currentBondsIssuanceIndex) { return (0, adjustedAmount); } adjustedAmount = adjustedAmount.wadRayMul(pool.getBondIssuanceMultiplierForTick(rate, bondsIssuanceIndex)); uint128 adjustedAmountUsedForBondsIssuance; if (tick.adjustedTotalAmount > 0) { adjustedAmountUsedForBondsIssuance = adjustedAmount .wadMul(tick.adjustedTotalAmount - tick.adjustedRemainingAmount) .wadDiv(tick.adjustedTotalAmount + tick.adjustedWithdrawnAmount); } if (adjustedAmount >= adjustedAmountUsedForBondsIssuance) { if (tick.adjustedTotalAmount > tick.adjustedRemainingAmount) { bondsQuantity = tick.bondsQuantity.wadMul(adjustedAmountUsedForBondsIssuance).wadDiv( tick.adjustedTotalAmount - tick.adjustedRemainingAmount ); } adjustedDepositedAmount = (adjustedAmount - adjustedAmountUsedForBondsIssuance); } else { /** * This condition is obtained when precision problems occur in the computation of `adjustedAmountUsedForBondsIssuance`. * Such problems have been observed when dealing with amounts way lower than a WAD. * In this case, the remaining and withdrawn amounts are assumed at 0. * Therefore, the deposited amount is returned as 0 and the bonds quantity is computed using only the adjusted total amount. */ bondsQuantity = tick.bondsQuantity.wadMul(adjustedAmount).wadDiv(tick.adjustedTotalAmount); adjustedDepositedAmount = 0; } } /** * @dev Updates tick data after a withdrawal consisting of only amount deposited to yield provider **/ function withdrawDepositedAmountForTick( Types.Pool storage pool, uint128 rate, uint128 adjustedAmountToWithdraw, uint128 bondsIssuanceIndex ) public returns (uint128 normalizedAmountToWithdraw) { Types.Tick storage tick = pool.ticks[rate]; pool.collectFees(rate); if (bondsIssuanceIndex <= pool.state.currentBondsIssuanceIndex) { uint128 feesShareToWithdraw = pool.getAccruedFeesShare(rate, adjustedAmountToWithdraw); tick.accruedFees -= feesShareToWithdraw; tick.adjustedTotalAmount -= adjustedAmountToWithdraw; tick.adjustedRemainingAmount -= adjustedAmountToWithdraw; normalizedAmountToWithdraw = adjustedAmountToWithdraw.wadRayMul(tick.atlendisLiquidityRatio) + feesShareToWithdraw; pool.state.normalizedAvailableDeposits -= normalizedAmountToWithdraw.round(); // register withdrawn amount from partially matched positions // to maintain the proportion of bonds in each subsequent position the same if (tick.bondsQuantity > 0) { tick.adjustedWithdrawnAmount += adjustedAmountToWithdraw; } emit TickWithdrawRemaining( pool.parameters.POOL_HASH, rate, adjustedAmountToWithdraw, tick.atlendisLiquidityRatio, feesShareToWithdraw ); } else { tick.adjustedPendingAmount -= adjustedAmountToWithdraw; normalizedAmountToWithdraw = adjustedAmountToWithdraw.wadRayMul(tick.yieldProviderLiquidityRatio); emit TickWithdrawPending(pool.parameters.POOL_HASH, rate, adjustedAmountToWithdraw); } // update lowerInterestRate if necessary if ((rate == pool.state.lowerInterestRate) && tick.adjustedTotalAmount == 0) { uint128 nextRate = rate + pool.parameters.RATE_SPACING; while (nextRate <= pool.parameters.MAX_RATE && pool.ticks[nextRate].adjustedTotalAmount == 0) { nextRate += pool.parameters.RATE_SPACING; } if (nextRate >= pool.parameters.MAX_RATE) { pool.state.lowerInterestRate = 0; } else { pool.state.lowerInterestRate = nextRate; } } } /** * @dev Updates tick data after a repayment **/ function repayForTick( Types.Pool storage pool, uint128 rate, uint128 lateRepayFeePerBond ) public returns (uint128 normalizedRepayAmountForTick, uint128 lateRepayFeeForTick) { Types.Tick storage tick = pool.ticks[rate]; if (tick.bondsQuantity > 0) { normalizedRepayAmountForTick = getTimeValue(pool, tick.bondsQuantity, rate); lateRepayFeeForTick = lateRepayFeePerBond.wadMul(normalizedRepayAmountForTick); uint128 bondPaidInterests = normalizedRepayAmountForTick - tick.normalizedLoanedAmount; // update liquidity ratio with interests from bonds, yield provider and liquidity rewards tick.atlendisLiquidityRatio += (tick.accruedFees + bondPaidInterests + lateRepayFeeForTick) .wadDiv(tick.adjustedTotalAmount) .wadToRay(); // update tick amounts tick.bondsQuantity = 0; tick.adjustedWithdrawnAmount = 0; tick.normalizedLoanedAmount = 0; tick.accruedFees = 0; tick.adjustedRemainingAmount = tick.adjustedTotalAmount; emit TickRepay(pool.parameters.POOL_HASH, rate, tick.adjustedTotalAmount, tick.atlendisLiquidityRatio); } } /** * @dev Updates tick data after a repayment **/ function includePendingDepositsForTick( Types.Pool storage pool, uint128 rate, bool bondsIssuanceIndexAlreadyIncremented ) internal returns (bool pendingDepositsExist) { Types.Tick storage tick = pool.ticks[rate]; if (tick.adjustedPendingAmount > 0) { if (!bondsIssuanceIndexAlreadyIncremented) { pool.state.currentBondsIssuanceIndex += 1; } // include pending deposit amount into tick excluding them from bonds interest from current issuance tick.bondsIssuanceIndexMultiplier[pool.state.currentBondsIssuanceIndex] = pool .state .yieldProviderLiquidityRatio .rayDiv(tick.atlendisLiquidityRatio); uint128 adjustedPendingAmount = tick.adjustedPendingAmount.wadRayMul( tick.bondsIssuanceIndexMultiplier[pool.state.currentBondsIssuanceIndex] ); // update global pool state pool.state.normalizedAvailableDeposits += tick.adjustedPendingAmount.wadRayMul( pool.state.yieldProviderLiquidityRatio ); // update tick amounts tick.adjustedTotalAmount += adjustedPendingAmount; tick.adjustedRemainingAmount = tick.adjustedTotalAmount; tick.adjustedPendingAmount = 0; emit TickPendingDeposit( pool.parameters.POOL_HASH, rate, adjustedPendingAmount, !bondsIssuanceIndexAlreadyIncremented ); return true; } return false; } /** * @dev Top up liquidity rewards for later distribution **/ function topUpLiquidityRewards(Types.Pool storage pool, uint128 normalizedAmount) public returns (uint128 yieldProviderLiquidityRatio) { yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); pool.state.remainingAdjustedLiquidityRewardsReserve += normalizedAmount.wadRayDiv(yieldProviderLiquidityRatio); } /** * @dev Distributes remaining liquidity rewards reserve to lenders * Called in case of pool default **/ function distributeLiquidityRewards(Types.Pool storage pool) public returns (uint128 distributedLiquidityRewards) { uint128 currentInterestRate = pool.state.lowerInterestRate; uint128 yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); distributedLiquidityRewards = pool.state.remainingAdjustedLiquidityRewardsReserve.wadRayMul( yieldProviderLiquidityRatio ); pool.state.normalizedAvailableDeposits += distributedLiquidityRewards; pool.state.remainingAdjustedLiquidityRewardsReserve = 0; while (pool.ticks[currentInterestRate].bondsQuantity > 0 && currentInterestRate <= pool.parameters.MAX_RATE) { pool.ticks[currentInterestRate].accruedFees += distributedLiquidityRewards .wadMul(pool.ticks[currentInterestRate].bondsQuantity) .wadDiv(pool.state.bondsIssuedQuantity); currentInterestRate += pool.parameters.RATE_SPACING; } } /** * @dev Updates tick data to reflect all fees accrued since last call * Accrued fees are composed of the yield provider liquidity ratio increase * and liquidity rewards paid by the borrower **/ function collectFeesForTick( Types.Pool storage pool, uint128 rate, uint128 yieldProviderLiquidityRatio ) internal { Types.Tick storage tick = pool.ticks[rate]; if (tick.lastFeeDistributionTimestamp < block.timestamp) { ( uint128 updatedAtlendisLiquidityRatio, uint128 updatedAccruedFees, uint128 liquidityRewardsIncrease, uint128 yieldProviderLiquidityRatioIncrease ) = pool.peekFeesForTick(rate, yieldProviderLiquidityRatio); // update global deposited amount pool.state.remainingAdjustedLiquidityRewardsReserve -= liquidityRewardsIncrease.wadRayDiv( yieldProviderLiquidityRatio ); pool.state.normalizedAvailableDeposits += liquidityRewardsIncrease + tick.adjustedRemainingAmount.wadRayMul(yieldProviderLiquidityRatioIncrease); // update tick data uint128 accruedFeesIncrease = updatedAccruedFees - tick.accruedFees; if (tick.atlendisLiquidityRatio == 0) { tick.yieldProviderLiquidityRatio = yieldProviderLiquidityRatio; emit TickInitialized(pool.parameters.POOL_HASH, rate, yieldProviderLiquidityRatio); } tick.atlendisLiquidityRatio = updatedAtlendisLiquidityRatio; tick.accruedFees = updatedAccruedFees; // update checkpoint data tick.lastFeeDistributionTimestamp = uint128(block.timestamp); emit CollectFeesForTick( pool.parameters.POOL_HASH, rate, pool.state.remainingAdjustedLiquidityRewardsReserve.wadRayMul(yieldProviderLiquidityRatio), accruedFeesIncrease ); } } function collectFees(Types.Pool storage pool, uint128 rate) internal { uint128 yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); pool.collectFeesForTick(rate, yieldProviderLiquidityRatio); pool.ticks[rate].yieldProviderLiquidityRatio = yieldProviderLiquidityRatio; } function collectFees(Types.Pool storage pool) internal { uint128 yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); for ( uint128 currentInterestRate = pool.state.lowerInterestRate; currentInterestRate <= pool.parameters.MAX_RATE; currentInterestRate += pool.parameters.RATE_SPACING ) { pool.collectFeesForTick(currentInterestRate, yieldProviderLiquidityRatio); } pool.state.yieldProviderLiquidityRatio = yieldProviderLiquidityRatio; } /** * @dev Peek updated liquidity ratio and accrued fess for the target tick * Used to compute a position balance without updating storage **/ function peekFeesForTick( Types.Pool storage pool, uint128 rate, uint128 yieldProviderLiquidityRatio ) internal view returns ( uint128 updatedAtlendisLiquidityRatio, uint128 updatedAccruedFees, uint128 liquidityRewardsIncrease, uint128 yieldProviderLiquidityRatioIncrease ) { Types.Tick storage tick = pool.ticks[rate]; if (tick.atlendisLiquidityRatio == 0) { return (yieldProviderLiquidityRatio, 0, 0, 0); } updatedAtlendisLiquidityRatio = tick.atlendisLiquidityRatio; updatedAccruedFees = tick.accruedFees; uint128 referenceLiquidityRatio; if (pool.state.yieldProviderLiquidityRatio > tick.yieldProviderLiquidityRatio) { referenceLiquidityRatio = pool.state.yieldProviderLiquidityRatio; } else { referenceLiquidityRatio = tick.yieldProviderLiquidityRatio; } yieldProviderLiquidityRatioIncrease = yieldProviderLiquidityRatio - referenceLiquidityRatio; // get additional fees from liquidity rewards liquidityRewardsIncrease = pool.getLiquidityRewardsIncrease(rate); uint128 currentNormalizedRemainingLiquidityRewards = pool.state.remainingAdjustedLiquidityRewardsReserve.wadRayMul( yieldProviderLiquidityRatio ); if (liquidityRewardsIncrease > currentNormalizedRemainingLiquidityRewards) { liquidityRewardsIncrease = currentNormalizedRemainingLiquidityRewards; } // if no ongoing loan, all deposited amount gets the yield provider // and liquidity rewards so the global liquidity ratio is updated if (pool.state.currentMaturity == 0) { updatedAtlendisLiquidityRatio += yieldProviderLiquidityRatioIncrease; if (tick.adjustedRemainingAmount > 0) { updatedAtlendisLiquidityRatio += liquidityRewardsIncrease.wadToRay().wadDiv(tick.adjustedRemainingAmount); } } // if ongoing loan, accruing fees components are added, liquidity ratio will be updated at repay time else { updatedAccruedFees += tick.adjustedRemainingAmount.wadRayMul(yieldProviderLiquidityRatioIncrease) + liquidityRewardsIncrease; } } /** * @dev Computes liquidity rewards amount to be paid to lenders since last fee collection * Liquidity rewards are paid to the unborrowed amount, and distributed to all ticks depending * on their normalized amounts **/ function getLiquidityRewardsIncrease(Types.Pool storage pool, uint128 rate) internal view returns (uint128 liquidityRewardsIncrease) { Types.Tick storage tick = pool.ticks[rate]; if (pool.state.normalizedAvailableDeposits > 0) { liquidityRewardsIncrease = (pool.parameters.LIQUIDITY_REWARDS_DISTRIBUTION_RATE * (uint128(block.timestamp) - tick.lastFeeDistributionTimestamp)) .wadMul(pool.parameters.MAX_BORROWABLE_AMOUNT - pool.state.normalizedBorrowedAmount) .wadDiv(pool.parameters.MAX_BORROWABLE_AMOUNT) .wadMul(tick.adjustedRemainingAmount.wadRayMul(tick.atlendisLiquidityRatio)) .wadDiv(pool.state.normalizedAvailableDeposits); } } function getTickBondPrice(uint128 rate, uint128 loanDuration) internal pure returns (uint128 price) { price = uint128(WAD).wadDiv(uint128(WAD + (uint256(rate) * uint256(loanDuration)) / uint256(SECONDS_PER_YEAR))); } function depositToYieldProvider( Types.Pool storage pool, address from, uint128 normalizedAmount ) public { IERC20Upgradeable underlyingToken = IERC20Upgradeable(pool.parameters.UNDERLYING_TOKEN); uint128 scaledAmount = normalizedAmount.scaleFromWad(pool.parameters.TOKEN_DECIMALS); ILendingPool yieldProvider = pool.parameters.YIELD_PROVIDER; underlyingToken.safeIncreaseAllowance(address(yieldProvider), scaledAmount); underlyingToken.safeTransferFrom(from, address(this), scaledAmount); yieldProvider.deposit(pool.parameters.UNDERLYING_TOKEN, scaledAmount, address(this), 0); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; /** * @title Scaling library * @author Atlendis * @dev Scale an arbitrary number to or from WAD precision **/ library Scaling { uint256 internal constant WAD = 1e18; /** * @notice Scales an input amount to wad precision **/ function scaleToWad(uint128 a, uint256 precision) internal pure returns (uint128) { return uint128((uint256(a) * WAD) / 10**precision); } /** * @notice Scales an input amount from wad to target precision **/ function scaleFromWad(uint128 a, uint256 precision) internal pure returns (uint128) { return uint128((uint256(a) * 10**precision) / WAD); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "../extensions/AaveILendingPool.sol"; library Types { struct PositionDetails { uint128 adjustedBalance; uint128 rate; bytes32 poolHash; address underlyingToken; uint128 bondsIssuanceIndex; uint128 remainingBonds; uint128 bondsMaturity; uint128 creationTimestamp; } struct Tick { mapping(uint128 => uint128) bondsIssuanceIndexMultiplier; uint128 bondsQuantity; uint128 adjustedTotalAmount; uint128 adjustedRemainingAmount; uint128 adjustedWithdrawnAmount; uint128 adjustedPendingAmount; uint128 normalizedLoanedAmount; uint128 lastFeeDistributionTimestamp; uint128 atlendisLiquidityRatio; uint128 yieldProviderLiquidityRatio; uint128 accruedFees; } struct PoolParameters { bytes32 POOL_HASH; address UNDERLYING_TOKEN; uint8 TOKEN_DECIMALS; ILendingPool YIELD_PROVIDER; uint128 MIN_RATE; uint128 MAX_RATE; uint128 RATE_SPACING; uint128 MAX_BORROWABLE_AMOUNT; uint128 LOAN_DURATION; uint128 LIQUIDITY_REWARDS_DISTRIBUTION_RATE; uint128 COOLDOWN_PERIOD; uint128 REPAYMENT_PERIOD; uint128 LATE_REPAY_FEE_PER_BOND_RATE; uint128 ESTABLISHMENT_FEE_RATE; uint128 REPAYMENT_FEE_RATE; uint128 LIQUIDITY_REWARDS_ACTIVATION_THRESHOLD; bool EARLY_REPAY; } struct PoolState { bool active; bool defaulted; bool closed; uint128 currentMaturity; uint128 bondsIssuedQuantity; uint128 normalizedBorrowedAmount; uint128 normalizedAvailableDeposits; uint128 lowerInterestRate; uint128 nextLoanMinStart; uint128 remainingAdjustedLiquidityRewardsReserve; uint128 yieldProviderLiquidityRatio; uint128 currentBondsIssuanceIndex; uint128 defaultTimestamp; } struct Pool { PoolParameters parameters; PoolState state; mapping(uint256 => Tick) ticks; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "./WadRayMath.sol"; /** * @title Uint128WadRayMath library **/ library Uint128WadRayMath { using WadRayMath for uint256; /** * @dev Multiplies a wad to a ray, making back and forth conversions * @param a Wad * @param b Ray * @return The result of a*b, in wad **/ function wadRayMul(uint128 a, uint128 b) internal pure returns (uint128) { return uint128(uint256(a).wadToRay().rayMul(uint256(b)).rayToWad()); } /** * @dev Divides a wad to a ray, making back and forth conversions * @param a Wad * @param b Ray * @return The result of a/b, in wad **/ function wadRayDiv(uint128 a, uint128 b) internal pure returns (uint128) { return uint128(uint256(a).wadToRay().rayDiv(uint256(b)).rayToWad()); } /** * @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(uint128 a, uint128 b) internal pure returns (uint128) { return uint128(uint256(a).rayDiv(uint256(b))); } /** * @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(uint128 a, uint128 b) internal pure returns (uint128) { return uint128(uint256(a).wadMul(uint256(b))); } /** * @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(uint128 a, uint128 b) internal pure returns (uint128) { return uint128(uint256(a).wadDiv(uint256(b))); } /** * @dev Converts wad up to ray * @param a Wad * @return a converted in ray **/ function wadToRay(uint128 a) internal pure returns (uint128) { return uint128(uint256(a).wadToRay()); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import {PoolLogic} from "./lib/PoolLogic.sol"; import {Scaling} from "./lib/Scaling.sol"; import {Uint128WadRayMath} from "./lib/Uint128WadRayMath.sol"; import "./extensions/AaveILendingPool.sol"; import "./extensions/IERC20PartialDecimals.sol"; import "./lib/Errors.sol"; import "./lib/Roles.sol"; import "./lib/Types.sol"; import "./interfaces/IPoolsController.sol"; abstract contract PoolsController is AccessControlUpgradeable, PausableUpgradeable, IPoolsController { using PoolLogic for Types.Pool; using Scaling for uint128; using Uint128WadRayMath for uint128; // borrower address to pool hash mapping(address => bytes32) public borrowerAuthorizedPools; // interest rate pool mapping(bytes32 => Types.Pool) internal pools; // protocol fees per pool mapping(bytes32 => uint128) internal protocolFees; function _initialize() internal onlyInitializing { // both initializers below are called to comply with OpenZeppelin's // recommendations even if in practice they don't do anything __AccessControl_init(); __Pausable_init_unchained(); } // VIEW FUNCTIONS /** * @notice Returns the parameters of a pool * @param poolHash The identifier of the pool * @return underlyingToken Address of the underlying token of the pool * @return minRate Minimum rate of deposits accepted in the pool * @return maxRate Maximum rate of deposits accepted in the pool * @return rateSpacing Difference between two rates in the pool * @return maxBorrowableAmount Maximum amount of tokens that can be borrowed from the pool * @return loanDuration Duration of a loan in the pool * @return liquidityRewardsDistributionRate Rate at which liquidity rewards are distributed to lenders * @return cooldownPeriod Period after a loan during which a borrower cannot take another loan * @return repaymentPeriod Period after a loan end during which a borrower can repay without penalty * @return lateRepayFeePerBondRate Penalty a borrower has to pay when it repays late * @return liquidityRewardsActivationThreshold Minimum amount of liqudity rewards a borrower has to * deposit to active the pool **/ function getPoolParameters(bytes32 poolHash) external view override returns ( address underlyingToken, uint128 minRate, uint128 maxRate, uint128 rateSpacing, uint128 maxBorrowableAmount, uint128 loanDuration, uint128 liquidityRewardsDistributionRate, uint128 cooldownPeriod, uint128 repaymentPeriod, uint128 lateRepayFeePerBondRate, uint128 liquidityRewardsActivationThreshold ) { Types.PoolParameters storage poolParameters = pools[poolHash].parameters; return ( poolParameters.UNDERLYING_TOKEN, poolParameters.MIN_RATE, poolParameters.MAX_RATE, poolParameters.RATE_SPACING, poolParameters.MAX_BORROWABLE_AMOUNT, poolParameters.LOAN_DURATION, poolParameters.LIQUIDITY_REWARDS_DISTRIBUTION_RATE, poolParameters.COOLDOWN_PERIOD, poolParameters.REPAYMENT_PERIOD, poolParameters.LATE_REPAY_FEE_PER_BOND_RATE, poolParameters.LIQUIDITY_REWARDS_ACTIVATION_THRESHOLD ); } /** * @notice Returns the fee rates of a pool * @return establishmentFeeRate Amount of fees paid to the protocol at borrow time * @return repaymentFeeRate Amount of fees paid to the protocol at repay time **/ function getPoolFeeRates(bytes32 poolHash) external view override returns (uint128 establishmentFeeRate, uint128 repaymentFeeRate) { Types.PoolParameters storage poolParameters = pools[poolHash].parameters; return (poolParameters.ESTABLISHMENT_FEE_RATE, poolParameters.REPAYMENT_FEE_RATE); } /** * @notice Returns the state of a pool * @param poolHash The identifier of the pool * @return active Signals if a pool is active and ready to accept deposits * @return defaulted Signals if a pool was defaulted * @return closed Signals if a pool was closed * @return currentMaturity End timestamp of current loan * @return bondsIssuedQuantity Amount of bonds issued, to be repaid at maturity * @return normalizedBorrowedAmount Actual amount of tokens that were borrowed * @return normalizedAvailableDeposits Actual amount of tokens available to be borrowed * @return lowerInterestRate Minimum rate at which a deposit was made * @return nextLoanMinStart Cool down period, minimum timestamp after which a new loan can be taken * @return remainingAdjustedLiquidityRewardsReserve Remaining liquidity rewards to be distributed to lenders * @return yieldProviderLiquidityRatio Last recorded yield provider liquidity ratio * @return currentBondsIssuanceIndex Current borrow period identifier of the pool **/ function getPoolState(bytes32 poolHash) external view override returns ( bool active, bool defaulted, bool closed, uint128 currentMaturity, uint128 bondsIssuedQuantity, uint128 normalizedBorrowedAmount, uint128 normalizedAvailableDeposits, uint128 lowerInterestRate, uint128 nextLoanMinStart, uint128 remainingAdjustedLiquidityRewardsReserve, uint128 yieldProviderLiquidityRatio, uint128 currentBondsIssuanceIndex ) { Types.PoolState storage poolState = pools[poolHash].state; return ( poolState.active, poolState.defaulted, poolState.closed, poolState.currentMaturity, poolState.bondsIssuedQuantity, poolState.normalizedBorrowedAmount, poolState.normalizedAvailableDeposits, poolState.lowerInterestRate, poolState.nextLoanMinStart, poolState.remainingAdjustedLiquidityRewardsReserve, poolState.yieldProviderLiquidityRatio, poolState.currentBondsIssuanceIndex ); } /** * @notice Returns the state of a pool * @return earlyRepay Flag that signifies whether the early repay feature is activated or not **/ function isEarlyRepay(bytes32 poolHash) external view override returns (bool earlyRepay) { return pools[poolHash].parameters.EARLY_REPAY; } /** * @notice Returns the state of a pool * @return defaultTimestamp The timestamp at which the pool was defaulted **/ function getDefaultTimestamp(bytes32 poolHash) external view override returns (uint128 defaultTimestamp) { return pools[poolHash].state.defaultTimestamp; } // PROTOCOL MANAGEMENT function getProtocolFees(bytes32 poolHash) public view returns (uint128) { return protocolFees[poolHash].scaleFromWad(pools[poolHash].parameters.TOKEN_DECIMALS); } /** * @notice Withdraws protocol fees to a target address * @param poolHash The identifier of the pool * @param amount The amount of tokens claimed * @param to The address receiving the fees **/ function claimProtocolFees( bytes32 poolHash, uint128 amount, address to ) external override onlyRole(Roles.GOVERNANCE_ROLE) { uint128 normalizedAmount = amount.scaleToWad(pools[poolHash].parameters.TOKEN_DECIMALS); if (pools[poolHash].parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } if (normalizedAmount > protocolFees[poolHash]) { revert Errors.PC_NOT_ENOUGH_PROTOCOL_FEES(); } protocolFees[poolHash] -= normalizedAmount; pools[poolHash].parameters.YIELD_PROVIDER.withdraw(pools[poolHash].parameters.UNDERLYING_TOKEN, amount, to); emit ClaimProtocolFees(poolHash, normalizedAmount, to); } /** * @notice Stops all actions on all pools **/ function freezePool() external override onlyRole(Roles.GOVERNANCE_ROLE) { _pause(); } /** * @notice Cancel a freeze, makes actions available again on all pools **/ function unfreezePool() external override onlyRole(Roles.GOVERNANCE_ROLE) { _unpause(); } // BORROWER MANAGEMENT /** * @notice Creates a new pool * @param params The parameters of the new pool **/ function createNewPool(PoolCreationParams calldata params) external override onlyRole(Roles.GOVERNANCE_ROLE) { // run verifications on parameters value verifyPoolCreationParameters(params); // initialize pool state and parameters pools[params.poolHash].parameters = Types.PoolParameters({ POOL_HASH: params.poolHash, UNDERLYING_TOKEN: params.underlyingToken, TOKEN_DECIMALS: IERC20PartialDecimals(params.underlyingToken).decimals(), YIELD_PROVIDER: params.yieldProvider, MIN_RATE: params.minRate, MAX_RATE: params.maxRate, RATE_SPACING: params.rateSpacing, MAX_BORROWABLE_AMOUNT: params.maxBorrowableAmount, LOAN_DURATION: params.loanDuration, LIQUIDITY_REWARDS_DISTRIBUTION_RATE: params.distributionRate, COOLDOWN_PERIOD: params.cooldownPeriod, REPAYMENT_PERIOD: params.repaymentPeriod, LATE_REPAY_FEE_PER_BOND_RATE: params.lateRepayFeePerBondRate, ESTABLISHMENT_FEE_RATE: params.establishmentFeeRate, REPAYMENT_FEE_RATE: params.repaymentFeeRate, LIQUIDITY_REWARDS_ACTIVATION_THRESHOLD: params.liquidityRewardsActivationThreshold, EARLY_REPAY: params.earlyRepay }); pools[params.poolHash].state.yieldProviderLiquidityRatio = uint128( params.yieldProvider.getReserveNormalizedIncome(address(params.underlyingToken)) ); emit PoolCreated(params); if (pools[params.poolHash].parameters.LIQUIDITY_REWARDS_ACTIVATION_THRESHOLD == 0) { pools[params.poolHash].state.active = true; emit PoolActivated(pools[params.poolHash].parameters.POOL_HASH); } } /** * @notice Verifies that conditions to create a new pool are met * @param params The parameters of the new pool **/ function verifyPoolCreationParameters(PoolCreationParams calldata params) internal view { if ((params.maxRate - params.minRate) % params.rateSpacing != 0) { revert Errors.PC_RATE_SPACING_COMPLIANCE(); } if (params.poolHash == bytes32(0)) { revert Errors.PC_ZERO_POOL(); } if (pools[params.poolHash].parameters.POOL_HASH != bytes32(0)) { revert Errors.PC_POOL_ALREADY_SET_FOR_BORROWER(); } uint256 yieldProviderLiquidityRatio = params.yieldProvider.getReserveNormalizedIncome(params.underlyingToken); if (yieldProviderLiquidityRatio < PoolLogic.RAY) { revert Errors.PC_POOL_TOKEN_NOT_SUPPORTED(); } if (params.establishmentFeeRate > PoolLogic.WAD) { revert Errors.PC_ESTABLISHMENT_FEES_TOO_HIGH(); } } /** * @notice Allow an address to interact with a borrower pool * @param borrowerAddress The address to allow * @param poolHash The identifier of the pool **/ function allow(address borrowerAddress, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (poolHash == bytes32(0)) { revert Errors.PC_ZERO_POOL(); } if (borrowerAddress == address(0)) { revert Errors.PC_ZERO_ADDRESS(); } if (pools[poolHash].parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } if (borrowerAuthorizedPools[borrowerAddress] != bytes32(0)) { revert Errors.PC_BORROWER_ALREADY_AUTHORIZED(); } grantRole(Roles.BORROWER_ROLE, borrowerAddress); borrowerAuthorizedPools[borrowerAddress] = poolHash; emit BorrowerAllowed(borrowerAddress, poolHash); } /** * @notice Remove borrower pool interaction rights from an address * @param borrowerAddress The address to disallow * @param poolHash The identifier of the pool **/ function disallow(address borrowerAddress, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (poolHash == bytes32(0)) { revert Errors.PC_ZERO_POOL(); } if (borrowerAddress == address(0)) { revert Errors.PC_ZERO_ADDRESS(); } if (pools[poolHash].parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } if (borrowerAuthorizedPools[borrowerAddress] != poolHash) { revert Errors.PC_DISALLOW_UNMATCHED_BORROWER(); } revokeRole(Roles.BORROWER_ROLE, borrowerAddress); delete borrowerAuthorizedPools[borrowerAddress]; emit BorrowerDisallowed(borrowerAddress, poolHash); } /** * @notice Flags the pool as closed * @param poolHash The identifier of the pool **/ function closePool(bytes32 poolHash, address to) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (poolHash == bytes32(0)) { revert Errors.PC_ZERO_POOL(); } if (to == address(0)) { revert Errors.PC_ZERO_ADDRESS(); } Types.Pool storage pool = pools[poolHash]; if (pool.parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } if (pool.state.closed) { revert Errors.PC_POOL_ALREADY_CLOSED(); } pool.state.closed = true; uint128 remainingNormalizedLiquidityRewardsReserve = 0; if (pool.state.remainingAdjustedLiquidityRewardsReserve > 0) { uint128 yieldProviderLiquidityRatio = uint128( pool.parameters.YIELD_PROVIDER.getReserveNormalizedIncome(address(pool.parameters.UNDERLYING_TOKEN)) ); remainingNormalizedLiquidityRewardsReserve = pool.state.remainingAdjustedLiquidityRewardsReserve.wadRayMul( yieldProviderLiquidityRatio ); pool.state.remainingAdjustedLiquidityRewardsReserve = 0; pool.parameters.YIELD_PROVIDER.withdraw( pools[poolHash].parameters.UNDERLYING_TOKEN, remainingNormalizedLiquidityRewardsReserve.scaleFromWad(pool.parameters.TOKEN_DECIMALS), to ); } emit PoolClosed(poolHash, remainingNormalizedLiquidityRewardsReserve); } /** * @notice Flags the pool as defaulted * @param poolHash The identifier of the pool to default **/ function setDefault(bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { Types.Pool storage pool = pools[poolHash]; if (pool.state.defaulted) { revert Errors.PC_POOL_DEFAULTED(); } if (pool.state.currentMaturity == 0) { revert Errors.PC_NO_ONGOING_LOAN(); } if (block.timestamp < pool.state.currentMaturity + pool.parameters.REPAYMENT_PERIOD) { revert Errors.PC_REPAYMENT_PERIOD_ONGOING(); } pool.state.defaulted = true; pool.state.defaultTimestamp = uint128(block.timestamp); uint128 distributedLiquidityRewards = pool.distributeLiquidityRewards(); emit Default(poolHash, distributedLiquidityRewards); } // POOL PARAMETERS MANAGEMENT /** * @notice Set the maximum amount of tokens that can be borrowed in the target pool **/ function setMaxBorrowableAmount(uint128 maxBorrowableAmount, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (pools[poolHash].parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } pools[poolHash].parameters.MAX_BORROWABLE_AMOUNT = maxBorrowableAmount; emit SetMaxBorrowableAmount(maxBorrowableAmount, poolHash); } /** * @notice Set the pool liquidity rewards distribution rate **/ function setLiquidityRewardsDistributionRate(uint128 distributionRate, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (pools[poolHash].parameters.POOL_HASH != poolHash) { revert Errors.PC_POOL_NOT_ACTIVE(); } pools[poolHash].parameters.LIQUIDITY_REWARDS_DISTRIBUTION_RATE = distributionRate; emit SetLiquidityRewardsDistributionRate(distributionRate, poolHash); } /** * @notice Set the pool establishment protocol fee rate **/ function setEstablishmentFeeRate(uint128 establishmentFeeRate, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (!pools[poolHash].state.active) { revert Errors.PC_POOL_NOT_ACTIVE(); } if (establishmentFeeRate > PoolLogic.WAD) { revert Errors.PC_ESTABLISHMENT_FEES_TOO_HIGH(); } pools[poolHash].parameters.ESTABLISHMENT_FEE_RATE = establishmentFeeRate; emit SetEstablishmentFeeRate(establishmentFeeRate, poolHash); } /** * @notice Set the pool repayment protocol fee rate **/ function setRepaymentFeeRate(uint128 repaymentFeeRate, bytes32 poolHash) external override onlyRole(Roles.GOVERNANCE_ROLE) { if (!pools[poolHash].state.active) { revert Errors.PC_POOL_NOT_ACTIVE(); } pools[poolHash].parameters.REPAYMENT_FEE_RATE = repaymentFeeRate; emit SetRepaymentFeeRate(repaymentFeeRate, poolHash); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// 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 { } 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; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library StringsUpgradeable { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// 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 (last updated v4.5.0) (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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, 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: AGPL-3.0 pragma solidity ^0.8.0; /** * @title Rounding library * @author Atlendis * @dev Rounding utilities to mitigate precision loss when doing wad ray math operations **/ library Rounding { using Rounding for uint128; uint128 internal constant PRECISION = 1e3; /** * @notice rounds the input number with the default precision **/ function round(uint128 amount) internal pure returns (uint128) { return (amount / PRECISION) * PRECISION; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "./Errors.sol"; /** * @title WadRayMath library * @author Aave * @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 halfWAD = WAD / 2; uint256 internal constant RAY = 1e27; uint256 internal constant halfRAY = 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 halfRAY; } /** * @return Half ray, 1e18/2 **/ function halfWad() internal pure returns (uint256) { return halfWAD; } /** * @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; } if (a > (type(uint256).max - halfWAD) / b) { revert Errors.MATH_MULTIPLICATION_OVERFLOW(); } return (a * b + halfWAD) / 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) { if (b == 0) { revert Errors.MATH_DIVISION_BY_ZERO(); } uint256 halfB = b / 2; if (a > (type(uint256).max - halfB) / WAD) { revert 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; } if (a > (type(uint256).max - halfRAY) / b) { revert Errors.MATH_MULTIPLICATION_OVERFLOW(); } return (a * b + halfRAY) / 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) { if (b == 0) { revert Errors.MATH_DIVISION_BY_ZERO(); } uint256 halfB = b / 2; if (a > (type(uint256).max - halfB) / RAY) { revert 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; if (result < halfRatio) { revert 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; if (result / WAD_RAY_RATIO != a) { revert Errors.MATH_MULTIPLICATION_OVERFLOW(); } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; /** * @dev Partial interface for the optional metadata functions from the ERC20 standard. */ interface IERC20PartialDecimals { /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; library Roles { bytes32 public constant BORROWER_ROLE = keccak256("BORROWER_ROLE"); bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); bytes32 public constant POSITION_ROLE = keccak256("POSITION_ROLE"); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import "../lib/Types.sol"; /** * @title IPoolsController * @notice Management of the pools **/ interface IPoolsController { // EVENTS /** * @notice Emitted after a pool was creted **/ event PoolCreated(PoolCreationParams params); /** * @notice Emitted after a borrower address was allowed to borrow from a pool * @param borrowerAddress The address to allow * @param poolHash The identifier of the pool **/ event BorrowerAllowed(address borrowerAddress, bytes32 poolHash); /** * @notice Emitted after a borrower address was disallowed to borrow from a pool * @param borrowerAddress The address to disallow * @param poolHash The identifier of the pool **/ event BorrowerDisallowed(address borrowerAddress, bytes32 poolHash); /** * @notice Emitted when a pool is active, i.e. after the borrower deposits enough tokens * in its pool liquidity rewards reserve as agreed before the pool creation * @param poolHash The identifier of the pool **/ event PoolActivated(bytes32 poolHash); /** * @notice Emitted after pool is closed * @param poolHash The identifier of the pool * @param collectedLiquidityRewards The amount of liquidity rewards to have been collected at closing time **/ event PoolClosed(bytes32 poolHash, uint128 collectedLiquidityRewards); /** * @notice Emitted when a pool defaults on its loan repayment * @param poolHash The identifier of the pool * @param distributedLiquidityRewards The remaining liquidity rewards distributed to * bond holders **/ event Default(bytes32 poolHash, uint128 distributedLiquidityRewards); /** * @notice Emitted after governance sets the maximum borrowable amount for a pool **/ event SetMaxBorrowableAmount(uint128 maxTokenDeposit, bytes32 poolHash); /** * @notice Emitted after governance sets the liquidity rewards distribution rate for a pool **/ event SetLiquidityRewardsDistributionRate(uint128 distributionRate, bytes32 poolHash); /** * @notice Emitted after governance sets the establishment fee for a pool **/ event SetEstablishmentFeeRate(uint128 establishmentRate, bytes32 poolHash); /** * @notice Emitted after governance sets the repayment fee for a pool **/ event SetRepaymentFeeRate(uint128 repaymentFeeRate, bytes32 poolHash); /** * @notice Emitted after governance claims the fees associated with a pool * @param poolHash The identifier of the pool * @param normalizedAmount The amount of tokens claimed * @param to The address receiving the fees **/ event ClaimProtocolFees(bytes32 poolHash, uint128 normalizedAmount, address to); // VIEW METHODS /** * @notice Returns the parameters of a pool * @param poolHash The identifier of the pool * @return underlyingToken Address of the underlying token of the pool * @return minRate Minimum rate of deposits accepted in the pool * @return maxRate Maximum rate of deposits accepted in the pool * @return rateSpacing Difference between two rates in the pool * @return maxBorrowableAmount Maximum amount of tokens that can be borrowed from the pool * @return loanDuration Duration of a loan in the pool * @return liquidityRewardsDistributionRate Rate at which liquidity rewards are distributed to lenders * @return cooldownPeriod Period after a loan during which a borrower cannot take another loan * @return repaymentPeriod Period after a loan end during which a borrower can repay without penalty * @return lateRepayFeePerBondRate Penalty a borrower has to pay when it repays late * @return liquidityRewardsActivationThreshold Minimum amount of liqudity rewards a borrower has to * deposit to active the pool **/ function getPoolParameters(bytes32 poolHash) external view returns ( address underlyingToken, uint128 minRate, uint128 maxRate, uint128 rateSpacing, uint128 maxBorrowableAmount, uint128 loanDuration, uint128 liquidityRewardsDistributionRate, uint128 cooldownPeriod, uint128 repaymentPeriod, uint128 lateRepayFeePerBondRate, uint128 liquidityRewardsActivationThreshold ); /** * @notice Returns the fee rates of a pool * @return establishmentFeeRate Amount of fees paid to the protocol at borrow time * @return repaymentFeeRate Amount of fees paid to the protocol at repay time **/ function getPoolFeeRates(bytes32 poolHash) external view returns (uint128 establishmentFeeRate, uint128 repaymentFeeRate); /** * @notice Returns the state of a pool * @param poolHash The identifier of the pool * @return active Signals if a pool is active and ready to accept deposits * @return defaulted Signals if a pool was defaulted * @return closed Signals if a pool was closed * @return currentMaturity End timestamp of current loan * @return bondsIssuedQuantity Amount of bonds issued, to be repaid at maturity * @return normalizedBorrowedAmount Actual amount of tokens that were borrowed * @return normalizedAvailableDeposits Actual amount of tokens available to be borrowed * @return lowerInterestRate Minimum rate at which a deposit was made * @return nextLoanMinStart Cool down period, minimum timestamp after which a new loan can be taken * @return remainingAdjustedLiquidityRewardsReserve Remaining liquidity rewards to be distributed to lenders * @return yieldProviderLiquidityRatio Last recorded yield provider liquidity ratio * @return currentBondsIssuanceIndex Current borrow period identifier of the pool **/ function getPoolState(bytes32 poolHash) external view returns ( bool active, bool defaulted, bool closed, uint128 currentMaturity, uint128 bondsIssuedQuantity, uint128 normalizedBorrowedAmount, uint128 normalizedAvailableDeposits, uint128 lowerInterestRate, uint128 nextLoanMinStart, uint128 remainingAdjustedLiquidityRewardsReserve, uint128 yieldProviderLiquidityRatio, uint128 currentBondsIssuanceIndex ); /** * @notice Signals whether the early repay feature is activated or not * @return earlyRepay Flag that signifies whether the early repay feature is activated or not **/ function isEarlyRepay(bytes32 poolHash) external view returns (bool earlyRepay); /** * @notice Returns the state of a pool * @return defaultTimestamp The timestamp at which the pool was defaulted **/ function getDefaultTimestamp(bytes32 poolHash) external view returns (uint128 defaultTimestamp); // GOVERNANCE METHODS /** * @notice Parameters used for a pool creation * @param poolHash The identifier of the pool * @param underlyingToken Address of the pool underlying token * @param yieldProvider Yield provider of the pool * @param minRate Minimum bidding rate for the pool * @param maxRate Maximum bidding rate for the pool * @param rateSpacing Difference between two tick rates in the pool * @param maxBorrowableAmount Maximum amount of tokens a borrower can get from a pool * @param loanDuration Duration of a loan i.e. maturity of the issued bonds * @param distributionRate Rate at which the liquidity rewards are distributed to unmatched positions * @param cooldownPeriod Period of time after a repay during which the borrow cannot take a loan * @param repaymentPeriod Period after the end of a loan during which the borrower can repay without penalty * @param lateRepayFeePerBondRate Additional fees applied when a borrower repays its loan after the repayment period ends * @param establishmentFeeRate Fees paid to Atlendis at borrow time * @param repaymentFeeRate Fees paid to Atlendis at repay time * @param liquidityRewardsActivationThreshold Amount of tokens the borrower has to lock into the liquidity * @param earlyRepay Is early repay activated * rewards reserve to activate the pool **/ struct PoolCreationParams { bytes32 poolHash; address underlyingToken; ILendingPool yieldProvider; uint128 minRate; uint128 maxRate; uint128 rateSpacing; uint128 maxBorrowableAmount; uint128 loanDuration; uint128 distributionRate; uint128 cooldownPeriod; uint128 repaymentPeriod; uint128 lateRepayFeePerBondRate; uint128 establishmentFeeRate; uint128 repaymentFeeRate; uint128 liquidityRewardsActivationThreshold; bool earlyRepay; } /** * @notice Creates a new pool * @param params A struct defining the pool creation parameters **/ function createNewPool(PoolCreationParams calldata params) external; /** * @notice Allow an address to interact with a borrower pool * @param borrowerAddress The address to allow * @param poolHash The identifier of the pool **/ function allow(address borrowerAddress, bytes32 poolHash) external; /** * @notice Remove pool interaction rights from an address * @param borrowerAddress The address to disallow * @param poolHash The identifier of the borrower pool **/ function disallow(address borrowerAddress, bytes32 poolHash) external; /** * @notice Flags the pool as closed * @param poolHash The identifier of the pool to be closed * @param to An address to which the remaining liquidity rewards will be sent **/ function closePool(bytes32 poolHash, address to) external; /** * @notice Flags the pool as defaulted * @param poolHash The identifier of the pool to default **/ function setDefault(bytes32 poolHash) external; /** * @notice Set the maximum amount of tokens that can be borrowed in the target pool **/ function setMaxBorrowableAmount(uint128 maxTokenDeposit, bytes32 poolHash) external; /** * @notice Set the pool liquidity rewards distribution rate **/ function setLiquidityRewardsDistributionRate(uint128 distributionRate, bytes32 poolHash) external; /** * @notice Set the pool establishment protocol fee rate **/ function setEstablishmentFeeRate(uint128 establishmentFeeRate, bytes32 poolHash) external; /** * @notice Set the pool repayment protocol fee rate **/ function setRepaymentFeeRate(uint128 repaymentFeeRate, bytes32 poolHash) external; /** * @notice Withdraws protocol fees to a target address * @param poolHash The identifier of the pool * @param normalizedAmount The amount of tokens claimed * @param to The address receiving the fees **/ function claimProtocolFees( bytes32 poolHash, uint128 normalizedAmount, address to ) external; /** * @notice Stops all actions on all pools **/ function freezePool() external; /** * @notice Cancel a freeze, makes actions available again on all pools **/ function unfreezePool() external; }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "istanbul", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "src/lib/PoolLogic.sol": { "PoolLogic": "0xd2b8552104cf999e0eead79800f0ff08cd4ed5fd" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"BP_BOND_ISSUANCE_ID_TOO_HIGH","type":"error"},{"inputs":[],"name":"BP_BORROW_COOLDOWN_PERIOD_NOT_OVER","type":"error"},{"inputs":[],"name":"BP_BORROW_MAX_BORROWABLE_AMOUNT_EXCEEDED","type":"error"},{"inputs":[],"name":"BP_BORROW_OUT_OF_BOUND_AMOUNT","type":"error"},{"inputs":[],"name":"BP_BORROW_UNSUFFICIENT_BORROWABLE_AMOUNT_WITHIN_BRACKETS","type":"error"},{"inputs":[],"name":"BP_EARLY_REPAY_NOT_ACTIVATED","type":"error"},{"inputs":[],"name":"BP_LOAN_ONGOING","type":"error"},{"inputs":[],"name":"BP_MULTIPLE_BORROW_AFTER_MATURITY","type":"error"},{"inputs":[],"name":"BP_NO_DEPOSIT_TO_WITHDRAW","type":"error"},{"inputs":[],"name":"BP_OUT_OF_BOUND_MAX_RATE","type":"error"},{"inputs":[],"name":"BP_OUT_OF_BOUND_MIN_RATE","type":"error"},{"inputs":[],"name":"BP_POOL_CLOSED","type":"error"},{"inputs":[],"name":"BP_POOL_DEFAULTED","type":"error"},{"inputs":[],"name":"BP_POOL_NOT_ACTIVE","type":"error"},{"inputs":[],"name":"BP_RATE_SPACING","type":"error"},{"inputs":[],"name":"BP_REPAY_NO_ACTIVE_LOAN","type":"error"},{"inputs":[],"name":"BP_TARGET_BOND_ISSUANCE_INDEX_EMPTY","type":"error"},{"inputs":[],"name":"BP_UNMATCHED_TOKEN","type":"error"},{"inputs":[],"name":"MATH_ADDITION_OVERFLOW","type":"error"},{"inputs":[],"name":"MATH_DIVISION_BY_ZERO","type":"error"},{"inputs":[],"name":"MATH_MULTIPLICATION_OVERFLOW","type":"error"},{"inputs":[],"name":"PC_BORROWER_ALREADY_AUTHORIZED","type":"error"},{"inputs":[],"name":"PC_DISALLOW_UNMATCHED_BORROWER","type":"error"},{"inputs":[],"name":"PC_ESTABLISHMENT_FEES_TOO_HIGH","type":"error"},{"inputs":[],"name":"PC_NOT_ENOUGH_PROTOCOL_FEES","type":"error"},{"inputs":[],"name":"PC_NO_ONGOING_LOAN","type":"error"},{"inputs":[],"name":"PC_POOL_ALREADY_CLOSED","type":"error"},{"inputs":[],"name":"PC_POOL_ALREADY_SET_FOR_BORROWER","type":"error"},{"inputs":[],"name":"PC_POOL_DEFAULTED","type":"error"},{"inputs":[],"name":"PC_POOL_NOT_ACTIVE","type":"error"},{"inputs":[],"name":"PC_POOL_TOKEN_NOT_SUPPORTED","type":"error"},{"inputs":[],"name":"PC_RATE_SPACING_COMPLIANCE","type":"error"},{"inputs":[],"name":"PC_REPAYMENT_PERIOD_ONGOING","type":"error"},{"inputs":[],"name":"PC_ZERO_ADDRESS","type":"error"},{"inputs":[],"name":"PC_ZERO_POOL","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedBorrowedAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"establishmentFees","type":"uint128"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrowerAddress","type":"address"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"BorrowerAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrowerAddress","type":"address"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"BorrowerDisallowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedAmount","type":"uint128"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"ClaimProtocolFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"remainingLiquidityRewards","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"addedAccruedFees","type":"uint128"}],"name":"CollectFeesForTick","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"distributedLiquidityRewards","type":"uint128"}],"name":"Default","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedRepayAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"repaymentFee","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"normalizedDepositsAfterRepay","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"nextLoanMinStart","type":"uint128"}],"name":"EarlyRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedBorrowedAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"establishmentFees","type":"uint128"}],"name":"FurtherBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedRepayAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"lateRepayFee","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"repaymentFee","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"normalizedDepositsAfterRepay","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"nextLoanMinStart","type":"uint128"}],"name":"LateRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"PoolActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"collectedLiquidityRewards","type":"uint128"}],"name":"PoolClosed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"contract ILendingPool","name":"yieldProvider","type":"address"},{"internalType":"uint128","name":"minRate","type":"uint128"},{"internalType":"uint128","name":"maxRate","type":"uint128"},{"internalType":"uint128","name":"rateSpacing","type":"uint128"},{"internalType":"uint128","name":"maxBorrowableAmount","type":"uint128"},{"internalType":"uint128","name":"loanDuration","type":"uint128"},{"internalType":"uint128","name":"distributionRate","type":"uint128"},{"internalType":"uint128","name":"cooldownPeriod","type":"uint128"},{"internalType":"uint128","name":"repaymentPeriod","type":"uint128"},{"internalType":"uint128","name":"lateRepayFeePerBondRate","type":"uint128"},{"internalType":"uint128","name":"establishmentFeeRate","type":"uint128"},{"internalType":"uint128","name":"repaymentFeeRate","type":"uint128"},{"internalType":"uint128","name":"liquidityRewardsActivationThreshold","type":"uint128"},{"internalType":"bool","name":"earlyRepay","type":"bool"}],"indexed":false,"internalType":"struct IPoolsController.PoolCreationParams","name":"params","type":"tuple"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedRepayAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"repaymentFee","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"normalizedDepositsAfterRepay","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"nextLoanMinStart","type":"uint128"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"establishmentRate","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"SetEstablishmentFeeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"distributionRate","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"SetLiquidityRewardsDistributionRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"maxTokenDeposit","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"SetMaxBorrowableAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"repaymentFeeRate","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"SetRepaymentFeeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedRemainingAmountReduction","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"loanedAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"unborrowedRatio","type":"uint128"}],"name":"TickBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"}],"name":"TickInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedPendingDeposit","type":"uint128"}],"name":"TickLoanDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedAvailableDeposit","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"}],"name":"TickNoLoanDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedPendingAmount","type":"uint128"},{"indexed":false,"internalType":"bool","name":"poolBondIssuanceIndexIncremented","type":"bool"}],"name":"TickPendingDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedRemainingAmount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"}],"name":"TickRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedAmountToWithdraw","type":"uint128"}],"name":"TickWithdrawPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"rate","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"adjustedAmountToWithdraw","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"accruedFeesToWithdraw","type":"uint128"}],"name":"TickWithdrawRemaining","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"normalizedAmount","type":"uint128"}],"name":"TopUpLiquidityRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrowerAddress","type":"address"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"allow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint128","name":"loanAmount","type":"uint128"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowerAuthorizedPools","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"to","type":"address"}],"name":"claimProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"address","name":"to","type":"address"}],"name":"closePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"collectFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"}],"name":"collectFeesForTick","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"contract ILendingPool","name":"yieldProvider","type":"address"},{"internalType":"uint128","name":"minRate","type":"uint128"},{"internalType":"uint128","name":"maxRate","type":"uint128"},{"internalType":"uint128","name":"rateSpacing","type":"uint128"},{"internalType":"uint128","name":"maxBorrowableAmount","type":"uint128"},{"internalType":"uint128","name":"loanDuration","type":"uint128"},{"internalType":"uint128","name":"distributionRate","type":"uint128"},{"internalType":"uint128","name":"cooldownPeriod","type":"uint128"},{"internalType":"uint128","name":"repaymentPeriod","type":"uint128"},{"internalType":"uint128","name":"lateRepayFeePerBondRate","type":"uint128"},{"internalType":"uint128","name":"establishmentFeeRate","type":"uint128"},{"internalType":"uint128","name":"repaymentFeeRate","type":"uint128"},{"internalType":"uint128","name":"liquidityRewardsActivationThreshold","type":"uint128"},{"internalType":"bool","name":"earlyRepay","type":"bool"}],"internalType":"struct IPoolsController.PoolCreationParams","name":"params","type":"tuple"}],"name":"createNewPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"rate","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint128","name":"normalizedAmount","type":"uint128"}],"name":"deposit","outputs":[{"internalType":"uint128","name":"adjustedAmount","type":"uint128"},{"internalType":"uint128","name":"bondsIssuanceIndex","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrowerAddress","type":"address"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"disallow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"normalizedBorrowedAmount","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"estimateLoanRate","outputs":[{"internalType":"uint128","name":"estimatedRate","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freezePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"},{"internalType":"uint128","name":"adjustedAmount","type":"uint128"},{"internalType":"uint128","name":"bondsIssuanceIndex","type":"uint128"}],"name":"getAmountRepartition","outputs":[{"internalType":"uint128","name":"bondsQuantity","type":"uint128"},{"internalType":"uint128","name":"normalizedDepositedAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getDefaultTimestamp","outputs":[{"internalType":"uint128","name":"defaultTimestamp","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getPoolAggregates","outputs":[{"internalType":"uint128","name":"weightedAverageLendingRate","type":"uint128"},{"internalType":"uint128","name":"adjustedPendingDeposits","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getPoolFeeRates","outputs":[{"internalType":"uint128","name":"establishmentFeeRate","type":"uint128"},{"internalType":"uint128","name":"repaymentFeeRate","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getPoolMaturity","outputs":[{"internalType":"uint128","name":"poolCurrentMaturity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getPoolParameters","outputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint128","name":"minRate","type":"uint128"},{"internalType":"uint128","name":"maxRate","type":"uint128"},{"internalType":"uint128","name":"rateSpacing","type":"uint128"},{"internalType":"uint128","name":"maxBorrowableAmount","type":"uint128"},{"internalType":"uint128","name":"loanDuration","type":"uint128"},{"internalType":"uint128","name":"liquidityRewardsDistributionRate","type":"uint128"},{"internalType":"uint128","name":"cooldownPeriod","type":"uint128"},{"internalType":"uint128","name":"repaymentPeriod","type":"uint128"},{"internalType":"uint128","name":"lateRepayFeePerBondRate","type":"uint128"},{"internalType":"uint128","name":"liquidityRewardsActivationThreshold","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getPoolState","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"defaulted","type":"bool"},{"internalType":"bool","name":"closed","type":"bool"},{"internalType":"uint128","name":"currentMaturity","type":"uint128"},{"internalType":"uint128","name":"bondsIssuedQuantity","type":"uint128"},{"internalType":"uint128","name":"normalizedBorrowedAmount","type":"uint128"},{"internalType":"uint128","name":"normalizedAvailableDeposits","type":"uint128"},{"internalType":"uint128","name":"lowerInterestRate","type":"uint128"},{"internalType":"uint128","name":"nextLoanMinStart","type":"uint128"},{"internalType":"uint128","name":"remainingAdjustedLiquidityRewardsReserve","type":"uint128"},{"internalType":"uint128","name":"yieldProviderLiquidityRatio","type":"uint128"},{"internalType":"uint128","name":"currentBondsIssuanceIndex","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"getProtocolFees","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"bool","name":"earlyRepay","type":"bool"}],"name":"getRepayAmounts","outputs":[{"internalType":"uint128","name":"normalizedRepayAmount","type":"uint128"},{"internalType":"uint128","name":"lateRepayFee","type":"uint128"},{"internalType":"uint128","name":"repaymentFees","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"}],"name":"getTickAmounts","outputs":[{"internalType":"uint128","name":"adjustedTotalAmount","type":"uint128"},{"internalType":"uint128","name":"adjustedRemainingAmount","type":"uint128"},{"internalType":"uint128","name":"bondsQuantity","type":"uint128"},{"internalType":"uint128","name":"adjustedPendingAmount","type":"uint128"},{"internalType":"uint128","name":"atlendisLiquidityRatio","type":"uint128"},{"internalType":"uint128","name":"accruedFees","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"pool","type":"string"},{"internalType":"uint128","name":"rate","type":"uint128"}],"name":"getTickLastUpdate","outputs":[{"internalType":"uint128","name":"lastFeeDistributionTimestamp","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"}],"name":"getTickLiquidityRatio","outputs":[{"internalType":"uint128","name":"liquidityRatio","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"},{"internalType":"uint128","name":"adjustedAmount","type":"uint128"},{"internalType":"uint128","name":"bondsIssuanceIndex","type":"uint128"}],"name":"getWithdrawAmounts","outputs":[{"internalType":"uint128","name":"adjustedAmountToWithdraw","type":"uint128"},{"internalType":"uint128","name":"depositedAmountToWithdraw","type":"uint128"},{"internalType":"uint128","name":"remainingBondsQuantity","type":"uint128"},{"internalType":"uint128","name":"bondsMaturity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"governance","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"isEarlyRepay","outputs":[{"internalType":"bool","name":"earlyRepay","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"setDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"establishmentFeeRate","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"setEstablishmentFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"distributionRate","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"setLiquidityRewardsDistributionRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"maxBorrowableAmount","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"setMaxBorrowableAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"repaymentFeeRate","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"name":"setRepaymentFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"topUpLiquidityRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unfreezePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"adjustedAmount","type":"uint128"},{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"oldRate","type":"uint128"},{"internalType":"uint128","name":"newRate","type":"uint128"},{"internalType":"uint128","name":"oldBondsIssuanceIndex","type":"uint128"}],"name":"updateRate","outputs":[{"internalType":"uint128","name":"newAdjustedAmount","type":"uint128"},{"internalType":"uint128","name":"newBondsIssuanceIndex","type":"uint128"},{"internalType":"uint128","name":"normalizedAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"uint128","name":"rate","type":"uint128"},{"internalType":"uint128","name":"adjustedAmountToWithdraw","type":"uint128"},{"internalType":"uint128","name":"bondsIssuanceIndex","type":"uint128"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint128","name":"normalizedDepositedAmountToWithdraw","type":"uint128"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50615e6380620000216000396000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c8063a217fddf1161015c578063c6252dae116100ce578063dc3fbbbc11610087578063dc3fbbbc14610825578063ed156a8c14610838578063f3b0c7cc146108ef578063f3d1d26314610912578063f9ae48e014610925578063fe04d4c51461093857600080fd5b8063c6252dae146106a0578063c8954db9146106d3578063ce5be488146106e6578063d547741f146106f9578063d595408a1461070c578063db2de9521461081257600080fd5b8063b74e0b1311610120578063b74e0b1314610639578063c098dab81461064c578063c38262e31461065f578063c3d31fce14610672578063c4d66de81461067a578063c5ba771d1461068d57600080fd5b8063a217fddf146104f7578063a983dcc0146104ff578063aa5976c114610512578063aab19db414610613578063b4ce44e51461062657600080fd5b80635790966e116101f55780637ded35c5116101b95780637ded35c514610466578063817db73b146104a35780638f13efc4146104b657806391d14854146104c95780639611d5dd146104dc5780639f3a348e146104e457600080fd5b80635790966e146103b05780635c975abb146103c35780635e585644146103ce57806371075b79146103f457806373356df01461045357600080fd5b806336568abe1161024757806336568abe1461031857806339f735831461032b578063402d88831461035657806346d070121461035e5780634803779314610371578063518af6271461038457600080fd5b806301ffc9a714610284578063248a9ca3146102ac5780632f2ff15d146102dd57806330b12d05146102f2578063330ca03614610305575b600080fd5b61029761029236600461530f565b610958565b60405190151581526020015b60405180910390f35b6102cf6102ba366004615339565b60009081526065602052604090206001015490565b6040519081526020016102a3565b6102f06102eb366004615377565b61098f565b005b6102f06103003660046153c7565b6109ba565b6102f0610313366004615377565b610a72565b6102f0610326366004615377565b610d25565b61033e6103393660046153f3565b610da8565b6040516001600160801b0390911681526020016102a3565b6102f0610df5565b61033e61036c366004615418565b61143e565b6102f061037f36600461549b565b6114aa565b61033e610392366004615339565b600090815260ca60205260409020600f01546001600160801b031690565b6102f06103be3660046154b4565b6119f9565b60975460ff16610297565b6102976103dc366004615339565b600090815260ca602052604090206009015460ff1690565b610433610402366004615339565b600090815260ca6020526040902060078101546008909101546001600160801b03600160801b909204821692911690565b604080516001600160801b039384168152929091166020830152016102a3565b6102f06104613660046154d2565b611b26565b610479610474366004615509565b611d17565b604080516001600160801b03948516815292841660208401529216918101919091526060016102a3565b6102f06104b1366004615339565b611fef565b6102f06104c43660046153c7565b612029565b6102976104d7366004615377565b6120d4565b6102f06120ff565b61033e6104f2366004615339565b612123565b6102cf600081565b6102f061050d366004615339565b61215f565b6105a2610520366004615339565b600090815260ca602052604090206001810154600382015460048301546005840154600685015460078601546008909601546001600160a01b03909516966001600160801b0380861697600160801b96879004821697828716979687900483169683871696819004841695848116959082900485169493841693919092041690565b604080516001600160a01b03909c168c526001600160801b039a8b1660208d0152988a16988b019890985295881660608a0152938716608089015291861660a0880152851660c0870152841660e08601528316610100850152821661012084015216610140820152610160016102a3565b6102f06106213660046154b4565b612339565b610433610634366004615339565b612465565b610433610647366004615571565b6125e9565b61047961065a3660046155d4565b61282b565b6102f061066d3660046153c7565b612a50565b6102f0612b31565b6102f0610688366004615600565b612b52565b6102f061069b3660046153f3565b612c94565b61033e6106ae366004615339565b600090815260ca60205260409020600a0154630100000090046001600160801b031690565b6102f06106e136600461561d565b612ccf565b61033e6106f43660046153c7565b612f3c565b6102f0610707366004615377565b6131e8565b61079f61071a366004615339565b600090815260ca60205260409020600a810154600b820154600c830154600d840154600e9094015460ff80851696610100860482169662010000870490921695630100000090046001600160801b039081169581811695600160801b918290048316958382169591839004841694848316949284900483169383831693920490911690565b604080519c15158d529a151560208d0152981515998b01999099526001600160801b0396871660608b015294861660808a015292851660a089015290841660c0880152831660e08701528216610100860152811661012085015291821661014084015216610160820152610180016102a3565b6102f061082036600461563a565b61320e565b61033e610833366004615668565b61384c565b6108ad6108463660046153f3565b600091825260ca602090815260408084206001600160801b0393841685526010019091529091206001810154600282015460038301546004840154600590940154600160801b80850487169793871696948516959285169492819004831693910490911690565b604080516001600160801b03978816815295871660208701529386169385019390935290841660608401528316608083015290911660a082015260c0016102a3565b6109026108fd366004615571565b613af9565b6040516102a394939291906156c2565b6104336109203660046156ed565b613c0c565b6102f06109333660046153c7565b613ee4565b6102cf610946366004615600565b60c96020526000908152604090205481565b60006001600160e01b03198216637965db0b60e01b148061098957506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000828152606560205260409020600101546109ab8133613f8f565b6109b58383613ff3565b505050565b600080516020615dce8339815191526109d38133613f8f565b600082815260ca60205260409020600a015460ff16610a0557604051630951899960e01b815260040160405180910390fd5b600082815260ca602090815260409182902060080180546001600160801b0319166001600160801b03871690811790915582519081529081018490527fe3ed7df90f4e708b201a97ff43820f24fa973238a9856a431e4c54ba31151bc991015b60405180910390a1505050565b600080516020615dce833981519152610a8b8133613f8f565b82610aa957604051635342e0a760e11b815260040160405180910390fd5b6001600160a01b038216610ad05760405163f15e745f60e01b815260040160405180910390fd5b600083815260ca6020526040902080548414610aff57604051630951899960e01b815260040160405180910390fd5b600a81015462010000900460ff1615610b2b5760405163235893eb60e21b815260040160405180910390fd5b600a8101805462ff0000191662010000179055600d8101546000906001600160801b03600160801b9091041615610cdc576002820154600183015460405163d15e005360e01b81526001600160a01b039182166004820152600092919091169063d15e00539060240160206040518083038186803b158015610bac57600080fd5b505afa158015610bc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be49190615737565b600d840154909150610c0690600160801b90046001600160801b031682614079565b600d840180546001600160801b039081169091556002850154600089815260ca60205260409020600190810154908701549395506001600160a01b03918216936369328dec939190921691610c689187169060ff600160a01b909104166140a8565b886040518463ffffffff1660e01b8152600401610c8793929190615750565b602060405180830381600087803b158015610ca157600080fd5b505af1158015610cb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd99190615737565b50505b604080518681526001600160801b03831660208201527fdb941e881e2a4a824775895cb9b4908cbc030460e7356912047a04e2c8f3281691015b60405180910390a15050505050565b6001600160a01b0381163314610d9a5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610da482826140db565b5050565b600082815260ca602090815260408083206001600160801b038086168552601090910190925290912060040154600160801b900416806109895750676765c793fa10079d601b1b92915050565b60975460ff1615610e185760405162461bcd60e51b8152600401610d919061577c565b600080516020615dee833981519152610e318133613f8f565b33600090815260c9602090815260408083205480845260ca909252909120600a810154610100900460ff1615610e7a5760405163056efc7b60e01b815260040160405180910390fd5b600a810154630100000090046001600160801b0316610eac57604051631e13931b60e21b815260040160405180910390fd5b600a8101544263010000009091046001600160801b031611808015610ed65750600982015460ff16155b15610ef457604051632b604eef60e01b815260040160405180910390fd5b610efd82614142565b604051631d398d8d60e21b81526004810183905260009081908190819073d2b8552104cf999e0eead79800f0ff08cd4ed5fd906374e636349060240160206040518083038186803b158015610f5157600080fd5b505af4158015610f65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8991906157a6565b600c870154909150600160801b90046001600160801b03165b60038701546001600160801b03600160801b9091048116908216116110c557604051634de6e91560e01b8152600481018890526001600160801b03808316602483015283166044820152600090819073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90634de6e91590606401604080518083038186803b15801561102757600080fd5b505af415801561103b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105f91906157c3565b909250905061106e8183615808565b6110789086615808565b94506110848188615808565b965060006110938a8589614252565b9050808061109e5750865b60048b01549097506110be93506001600160801b03169150839050615808565b9050610fa2565b50604051635ccb5f2f60e01b8152600481018790526001600160801b038316602482015260009073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90635ccb5f2f9060440160206040518083038186803b15801561112357600080fd5b505af4158015611137573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115b91906157a6565b90506111678184615808565b604051636839a26160e01b815290935073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90636839a261906111a5908a9033908890600401615833565b60006040518083038186803b1580156111bd57600080fd5b505af41580156111d1573d6000803e3d6000fd5b50505060068801546111ed91506001600160801b031642615808565b600d880180546001600160801b039283166001600160801b031991821617909155600b890180549091169055600089815260cb602052604081208054849391929161123a91859116615808565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508287600a0160020160008282829054906101000a90046001600160801b03166112879190615808565b82546101009290920a6001600160801b038181021990931691831602179091556006890154600a8a01546112cf9350600160801b909104821691630100000090910416615808565b6001600160801b031642111561134f57600c870154600d880154604080516001600160801b038088168252808a16602083015280861692820192909252928116606084015216608082015288907f0a8858cdd0551a157921eb1032d7f466feec7752fffc78f8b3c52bb435e331b09060a0015b60405180910390a261140f565b600a8701544263010000009091046001600160801b031611156113b957600c870154600d8801546040518a927fa2b36883c3fcb197b500ec4377e7899f16820387a05442f947c237578dd03f819261134292889287926001600160801b03918216929116906156c2565b600c870154600d8801546040518a927fcde954d9667cdba90232367e5f743ea631856cdec43e595635130c08e20ffa939261140692889287926001600160801b03918216929116906156c2565b60405180910390a25b505050600a840180546301000000600160981b0319169055505050600b0180546001600160801b031690555050565b60008060ca6000868660405160200161145892919061585b565b60408051601f19818403018152918152815160209283012083528282019390935290820160009081206001600160801b03808816835260109091019092529190912060040154169150505b9392505050565b600080516020615dce8339815191526114c38133613f8f565b6114cc82614496565b604051806102200160405280836000013581526020018360200160208101906114f59190615600565b6001600160a01b031681526020018360200160208101906115169190615600565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561154e57600080fd5b505afa158015611562573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611586919061588a565b60ff16815260200161159e6060850160408601615600565b6001600160a01b031681526020016115bc608085016060860161561d565b6001600160801b031681526020016115da60a085016080860161561d565b6001600160801b031681526020016115f860c0850160a0860161561d565b6001600160801b0316815260200161161660e0850160c0860161561d565b6001600160801b03168152602001611635610100850160e0860161561d565b6001600160801b031681526020016116556101208501610100860161561d565b6001600160801b031681526020016116756101408501610120860161561d565b6001600160801b031681526020016116956101608501610140860161561d565b6001600160801b031681526020016116b56101808501610160860161561d565b6001600160801b031681526020016116d56101a08501610180860161561d565b6001600160801b031681526020016116f56101c085016101a0860161561d565b6001600160801b031681526020016117156101e085016101c0860161561d565b6001600160801b0316815260200161173561020085016101e086016158ad565b151590528235600090815260ca602090815260409182902083518155908301516001820180548585015160ff16600160a01b026001600160a81b03199091166001600160a01b0393841617179055606080850151600284018054919093166001600160a01b03199190911617909155608084015160a08501516001600160801b03908116600160801b90810292821692909217600385015560c086015160e08701518216830290821617600485015561010086015161012087015182168302908216176005850155610140860151610160870151821683029082161760068501556101808601516101a0870151821683029082161760078501556101c08601516101e087015182169092029116176008830155610200909301516009909101805491151560ff19909216919091179055611873918401908401615600565b6001600160a01b031663d15e00536118916040850160208601615600565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b1580156118d057600080fd5b505afa1580156118e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119089190615737565b8235600090815260ca602052604090819020600e0180546001600160801b0319166001600160801b039390931692909217909155517fe71e8fe4301d57c752d83a095e456f17300a88ea18c7daef3b9e035a9ed1aa659061196a9084906158c8565b60405180910390a18135600090815260ca6020526040902060080154600160801b90046001600160801b0316610da4578135600090815260ca602052604090819020600a8101805460ff191660011790555490517f843e4390b5f579b124a883bb556fa80a421be9d5301b2e7267641cc23ea077e4916119ed9190815260200190565b60405180910390a15050565b600080516020615dce833981519152611a128133613f8f565b81611a3057604051635342e0a760e11b815260040160405180910390fd5b6001600160a01b038316611a575760405163f15e745f60e01b815260040160405180910390fd5b600082815260ca60205260409020548214611a8557604051630951899960e01b815260040160405180910390fd5b6001600160a01b038316600090815260c960205260409020548214611abd5760405163e2106cd360e01b815260040160405180910390fd5b611ad5600080516020615dee833981519152846131e8565b6001600160a01b038316600081815260c96020908152604080832092909255815192835282018490527fbca7a5cbff6e29c636e92cf5ee4128086f7e982204b0e420cb972819f12f88fe9101610a65565b600080516020615dce833981519152611b3f8133613f8f565b600084815260ca6020526040812060010154611b6f906001600160801b03861690600160a01b900460ff16614667565b600086815260ca60205260409020549091508514611ba057604051630951899960e01b815260040160405180910390fd5b600085815260cb60205260409020546001600160801b039081169082161115611bdc57604051639f747ffd60e01b815260040160405180910390fd5b600085815260cb602052604081208054839290611c039084906001600160801b0316615a5f565b82546001600160801b039182166101009390930a928302919092021990911617905550600085815260ca6020526040908190206002810154600191909101549151631a4ca37b60e21b81526001600160a01b03918216926369328dec92611c739291169088908890600401615750565b602060405180830381600087803b158015611c8d57600080fd5b505af1158015611ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc59190615737565b50604080518681526001600160801b03831660208201526001600160a01b038516918101919091527f150a2e161f87df4cc1b0c4345f9779d342fa2bcc2bf767efbb54005e811079f190606001610d16565b6000806000611d2860975460ff1690565b15611d455760405162461bcd60e51b8152600401610d919061577c565b600080516020615e0e833981519152611d5e8133613f8f565b600088815260ca60205260409020600a81015462010000900460ff1615611d985760405163df00603160e01b815260040160405180910390fd5b6000611da68a8a8d8a6125e9565b5090506001600160801b03811615611dd157604051630d290b7960e31b815260040160405180910390fd5b60038201546001600160801b039081169089161015611e035760405163fca6b5cd60e01b815260040160405180910390fd5b60038201546001600160801b03600160801b90910481169089161115611e3c576040516393c139e960e01b815260040160405180910390fd5b600482015460038301546001600160801b0391821691611e5d91168a615a5f565b611e679190615a9d565b6001600160801b031615611e8e5760405163270955a760e01b815260040160405180910390fd5b6000611eae611e9e848c8b61468f565b6001600160801b038e1690614079565b604051636ef48f5760e01b815290915073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90636ef48f5790611eee9086908e9086908e90600401615ac3565b60206040518083038186803b158015611f0657600080fd5b505af4158015611f1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f3e91906157a6565b604051631cd2ca1960e11b8152600481018590526001600160801b03808c1660248301528216604482015290955073d2b8552104cf999e0eead79800f0ff08cd4ed5fd906339a5943290606401604080518083038186803b158015611fa257600080fd5b505af4158015611fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fda91906157c3565b909d909c50949a509398505050505050505050565b60975460ff16156120125760405162461bcd60e51b8152600401610d919061577c565b600081815260ca60205260409020610da481614142565b600080516020615dce8339815191526120428133613f8f565b600082815260ca6020526040902054821461207057604051630951899960e01b815260040160405180910390fd5b600082815260ca602090815260409182902060040180546001600160801b03908116600160801b9188169182021790915582519081529081018490527f796282fae5ebb944d98f910eb76fbde5bc1dc06dd9da67632dc4561d96e7c30d9101610a65565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600080516020615dce8339815191526121188133613f8f565b6121206146da565b50565b600081815260ca602090815260408083206001015460cb909252822054610989916001600160801b0390911690600160a01b900460ff166140a8565b600080516020615dce8339815191526121788133613f8f565b600082815260ca60205260409020600a810154610100900460ff16156121b0576040516209157360e41b815260040160405180910390fd5b600a810154630100000090046001600160801b03166121e257604051633aa99c9b60e01b815260040160405180910390fd5b6006810154600a820154612210916001600160801b03600160801b9091048116916301000000900416615808565b6001600160801b031642101561223957604051632b797db960e11b815260040160405180910390fd5b600a8101805461010061ff0019909116179055600f810180546001600160801b031916426001600160801b0316179055604051631d8e67b960e31b81526004810182905260009073d2b8552104cf999e0eead79800f0ff08cd4ed5fd9063ec733dc89060240160206040518083038186803b1580156122b757600080fd5b505af41580156122cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ef91906157a6565b604080518681526001600160801b03831660208201529192507f4eb384de78f7c7a5dd1dc12268658939004561d9dc1572c852f6c0873cca67b2910160405180910390a150505050565b600080516020615dce8339815191526123528133613f8f565b8161237057604051635342e0a760e11b815260040160405180910390fd5b6001600160a01b0383166123975760405163f15e745f60e01b815260040160405180910390fd5b600082815260ca602052604090205482146123c557604051630951899960e01b815260040160405180910390fd5b6001600160a01b038316600090815260c96020526040902054156123fc5760405163a8b7bea160e01b815260040160405180910390fd5b612414600080516020615dee8339815191528461098f565b6001600160a01b038316600081815260c96020908152604091829020859055815192835282018490527f3a3825a3ceba48aadc12396e4232a6098dea62688b3cbc06d0f1f3ed26fbcc259101610a65565b600081815260ca60205260408120600a8101548291908190630100000090046001600160801b03166124b75760048201546124b090600160801b90046001600160801b031686612f3c565b93506125e2565b600381015460009081906001600160801b03165b600484015460038501546124f2916001600160801b0390811691600160801b900416615808565b6001600160801b0316816001600160801b0316146125c9576001600160801b03808216600090815260108701602052604090206003015461253c91600160801b909104168261476d565b6125469084615808565b6001600160801b03808316600090815260108801602052604090206003015491945061257a91600160801b90041683615808565b6001600160801b0380831660009081526010880160205260409020600301549193506125a7911687615808565b60048501549096506125c2906001600160801b031682615808565b90506124cb565b6125dc6001600160801b03841683614785565b96505050505b5050915091565b600084815260ca602052604080822060028101546001820154925163d15e005360e01b81526001600160a01b03938416600482015284938492169063d15e00539060240160206040518083038186803b15801561264557600080fd5b505afa158015612659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267d9190615737565b600e8301549091506001600160801b03600160801b909104811690861611156126c05760006126b56001600160801b03881683614079565b935093505050612822565b604051633d29c20960e01b815260009073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90633d29c209906127009086908c908c908c90600401615ac3565b604080518083038186803b15801561271757600080fd5b505af415801561272b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274f91906157c3565b9095509050600080612762858b8661479d565b5050604051632ca9e11360e11b8152919350915060009073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90635953c226906127a99089908f9089908890600401615ac3565b60206040518083038186803b1580156127c157600080fd5b505af41580156127d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f991906157a6565b90508061280f6001600160801b03861685614079565b6128199190615808565b96505050505050505b94509492505050565b600082815260ca6020526040808220905163839a9c2960e01b81526004810191909152821515602482015281908190819073d2b8552104cf999e0eead79800f0ff08cd4ed5fd9063839a9c299060440160206040518083038186803b15801561289357600080fd5b505af41580156128a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128cb91906157a6565b600087815260ca6020526040908190209051631d398d8d60e21b815291925061297b91839173d2b8552104cf999e0eead79800f0ff08cd4ed5fd916374e636349161291c9160040190815260200190565b60206040518083038186803b15801561293457600080fd5b505af4158015612948573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296c91906157a6565b6001600160801b03169061476d565b600087815260ca6020526040902090935073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90635ccb5f2f906129b28685615808565b6040516001600160e01b031960e085901b16815260048101929092526001600160801b0316602482015260440160206040518083038186803b1580156129f757600080fd5b505af4158015612a0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a2f91906157a6565b915082612a3c8383615808565b612a469190615808565b9350509250925092565b600080516020615dce833981519152612a698133613f8f565b600082815260ca60205260409020600a015460ff16612a9b57604051630951899960e01b815260040160405180910390fd5b670de0b6b3a7640000836001600160801b03161115612acd57604051633dedb6cb60e21b815260040160405180910390fd5b600082815260ca602090815260409182902060070180546001600160801b03908116600160801b9188169182021790915582519081529081018490527f738c64322e7db945ad7f301e0af4f8b02a5a5cb336140b950d2a7b4340569b7d9101610a65565b600080516020615dce833981519152612b4a8133613f8f565b612120614957565b600054610100900460ff16612b6d5760005460ff1615612b71565b303b155b612bd45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610d91565b600054610100900460ff16158015612bf6576000805461ffff19166101011790555b612bfe6149af565b6001600160a01b038216612c10573391505b612c1b600083613ff3565b612c33600080516020615dce83398151915283613ff3565b612c59600080516020615dee833981519152600080516020615dce8339815191526149e8565b612c7f600080516020615e0e833981519152600080516020615dce8339815191526149e8565b8015610da4576000805461ff00191690555050565b60975460ff1615612cb75760405162461bcd60e51b8152600401610d919061577c565b600082815260ca602052604090206109b58183614a33565b60975460ff1615612cf25760405162461bcd60e51b8152600401610d919061577c565b600080516020615dee833981519152612d0b8133613f8f565b33600090815260c96020908152604080832054835260ca90915281206001810154909190612d4d906001600160801b03861690600160a01b900460ff16614667565b604051636839a26160e01b815290915073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90636839a26190612d8b90859033908690600401615833565b60006040518083038186803b158015612da357600080fd5b505af4158015612db7573d6000803e3d6000fd5b5050604051631a727b1960e31b8152600481018590526001600160801b03841660248201526000925073d2b8552104cf999e0eead79800f0ff08cd4ed5fd915063d393d8c89060440160206040518083038186803b158015612e1857600080fd5b505af4158015612e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5091906157a6565b600a84015490915060ff16158015612e9957506008830154600d8401546001600160801b03600160801b92839004811692612e8d92041683614079565b6001600160801b031610155b15612eea57600a8301805460ff1916600117905582546040517f843e4390b5f579b124a883bb556fa80a421be9d5301b2e7267641cc23ea077e491612ee19190815260200190565b60405180910390a15b33600090815260c960209081526040918290205482519081526001600160801b038516918101919091527f4e4bba6240da8987fe01edc7c3e8498d355a5f0ad7eb7ff2995f3393d8c030a79101610d16565b600081815260ca60205260408120600a8101548190630100000090046001600160801b0316151580612f775750600a820154610100900460ff165b80612f8c5750600a82015462010000900460ff165b80612f9c5750600a82015460ff16155b15612fac57600092505050610989565b60048201546001600160801b03600160801b90910481169086161115612fe4576004820154600160801b90046001600160801b031694505b6002810154600182015460405163d15e005360e01b81526001600160a01b039182166004820152600092919091169063d15e00539060240160206040518083038186803b15801561303457600080fd5b505afa158015613048573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061306c9190615737565b60038401549091506001600160801b03168660005b600485015460038601546130a8916001600160801b0390811691600160801b900416615808565b6001600160801b0316836001600160801b0316146131955760006130cd87858761479d565b5050506001600160801b03808616600090815260108a016020526040812060020154929350916130fe911683614079565b9050836001600160801b0316816001600160801b0316101561314a576131248185615a5f565b93506131396001600160801b0382168661476d565b6131439084615808565b9250613174565b61315d6001600160801b0385168661476d565b6131679084615808565b9250600093505050613195565b5050600485015461318e906001600160801b031684615808565b9250613081565b816001600160801b0316896001600160801b031614156131be5760009650505050505050610989565b6131db6131cb838b615a5f565b6001600160801b03831690614785565b9998505050505050505050565b6000828152606560205260409020600101546132048133613f8f565b6109b583836140db565b60975460ff16156132315760405162461bcd60e51b8152600401610d919061577c565b600080516020615dee83398151915261324a8133613f8f565b33600090815260c9602090815260408083205480845260ca909252909120600a81015462010000900460ff16156132945760405163df00603160e01b815260040160405180910390fd5b600a810154610100900460ff16156132bf5760405163056efc7b60e01b815260040160405180910390fd5b600a810154630100000090046001600160801b0316158015906132f55750600a810154630100000090046001600160801b031642115b1561331357604051630fda546f60e41b815260040160405180910390fd5b6001810154600090613339906001600160801b03871690600160a01b900460ff16614667565b6007830154909150600090613361906001600160801b0380851691600160801b90041661476d565b9050600061336f8284615a5f565b6004850154600b8601549192506001600160801b03600160801b9182900481169261339e928792910416615808565b6001600160801b031611156133c65760405163aab4fe5160e01b815260040160405180910390fd5b600d8401546001600160801b03164210156133f45760405163af26604960e01b815260040160405180910390fd5b6133fd84614142565b600c8401546001600160801b03908116908416111561342f57604051634421566d60e11b815260040160405180910390fd5b6004840154600c850154849160009161345b916001600160801b0390811691600160801b900416615a5f565b90505b6000826001600160801b031611801561348e575060038601546001600160801b03600160801b9091048116908216105b156135f95760048601546134ab906001600160801b031682615808565b6001600160801b03808216600090815260108901602052604090206002015491925016156135f457604051634c5e028760e11b8152600481018790526001600160801b03808316602483015283166044820152600090819073d2b8552104cf999e0eead79800f0ff08cd4ed5fd906398bc050e90606401604080518083038186803b15801561353957600080fd5b505af415801561354d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061357191906157c3565b60405163ca8701f360e01b8152919350915073d2b8552104cf999e0eead79800f0ff08cd4ed5fd9063ca8701f3906135b3908b90879087908790600401615ac3565b60006040518083038186803b1580156135cb57600080fd5b505af41580156135df573d6000803e3d6000fd5b5050505080846135ef9190615a5f565b935050505b61345e565b6001600160801b0382161561362157604051630f0b666f60e01b815260040160405180910390fd5b600a860154630100000090046001600160801b03166136bc576005860154613652906001600160801b031642615aea565b600a870180546301000000600160981b03191663010000006001600160801b0393841602179055604080518583168152918616602083015288917f02936c9aa03a5ee03914509058d0ab4fc30b8f8cb97922e9f8885bf0651263c0910160405180910390a2613702565b604080516001600160801b0380861682528616602082015288917f2a5361599b6218cb63221765b277f7026a473b717f40193ee25deae176e748dc910160405180910390a25b600087815260cb6020526040812080548692906137299084906001600160801b0316615808565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508486600a0160010160108282829054906101000a90046001600160801b03166137769190615808565b82546101009290920a6001600160801b03818102199093169183160217909155600288015460018901546001600160a01b0391821693506369328dec92918116916137ce9190881690600160a01b900460ff166140a8565b8d6040518463ffffffff1660e01b81526004016137ed93929190615750565b602060405180830381600087803b15801561380757600080fd5b505af115801561381b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061383f9190615737565b5050505050505050505050565b600061385a60975460ff1690565b156138775760405162461bcd60e51b8152600401610d919061577c565b600080516020615e0e8339815191526138908133613f8f565b600087815260ca60205260409020600e8101546138be90600160801b90046001600160801b03166001615808565b6001600160801b0316856001600160801b031611156138f05760405163019894df60e21b815260040160405180910390fd5b600e8101546001600160801b03600160801b90910481169086161180158161393657506001600160801b0380891660009081526010840160205260409020600201541615155b80613967575080801561396757506001600160801b0380891660009081526010840160205260409020600301541615155b6139845760405163436ec62560e01b815260040160405180910390fd5b6000876001600160801b0316116139ae57604051636ba603b360e11b815260040160405180910390fd5b604051636ef48f5760e01b815273d2b8552104cf999e0eead79800f0ff08cd4ed5fd90636ef48f57906139eb9085908c908c908c90600401615ac3565b60206040518083038186803b158015613a0357600080fd5b505af4158015613a17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a3b91906157a6565b600283015460018401549195506001600160a01b03908116916369328dec91811690613a7b906001600160801b03891690600160a01b900460ff166140a8565b886040518463ffffffff1660e01b8152600401613a9a93929190615750565b602060405180830381600087803b158015613ab457600080fd5b505af1158015613ac8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aec9190615737565b5050505095945050505050565b600084815260ca60205260408120600a8101548291829182919060ff16613b335760405163c6d9b58160e01b815260040160405180910390fd5b604051633d29c20960e01b815273d2b8552104cf999e0eead79800f0ff08cd4ed5fd90633d29c20990613b709084908c908c908c90600401615ac3565b604080518083038186803b158015613b8757600080fd5b505af4158015613b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bbf91906157c3565b95509250613be1613bd1828a8961468f565b6001600160801b03871690614aff565b935080600a0160000160039054906101000a90046001600160801b0316915050945094509450949050565b600080613c1b60975460ff1690565b15613c385760405162461bcd60e51b8152600401610d919061577c565b600080516020615e0e833981519152613c518133613f8f565b600087815260ca60205260409020600a810154610100900460ff1615613c8a5760405163056efc7b60e01b815260040160405180910390fd5b600a81015460ff16613caf5760405163c6d9b58160e01b815260040160405180910390fd5b600a81015462010000900460ff1615613cdb5760405163df00603160e01b815260040160405180910390fd5b60018101546001600160a01b03888116911614613d0b5760405163623957d360e01b815260040160405180910390fd5b60038101546001600160801b03908116908a161015613d3d5760405163fca6b5cd60e01b815260040160405180910390fd5b60038101546001600160801b03600160801b9091048116908a161115613d76576040516393c139e960e01b815260040160405180910390fd5b600481015460038201546001600160801b0391821691613d9791168b615a5f565b613da19190615a9d565b6001600160801b031615613dc85760405163270955a760e01b815260040160405180910390fd5b604051631cd2ca1960e11b8152600481018290526001600160801b03808b166024830152861660448201526000945084935073d2b8552104cf999e0eead79800f0ff08cd4ed5fd906339a5943290606401604080518083038186803b158015613e3057600080fd5b505af4158015613e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e6891906157c3565b604051636839a26160e01b8152919550935073d2b8552104cf999e0eead79800f0ff08cd4ed5fd90636839a26190613ea89084908a908a90600401615833565b60006040518083038186803b158015613ec057600080fd5b505af4158015613ed4573d6000803e3d6000fd5b5050505050509550959350505050565b600080516020615dce833981519152613efd8133613f8f565b600082815260ca60205260409020548214613f2b57604051630951899960e01b815260040160405180910390fd5b600082815260ca602090815260409182902060050180546001600160801b03908116600160801b9188169182021790915582519081529081018490527f7016612eedf00917c664926ac3c77a4800fefe912e91632376418e8fc6e621619101610a65565b613f9982826120d4565b610da457613fb1816001600160a01b03166014614b29565b613fbc836020614b29565b604051602001613fcd929190615b2e565b60408051601f198184030181529082905262461bcd60e51b8252610d9191600401615ba3565b613ffd82826120d4565b610da45760008281526065602090815260408083206001600160a01b03851684529091529020805460ff191660011790556140353390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006114a36140a3836001600160801b031661409d866001600160801b0316614cc5565b90614d04565b614d95565b6000670de0b6b3a76400006140be83600a615cba565b6140d1906001600160801b038616615cc6565b6114a39190615ce5565b6140e582826120d4565b15610da45760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6002810154600182015460405163d15e005360e01b81526001600160a01b039182166004820152600092919091169063d15e00539060240160206040518083038186803b15801561419257600080fd5b505afa1580156141a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ca9190615737565b600c830154909150600160801b90046001600160801b03165b60038301546001600160801b03600160801b90910481169082161161422c5761420d838284614dee565b6004830154614225906001600160801b031682615808565b90506141e3565b50600e9190910180546001600160801b0319166001600160801b03909216919091179055565b6001600160801b0380831660009081526010850160205260408120600381015491929091161561448b57826142ce57600e85018054600191906010906142a9908490600160801b90046001600160801b0316615808565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b6004810154600e8601546142f6916001600160801b0391821691600160801b90910416615023565b600e86018054600160801b908190046001600160801b0390811660009081526020869052604080822080546001600160801b031916968416969096179095559254919091048116825291812054600384015491926143579281169116614079565b600e870154600384015491925061437a916001600160801b039081169116614079565b600c870180546000906143979084906001600160801b0316615808565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550808260010160108282829054906101000a90046001600160801b03166143e19190615808565b82546101009290920a6001600160801b038181021990931691831602179091556001840154600285018054600160801b90920483166001600160801b03199283161790556003850180549091169055875460408051918252888316602083015291841691810191909152851560608201527f6ed432acb9733bda7d5f090ab5f35a9a2e19974dba6ae2dd7078c84b92388f96915060800160405180910390a16001925050506114a3565b506000949350505050565b6144a660c0820160a0830161561d565b6144b6608083016060840161561d565b6144c660a084016080850161561d565b6144d09190615a5f565b6144da9190615a9d565b6001600160801b031615614501576040516334cc2fed60e01b815260040160405180910390fd5b803561452057604051635342e0a760e11b815260040160405180910390fd5b8035600090815260ca60205260409020541561454f57604051631953bac160e11b815260040160405180910390fd5b60006145616060830160408401615600565b6001600160a01b031663d15e005361457f6040850160208601615600565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b1580156145be57600080fd5b505afa1580156145d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145f69190615737565b9050676765c793fa10079d601b1b8110156146245760405163fee5a60360e01b815260040160405180910390fd5b670de0b6b3a764000061463f6101a08401610180850161561d565b6001600160801b03161115610da457604051633dedb6cb60e21b815260040160405180910390fd5b600061467482600a615cba565b6140d1670de0b6b3a76400006001600160801b038616615cc6565b6001600160801b03808316600090815260108501602090815260408083208585168452918290529091205490911690816146d257676765c793fa10079d601b1b91505b509392505050565b60975460ff166147235760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610d91565b6097805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60006114a36001600160801b0384811690841661503b565b60006114a36001600160801b038481169084166150b2565b6001600160801b0380831660009081526010850160205260408120600481015491928392839283929091600160801b9004166147e75785600080600094509450945094505061494e565b60048101546005820154600e8a01546001600160801b03600160801b938490048116985092820483169650600092918216911611156148345750600e8801546001600160801b0316614844565b5060058101546001600160801b03165b61484e8188615a5f565b925061485a8989615147565b600d8a015490945060009061487f90600160801b90046001600160801b031689614079565b9050806001600160801b0316856001600160801b0316111561489f578094505b600a8a0154630100000090046001600160801b0316614919576148c28488615808565b60028401549097506001600160801b031615614914576002830154614907906001600160801b03908116906148f890881661521a565b6001600160801b031690614785565b6149119088615808565b96505b61494a565b60028301548590614933906001600160801b031686614079565b61493d9190615808565b6149479087615808565b95505b5050505b93509350935093565b60975460ff161561497a5760405162461bcd60e51b8152600401610d919061577c565b6097805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586147503390565b600054610100900460ff166149d65760405162461bcd60e51b8152600401610d9190615cf9565b6149de61522e565b6149e6615255565b565b600082815260656020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6002820154600183015460405163d15e005360e01b81526001600160a01b039182166004820152600092919091169063d15e00539060240160206040518083038186803b158015614a8357600080fd5b505afa158015614a97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614abb9190615737565b9050614ac8838383614dee565b6001600160801b039182166000908152601090930160205260409092206005018054919092166001600160801b0319909116179055565b60006114a36140a3836001600160801b0316614b23866001600160801b0316614cc5565b90615288565b60606000614b38836002615cc6565b614b43906002615aea565b67ffffffffffffffff811115614b5b57614b5b615d44565b6040519080825280601f01601f191660200182016040528015614b85576020820181803683370190505b509050600360fc1b81600081518110614ba057614ba0615d5a565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614bcf57614bcf615d5a565b60200101906001600160f81b031916908160001a9053506000614bf3846002615cc6565b614bfe906001615aea565b90505b6001811115614c76576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614c3257614c32615d5a565b1a60f81b828281518110614c4857614c48615d5a565b60200101906001600160f81b031916908160001a90535060049490941c93614c6f81615d70565b9050614c01565b5083156114a35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d91565b600080614cd6633b9aca0084615cc6565b905082614ce7633b9aca0083615ce5565b146109895760405162a748d160e21b815260040160405180910390fd5b6000821580614d11575081155b15614d1e57506000610989565b81614d356002676765c793fa10079d601b1b615ce5565b614d4190600019615d87565b614d4b9190615ce5565b831115614d6a5760405162a748d160e21b815260040160405180910390fd5b676765c793fa10079d601b1b614d81600282615ce5565b614d8b8486615cc6565b6140d19190615aea565b600080614da76002633b9aca00615ce5565b90506000614db58483615aea565b905081811015614dd8576040516307af724160e11b815260040160405180910390fd5b614de6633b9aca0082615ce5565b949350505050565b6001600160801b038083166000908152601085016020526040902060048101549091429116101561501d576000808080614e2988888861479d565b92965090945092509050614e466001600160801b03831687614aff565b600d89018054601090614e6a908490600160801b90046001600160801b0316615a5f565b82546101009290920a6001600160801b038181021990931691831602179091556002870154614e9b92501682614079565b614ea59083615808565b600c89018054600090614ec29084906001600160801b0316615808565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555060008560050160109054906101000a90046001600160801b031684614f0b9190615a5f565b6004870154909150600160801b90046001600160801b0316614f8b576005860180546001600160801b0319166001600160801b038981169182179092558a5460408051918252928b166020820152918201527f1ffcce3f36329128f865c47cacfc17ad27e65f7039d7aacd9d6350644c465a919060600160405180910390a15b6005860180546001600160801b03908116600160801b8783168102919091179092556001600160801b03198782168302164282161760048901558a54600d8c01547fda58df82953130c96b08dcda8a76478b2a4e0c16cddf3e59ac1ccf39d7b027639391928c92614ffe9204168b614079565b8460405161500f9493929190615ac3565b60405180910390a150505050505b50505050565b60006114a36001600160801b03848116908416615288565b6000821580615048575081155b1561505557506000610989565b816150696002670de0b6b3a7640000615ce5565b61507590600019615d87565b61507f9190615ce5565b83111561509e5760405162a748d160e21b815260040160405180910390fd5b670de0b6b3a7640000614d81600282615ce5565b6000816150d2576040516308693c7b60e31b815260040160405180910390fd5b60006150df600284615ce5565b9050670de0b6b3a76400006150f682600019615d87565b6151009190615ce5565b84111561511f5760405162a748d160e21b815260040160405180910390fd5b8281615133670de0b6b3a764000087615cc6565b61513d9190615aea565b614de69190615ce5565b6001600160801b0380821660009081526010840160205260408120600c85015491929091161561521357600c84015460048201546002830154614de6926001600160801b03908116926148f8926151a99290811691600160801b900416614079565b6004880154600b89015461296c916001600160801b03600160801b918290048116926148f8926151dc9291041683615a5f565b60048901546151f4906001600160801b031642615a5f565b60058d015461296c9190600160801b90046001600160801b0316615d9e565b5092915050565b6000610989826001600160801b0316614cc5565b600054610100900460ff166149e65760405162461bcd60e51b8152600401610d9190615cf9565b600054610100900460ff1661527c5760405162461bcd60e51b8152600401610d9190615cf9565b6097805460ff19169055565b6000816152a8576040516308693c7b60e31b815260040160405180910390fd5b60006152b5600284615ce5565b9050676765c793fa10079d601b1b6152cf82600019615d87565b6152d99190615ce5565b8411156152f85760405162a748d160e21b815260040160405180910390fd5b8281615133676765c793fa10079d601b1b87615cc6565b60006020828403121561532157600080fd5b81356001600160e01b0319811681146114a357600080fd5b60006020828403121561534b57600080fd5b5035919050565b6001600160a01b038116811461212057600080fd5b803561537281615352565b919050565b6000806040838503121561538a57600080fd5b82359150602083013561539c81615352565b809150509250929050565b6001600160801b038116811461212057600080fd5b8035615372816153a7565b600080604083850312156153da57600080fd5b82356153e5816153a7565b946020939093013593505050565b6000806040838503121561540657600080fd5b82359150602083013561539c816153a7565b60008060006040848603121561542d57600080fd5b833567ffffffffffffffff8082111561544557600080fd5b818601915086601f83011261545957600080fd5b81358181111561546857600080fd5b87602082850101111561547a57600080fd5b60209283019550935050840135615490816153a7565b809150509250925092565b600061020082840312156154ae57600080fd5b50919050565b600080604083850312156154c757600080fd5b82356153e581615352565b6000806000606084860312156154e757600080fd5b8335925060208401356154f9816153a7565b9150604084013561549081615352565b600080600080600060a0868803121561552157600080fd5b853561552c816153a7565b9450602086013593506040860135615543816153a7565b92506060860135615553816153a7565b91506080860135615563816153a7565b809150509295509295909350565b6000806000806080858703121561558757600080fd5b843593506020850135615599816153a7565b925060408501356155a9816153a7565b915060608501356155b9816153a7565b939692955090935050565b8035801515811461537257600080fd5b600080604083850312156155e757600080fd5b823591506155f7602084016155c4565b90509250929050565b60006020828403121561561257600080fd5b81356114a381615352565b60006020828403121561562f57600080fd5b81356114a3816153a7565b6000806040838503121561564d57600080fd5b823561565881615352565b9150602083013561539c816153a7565b600080600080600060a0868803121561568057600080fd5b853594506020860135615692816153a7565b935060408601356156a2816153a7565b925060608601356156b2816153a7565b9150608086013561556381615352565b6001600160801b03948516815292841660208401529083166040830152909116606082015260800190565b600080600080600060a0868803121561570557600080fd5b8535615710816153a7565b945060208601359350604086013561572781615352565b9250606086013561555381615352565b60006020828403121561574957600080fd5b5051919050565b6001600160a01b0393841681526001600160801b03929092166020830152909116604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6000602082840312156157b857600080fd5b81516114a3816153a7565b600080604083850312156157d657600080fd5b82516157e1816153a7565b602084015190925061539c816153a7565b634e487b7160e01b600052601160045260246000fd5b60006001600160801b0380831681851680830382111561582a5761582a6157f2565b01949350505050565b9283526001600160a01b039190911660208301526001600160801b0316604082015260600190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561589c57600080fd5b815160ff811681146114a357600080fd5b6000602082840312156158bf57600080fd5b6114a3826155c4565b8135815261020081016158dd60208401615367565b6001600160a01b031660208301526158f760408401615367565b6001600160a01b03166040830152615911606084016153bc565b6001600160801b0316606083015261592b608084016153bc565b6001600160801b0316608083015261594560a084016153bc565b6001600160801b031660a083015261595f60c084016153bc565b6001600160801b031660c083015261597960e084016153bc565b6001600160801b031660e08301526101006159958482016153bc565b6001600160801b0316908301526101206159b08482016153bc565b6001600160801b0316908301526101406159cb8482016153bc565b6001600160801b0316908301526101606159e68482016153bc565b6001600160801b031690830152610180615a018482016153bc565b6001600160801b0316908301526101a0615a1c8482016153bc565b6001600160801b0316908301526101c0615a378482016153bc565b6001600160801b0316908301526101e0615a528482016155c4565b1515920191909152919050565b60006001600160801b0383811690831681811015615a7f57615a7f6157f2565b039392505050565b634e487b7160e01b600052601260045260246000fd5b60006001600160801b0380841680615ab757615ab7615a87565b92169190910692915050565b9384526001600160801b039283166020850152908216604084015216606082015260800190565b60008219821115615afd57615afd6157f2565b500190565b60005b83811015615b1d578181015183820152602001615b05565b8381111561501d5750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615b66816017850160208801615b02565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615b97816028840160208801615b02565b01602801949350505050565b6020815260008251806020840152615bc2816040850160208701615b02565b601f01601f19169190910160400192915050565b600181815b80851115615c11578160001904821115615bf757615bf76157f2565b80851615615c0457918102915b93841c9390800290615bdb565b509250929050565b600082615c2857506001610989565b81615c3557506000610989565b8160018114615c4b5760028114615c5557615c71565b6001915050610989565b60ff841115615c6657615c666157f2565b50506001821b610989565b5060208310610133831016604e8410600b8410161715615c94575081810a610989565b615c9e8383615bd6565b8060001904821115615cb257615cb26157f2565b029392505050565b60006114a38383615c19565b6000816000190483118215151615615ce057615ce06157f2565b500290565b600082615cf457615cf4615a87565b500490565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081615d7f57615d7f6157f2565b506000190190565b600082821015615d9957615d996157f2565b500390565b60006001600160801b0380831681851681830481118215151615615dc457615dc46157f2565b0294935050505056fe71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb12344277e405079ec07749d374ba0b5862a4e45a6a05ac889dbb4a991c6f9354d27160668f6d81898b09bdae61c61d2c7d23fe33a52ae9b38e5b92f00ced3806ba2646970667358221220306be898b2c1eb91cd9390e9302219603ccf20cc4e7a9868384625687266a86564736f6c63430008090033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.