Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Multichain Info
No addresses found
Latest 11 from a total of 11 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Sync Global Acco... | 21824285 | 40 days ago | IN | 0 ETH | 0.00048728 | ||||
Liquidate | 21230604 | 123 days ago | IN | 0 ETH | 0.11772914 | ||||
Liquidate | 21126256 | 137 days ago | IN | 0 ETH | 0.0017995 | ||||
Liquidate | 21117096 | 139 days ago | IN | 0 ETH | 0.01019792 | ||||
Liquidate | 21045937 | 149 days ago | IN | 0 ETH | 0.01289949 | ||||
Liquidate | 21041068 | 149 days ago | IN | 0 ETH | 0.00627087 | ||||
Liquidate | 21035835 | 150 days ago | IN | 0 ETH | 0.00814956 | ||||
Liquidate | 20771166 | 187 days ago | IN | 0 ETH | 0.00075964 | ||||
Liquidate | 20760343 | 189 days ago | IN | 0 ETH | 0.00075944 | ||||
Liquidate | 20459374 | 231 days ago | IN | 0 ETH | 0.07090774 | ||||
Liquidate | 20459343 | 231 days ago | IN | 0 ETH | 0.11046817 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x6101a060 | 19437159 | 374 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x42cAD7fF...37AfD6339 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
CdpManager
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./Interfaces/ICdpManager.sol"; import "./Interfaces/ICollSurplusPool.sol"; import "./Interfaces/IEBTCToken.sol"; import "./Interfaces/ISortedCdps.sol"; import "./Dependencies/ICollateralTokenOracle.sol"; import "./CdpManagerStorage.sol"; import "./Dependencies/Proxy.sol"; import "./Dependencies/EbtcBase.sol"; import "./Dependencies/EbtcMath.sol"; /// @title CdpManager is mainly in charge of all Cdp related core processing like collateral & debt accounting, split fee calculation, redemption, etc /// @notice Except for redemption, end user typically will interact with BorrowerOeprations for individual Cdp actions /// @dev CdpManager also handles liquidation through delegatecall to LiquidationLibrary contract CdpManager is CdpManagerStorage, ICdpManager, Proxy { // --- Dependency setter --- /// @notice Constructor for CdpManager contract. /// @dev Sets up dependencies and initial staking reward split. /// @param _liquidationLibraryAddress Address of the liquidation library. /// @param _authorityAddress Address of the authority. /// @param _borrowerOperationsAddress Address of BorrowerOperations. /// @param _collSurplusPoolAddress Address of CollSurplusPool. /// @param _ebtcTokenAddress Address of the eBTC token. /// @param _sortedCdpsAddress Address of the SortedCDPs. /// @param _activePoolAddress Address of the ActivePool. /// @param _priceFeedAddress Address of the price feed. /// @param _collTokenAddress Address of the collateral token. constructor( address _liquidationLibraryAddress, address _authorityAddress, address _borrowerOperationsAddress, address _collSurplusPoolAddress, address _ebtcTokenAddress, address _sortedCdpsAddress, address _activePoolAddress, address _priceFeedAddress, address _collTokenAddress ) CdpManagerStorage( _liquidationLibraryAddress, _authorityAddress, _borrowerOperationsAddress, _collSurplusPoolAddress, _ebtcTokenAddress, _sortedCdpsAddress, _activePoolAddress, _priceFeedAddress, _collTokenAddress ) { stakingRewardSplit = STAKING_REWARD_SPLIT; // Emit initial value for analytics emit StakingRewardSplitSet(stakingRewardSplit); (uint256 _oldIndex, uint256 _newIndex) = _readStEthIndex(); _syncStEthIndex(_oldIndex, _newIndex); systemStEthFeePerUnitIndex = DECIMAL_PRECISION; } // --- Cdp Liquidation functions --- // ----------------------------------------------------------------- // Cdp ICR | Liquidation Behavior (TODO gas compensation?) // // < MCR | debt could be fully repaid by liquidator // | and ALL collateral transferred to liquidator // | OR debt could be partially repaid by liquidator and // | liquidator could get collateral of (repaidDebt * max(LICR, min(ICR, MCR)) / price) // // > MCR & < TCR | only liquidatable in Recovery Mode (TCR < CCR) // | debt could be fully repaid by liquidator // | and up to (repaid debt * MCR) worth of collateral // | transferred to liquidator while the residue of collateral // | will be available in CollSurplusPool for owner to claim // | OR debt could be partially repaid by liquidator and // | liquidator could get collateral of (repaidDebt * max(LICR, min(ICR, MCR)) / price) // ----------------------------------------------------------------- /// @notice Fully liquidate a single Cdp by ID. Cdp must meet the criteria for liquidation at the time of execution. /// @notice callable by anyone, attempts to liquidate the CdpId. Executes successfully if Cdp meets the conditions for liquidation (e.g. in Normal Mode, it liquidates if the Cdp's ICR < the system MCR). /// @dev forwards msg.data directly to the liquidation library using OZ proxy core delegation function /// @param _cdpId ID of the Cdp to liquidate. function liquidate(bytes32 _cdpId) external override { _delegate(liquidationLibrary); } /// @notice Partially liquidate a single Cdp. /// @dev forwards msg.data directly to the liquidation library using OZ proxy core delegation function /// @param _cdpId ID of the Cdp to partially liquidate. /// @param _partialAmount Amount to partially liquidate. /// @param _upperPartialHint Upper hint for reinsertion of the Cdp into the linked list. /// @param _lowerPartialHint Lower hint for reinsertion of the Cdp into the linked list. function partiallyLiquidate( bytes32 _cdpId, uint256 _partialAmount, bytes32 _upperPartialHint, bytes32 _lowerPartialHint ) external override { _requireAmountGreaterThanMin(_partialAmount); _delegate(liquidationLibrary); } // --- Batch/Sequence liquidation functions --- /// @notice Attempt to liquidate a custom list of Cdps provided by the caller /// @notice Callable by anyone, accepts a custom list of Cdps addresses as an argument. /// @notice Steps through the provided list and attempts to liquidate every Cdp, until it reaches the end or it runs out of gas. /// @notice A Cdp is liquidated only if it meets the conditions for liquidation. /// @dev forwards msg.data directly to the liquidation library using OZ proxy core delegation function /// @param _cdpArray Array of Cdps to liquidate. function batchLiquidateCdps(bytes32[] memory _cdpArray) external override { _delegate(liquidationLibrary); } // --- Redemption functions --- /// @notice // Redeem as much collateral as possible from given Cdp in exchange for EBTC up to specified maximum /// @param _redeemColFromCdp Struct containing variables for redeeming collateral. /// @return singleRedemption Struct containing redemption values. function _redeemCollateralFromCdp( SingleRedemptionInputs memory _redeemColFromCdp ) internal returns (SingleRedemptionValues memory singleRedemption) { // Determine the remaining amount (lot) to be redeemed, // capped by the entire debt of the Cdp minus the liquidation reserve singleRedemption.debtToRedeem = EbtcMath._min( _redeemColFromCdp.maxEBTCamount, Cdps[_redeemColFromCdp.cdpId].debt /// @audit Redeem everything ); singleRedemption.collSharesDrawn = collateral.getSharesByPooledEth( (singleRedemption.debtToRedeem * DECIMAL_PRECISION) / _redeemColFromCdp.price ); // Repurposing this struct here to avoid stack too deep. CdpDebtAndCollShares memory _oldDebtAndColl = CdpDebtAndCollShares( Cdps[_redeemColFromCdp.cdpId].debt, Cdps[_redeemColFromCdp.cdpId].coll ); // Decrease the debt and collateral of the current Cdp according to the EBTC lot and corresponding ETH to send uint256 newDebt = _oldDebtAndColl.debt - singleRedemption.debtToRedeem; uint256 newColl = _oldDebtAndColl.collShares - singleRedemption.collSharesDrawn; if (newDebt == 0) { // No debt remains, close Cdp // No debt left in the Cdp, therefore the cdp gets closed { address _borrower = sortedCdps.getOwnerAddress(_redeemColFromCdp.cdpId); uint256 _liquidatorRewardShares = uint256( Cdps[_redeemColFromCdp.cdpId].liquidatorRewardShares ); singleRedemption.collSurplus = newColl; // Collateral surplus processed on full redemption singleRedemption.liquidatorRewardShares = _liquidatorRewardShares; singleRedemption.fullRedemption = true; _closeCdpByRedemption( _redeemColFromCdp.cdpId, 0, newColl, _liquidatorRewardShares, _borrower ); emit CdpUpdated( _redeemColFromCdp.cdpId, _borrower, msg.sender, _oldDebtAndColl.debt, _oldDebtAndColl.collShares, 0, 0, 0, CdpOperation.redeemCollateral ); } } else { // Debt remains, reinsert Cdp uint256 newNICR = EbtcMath._computeNominalCR(newColl, newDebt); /* * If the provided hint is out of date, we bail since trying to reinsert without a good hint will almost * certainly result in running out of gas. * * If the resultant net coll of the partial is less than the minimum, we bail. */ if ( newNICR != _redeemColFromCdp.partialRedemptionHintNICR || collateral.getPooledEthByShares(newColl) < MIN_NET_STETH_BALANCE || newDebt < MIN_CHANGE ) { _updateStakeAndTotalStakes(_redeemColFromCdp.cdpId); emit CdpUpdated( _redeemColFromCdp.cdpId, ISortedCdps(sortedCdps).getOwnerAddress(_redeemColFromCdp.cdpId), msg.sender, _oldDebtAndColl.debt, _oldDebtAndColl.collShares, _oldDebtAndColl.debt, _oldDebtAndColl.collShares, Cdps[_redeemColFromCdp.cdpId].stake, CdpOperation.failedPartialRedemption ); singleRedemption.cancelledPartial = true; return singleRedemption; } singleRedemption.newPartialNICR = newNICR; Cdps[_redeemColFromCdp.cdpId].debt = newDebt; Cdps[_redeemColFromCdp.cdpId].coll = newColl; _updateStakeAndTotalStakes(_redeemColFromCdp.cdpId); emit CdpUpdated( _redeemColFromCdp.cdpId, ISortedCdps(sortedCdps).getOwnerAddress(_redeemColFromCdp.cdpId), msg.sender, _oldDebtAndColl.debt, _oldDebtAndColl.collShares, newDebt, newColl, Cdps[_redeemColFromCdp.cdpId].stake, CdpOperation.redeemCollateral ); } return singleRedemption; } /* * Called when a full redemption occurs, and closes the cdp. * The redeemer swaps (debt) EBTC for (debt) * worth of stETH, so the stETH liquidation reserve is all that remains. * In order to close the cdp, the stETH liquidation reserve is returned to the Cdp owner, * The debt recorded on the cdp's struct is zero'd elswhere, in _closeCdp. * Any surplus stETH left in the cdp, is sent to the Coll surplus pool, and can be later claimed by the borrower. */ function _closeCdpByRedemption( bytes32 _cdpId, uint256 _EBTC, uint256 _collSurplus, uint256 _liquidatorRewardShares, address _borrower ) internal { _closeCdpWithoutRemovingSortedCdps(_cdpId, Status.closedByRedemption); // Update Active Pool EBTC, and send ETH to account activePool.decreaseSystemDebt(_EBTC); // Register stETH surplus from upcoming transfers of stETH collateral and liquidator reward shares collSurplusPool.increaseSurplusCollShares( _cdpId, _borrower, _collSurplus, _liquidatorRewardShares ); // CEI: send stETH coll and liquidator reward shares from Active Pool to CollSurplus Pool activePool.transferSystemCollSharesAndLiquidatorReward( address(collSurplusPool), _collSurplus, _liquidatorRewardShares ); } /// @notice Returns true if the CdpId specified is the lowest-ICR Cdp in the linked list that still has MCR > ICR /// @dev Returns false if the specified CdpId hint is blank /// @dev Returns false if the specified CdpId hint doesn't exist in the list /// @dev Returns false if the ICR of the specified CdpId is < MCR /// @dev Returns true if the specified CdpId is not blank, exists in the list, has an ICR > MCR, and the next lower Cdp in the list is either blank or has an ICR < MCR. function _isValidFirstRedemptionHint( bytes32 _firstRedemptionHint, uint256 _price ) internal view returns (bool) { if ( _firstRedemptionHint == sortedCdps.nonExistId() || !sortedCdps.contains(_firstRedemptionHint) || getSyncedICR(_firstRedemptionHint, _price) < MCR ) { return false; } bytes32 nextCdp = sortedCdps.getNext(_firstRedemptionHint); return nextCdp == sortedCdps.nonExistId() || getSyncedICR(nextCdp, _price) < MCR; } /// @notice Send _debt EBTC to the system and redeem the corresponding amount of collateral /// @notice from as many Cdps as are needed to fill the redemption request. /// @notice /// @notice Note that if _debt is very large, this function can run out of gas, specially if traversed cdps are small (meaning many small Cdps are redeemed against). /// @notice This can be easily avoided by splitting the total _debt in appropriate chunks and calling the function multiple times. /// @notice /// @notice There is a optional parameter `_maxIterations` which can also be provided, so the loop through Cdps is capped (if it’s zero, it will be ignored). /// @notice This makes it easier to avoid OOG for the frontend, as only knowing approximately the average cost of an iteration is enough, /// @notice without needing to know the "topology" of the cdp list. It also avoids the need to set the cap in stone in the contract, /// @notice nor doing gas calculations, as both gas price and opcode costs can vary. /// @notice /// @notice All Cdps that are redeemed from -- with the likely exception of the last one -- will end up with no debt left, /// @notice therefore they will be closed. /// @notice If the last Cdp does have some remaining debt & collateral (it has a valid meaningful ICR) then reinsertion of the CDP /// @notice could be anywhere in the entire SortedCdps list, therefore this redemption requires a hint. /// @notice /// @notice A frontend should use HintHelper.getRedemptionHints() to calculate what the ICR of this Cdp will be after redemption, /// @notice and pass a hint for its position in the SortedCdps list along with the ICR value that the hint was found for. /// @notice /// @notice If another transaction modifies the list between calling getRedemptionHints() /// @notice and passing the hints to redeemCollateral(), it is very likely that the last (partially) /// @notice redeemed Cdp would end up with a different ICR than what the hint is for. /// @notice /// @notice In this case, the redemption will stop after the last completely redeemed Cdp and the sender will keep the /// @notice remaining EBTC amount, which they can attempt to redeem later. /// @param _debt The total eBTC debt amount to be redeemed /// @param _firstRedemptionHint The first CdpId to be considered for redemption, could get from HintHelper.getRedemptionHints() /// @param _upperPartialRedemptionHint The first CdpId to be considered for redemption, could get from HintHelper.getApproxHint(_partialRedemptionHintNICR) then SortedCdps.findInsertPosition(_partialRedemptionHintNICR) /// @param _lowerPartialRedemptionHint The first CdpId to be considered for redemption, could get from HintHelper.getApproxHint(_partialRedemptionHintNICR) then SortedCdps.findInsertPosition(_partialRedemptionHintNICR) /// @param _partialRedemptionHintNICR The new Nominal Collateral Ratio (NICR) of the last redeemed CDP after partial redemption, could get from HintHelper.getRedemptionHints() /// @param _maxIterations The maximum allowed iteration along the SortedCdps loop, if zero then there is no limit /// @param _maxFeePercentage The maximum allowed redemption fee for this redemption function redeemCollateral( uint256 _debt, bytes32 _firstRedemptionHint, bytes32 _upperPartialRedemptionHint, bytes32 _lowerPartialRedemptionHint, uint256 _partialRedemptionHintNICR, uint256 _maxIterations, uint256 _maxFeePercentage ) external override nonReentrantSelfAndBOps { RedemptionTotals memory totals; // early check to ensure redemption is not paused require(redemptionsPaused == false, "CdpManager: Redemptions Paused"); _requireValidMaxFeePercentage(_maxFeePercentage); _syncGlobalAccounting(); // Apply state, we will syncGracePeriod at end of function totals.price = priceFeed.fetchPrice(); { ( uint256 tcrAtStart, uint256 systemCollSharesAtStart, uint256 systemDebtAtStart ) = _getTCRWithSystemDebtAndCollShares(totals.price); totals.tcrAtStart = tcrAtStart; totals.systemCollSharesAtStart = systemCollSharesAtStart; totals.systemDebtAtStart = systemDebtAtStart; if (!activePool.twapDisabled()) { try activePool.observe() returns (uint256 _twapSystemDebtAtStart) { // @audit Return the smaller value of the two, bias towards a larger redemption scaling fee totals.twapSystemDebtAtStart = EbtcMath._min( _twapSystemDebtAtStart, systemDebtAtStart ); } catch { totals.twapSystemDebtAtStart = systemDebtAtStart; } } else { totals.twapSystemDebtAtStart = systemDebtAtStart; } } _requireTCRisNotBelowMCR(totals.price, totals.tcrAtStart); _requireAmountGreaterThanMin(_debt); _requireEbtcBalanceCoversRedemptionAndWithinSupply( msg.sender, _debt, totals.systemDebtAtStart ); totals.remainingDebtToRedeem = _debt; address currentBorrower; bytes32 _cId = _firstRedemptionHint; if (_isValidFirstRedemptionHint(_firstRedemptionHint, totals.price)) { currentBorrower = sortedCdps.getOwnerAddress(_firstRedemptionHint); } else { _cId = sortedCdps.getLast(); currentBorrower = sortedCdps.getOwnerAddress(_cId); // Find the first cdp with ICR >= MCR while (currentBorrower != address(0) && getSyncedICR(_cId, totals.price) < MCR) { _cId = sortedCdps.getPrev(_cId); currentBorrower = sortedCdps.getOwnerAddress(_cId); } } // Loop through the Cdps starting from the one with lowest collateral // ratio until _amount of EBTC is exchanged for collateral if (_maxIterations == 0) { _maxIterations = type(uint256).max; } bytes32 _firstRedeemed = _cId; bytes32 _lastRedeemed = _cId; uint256 _numCdpsFullyRedeemed; /** Core Redemption Loop */ uint256 _partialRedeemedNewNICR; while ( currentBorrower != address(0) && totals.remainingDebtToRedeem > 0 && _maxIterations > 0 ) { // Save the address of the Cdp preceding the current one, before potentially modifying the list { _syncAccounting(_cId); /// @audit This happens even if the re-insertion doesn't SingleRedemptionInputs memory _redeemColFromCdp = SingleRedemptionInputs( _cId, totals.remainingDebtToRedeem, totals.price, _upperPartialRedemptionHint, _lowerPartialRedemptionHint, _partialRedemptionHintNICR ); SingleRedemptionValues memory singleRedemption = _redeemCollateralFromCdp( _redeemColFromCdp ); // Partial redemption was cancelled (out-of-date hint, or new net debt < minimum), // therefore we could not redeem from the last Cdp if (singleRedemption.cancelledPartial) break; // prepare for reinsertion if there is partial redemption if (singleRedemption.newPartialNICR > 0) { _partialRedeemedNewNICR = singleRedemption.newPartialNICR; } totals.debtToRedeem = totals.debtToRedeem + singleRedemption.debtToRedeem; totals.collSharesDrawn = totals.collSharesDrawn + singleRedemption.collSharesDrawn; totals.remainingDebtToRedeem = totals.remainingDebtToRedeem - singleRedemption.debtToRedeem; totals.totalCollSharesSurplus = totals.totalCollSharesSurplus + singleRedemption.collSurplus; bytes32 _nextId = sortedCdps.getPrev(_cId); if (singleRedemption.fullRedemption) { _lastRedeemed = _cId; _numCdpsFullyRedeemed = _numCdpsFullyRedeemed + 1; _cId = _nextId; } address nextUserToCheck = sortedCdps.getOwnerAddress(_nextId); currentBorrower = nextUserToCheck; } _maxIterations--; } require(totals.collSharesDrawn > 0, "CdpManager: Unable to redeem any amount"); // remove from sortedCdps if (_numCdpsFullyRedeemed == 1) { sortedCdps.remove(_firstRedeemed); } else if (_numCdpsFullyRedeemed > 1) { bytes32[] memory _toRemoveIds = _getCdpIdsToRemove( _lastRedeemed, _numCdpsFullyRedeemed, _firstRedeemed ); sortedCdps.batchRemove(_toRemoveIds); } // reinsert partially redemeed CDP if any if (_cId != bytes32(0) && _partialRedeemedNewNICR > 0) { sortedCdps.reInsert( _cId, _partialRedeemedNewNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint ); } // Decay the baseRate due to time passed, and then increase it according to the size of this redemption. // Use the saved total EBTC supply value, from before it was reduced by the redemption. _updateBaseRateFromRedemption( totals.collSharesDrawn, totals.price, totals.twapSystemDebtAtStart ); // Calculate the ETH fee totals.feeCollShares = _getRedemptionFee(totals.collSharesDrawn); _requireUserAcceptsFee(totals.feeCollShares, totals.collSharesDrawn, _maxFeePercentage); totals.collSharesToRedeemer = totals.collSharesDrawn - totals.feeCollShares; _syncGracePeriodForGivenValues( totals.systemCollSharesAtStart - totals.collSharesDrawn - totals.totalCollSharesSurplus, totals.systemDebtAtStart - totals.debtToRedeem, totals.price ); emit Redemption( _debt, totals.debtToRedeem, totals.collSharesDrawn, totals.feeCollShares, msg.sender ); // Burn the total eBTC that is redeemed ebtcToken.burn(msg.sender, totals.debtToRedeem); // Update Active Pool eBTC debt internal accounting activePool.decreaseSystemDebt(totals.debtToRedeem); // Allocate the stETH fee to the FeeRecipient activePool.allocateSystemCollSharesToFeeRecipient(totals.feeCollShares); // CEI: Send the stETH drawn to the redeemer activePool.transferSystemCollShares(msg.sender, totals.collSharesToRedeemer); // final check if we not in RecoveryMode at redemption start if (!_checkRecoveryModeForTCR(totals.tcrAtStart)) { require( !_checkRecoveryMode(totals.price), "CdpManager: redemption should not trigger RecoveryMode" ); } } // --- Helper functions --- function _getCdpIdsToRemove( bytes32 _start, uint256 _total, bytes32 _end ) internal view returns (bytes32[] memory) { uint256 _cnt = _total; bytes32 _id = _start; bytes32[] memory _toRemoveIds = new bytes32[](_total); while (_cnt > 0 && _id != bytes32(0)) { _toRemoveIds[_total - _cnt] = _id; _cnt = _cnt - 1; _id = sortedCdps.getNext(_id); } require(_toRemoveIds[0] == _start, "CdpManager: batchRemoveSortedCdpIds check start error"); require( _toRemoveIds[_total - 1] == _end, "CdpManager: batchRemoveSortedCdpIds check end error" ); return _toRemoveIds; } /// @notice Synchorize the accounting for the specified Cdp /// @notice It will synchronize global accounting with stETH share index first /// @notice then apply split fee and debt redistribution if any /// @param _cdpId cdpId to sync pending accounting state for function syncAccounting(bytes32 _cdpId) external virtual override { /// @audit Opening can cause invalid reordering of Cdps due to changing values without reInserting into sortedCdps _requireCallerIsBorrowerOperations(); return _syncAccounting(_cdpId); } /// @notice Update stake for the specified Cdp and total stake within the system. /// @dev Only BorrowerOperations is allowed to call this function /// @param _cdpId cdpId to update stake for function updateStakeAndTotalStakes(bytes32 _cdpId) external override returns (uint256) { _requireCallerIsBorrowerOperations(); return _updateStakeAndTotalStakes(_cdpId); } /// @notice Close the specified Cdp by ID. /// @dev Only BorrowerOperations is allowed to call this function. /// @dev This will close the Cdp and update its status to `closedByOwner` /// @dev The collateral and debt will be zero'd out /// @dev The Cdp will be removed from the sorted list /// @dev The close will emit a `CdpUpdated` event containing closing details /// @param _cdpId ID of the Cdp to close /// @param _borrower Address of the Cdp borrower /// @param _debt The recorded Cdp debt prior to closing /// @param _coll The recorded Cdp collateral shares prior to closing function closeCdp( bytes32 _cdpId, address _borrower, uint256 _debt, uint256 _coll ) external override { _requireCallerIsBorrowerOperations(); emit CdpUpdated(_cdpId, _borrower, msg.sender, _debt, _coll, 0, 0, 0, CdpOperation.closeCdp); return _closeCdp(_cdpId, Status.closedByOwner); } // --- Recovery Mode and TCR functions --- /// @notice Get the sum of debt units assigned to all Cdps within eBTC system /// @dev It is actually the `systemDebt` value of the ActivePool. /// @return entireSystemDebt entire system debt accounting value function getSystemDebt() public view returns (uint256 entireSystemDebt) { return _getSystemDebt(); } /// @notice The total collateralization ratio (TCR) of the system as a cached "view" (maybe outdated) /// @dev It is based on the current recorded system debt and collateral. /// @dev Possible split fee is not considered with this function. /// @dev Please use getSyncedTCR() otherwise /// @param _price The current stETH:BTC price /// @return TCR The cached total collateralization ratio (TCR) of the system (does not take into account pending global state) function getCachedTCR(uint256 _price) external view override returns (uint256) { return _getCachedTCR(_price); } /// @notice Whether or not the system is in Recovery Mode (TCR is below the CCR) /// @dev Possible split fee is not considered with this function. /// @dev Please use getSyncedTCR() otherwise /// @param _price The current stETH:BTC price /// @return True if system is in recovery mode with cached values (TCR < CCR), false otherwise function checkRecoveryMode(uint256 _price) external view override returns (bool) { return _checkRecoveryMode(_price); } // Check whether or not the system *would be* in Recovery Mode, // given an ETH:USD price, and the entire system coll and debt. function _checkPotentialRecoveryMode( uint256 _systemCollShares, uint256 _systemDebt, uint256 _price ) internal view returns (bool) { uint256 TCR = _computeTCRWithGivenSystemValues(_systemCollShares, _systemDebt, _price); return TCR < CCR; } // --- Redemption fee functions --- /* * This function has two impacts on the baseRate state variable: * 1) decays the baseRate based on time passed since last redemption or EBTC borrowing operation. * then, * 2) increases the baseRate based on the amount redeemed, as a proportion of total supply */ function _updateBaseRateFromRedemption( uint256 _ETHDrawn, uint256 _price, uint256 _totalEBTCSupply ) internal returns (uint256) { uint256 decayedBaseRate = _calcDecayedBaseRate(); /* Convert the drawn ETH back to EBTC at face value rate (1 EBTC:1 USD), in order to get * the fraction of total supply that was redeemed at face value. */ uint256 redeemedEBTCFraction = (collateral.getPooledEthByShares(_ETHDrawn) * _price) / _totalEBTCSupply; uint256 newBaseRate = decayedBaseRate + (redeemedEBTCFraction / beta); newBaseRate = EbtcMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100% require(newBaseRate > 0, "CdpManager: new baseRate is zero!"); // Base rate is always non-zero after redemption // Update the baseRate state variable baseRate = newBaseRate; emit BaseRateUpdated(newBaseRate); _updateLastRedemptionTimestamp(); return newBaseRate; } /// @return current fee rate for redemption with base rate function getRedemptionRate() public view override returns (uint256) { return _calcRedemptionRate(baseRate); } /// @return current fee rate for redemption with decayed base rate function getRedemptionRateWithDecay() public view override returns (uint256) { return _calcRedemptionRate(_calcDecayedBaseRate()); } function _calcRedemptionRate(uint256 _baseRate) internal view returns (uint256) { return EbtcMath._min( redemptionFeeFloor + _baseRate, DECIMAL_PRECISION // cap at a maximum of 100% ); } function _getRedemptionFee(uint256 _ETHDrawn) internal view returns (uint256) { return _calcRedemptionFee(getRedemptionRate(), _ETHDrawn); } /// @return redemption fee for the specified collateral amount /// @param _stETHToRedeem The total expected stETH amount to redeem function getRedemptionFeeWithDecay( uint256 _stETHToRedeem ) external view override returns (uint256) { return _calcRedemptionFee(getRedemptionRateWithDecay(), _stETHToRedeem); } function _calcRedemptionFee( uint256 _redemptionRate, uint256 _ETHDrawn ) internal pure returns (uint256) { uint256 redemptionFee = (_redemptionRate * _ETHDrawn) / DECIMAL_PRECISION; require(redemptionFee < _ETHDrawn, "CdpManager: Fee would eat up all returned collateral"); return redemptionFee; } function _decayBaseRate() internal { uint256 decayedBaseRate = _calcDecayedBaseRate(); require(decayedBaseRate <= DECIMAL_PRECISION, "CdpManager: baseRate too large!"); // The baseRate can decay to 0 baseRate = decayedBaseRate; emit BaseRateUpdated(decayedBaseRate); _updateLastRedemptionTimestamp(); } // --- Internal fee functions --- // Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing. function _updateLastRedemptionTimestamp() internal { uint256 timePassed = block.timestamp > lastRedemptionTimestamp ? block.timestamp - lastRedemptionTimestamp : 0; if (timePassed >= SECONDS_IN_ONE_MINUTE) { // Using the effective elapsed time that is consumed so far to update lastRedemptionTimestamp // instead block.timestamp for consistency with _calcDecayedBaseRate() lastRedemptionTimestamp += _minutesPassedSinceLastRedemption() * SECONDS_IN_ONE_MINUTE; emit LastRedemptionTimestampUpdated(block.timestamp); } } function _calcDecayedBaseRate() internal view returns (uint256) { uint256 minutesPassed = _minutesPassedSinceLastRedemption(); uint256 decayFactor = EbtcMath._decPow(minuteDecayFactor, minutesPassed); return (baseRate * decayFactor) / DECIMAL_PRECISION; } function _minutesPassedSinceLastRedemption() internal view returns (uint256) { return block.timestamp > lastRedemptionTimestamp ? ((block.timestamp - lastRedemptionTimestamp) / SECONDS_IN_ONE_MINUTE) : 0; } /// @return timestamp when this contract is deployed function getDeploymentStartTime() public view returns (uint256) { return deploymentStartTime; } /// @notice Check whether or not the system *would be* in Recovery Mode, /// @notice given an ETH:eBTC price, and the entire system coll and debt. /// @param _systemCollShares The total collateral of the system to be used for the TCR calculation /// @param _systemDebt The total debt of the system to be used for the TCR calculation /// @param _price The ETH:eBTC price to be used for the TCR calculation /// @return flag (true or false) whether the system would be in Recovery Mode for specified status parameters function checkPotentialRecoveryMode( uint256 _systemCollShares, uint256 _systemDebt, uint256 _price ) external view returns (bool) { return _checkPotentialRecoveryMode(_systemCollShares, _systemDebt, _price); } // --- 'require' wrapper functions --- function _requireEbtcBalanceCoversRedemptionAndWithinSupply( address _redeemer, uint256 _amount, uint256 _totalSupply ) internal view { uint256 callerBalance = ebtcToken.balanceOf(_redeemer); require( callerBalance >= _amount, "CdpManager: Requested redemption amount must be <= user's EBTC token balance" ); require( callerBalance <= _totalSupply, "CdpManager: redeemer's EBTC balance exceeds total supply!" ); } function _requireAmountGreaterThanMin(uint256 _amount) internal pure { require(_amount >= MIN_CHANGE, "CdpManager: Amount must be greater than min"); } function _requireTCRisNotBelowMCR(uint256 _price, uint256 _TCR) internal view { require(_TCR >= MCR, "CdpManager: Cannot redeem when TCR < MCR"); } function _requireValidMaxFeePercentage(uint256 _maxFeePercentage) internal view { require( _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= DECIMAL_PRECISION, "Max fee percentage must be between redemption fee floor and 100%" ); } // --- Governance Parameters --- /// @notice Set the staking reward split percentage /// @dev Only callable by authorized addresses /// @param _stakingRewardSplit New staking reward split percentage value function setStakingRewardSplit(uint256 _stakingRewardSplit) external requiresAuth { require( _stakingRewardSplit <= MAX_REWARD_SPLIT, "CDPManager: new staking reward split exceeds max" ); syncGlobalAccountingAndGracePeriod(); stakingRewardSplit = _stakingRewardSplit; emit StakingRewardSplitSet(_stakingRewardSplit); } /// @notice Set the minimum redemption fee floor percentage /// @dev Only callable by authorized addresses /// @param _redemptionFeeFloor New minimum redemption fee floor percentage function setRedemptionFeeFloor(uint256 _redemptionFeeFloor) external requiresAuth { require( _redemptionFeeFloor >= MIN_REDEMPTION_FEE_FLOOR, "CDPManager: new redemption fee floor is lower than minimum" ); require( _redemptionFeeFloor <= DECIMAL_PRECISION, "CDPManager: new redemption fee floor is higher than maximum" ); syncGlobalAccountingAndGracePeriod(); redemptionFeeFloor = _redemptionFeeFloor; emit RedemptionFeeFloorSet(_redemptionFeeFloor); } /// @notice Set the minute decay factor for the redemption fee rate /// @dev Only callable by authorized addresses /// @param _minuteDecayFactor New minute decay factor value function setMinuteDecayFactor(uint256 _minuteDecayFactor) external requiresAuth { require( _minuteDecayFactor >= MIN_MINUTE_DECAY_FACTOR, "CDPManager: new minute decay factor out of range" ); require( _minuteDecayFactor <= MAX_MINUTE_DECAY_FACTOR, "CDPManager: new minute decay factor out of range" ); syncGlobalAccountingAndGracePeriod(); // decay first according to previous factor _decayBaseRate(); // set new factor after decaying minuteDecayFactor = _minuteDecayFactor; emit MinuteDecayFactorSet(_minuteDecayFactor); } /// @notice Set the beta value that controls redemption fee rate /// @dev Only callable by authorized addresses /// @param _beta New beta value function setBeta(uint256 _beta) external requiresAuth { syncGlobalAccountingAndGracePeriod(); _decayBaseRate(); beta = _beta; emit BetaSet(_beta); } /// @notice Pause or unpause redemptions /// @dev Only callable by authorized addresses /// @param _paused True to pause redemptions, false to unpause function setRedemptionsPaused(bool _paused) external requiresAuth { syncGlobalAccountingAndGracePeriod(); _decayBaseRate(); redemptionsPaused = _paused; emit RedemptionsPaused(_paused); } // --- Cdp property getters --- /// @notice Get status of a Cdp. Named enum values can be found in ICdpManagerData.Status /// @param _cdpId ID of the Cdp to get status for /// @return Status code of the Cdp function getCdpStatus(bytes32 _cdpId) external view override returns (uint256) { return uint256(Cdps[_cdpId].status); } /// @notice Get stake value of a Cdp /// @param _cdpId ID of the Cdp to get stake for /// @return Stake value of the Cdp function getCdpStake(bytes32 _cdpId) external view override returns (uint256) { return Cdps[_cdpId].stake; } /// @notice Get stored debt value of a Cdp, in eBTC units /// @notice Cached value - does not include pending changes from redistributions /// @param _cdpId ID of the Cdp to get debt for /// @return Debt value of the Cdp in eBTC function getCdpDebt(bytes32 _cdpId) external view override returns (uint256) { return Cdps[_cdpId].debt; } /// @notice Get stored collateral value of a Cdp, in stETH shares /// @notice Cached value - does not include pending changes from staking yield /// @param _cdpId ID of the Cdp to get collateral for /// @return Collateral value of the Cdp in stETH shares function getCdpCollShares(bytes32 _cdpId) external view override returns (uint256) { return Cdps[_cdpId].coll; } /// @notice Get shares value of the liquidator gas incentive reward stored for a Cdp. /// @notice The value stored is processed when a Cdp closes. /// @dev Upon closing by borrower, This value is returned directly to the borrower. /// @dev Upon closing by a position manager, This value is returned directly to the position manager. /// @dev Upon a full liquidation, This value is given to liquidators upon fully liquidating the Cdp /// @dev Upon redemption, This value is sent to the CollSurplusPool for reclaiming by the borrower. /// @param _cdpId ID of the Cdp to get liquidator reward shares for /// @return Liquidator reward shares value of the Cdp function getCdpLiquidatorRewardShares(bytes32 _cdpId) external view override returns (uint256) { return uint256(Cdps[_cdpId].liquidatorRewardShares); } // --- Cdp property setters, called by BorrowerOperations --- /// @notice Initialize all state for new Cdp /// @dev Only callable by BorrowerOperations, critical trust assumption /// @dev Requires Cdp to be already inserted into linked list correctly /// @param _cdpId ID of Cdp to initialize state for /// @param _debt Initial debt units of Cdp /// @param _coll Initial collateral shares of Cdp /// @param _liquidatorRewardShares Liquidator reward shares for Cdp liquidation gas stipend /// @param _borrower Address of the Cdp borrower function initializeCdp( bytes32 _cdpId, uint256 _debt, uint256 _coll, uint256 _liquidatorRewardShares, address _borrower ) external { _requireCallerIsBorrowerOperations(); Cdps[_cdpId].debt = _debt; Cdps[_cdpId].coll = _coll; Cdps[_cdpId].status = Status.active; Cdps[_cdpId].liquidatorRewardShares = EbtcMath.toUint128(_liquidatorRewardShares); cdpStEthFeePerUnitIndex[_cdpId] = systemStEthFeePerUnitIndex; /// @audit We critically assume global accounting is synced here _updateRedistributedDebtIndex(_cdpId); uint256 stake = _updateStakeAndTotalStakes(_cdpId); // Previous debt and coll are known to be zero upon opening a new Cdp emit CdpUpdated( _cdpId, _borrower, msg.sender, 0, 0, _debt, _coll, stake, CdpOperation.openCdp ); } /// @notice Set new Cdp debt and collateral values, updating stake accordingly /// @dev Only callable by BorrowerOperations, critical trust assumption /// @param _cdpId ID of Cdp to update state for /// @param _borrower Address of the Cdp borrower /// @param _coll Previous collateral shares of Cdp, before update /// @param _debt Previous debt units of Cdp, before update. /// @param _newColl New collateral shares of Cdp after update operation /// @param _newDebt New debt units of Cdp after update operation function updateCdp( bytes32 _cdpId, address _borrower, uint256 _coll, uint256 _debt, uint256 _newColl, uint256 _newDebt ) external { _requireCallerIsBorrowerOperations(); _setCdpCollShares(_cdpId, _newColl); _setCdpDebt(_cdpId, _newDebt); uint256 stake = _updateStakeAndTotalStakes(_cdpId); emit CdpUpdated( _cdpId, _borrower, msg.sender, _debt, _coll, _newDebt, _newColl, stake, CdpOperation.adjustCdp ); } /// @notice Set the collateral of a Cdp /// @param _cdpId The ID of the Cdp /// @param _newColl New collateral value, in stETH shares function _setCdpCollShares(bytes32 _cdpId, uint256 _newColl) internal { Cdps[_cdpId].coll = _newColl; } /// @notice Set the debt of a Cdp /// @param _cdpId The ID of the Cdp /// @param _newDebt New debt units value function _setCdpDebt(bytes32 _cdpId, uint256 _newDebt) internal { Cdps[_cdpId].debt = _newDebt; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./Interfaces/ICdpManager.sol"; import "./Interfaces/ICollSurplusPool.sol"; import "./Interfaces/IEBTCToken.sol"; import "./Interfaces/ISortedCdps.sol"; import "./Dependencies/EbtcBase.sol"; import "./Dependencies/ReentrancyGuard.sol"; import "./Dependencies/ICollateralTokenOracle.sol"; import "./Dependencies/AuthNoOwner.sol"; /// @title CDP Manager storage and shared functions with LiquidationLibrary /// @dev All features around Cdp management are split into separate parts to get around contract size limitations. /// @dev Liquidation related functions are delegated to LiquidationLibrary contract code. /// @dev Both CdpManager and LiquidationLibrary must maintain **the same storage layout**, so shared storage components /// @dev and shared functions are added here in CdpManagerStorage to de-dup code contract CdpManagerStorage is EbtcBase, ReentrancyGuard, ICdpManagerData, AuthNoOwner { // NOTE: No packing cause it's the last var, no need for u64 uint128 public constant UNSET_TIMESTAMP = type(uint128).max; uint128 public constant MINIMUM_GRACE_PERIOD = 15 minutes; uint128 public lastGracePeriodStartTimestamp = UNSET_TIMESTAMP; // use max to signify uint128 public recoveryModeGracePeriodDuration = MINIMUM_GRACE_PERIOD; /// @notice Start the recovery mode grace period, if the system is in RM and the grace period timestamp has not already been set /// @dev Trusted function to allow BorrowerOperations actions to set RM Grace Period /// @dev Assumes BorrowerOperations has correctly calculated and passed in the new system TCR /// @dev To maintain CEI compliance we use this trusted function /// @param tcr The TCR to be checked whether Grace Period should be started function notifyStartGracePeriod(uint256 tcr) external { _requireCallerIsBorrowerOperations(); _startGracePeriod(tcr); } /// @notice End the recovery mode grace period, if the system is no longer in RM /// @dev Trusted function to allow BorrowerOperations actions to set RM Grace Period /// @dev Assumes BorrowerOperations has correctly calculated and passed in the new system TCR /// @dev To maintain CEI compliance we use this trusted function /// @param tcr The TCR to be checked whether Grace Period should be ended function notifyEndGracePeriod(uint256 tcr) external { _requireCallerIsBorrowerOperations(); _endGracePeriod(tcr); } /// @dev Internal notify called by Redemptions and Liquidations /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set function _startGracePeriod(uint256 _tcr) internal { emit TCRNotified(_tcr); if (lastGracePeriodStartTimestamp == UNSET_TIMESTAMP) { lastGracePeriodStartTimestamp = uint128(block.timestamp); emit GracePeriodStart(); } } /// @notice Clear RM Grace Period timestamp if it has been set /// @notice No input validation, calling function must confirm that the system is not in recovery mode to be valid /// @dev Specified TCR is emitted for notification pruposes regardless of whether the Grace Period timestamp is set /// @dev Internal notify called by Redemptions and Liquidations function _endGracePeriod(uint256 _tcr) internal { emit TCRNotified(_tcr); if (lastGracePeriodStartTimestamp != UNSET_TIMESTAMP) { lastGracePeriodStartTimestamp = UNSET_TIMESTAMP; emit GracePeriodEnd(); } } function _syncGracePeriod() internal { uint256 price = priceFeed.fetchPrice(); uint256 tcr = _getCachedTCR(price); bool isRecoveryMode = _checkRecoveryModeForTCR(tcr); if (isRecoveryMode) { _startGracePeriod(tcr); } else { _endGracePeriod(tcr); } } /// @dev Set RM grace period based on specified system collShares, system debt, and price /// @dev Variant for internal use in redemptions and liquidations function _syncGracePeriodForGivenValues( uint256 systemCollShares, uint256 systemDebt, uint256 price ) internal { // Compute TCR with specified values uint256 newTCR = EbtcMath._computeCR( collateral.getPooledEthByShares(systemCollShares), systemDebt, price ); if (newTCR < CCR) { // Notify system is in RM _startGracePeriod(newTCR); } else { // Notify system is outside RM _endGracePeriod(newTCR); } } /// @notice Set grace period duratin /// @notice Permissioned governance function, must set grace period duration above hardcoded minimum /// @param _gracePeriod new grace period duration, in seconds function setGracePeriod(uint128 _gracePeriod) external requiresAuth { require( _gracePeriod >= MINIMUM_GRACE_PERIOD, "CdpManager: Grace period below minimum duration" ); syncGlobalAccountingAndGracePeriod(); recoveryModeGracePeriodDuration = _gracePeriod; emit GracePeriodDurationSet(_gracePeriod); } string public constant NAME = "CdpManager"; // --- Connected contract declarations --- address public immutable borrowerOperationsAddress; ICollSurplusPool immutable collSurplusPool; IEBTCToken public immutable override ebtcToken; address public immutable liquidationLibrary; // A doubly linked list of Cdps, sorted by their sorted by their collateral ratios ISortedCdps public immutable sortedCdps; // --- Data structures --- uint256 public constant SECONDS_IN_ONE_MINUTE = 60; uint256 public constant MIN_REDEMPTION_FEE_FLOOR = (DECIMAL_PRECISION * 5) / 1000; // 0.5% uint256 public redemptionFeeFloor = MIN_REDEMPTION_FEE_FLOOR; bool public redemptionsPaused; /* * Half-life of 12h. 12h = 720 min * (1/2) = d^720 => d = (1/2)^(1/720) */ uint256 public minuteDecayFactor = 999037758833783000; uint256 public constant MIN_MINUTE_DECAY_FACTOR = 1; // Non-zero uint256 public constant MAX_MINUTE_DECAY_FACTOR = 999999999999999999; // Corresponds to a very fast decay rate, but not too extreme uint256 internal immutable deploymentStartTime; /* * BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, * in order to calc the new base rate from a redemption. * Corresponds to (1 / ALPHA) in the Liquity white paper. */ uint256 public beta = 2; uint256 public baseRate; uint256 public stakingRewardSplit; // The timestamp of the latest fee operation (redemption or new EBTC issuance) uint256 public lastRedemptionTimestamp; mapping(bytes32 => Cdp) public Cdps; uint256 public override totalStakes; // Snapshot of the value of totalStakes, taken immediately after the latest liquidation and split fee claim uint256 public totalStakesSnapshot; // Snapshot of the total collateral across the ActivePool, immediately after the latest liquidation and split fee claim uint256 public totalCollateralSnapshot; /* * systemDebtRedistributionIndex track the sums of accumulated socialized liquidations per unit staked. * During its lifetime, each stake earns: * * A systemDebt increase of ( stake * [systemDebtRedistributionIndex - systemDebtRedistributionIndex(0)] ) * * Where systemDebtRedistributionIndex(0) are snapshots of systemDebtRedistributionIndex * for the active Cdp taken at the instant the stake was made */ uint256 public systemDebtRedistributionIndex; // Map active cdps to their RewardSnapshot (eBTC debt redistributed) mapping(bytes32 => uint256) public cdpDebtRedistributionIndex; // Error trackers for the cdp redistribution calculation uint256 public lastEBTCDebtErrorRedistribution; /* Global Index for (Full Price Per Share) of underlying collateral token */ uint256 public override stEthIndex; /* Global Fee accumulator (never decreasing) per stake unit in CDPManager, similar to systemDebtRedistributionIndex */ uint256 public override systemStEthFeePerUnitIndex; /* Global Fee accumulator calculation error due to integer division, similar to redistribution calculation */ uint256 public override systemStEthFeePerUnitIndexError; /* Individual CDP Fee accumulator tracker, used to calculate fee split distribution */ mapping(bytes32 => uint256) public cdpStEthFeePerUnitIndex; /// @notice Initializes the contract with the provided addresses and sets up the required initial state /// @param _liquidationLibraryAddress The address of the Liquidation Library /// @param _authorityAddress The address of the Authority /// @param _borrowerOperationsAddress The address of Borrower Operations /// @param _collSurplusPool The address of the Collateral Surplus Pool /// @param _ebtcToken The address of the eBTC Token contract /// @param _sortedCdps The address of the Sorted CDPs contract /// @param _activePool The address of the Active Pool /// @param _priceFeed The address of the Price Feed /// @param _collateral The address of the Collateral token constructor( address _liquidationLibraryAddress, address _authorityAddress, address _borrowerOperationsAddress, address _collSurplusPool, address _ebtcToken, address _sortedCdps, address _activePool, address _priceFeed, address _collateral ) EbtcBase(_activePool, _priceFeed, _collateral) { deploymentStartTime = block.timestamp; liquidationLibrary = _liquidationLibraryAddress; _initializeAuthority(_authorityAddress); borrowerOperationsAddress = _borrowerOperationsAddress; collSurplusPool = ICollSurplusPool(_collSurplusPool); ebtcToken = IEBTCToken(_ebtcToken); sortedCdps = ISortedCdps(_sortedCdps); } /// @notice BorrowerOperations and CdpManager share reentrancy status by confirming the other's locked flag before beginning operation /// @dev This is an alternative to the more heavyweight solution of both being able to set the reentrancy flag on a 3rd contract. modifier nonReentrantSelfAndBOps() { require(locked == OPEN, "CdpManager: Reentrancy in nonReentrant call"); require( ReentrancyGuard(borrowerOperationsAddress).locked() == OPEN, "BorrowerOperations: Reentrancy in nonReentrant call" ); locked = LOCKED; _; locked = OPEN; } function _closeCdp(bytes32 _cdpId, Status closedStatus) internal { _closeCdpWithoutRemovingSortedCdps(_cdpId, closedStatus); sortedCdps.remove(_cdpId); } function _closeCdpWithoutRemovingSortedCdps(bytes32 _cdpId, Status closedStatus) internal { require( closedStatus != Status.nonExistent && closedStatus != Status.active, "CdpManagerStorage: close non-exist or non-active CDP!" ); uint256 cdpIdsArrayLength = getActiveCdpsCount(); _requireMoreThanOneCdpInSystem(cdpIdsArrayLength); _removeStake(_cdpId); Cdps[_cdpId].status = closedStatus; Cdps[_cdpId].coll = 0; Cdps[_cdpId].debt = 0; Cdps[_cdpId].liquidatorRewardShares = 0; cdpDebtRedistributionIndex[_cdpId] = 0; cdpStEthFeePerUnitIndex[_cdpId] = 0; } /* * Updates snapshots of system total stakes and total collateral, * excluding a given collateral remainder from the calculation. * Used in a liquidation sequence. * * The calculation excludes a portion of collateral that is in the ActivePool: * * the total stETH liquidator reward compensation from the liquidation sequence * * The stETH as compensation must be excluded as it is always sent out at the very end of the liquidation sequence. */ function _updateSystemSnapshotsExcludeCollRemainder(uint256 _collRemainder) internal { uint256 _totalStakesSnapshot = totalStakes; totalStakesSnapshot = _totalStakesSnapshot; uint256 _totalCollateralSnapshot = activePool.getSystemCollShares() - _collRemainder; totalCollateralSnapshot = _totalCollateralSnapshot; emit SystemSnapshotsUpdated(_totalStakesSnapshot, _totalCollateralSnapshot); } /// @dev get the pending Cdp debt "reward" (i.e. the amount of extra debt assigned to the Cdp) from liquidation redistribution events, earned by their stake function _getPendingRedistributedDebt( bytes32 _cdpId ) internal view returns (uint256 pendingEBTCDebtReward, uint256 _debtIndexDiff) { Cdp storage cdp = Cdps[_cdpId]; if (cdp.status != Status.active) { return (0, 0); } _debtIndexDiff = systemDebtRedistributionIndex - cdpDebtRedistributionIndex[_cdpId]; if (_debtIndexDiff > 0) { pendingEBTCDebtReward = (cdp.stake * _debtIndexDiff) / DECIMAL_PRECISION; } else { return (0, 0); } } /* * A Cdp has pending redistributed debt if its snapshot is less than the current rewards per-unit-staked sum: * this indicates that redistributions have occured since the snapshot was made, and the user therefore has * pending debt */ function _hasRedistributedDebt(bytes32 _cdpId) internal view returns (bool) { if (Cdps[_cdpId].status != Status.active) { return false; } return (cdpDebtRedistributionIndex[_cdpId] < systemDebtRedistributionIndex); } /// @dev Sync Cdp debt redistribution index to global value function _updateRedistributedDebtIndex(bytes32 _cdpId) internal { uint256 _systemDebtRedistributionIndex = systemDebtRedistributionIndex; cdpDebtRedistributionIndex[_cdpId] = _systemDebtRedistributionIndex; emit CdpDebtRedistributionIndexUpdated(_cdpId, _systemDebtRedistributionIndex); } /// @dev Calculate the new collateral and debt values for a given CDP, based on pending state changes function _syncAccounting(bytes32 _cdpId) internal { // Ensure global states like systemStEthFeePerUnitIndex get updated in a timely fashion // whenever there is a CDP modification operation, // such as opening, closing, adding collateral, repaying debt, or liquidating _syncGlobalAccounting(); uint256 _oldPerUnitCdp = cdpStEthFeePerUnitIndex[_cdpId]; uint256 _systemStEthFeePerUnitIndex = systemStEthFeePerUnitIndex; ( uint256 _newColl, uint256 _newDebt, uint256 _feeSplitDistributed, uint _pendingDebt, uint256 _debtIndexDelta ) = _calcSyncedAccounting(_cdpId, _oldPerUnitCdp, _systemStEthFeePerUnitIndex); // If any collShares or debt changes occured if (_feeSplitDistributed > 0 || _debtIndexDelta > 0) { Cdp storage _cdp = Cdps[_cdpId]; uint prevCollShares = _cdp.coll; uint256 prevDebt = _cdp.debt; // Apply Fee Split if (_feeSplitDistributed > 0) { _applyAccumulatedFeeSplit( _cdpId, _newColl, _feeSplitDistributed, _oldPerUnitCdp, _systemStEthFeePerUnitIndex ); } // Apply Debt Redistribution if (_debtIndexDelta > 0) { _updateRedistributedDebtIndex(_cdpId); if (prevDebt != _newDebt) { { // Apply pending debt redistribution to this CDP _cdp.debt = _newDebt; } } } emit CdpUpdated( _cdpId, ISortedCdps(sortedCdps).getOwnerAddress(_cdpId), msg.sender, prevDebt, prevCollShares, _newDebt, _newColl, _cdp.stake, CdpOperation.syncAccounting ); } // sync per stake index for given CDP if (_oldPerUnitCdp != _systemStEthFeePerUnitIndex) { cdpStEthFeePerUnitIndex[_cdpId] = _systemStEthFeePerUnitIndex; } } // Remove borrower's stake from the totalStakes sum, and set their stake to 0 function _removeStake(bytes32 _cdpId) internal { uint256 _newTotalStakes = totalStakes - Cdps[_cdpId].stake; totalStakes = _newTotalStakes; Cdps[_cdpId].stake = 0; emit TotalStakesUpdated(_newTotalStakes); } // Update borrower's stake based on their latest collateral value // and update totalStakes accordingly as well function _updateStakeAndTotalStakes(bytes32 _cdpId) internal returns (uint256) { (uint256 newStake, uint256 oldStake) = _updateStakeForCdp(_cdpId); uint256 _newTotalStakes = totalStakes + newStake - oldStake; totalStakes = _newTotalStakes; emit TotalStakesUpdated(_newTotalStakes); return newStake; } // Update borrower's stake based on their latest collateral value function _updateStakeForCdp(bytes32 _cdpId) internal returns (uint256, uint256) { Cdp storage _cdp = Cdps[_cdpId]; uint256 newStake = _computeNewStake(_cdp.coll); uint256 oldStake = _cdp.stake; _cdp.stake = newStake; return (newStake, oldStake); } // Calculate a new stake based on the snapshots of the totalStakes and totalCollateral taken at the last liquidation function _computeNewStake(uint256 _coll) internal view returns (uint256) { uint256 stake; if (totalCollateralSnapshot == 0) { stake = _coll; } else { /* * The following check holds true because: * - The system always contains >= 1 cdp * - When we close or liquidate a cdp, we redistribute the pending rewards, * so if all cdps were closed/liquidated, * rewards would’ve been emptied and totalCollateralSnapshot would be zero too. */ require(totalStakesSnapshot > 0, "CdpManagerStorage: zero totalStakesSnapshot!"); stake = (_coll * totalStakesSnapshot) / totalCollateralSnapshot; } return stake; } // --- Recovery Mode and TCR functions --- // Calculate TCR given an price, and the entire system coll and debt. function _computeTCRWithGivenSystemValues( uint256 _systemCollShares, uint256 _systemDebt, uint256 _price ) internal view returns (uint256) { uint256 _totalColl = collateral.getPooledEthByShares(_systemCollShares); return EbtcMath._computeCR(_totalColl, _systemDebt, _price); } // --- Staking-Reward Fee split functions --- /// @notice Claim split fee if there is staking-reward coming /// @notice and update global index & fee-per-unit variables /// @dev only BorrowerOperations is allowed to call this /// @dev otherwise use syncGlobalAccountingAndGracePeriod() function syncGlobalAccounting() external { _requireCallerIsBorrowerOperations(); _syncGlobalAccounting(); } function _syncGlobalAccounting() internal { (uint256 _oldIndex, uint256 _newIndex) = _readStEthIndex(); _syncStEthIndex(_oldIndex, _newIndex); if (_newIndex > _oldIndex && totalStakes > 0) { ( uint256 _feeTaken, uint256 _newFeePerUnit, uint256 _perUnitError ) = _calcSyncedGlobalAccounting(_newIndex, _oldIndex); _takeSplitAndUpdateFeePerUnit(_feeTaken, _newFeePerUnit, _perUnitError); _updateSystemSnapshotsExcludeCollRemainder(0); } } /// @notice Claim fee split, if there is staking-reward coming /// @notice and update global index & fee-per-unit variables /// @notice and toggles Grace Period accordingly. /// @dev Call this if you want to help eBTC system to accrue split fee function syncGlobalAccountingAndGracePeriod() public { _syncGlobalAccounting(); // Apply // Could trigger RM _syncGracePeriod(); // Synch Grace Period } /// @return existing(old) local stETH index AND /// @return current(new) stETH index from collateral token function _readStEthIndex() internal view returns (uint256, uint256) { return (stEthIndex, collateral.getPooledEthByShares(DECIMAL_PRECISION)); } // Update the global index via collateral token function _syncStEthIndex(uint256 _oldIndex, uint256 _newIndex) internal { if (_newIndex != _oldIndex) { stEthIndex = _newIndex; emit StEthIndexUpdated(_oldIndex, _newIndex, block.timestamp); } } /// @notice Calculate fee for given pair of collateral indexes /// @param _newIndex The value synced with stETH.getPooledEthByShares(1e18) /// @param _prevIndex The cached global value of `stEthIndex` /// @return _feeTaken The fee split in collateral token which will be deduced from current total system collateral /// @return _deltaFeePerUnit The fee split increase per unit, used to added to `systemStEthFeePerUnitIndex` /// @return _perUnitError The fee split calculation error, used to update `systemStEthFeePerUnitIndexError` function calcFeeUponStakingReward( uint256 _newIndex, uint256 _prevIndex ) public view returns (uint256, uint256, uint256) { require(_newIndex > _prevIndex, "CDPManager: only take fee with bigger new index"); uint256 deltaIndex = _newIndex - _prevIndex; uint256 deltaIndexFees = (deltaIndex * stakingRewardSplit) / MAX_REWARD_SPLIT; // we take the fee for all CDPs immediately which is scaled by index precision uint256 _deltaFeeSplit = deltaIndexFees * getSystemCollShares(); uint256 _cachedAllStakes = totalStakes; // return the values to update the global fee accumulator uint256 _feeTaken = collateral.getSharesByPooledEth(_deltaFeeSplit) / DECIMAL_PRECISION; uint256 _deltaFeeSplitShare = (_feeTaken * DECIMAL_PRECISION) + systemStEthFeePerUnitIndexError; uint256 _deltaFeePerUnit = _deltaFeeSplitShare / _cachedAllStakes; uint256 _perUnitError = _deltaFeeSplitShare - (_deltaFeePerUnit * _cachedAllStakes); return (_feeTaken, _deltaFeePerUnit, _perUnitError); } // Take the cut from staking reward // and update global fee-per-unit accumulator function _takeSplitAndUpdateFeePerUnit( uint256 _feeTaken, uint256 _newPerUnit, uint256 _newErrorPerUnit ) internal { uint256 _oldPerUnit = systemStEthFeePerUnitIndex; systemStEthFeePerUnitIndex = _newPerUnit; systemStEthFeePerUnitIndexError = _newErrorPerUnit; require(activePool.getSystemCollShares() > _feeTaken, "CDPManager: fee split is too big"); activePool.allocateSystemCollSharesToFeeRecipient(_feeTaken); emit CollateralFeePerUnitUpdated(_oldPerUnit, _newPerUnit, _feeTaken); } // Apply accumulated fee split distributed to the CDP // and update its accumulator tracker accordingly function _applyAccumulatedFeeSplit( bytes32 _cdpId, uint256 _newColl, uint256 _feeSplitDistributed, uint256 _oldPerUnitCdp, uint256 _systemStEthFeePerUnitIndex ) internal { // apply split fee to given CDP Cdps[_cdpId].coll = _newColl; emit CdpFeeSplitApplied( _cdpId, _oldPerUnitCdp, _systemStEthFeePerUnitIndex, _feeSplitDistributed, _newColl ); } /// @notice Calculate the applied split fee(scaled by 1e18) and the resulting CDP collateral share after applied /// @param _cdpId The Cdp to which the calculated split fee is going to be applied /// @param _systemStEthFeePerUnitIndex The fee-per-stake-unit value to be used in fee split calculation, could be result of calcFeeUponStakingReward() /// @return _feeSplitDistributed The applied fee split to the specified Cdp (scaled up by 1e18) /// @return _cdpCol The new collateral share of the specified Cdp after fe split applied function getAccumulatedFeeSplitApplied( bytes32 _cdpId, uint256 _systemStEthFeePerUnitIndex ) public view returns (uint256, uint256) { uint256 _cdpStEthFeePerUnitIndex = cdpStEthFeePerUnitIndex[_cdpId]; uint256 _cdpCol = Cdps[_cdpId].coll; if ( _cdpStEthFeePerUnitIndex == 0 || _cdpCol == 0 || _cdpStEthFeePerUnitIndex == _systemStEthFeePerUnitIndex ) { return (0, _cdpCol); } uint256 _feeSplitDistributed = Cdps[_cdpId].stake * (_systemStEthFeePerUnitIndex - _cdpStEthFeePerUnitIndex); uint256 _scaledCdpColl = _cdpCol * DECIMAL_PRECISION; if (_scaledCdpColl > _feeSplitDistributed) { return ( _feeSplitDistributed, (_scaledCdpColl - _feeSplitDistributed) / DECIMAL_PRECISION ); } else { // extreme unlikely case to skip fee split on this CDP to avoid revert return (0, _cdpCol); } } // -- Modifier functions -- function _requireCdpIsActive(bytes32 _cdpId) internal view { require(Cdps[_cdpId].status == Status.active, "CdpManager: Cdp does not exist or is closed"); } function _requireMoreThanOneCdpInSystem(uint256 CdpOwnersArrayLength) internal view { require( CdpOwnersArrayLength > 1 && sortedCdps.getSize() > 1, "CdpManager: Only one cdp in the system" ); } function _requireCallerIsBorrowerOperations() internal view { require( msg.sender == borrowerOperationsAddress, "CdpManager: Caller is not the BorrowerOperations contract" ); } // --- Helper functions --- /// @notice Return the Nominal Collateral Ratio (NICR) of the specified Cdp as "cached view" (maybe outdated). /// @dev Takes a cdp's pending coll and debt rewards from redistributions into account. /// @param _cdpId The CdpId whose NICR to be queried /// @return The Nominal Collateral Ratio (NICR) of the specified Cdp. /// @dev Use getSyncedNominalICR() instead if pending fee split and debt redistribution should be considered function getCachedNominalICR(bytes32 _cdpId) external view returns (uint256) { (uint256 currentEBTCDebt, uint256 currentCollShares) = getSyncedDebtAndCollShares(_cdpId); uint256 NICR = EbtcMath._computeNominalCR(currentCollShares, currentEBTCDebt); return NICR; } /// @notice Return the Nominal Collateral Ratio (NICR) of the specified Cdp. /// @dev Takes a cdp's pending coll and debt rewards as well as stETH Index into account. /// @param _cdpId The CdpId whose NICR to be queried /// @return The Nominal Collateral Ratio (NICR) of the specified Cdp with fee split and debt redistribution considered. function getSyncedNominalICR(bytes32 _cdpId) external view returns (uint256) { (uint256 _oldIndex, uint256 _newIndex) = _readStEthIndex(); (, uint256 _newGlobalSplitIdx, ) = _calcSyncedGlobalAccounting(_newIndex, _oldIndex); (uint256 _newColl, uint256 _newDebt, , uint256 _pendingDebt, ) = _calcSyncedAccounting( _cdpId, cdpStEthFeePerUnitIndex[_cdpId], _newGlobalSplitIdx /// NOTE: This is latest index ); uint256 NICR = EbtcMath._computeNominalCR(_newColl, _newDebt); return NICR; } /// @notice Return the Individual Collateral Ratio (ICR) of the specified Cdp as "cached view" (maybe outdated). /// @param _cdpId The CdpId whose ICR to be queried /// @return The Individual Collateral Ratio (ICR) of the specified Cdp. /// @dev Use getSyncedICR() instead if pending fee split and debt redistribution should be considered function getCachedICR(bytes32 _cdpId, uint256 _price) public view returns (uint256) { (uint256 currentEBTCDebt, uint256 currentCollShares) = getSyncedDebtAndCollShares(_cdpId); uint256 ICR = _calculateCR(currentCollShares, currentEBTCDebt, _price); return ICR; } function _calculateCR( uint256 currentCollShare, uint256 currentDebt, uint256 _price ) internal view returns (uint256) { uint256 _underlyingCollateral = collateral.getPooledEthByShares(currentCollShare); return EbtcMath._computeCR(_underlyingCollateral, currentDebt, _price); } /// @notice Return the pending extra debt assigned to the Cdp from liquidation redistribution, calcualted by Cdp's stake /// @param _cdpId The CdpId whose pending debt redistribution to be queried /// @return pendingEBTCDebtReward The pending debt redistribution of the specified Cdp. function getPendingRedistributedDebt( bytes32 _cdpId ) public view returns (uint256 pendingEBTCDebtReward) { (uint256 _pendingDebt, ) = _getPendingRedistributedDebt(_cdpId); return _pendingDebt; } /// @return Whether the debt redistribution tracking index of the specified Cdp is less than the global tracking one (meaning it might get pending debt redistribution) /// @param _cdpId The CdpId whose debt redistribution tracking index to be queried against the gloabl one function hasPendingRedistributedDebt(bytes32 _cdpId) public view returns (bool) { return _hasRedistributedDebt(_cdpId); } // Return the Cdps entire debt and coll struct function _getSyncedDebtAndCollShares( bytes32 _cdpId ) internal view returns (CdpDebtAndCollShares memory) { (uint256 entireDebt, uint256 entireColl) = getSyncedDebtAndCollShares(_cdpId); return CdpDebtAndCollShares(entireDebt, entireColl); } /// @notice Calculate the Cdps entire debt and coll, including pending debt redistributions and collateral reduction from split fee. /// @param _cdpId The CdpId to be queried /// @return debt The total debt value of the Cdp including debt redistribution considered /// @return coll The total collateral value of the Cdp including possible fee split considered /// @dev Should always use this as the first(default) choice for Cdp position size query function getSyncedDebtAndCollShares( bytes32 _cdpId ) public view returns (uint256 debt, uint256 coll) { (uint256 _newColl, uint256 _newDebt, , , ) = _calcSyncedAccounting( _cdpId, cdpStEthFeePerUnitIndex[_cdpId], systemStEthFeePerUnitIndex ); coll = _newColl; debt = _newDebt; } /// @dev calculate pending global state change to be applied: /// @return split fee taken (if any) AND /// @return new split index per stake unit AND /// @return new split index error function _calcSyncedGlobalAccounting( uint256 _newIndex, uint256 _oldIndex ) internal view returns (uint256, uint256, uint256) { if (_newIndex > _oldIndex && totalStakes > 0) { /// @audit-ok We don't take the fee if we had a negative rebase ( uint256 _feeTaken, uint256 _deltaFeePerUnit, uint256 _perUnitError ) = calcFeeUponStakingReward(_newIndex, _oldIndex); // calculate new split per stake unit uint256 _newPerUnit = systemStEthFeePerUnitIndex + _deltaFeePerUnit; return (_feeTaken, _newPerUnit, _perUnitError); } else { return (0, systemStEthFeePerUnitIndex, systemStEthFeePerUnitIndexError); } } /// @dev calculate pending state change to be applied for given CDP and global split index(typically already synced): /// @return new CDP collateral share after pending change applied /// @return new CDP debt after pending change applied /// @return split fee applied to given CDP /// @return redistributed debt applied to given CDP /// @return delta between debt redistribution index of given CDP and global tracking index function _calcSyncedAccounting( bytes32 _cdpId, uint256 _cdpPerUnitIdx, uint256 _systemStEthFeePerUnitIndex ) internal view returns (uint256, uint256, uint256, uint256, uint256) { uint256 _feeSplitApplied; uint256 _newCollShare = Cdps[_cdpId].coll; // processing split fee to be applied if (_cdpPerUnitIdx != _systemStEthFeePerUnitIndex && _cdpPerUnitIdx > 0) { ( uint256 _feeSplitDistributed, uint256 _newCollShareAfter ) = getAccumulatedFeeSplitApplied(_cdpId, _systemStEthFeePerUnitIndex); _feeSplitApplied = _feeSplitDistributed; _newCollShare = _newCollShareAfter; } // processing redistributed debt to be applied ( uint256 _newDebt, uint256 pendingDebtRedistributed, uint256 _debtIndexDelta ) = _getSyncedCdpDebtAndRedistribution(_cdpId); return ( _newCollShare, _newDebt, _feeSplitApplied, pendingDebtRedistributed, _debtIndexDelta ); } /// @return CDP debt and pending redistribution from liquidation applied function _getSyncedCdpDebtAndRedistribution( bytes32 _cdpId ) internal view returns (uint256, uint256, uint256) { (uint256 pendingDebtRedistributed, uint256 _debtIndexDelta) = _getPendingRedistributedDebt( _cdpId ); uint256 _newDebt = Cdps[_cdpId].debt; if (pendingDebtRedistributed > 0) { _newDebt = _newDebt + pendingDebtRedistributed; } return (_newDebt, pendingDebtRedistributed, _debtIndexDelta); } /// @notice Calculate the Cdps entire debt, including pending debt redistributions. /// @param _cdpId The CdpId to be queried /// @return _newDebt The total debt value of the Cdp including debt redistribution considered /// @dev Should always use this as the first(default) choice for Cdp debt query function getSyncedCdpDebt(bytes32 _cdpId) public view returns (uint256) { (uint256 _newDebt, , ) = _getSyncedCdpDebtAndRedistribution(_cdpId); return _newDebt; } /// @notice Calculate the Cdps entire collateral, including pending fee split to be applied /// @param _cdpId The CdpId to be queried /// @return _newColl The total collateral value of the Cdp including fee split considered /// @dev Should always use this as the first(default) choice for Cdp collateral query function getSyncedCdpCollShares(bytes32 _cdpId) public view returns (uint256) { (uint256 _oldIndex, uint256 _newIndex) = _readStEthIndex(); (, uint256 _newGlobalSplitIdx, ) = _calcSyncedGlobalAccounting(_newIndex, _oldIndex); (uint256 _newColl, , , , ) = _calcSyncedAccounting( _cdpId, cdpStEthFeePerUnitIndex[_cdpId], _newGlobalSplitIdx ); return _newColl; } /// @notice Calculate the Cdps ICR, including pending debt distribution and fee split to be applied /// @param _cdpId The CdpId to be queried /// @param _price The ETH:eBTC price to be used in ICR calculation /// @return The ICR of the Cdp including debt distribution and fee split considered /// @dev Should always use this as the first(default) choice for Cdp ICR query function getSyncedICR(bytes32 _cdpId, uint256 _price) public view returns (uint256) { uint256 _debt = getSyncedCdpDebt(_cdpId); uint256 _collShare = getSyncedCdpCollShares(_cdpId); return _calculateCR(_collShare, _debt, _price); } /// @notice return system collateral share, including pending fee split to be taken function getSyncedSystemCollShares() public view returns (uint256) { (uint256 _oldIndex, uint256 _newIndex) = _readStEthIndex(); (uint256 _feeTaken, , ) = _calcSyncedGlobalAccounting(_newIndex, _oldIndex); uint256 _systemCollShare = activePool.getSystemCollShares(); if (_feeTaken > 0) { _systemCollShare = _systemCollShare - _feeTaken; } return _systemCollShare; } /// @notice Calculate the TCR, including pending debt distribution and fee split to be taken /// @param _price The ETH:eBTC price to be used in TCR calculation /// @return The TCR of the eBTC system including debt distribution and fee split considered /// @dev Should always use this as the first(default) choice for TCR query function getSyncedTCR(uint256 _price) public view returns (uint256) { uint256 _systemCollShare = getSyncedSystemCollShares(); uint256 _systemDebt = activePool.getSystemDebt(); return _calculateCR(_systemCollShare, _systemDebt, _price); } /// @notice Get the count of active Cdps in the system /// @return The number of current active Cdps (not closed) in the system. function getActiveCdpsCount() public view override returns (uint256) { return sortedCdps.getSize(); } /// @param icr The ICR of a Cdp to check if liquidatable /// @param tcr The TCR of the eBTC system used to determine if Recovery Mode is triggered /// @return whether the Cdp of specified icr is liquidatable with specified tcr /// @dev The flag will only be set to true if enough time has passed since Grace Period starts function canLiquidateRecoveryMode(uint256 icr, uint256 tcr) public view returns (bool) { return _checkICRAgainstTCR(icr, tcr) && _recoveryModeGracePeriodPassed(); } /// @dev Check if enough time has passed for grace period after enabled function _recoveryModeGracePeriodPassed() internal view returns (bool) { // we have waited enough uint128 cachedLastGracePeriodStartTimestamp = lastGracePeriodStartTimestamp; return cachedLastGracePeriodStartTimestamp != UNSET_TIMESTAMP && block.timestamp > cachedLastGracePeriodStartTimestamp + recoveryModeGracePeriodDuration; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.17; import {Authority} from "./Authority.sol"; /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. /// @author Modified by BadgerDAO to remove owner /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol) /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) contract AuthNoOwner { event AuthorityUpdated(address indexed user, Authority indexed newAuthority); Authority private _authority; bool private _authorityInitialized; modifier requiresAuth() virtual { require(isAuthorized(msg.sender, msg.sig), "Auth: UNAUTHORIZED"); _; } function authority() public view returns (Authority) { return _authority; } function authorityInitialized() public view returns (bool) { return _authorityInitialized; } function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { Authority auth = _authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be // aware that this makes protected functions uncallable even to the owner if the authority is out of order. return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)); } /// @notice Changed constructor to initialize to allow flexiblity of constructor vs initializer use /// @notice sets authorityInitiailzed flag to ensure only one use of function _initializeAuthority(address newAuthority) internal { require(address(_authority) == address(0), "Auth: authority is non-zero"); require(!_authorityInitialized, "Auth: authority already initialized"); _authority = Authority(newAuthority); _authorityInitialized = true; emit AuthorityUpdated(address(this), Authority(newAuthority)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.17; /// @notice A generic interface for a contract which provides authorization data to an Auth instance. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol) /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) interface Authority { function canCall(address user, address target, bytes4 functionSig) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; contract BaseMath { uint256 public constant DECIMAL_PRECISION = 1e18; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./BaseMath.sol"; import "./EbtcMath.sol"; import "../Interfaces/IActivePool.sol"; import "../Interfaces/IPriceFeed.sol"; import "../Interfaces/IEbtcBase.sol"; import "../Dependencies/ICollateralToken.sol"; /* * Base contract for CdpManager, BorrowerOperations. Contains global system constants and * common functions. */ contract EbtcBase is BaseMath, IEbtcBase { // Collateral Ratio applied for Liquidation Incentive // i.e., liquidator repay $1 worth of debt to get back $1.03 worth of collateral uint256 public constant LICR = 1030000000000000000; // 103% // Minimum collateral ratio for individual cdps uint256 public constant MCR = 1100000000000000000; // 110% // Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered. uint256 public constant CCR = 1250000000000000000; // 125% // Amount of stETH collateral to be locked in active pool on opening cdps uint256 public constant LIQUIDATOR_REWARD = 2e17; // Minimum amount of stETH collateral a CDP must have uint256 public constant MIN_NET_STETH_BALANCE = 2e18; uint256 public constant PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5% uint256 public constant BORROWING_FEE_FLOOR = 0; // 0.5% uint256 public constant STAKING_REWARD_SPLIT = 5_000; // taking 50% cut from staking reward uint256 public constant MAX_REWARD_SPLIT = 10_000; uint256 public constant MIN_CHANGE = 1000; IActivePool public immutable activePool; IPriceFeed public immutable override priceFeed; // the only collateral token allowed in CDP ICollateralToken public immutable collateral; /// @notice Initializes the contract with the provided addresses /// @param _activePoolAddress The address of the ActivePool contract /// @param _priceFeedAddress The address of the PriceFeed contract /// @param _collateralAddress The address of the CollateralToken contract constructor(address _activePoolAddress, address _priceFeedAddress, address _collateralAddress) { activePool = IActivePool(_activePoolAddress); priceFeed = IPriceFeed(_priceFeedAddress); collateral = ICollateralToken(_collateralAddress); } // --- Gas compensation functions --- function _calcNetStEthBalance(uint256 _stEthBalance) internal pure returns (uint256) { return _stEthBalance - LIQUIDATOR_REWARD; } /// @notice Get the entire system collateral /// @notice Entire system collateral = collateral allocated to system in ActivePool, using it's internal accounting /// @dev Collateral tokens stored in ActivePool for liquidator rewards, fees, or coll in CollSurplusPool, are not included function getSystemCollShares() public view returns (uint256 entireSystemColl) { return (activePool.getSystemCollShares()); } /** @notice Get the entire system debt @notice Entire system collateral = collateral stored in ActivePool, using their internal accounting */ function _getSystemDebt() internal view returns (uint256 entireSystemDebt) { return (activePool.getSystemDebt()); } function _getCachedTCR(uint256 _price) internal view returns (uint256 TCR) { (TCR, , ) = _getTCRWithSystemDebtAndCollShares(_price); } function _getTCRWithSystemDebtAndCollShares( uint256 _price ) internal view returns (uint256 TCR, uint256 _coll, uint256 _debt) { uint256 systemCollShares = getSystemCollShares(); uint256 systemDebt = _getSystemDebt(); uint256 _systemStEthBalance = collateral.getPooledEthByShares(systemCollShares); TCR = EbtcMath._computeCR(_systemStEthBalance, systemDebt, _price); return (TCR, systemCollShares, systemDebt); } function _checkRecoveryMode(uint256 _price) internal view returns (bool) { return _checkRecoveryModeForTCR(_getCachedTCR(_price)); } function _checkRecoveryModeForTCR(uint256 _tcr) internal view returns (bool) { return _tcr < CCR; } function _requireUserAcceptsFee( uint256 _fee, uint256 _amount, uint256 _maxFeePercentage ) internal pure { uint256 feePercentage = (_fee * DECIMAL_PRECISION) / _amount; require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum"); } // Convert debt denominated in ETH to debt denominated in BTC given that _price is ETH/BTC // _debt is denominated in ETH // _price is ETH/BTC function _convertDebtDenominationToBtc( uint256 _debt, uint256 _price ) internal pure returns (uint256) { return (_debt * _price) / DECIMAL_PRECISION; } /// @dev return true if given ICR is qualified for liquidation compared to configured threshold /// @dev this function ONLY checks numbers not check grace period switch for Recovery Mode function _checkICRAgainstLiqThreshold(uint256 _icr, uint _tcr) internal view returns (bool) { // Either undercollateralized // OR, it's RM AND they meet the requirement // Swapped Requirement && RM to save gas return _checkICRAgainstMCR(_icr) || (_checkICRAgainstTCR(_icr, _tcr) && _checkRecoveryModeForTCR(_tcr)); } /// @dev return true if given ICR is qualified for liquidation compared to MCR function _checkICRAgainstMCR(uint256 _icr) internal view returns (bool) { return _icr < MCR; } /// @dev return true if given ICR is qualified for liquidation compared to TCR /// @dev typically used in Recovery Mode function _checkICRAgainstTCR(uint256 _icr, uint _tcr) internal view returns (bool) { /// @audit is _icr <= _tcr more dangerous for overal system safety? /// @audit Should we use _icr < CCR to allow any risky CDP being liquidated? return _icr <= _tcr; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; library EbtcMath { uint256 internal constant DECIMAL_PRECISION = 1e18; uint256 public constant MAX_TCR = type(uint256).max; /* Precision for Nominal ICR (independent of price). Rationale for the value: * * - Making it “too high” could lead to overflows. * - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division. * * This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH, * and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator. * */ uint256 internal constant NICR_PRECISION = 1e20; function _min(uint256 _a, uint256 _b) internal pure returns (uint256) { return (_a < _b) ? _a : _b; } function _max(uint256 _a, uint256 _b) internal pure returns (uint256) { return (_a >= _b) ? _a : _b; } /** * credit to OpenZeppelin * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "EbtcMath: downcast to uint128 will overflow"); return uint128(value); } /* * Multiply two decimal numbers and use normal rounding rules: * -round product up if 19'th mantissa digit >= 5 * -round product down if 19'th mantissa digit < 5 * * Used only inside the exponentiation, _decPow(). */ function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) { uint256 prod_xy = x * y; decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION; } /* * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n. * * Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity. * * Called by two functions that represent time in units of minutes: * 1) CdpManager._calcDecayedBaseRate * 2) CommunityIssuance._getCumulativeIssuanceFraction * * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals * "minutes in 1000 years": 60 * 24 * 365 * 1000 * * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be * negligibly different from just passing the cap, since: * * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible */ function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint256) { if (_minutes > 525600000) { _minutes = 525600000; } // cap to avoid overflow if (_minutes == 0) { return DECIMAL_PRECISION; } uint256 y = DECIMAL_PRECISION; uint256 x = _base; uint256 n = _minutes; // Exponentiation-by-squaring while (n > 1) { if (n % 2 == 0) { x = decMul(x, x); n = n / 2; } else { // if (n % 2 != 0) y = decMul(x, y); x = decMul(x, x); n = (n - 1) / 2; } } return decMul(x, y); } function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint256) { return (_a >= _b) ? (_a - _b) : (_b - _a); } function _computeNominalCR(uint256 _collShares, uint256 _debt) internal pure returns (uint256) { if (_debt > 0) { return (_collShares * NICR_PRECISION) / _debt; } // Return the maximal value for uint256 if the Cdp has a debt of 0. Represents "infinite" CR. else { // if (_debt == 0) return MAX_TCR; } } /// @dev Compute collateralization ratio, given stETH balance, price, and debt balance function _computeCR( uint256 _stEthBalance, uint256 _debt, uint256 _price ) internal pure returns (uint256) { if (_debt > 0) { uint256 newCollRatio = (_stEthBalance * _price) / _debt; return newCollRatio; } // Return the maximal value for uint256 if the Cdp has a debt of 0. Represents "infinite" CR. else { // if (_debt == 0) return MAX_TCR; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IERC20.sol"; /** * Based on the stETH: * - https://docs.lido.fi/contracts/lido# */ interface ICollateralToken is IERC20 { // Returns the amount of shares that corresponds to _ethAmount protocol-controlled Ether function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256); // Returns the amount of Ether that corresponds to _sharesAmount token shares function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256); // Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account. function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256); // Returns the amount of shares owned by _account function sharesOf(address _account) external view returns (uint256); // Returns authorized oracle address function getOracle() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * Based on the stETH: * - https://docs.lido.fi/contracts/lido# */ interface ICollateralTokenOracle { // Return beacon specification data. function getBeaconSpec() external view returns ( uint64 epochsPerFrame, uint64 slotsPerEpoch, uint64 secondsPerSlot, uint64 genesisTime ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * Based on the OpenZeppelin IER20 interface: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol * * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); function increaseAllowance(address spender, uint256 addedValue) external returns (bool); function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); /** * @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 pragma solidity 0.8.17; /** * @dev Interface of the ERC2612 standard as defined in the EIP. * * Adds the {permit} method, which can be used to change one's * {IERC20-allowance} without having to send a transaction, by signing a * message. This allows users to spend tokens without having to hold Ether. * * See https://eips.ethereum.org/EIPS/eip-2612. * * Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/ */ interface IERC2612 { /** * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, * given `owner`'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current ERC2612 nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases `owner`'s nonce by one. This * prevents a signature from being used multiple times. * * `owner` can limit the time a Permit is valid for by setting `deadline` to * a value in the near future. The deadline argument can be set to uint256(-1) to * create Permits that effectively never expire. */ function nonces(address owner) external view returns (uint256); function version() external view returns (string memory); function permitTypeHash() external view returns (bytes32); function domainSeparator() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) pragma solidity 0.8.17; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. * @dev BadgerDAO: Simplified to the core delegation functionality, without any additional features. */ contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.17; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 internal constant OPEN = 1; uint256 internal constant LOCKED = 2; uint256 public locked = OPEN; modifier nonReentrant() virtual { require(locked == OPEN, "ReentrancyGuard: Reentrancy in nonReentrant call"); locked = LOCKED; _; locked = OPEN; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IPool.sol"; import "./ITwapWeightedObserver.sol"; interface IActivePool is IPool, ITwapWeightedObserver { // --- Events --- event ActivePoolEBTCDebtUpdated(uint256 _EBTCDebt); event SystemCollSharesUpdated(uint256 _coll); event FeeRecipientClaimableCollSharesIncreased(uint256 _coll, uint256 _fee); event FeeRecipientClaimableCollSharesDecreased(uint256 _coll, uint256 _fee); event FlashLoanSuccess( address indexed _receiver, address indexed _token, uint256 _amount, uint256 _fee ); event SweepTokenSuccess(address indexed _token, uint256 _amount, address indexed _recipient); // --- Functions --- function transferSystemCollShares(address _account, uint256 _amount) external; function increaseSystemCollShares(uint256 _value) external; function transferSystemCollSharesAndLiquidatorReward( address _account, uint256 _shares, uint256 _liquidatorRewardShares ) external; function allocateSystemCollSharesToFeeRecipient(uint256 _shares) external; function claimFeeRecipientCollShares(uint256 _shares) external; function feeRecipientAddress() external view returns (address); function getFeeRecipientClaimableCollShares() external view returns (uint256); }
// SPDX-License Identifier: MIT pragma solidity 0.8.17; interface IBaseTwapWeightedObserver { // NOTE: Packing manually is cheaper, but this is simpler to understand and follow struct PackedData { // Slot 0 // Seconds in a year: 3.154e+7 /// @dev Accumulator value recorded for TWAP Observer until last update uint128 observerCumuVal; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171 /// @dev Accumulator for TWAP globally uint128 accumulator; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171 // NOTE: We can further compress this slot but we will not be able to use only one (see u72 impl) /// So what's the point of making the code more complex? // Slot 1 /// @dev last update timestamp for TWAP Observer uint64 lastObserved; // Thousands of Years, if we use relative time we can use u32 | Relative to deploy time (as immutable) /// @dev last update timestamp for TWAP global track(spot) value uint64 lastAccrued; // Thousands of years // Expect eBTC debt to never surpass 100e27, which is 100 BILLION eBTC // log_2(100e27) = 96.3359147517 | log_2(100e27 / 1e18) = 36.5412090438 // We could use a u64 /// @dev average value since last observe uint128 lastObservedAverage; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IEbtcBase.sol"; import "./ICdpManagerData.sol"; // Common interface for the Cdp Manager. interface ICdpManager is IEbtcBase, ICdpManagerData { // --- Functions --- function liquidate(bytes32 _cdpId) external; function partiallyLiquidate( bytes32 _cdpId, uint256 _partialAmount, bytes32 _upperPartialHint, bytes32 _lowerPartialHint ) external; function batchLiquidateCdps(bytes32[] calldata _cdpArray) external; function redeemCollateral( uint256 _EBTCAmount, bytes32 _firstRedemptionHint, bytes32 _upperPartialRedemptionHint, bytes32 _lowerPartialRedemptionHint, uint256 _partialRedemptionHintNICR, uint256 _maxIterations, uint256 _maxFee ) external; function updateStakeAndTotalStakes(bytes32 _cdpId) external returns (uint256); function syncAccounting(bytes32 _cdpId) external; function closeCdp(bytes32 _cdpId, address _borrower, uint256 _debt, uint256 _coll) external; function getRedemptionRate() external view returns (uint256); function getRedemptionRateWithDecay() external view returns (uint256); function getRedemptionFeeWithDecay(uint256 _stETHToRedeem) external view returns (uint256); function getCdpStatus(bytes32 _cdpId) external view returns (uint256); function getCdpStake(bytes32 _cdpId) external view returns (uint256); function getCdpDebt(bytes32 _cdpId) external view returns (uint256); function getCdpCollShares(bytes32 _cdpId) external view returns (uint256); function getCdpLiquidatorRewardShares(bytes32 _cdpId) external view returns (uint); function initializeCdp( bytes32 _cdpId, uint256 _debt, uint256 _coll, uint256 _liquidatorRewardShares, address _borrower ) external; function updateCdp( bytes32 _cdpId, address _borrower, uint256 _coll, uint256 _debt, uint256 _newColl, uint256 _newDebt ) external; function getCachedTCR(uint256 _price) external view returns (uint256); function checkRecoveryMode(uint256 _price) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./ICollSurplusPool.sol"; import "./IEBTCToken.sol"; import "./ISortedCdps.sol"; import "./IActivePool.sol"; import "./IRecoveryModeGracePeriod.sol"; import "../Dependencies/ICollateralTokenOracle.sol"; // Common interface for the Cdp Manager. interface ICdpManagerData is IRecoveryModeGracePeriod { // --- Events --- event StakingRewardSplitSet(uint256 _stakingRewardSplit); event RedemptionFeeFloorSet(uint256 _redemptionFeeFloor); event MinuteDecayFactorSet(uint256 _minuteDecayFactor); event BetaSet(uint256 _beta); event RedemptionsPaused(bool _paused); event Liquidation(uint256 _liquidatedDebt, uint256 _liquidatedColl, uint256 _liqReward); event Redemption( uint256 _debtToRedeemExpected, uint256 _debtToRedeemActual, uint256 _collSharesSent, uint256 _feeCollShares, address indexed _redeemer ); event CdpUpdated( bytes32 indexed _cdpId, address indexed _borrower, address indexed _executor, uint256 _oldDebt, uint256 _oldCollShares, uint256 _debt, uint256 _collShares, uint256 _stake, CdpOperation _operation ); event CdpLiquidated( bytes32 indexed _cdpId, address indexed _borrower, uint _debt, uint _collShares, CdpOperation _operation, address indexed _liquidator, uint _premiumToLiquidator ); event CdpPartiallyLiquidated( bytes32 indexed _cdpId, address indexed _borrower, uint256 _debt, uint256 _collShares, CdpOperation operation, address indexed _liquidator, uint _premiumToLiquidator ); event BaseRateUpdated(uint256 _baseRate); event LastRedemptionTimestampUpdated(uint256 _lastFeeOpTime); event TotalStakesUpdated(uint256 _newTotalStakes); event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot); event SystemDebtRedistributionIndexUpdated(uint256 _systemDebtRedistributionIndex); event CdpDebtRedistributionIndexUpdated(bytes32 _cdpId, uint256 _cdpDebtRedistributionIndex); event CdpArrayIndexUpdated(bytes32 _cdpId, uint256 _newIndex); event StEthIndexUpdated(uint256 _oldIndex, uint256 _newIndex, uint256 _updTimestamp); event CollateralFeePerUnitUpdated(uint256 _oldPerUnit, uint256 _newPerUnit, uint256 _feeTaken); event CdpFeeSplitApplied( bytes32 _cdpId, uint256 _oldPerUnitCdp, uint256 _newPerUnitCdp, uint256 _collReduced, uint256 _collLeft ); enum CdpOperation { openCdp, closeCdp, adjustCdp, syncAccounting, liquidateInNormalMode, liquidateInRecoveryMode, redeemCollateral, partiallyLiquidate, failedPartialRedemption } enum Status { nonExistent, active, closedByOwner, closedByLiquidation, closedByRedemption } // Store the necessary data for a cdp struct Cdp { uint256 debt; uint256 coll; uint256 stake; uint128 liquidatorRewardShares; Status status; } /* * --- Variable container structs for liquidations --- * * These structs are used to hold, return and assign variables inside the liquidation functions, * in order to avoid the error: "CompilerError: Stack too deep". **/ struct CdpDebtAndCollShares { uint256 debt; uint256 collShares; } struct LiquidationLocals { bytes32 cdpId; uint256 partialAmount; // used only for partial liquidation, default 0 means full liquidation uint256 price; uint256 ICR; bytes32 upperPartialHint; bytes32 lowerPartialHint; bool recoveryModeAtStart; uint256 TCR; uint256 totalSurplusCollShares; uint256 totalCollSharesToSend; uint256 totalDebtToBurn; uint256 totalDebtToRedistribute; uint256 totalLiquidatorRewardCollShares; } struct LiquidationRecoveryModeLocals { uint256 entireSystemDebt; uint256 entireSystemColl; uint256 totalDebtToBurn; uint256 totalCollSharesToSend; uint256 totalSurplusCollShares; bytes32 cdpId; uint256 price; uint256 ICR; uint256 totalDebtToRedistribute; uint256 totalLiquidatorRewardCollShares; } struct LocalVariables_OuterLiquidationFunction { uint256 price; bool recoveryModeAtStart; uint256 liquidatedDebt; uint256 liquidatedColl; } struct LocalVariables_LiquidationSequence { uint256 i; uint256 ICR; bytes32 cdpId; bool backToNormalMode; uint256 entireSystemDebt; uint256 entireSystemColl; uint256 price; uint256 TCR; } struct SingleRedemptionInputs { bytes32 cdpId; uint256 maxEBTCamount; uint256 price; bytes32 upperPartialRedemptionHint; bytes32 lowerPartialRedemptionHint; uint256 partialRedemptionHintNICR; } struct LiquidationValues { uint256 entireCdpDebt; uint256 debtToBurn; uint256 totalCollToSendToLiquidator; uint256 debtToRedistribute; uint256 collSurplus; uint256 liquidatorCollSharesReward; } struct LiquidationTotals { uint256 totalDebtInSequence; uint256 totalDebtToBurn; uint256 totalCollToSendToLiquidator; uint256 totalDebtToRedistribute; uint256 totalCollSurplus; uint256 totalCollReward; } // --- Variable container structs for redemptions --- struct RedemptionTotals { uint256 remainingDebtToRedeem; uint256 debtToRedeem; uint256 collSharesDrawn; uint256 totalCollSharesSurplus; uint256 feeCollShares; uint256 collSharesToRedeemer; uint256 decayedBaseRate; uint256 price; uint256 systemDebtAtStart; uint256 twapSystemDebtAtStart; uint256 systemCollSharesAtStart; uint256 tcrAtStart; } struct SingleRedemptionValues { uint256 debtToRedeem; uint256 collSharesDrawn; uint256 collSurplus; uint256 liquidatorRewardShares; bool cancelledPartial; bool fullRedemption; uint256 newPartialNICR; } function getActiveCdpsCount() external view returns (uint256); function totalStakes() external view returns (uint256); function ebtcToken() external view returns (IEBTCToken); function systemStEthFeePerUnitIndex() external view returns (uint256); function systemStEthFeePerUnitIndexError() external view returns (uint256); function stEthIndex() external view returns (uint256); function calcFeeUponStakingReward( uint256 _newIndex, uint256 _prevIndex ) external view returns (uint256, uint256, uint256); function syncGlobalAccounting() external; // Accrues StEthFeeSplit without influencing Grace Period function syncGlobalAccountingAndGracePeriod() external; // Accrues StEthFeeSplit and influences Grace Period function getAccumulatedFeeSplitApplied( bytes32 _cdpId, uint256 _systemStEthFeePerUnitIndex ) external view returns (uint256, uint256); function getCachedNominalICR(bytes32 _cdpId) external view returns (uint256); function getCachedICR(bytes32 _cdpId, uint256 _price) external view returns (uint256); function getSyncedCdpDebt(bytes32 _cdpId) external view returns (uint256); function getSyncedCdpCollShares(bytes32 _cdpId) external view returns (uint256); function getSyncedICR(bytes32 _cdpId, uint256 _price) external view returns (uint256); function getSyncedTCR(uint256 _price) external view returns (uint256); function getSyncedSystemCollShares() external view returns (uint256); function getSyncedNominalICR(bytes32 _cdpId) external view returns (uint256); function getPendingRedistributedDebt(bytes32 _cdpId) external view returns (uint256); function hasPendingRedistributedDebt(bytes32 _cdpId) external view returns (bool); function getSyncedDebtAndCollShares( bytes32 _cdpId ) external view returns (uint256 debt, uint256 collShares); function canLiquidateRecoveryMode(uint256 icr, uint256 tcr) external view returns (bool); function totalCollateralSnapshot() external view returns (uint256); function totalStakesSnapshot() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface ICollSurplusPool { // --- Events --- event SurplusCollSharesAdded( bytes32 indexed _cdpId, address indexed _account, uint256 _claimableSurplusCollShares, uint256 _surplusCollSharesAddedFromCollateral, uint256 _surplusCollSharesAddedFromLiquidatorReward ); event CollSharesTransferred(address indexed _to, uint256 _amount); event SweepTokenSuccess(address indexed _token, uint256 _amount, address indexed _recipient); // --- Contract setters --- function getTotalSurplusCollShares() external view returns (uint256); function getSurplusCollShares(address _account) external view returns (uint256); function increaseSurplusCollShares( bytes32 _cdpId, address _account, uint256 _collateralShares, uint256 _liquidatorRewardShares ) external; function claimSurplusCollShares(address _account) external; function increaseTotalSurplusCollShares(uint256 _value) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IPriceFeed.sol"; interface IEbtcBase { function priceFeed() external view returns (IPriceFeed); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../Dependencies/IERC20.sol"; import "../Dependencies/IERC2612.sol"; interface IEBTCToken is IERC20, IERC2612 { // --- Functions --- function mint(address _account, uint256 _amount) external; function burn(address _account, uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; // Common interface for the Pools. interface IPool { // --- Events --- event ETHBalanceUpdated(uint256 _newBalance); event EBTCBalanceUpdated(uint256 _newBalance); event CollSharesTransferred(address indexed _to, uint256 _amount); // --- Functions --- function getSystemCollShares() external view returns (uint256); function getSystemDebt() external view returns (uint256); function increaseSystemDebt(uint256 _amount) external; function decreaseSystemDebt(uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IPriceFeed { // --- Events --- event LastGoodPriceUpdated(uint256 _lastGoodPrice); event PriceFeedStatusChanged(Status newStatus); event FallbackCallerChanged( address indexed _oldFallbackCaller, address indexed _newFallbackCaller ); event UnhealthyFallbackCaller(address indexed _fallbackCaller, uint256 timestamp); event CollateralFeedSourceUpdated(address indexed stEthFeed); // --- Structs --- struct ChainlinkResponse { uint80 roundEthBtcId; uint80 roundStEthEthId; uint256 answer; uint256 timestampEthBtc; uint256 timestampStEthEth; bool success; } struct FallbackResponse { uint256 answer; uint256 timestamp; bool success; } // --- Enum --- enum Status { chainlinkWorking, usingFallbackChainlinkUntrusted, bothOraclesUntrusted, usingFallbackChainlinkFrozen, usingChainlinkFallbackUntrusted } // --- Function --- function fetchPrice() external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; // Interface for State Updates that can trigger RM Liquidations interface IRecoveryModeGracePeriod { event TCRNotified(uint256 TCR); /// NOTE: Mostly for debugging to ensure synch // NOTE: Ts is implicit in events (it's added by GETH) event GracePeriodStart(); event GracePeriodEnd(); event GracePeriodDurationSet(uint256 _recoveryModeGracePeriodDuration); function notifyStartGracePeriod(uint256 tcr) external; function notifyEndGracePeriod(uint256 tcr) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; // Common interface for the SortedCdps Doubly Linked List. interface ISortedCdps { // --- Events --- event NodeAdded(bytes32 _id, uint _NICR); event NodeRemoved(bytes32 _id); // --- Functions --- function remove(bytes32 _id) external; function batchRemove(bytes32[] memory _ids) external; function reInsert(bytes32 _id, uint256 _newICR, bytes32 _prevId, bytes32 _nextId) external; function contains(bytes32 _id) external view returns (bool); function isFull() external view returns (bool); function isEmpty() external view returns (bool); function getSize() external view returns (uint256); function getMaxSize() external view returns (uint256); function getFirst() external view returns (bytes32); function getLast() external view returns (bytes32); function getNext(bytes32 _id) external view returns (bytes32); function getPrev(bytes32 _id) external view returns (bytes32); function validInsertPosition( uint256 _ICR, bytes32 _prevId, bytes32 _nextId ) external view returns (bool); function findInsertPosition( uint256 _ICR, bytes32 _prevId, bytes32 _nextId ) external view returns (bytes32, bytes32); function insert( address owner, uint256 _ICR, bytes32 _prevId, bytes32 _nextId ) external returns (bytes32); function getOwnerAddress(bytes32 _id) external pure returns (address); function nonExistId() external view returns (bytes32); function cdpCountOf(address owner) external view returns (uint256); function getCdpCountOf( address owner, bytes32 startNodeId, uint maxNodes ) external view returns (uint256, bytes32); function getCdpsOf(address owner) external view returns (bytes32[] memory); function getAllCdpsOf( address owner, bytes32 startNodeId, uint maxNodes ) external view returns (bytes32[] memory, uint256, bytes32); function cdpOfOwnerByIndex(address owner, uint256 index) external view returns (bytes32); function cdpOfOwnerByIdx( address owner, uint256 index, bytes32 startNodeId, uint maxNodes ) external view returns (bytes32, bool); function toCdpId( address owner, uint256 blockHeight, uint256 nonce ) external pure returns (bytes32); function nextCdpNonce() external view returns (uint256); }
// SPDX-License Identifier: MIT pragma solidity 0.8.17; import {IBaseTwapWeightedObserver} from "./IBaseTwapWeightedObserver.sol"; interface ITwapWeightedObserver is IBaseTwapWeightedObserver { event TwapDisabled(); function PERIOD() external view returns (uint256); function valueToTrack() external view returns (uint128); function timeToAccrue() external view returns (uint64); function getLatestAccumulator() external view returns (uint128); function observe() external returns (uint256); function update() external; function twapDisabled() external view returns (bool); }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_liquidationLibraryAddress","type":"address"},{"internalType":"address","name":"_authorityAddress","type":"address"},{"internalType":"address","name":"_borrowerOperationsAddress","type":"address"},{"internalType":"address","name":"_collSurplusPoolAddress","type":"address"},{"internalType":"address","name":"_ebtcTokenAddress","type":"address"},{"internalType":"address","name":"_sortedCdpsAddress","type":"address"},{"internalType":"address","name":"_activePoolAddress","type":"address"},{"internalType":"address","name":"_priceFeedAddress","type":"address"},{"internalType":"address","name":"_collTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_baseRate","type":"uint256"}],"name":"BaseRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_beta","type":"uint256"}],"name":"BetaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"}],"name":"CdpArrayIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_cdpDebtRedistributionIndex","type":"uint256"}],"name":"CdpDebtRedistributionIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_oldPerUnitCdp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newPerUnitCdp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collReduced","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collLeft","type":"uint256"}],"name":"CdpFeeSplitApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collShares","type":"uint256"},{"indexed":false,"internalType":"enum ICdpManagerData.CdpOperation","name":"_operation","type":"uint8"},{"indexed":true,"internalType":"address","name":"_liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"_premiumToLiquidator","type":"uint256"}],"name":"CdpLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collShares","type":"uint256"},{"indexed":false,"internalType":"enum ICdpManagerData.CdpOperation","name":"operation","type":"uint8"},{"indexed":true,"internalType":"address","name":"_liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"_premiumToLiquidator","type":"uint256"}],"name":"CdpPartiallyLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"uint256","name":"_oldDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_oldCollShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_stake","type":"uint256"},{"indexed":false,"internalType":"enum ICdpManagerData.CdpOperation","name":"_operation","type":"uint8"}],"name":"CdpUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldPerUnit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newPerUnit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_feeTaken","type":"uint256"}],"name":"CollateralFeePerUnitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_recoveryModeGracePeriodDuration","type":"uint256"}],"name":"GracePeriodDurationSet","type":"event"},{"anonymous":false,"inputs":[],"name":"GracePeriodEnd","type":"event"},{"anonymous":false,"inputs":[],"name":"GracePeriodStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastFeeOpTime","type":"uint256"}],"name":"LastRedemptionTimestampUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_liquidatedDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_liquidatedColl","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_liqReward","type":"uint256"}],"name":"Liquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minuteDecayFactor","type":"uint256"}],"name":"MinuteDecayFactorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_debtToRedeemExpected","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_debtToRedeemActual","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collSharesSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_feeCollShares","type":"uint256"},{"indexed":true,"internalType":"address","name":"_redeemer","type":"address"}],"name":"Redemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_redemptionFeeFloor","type":"uint256"}],"name":"RedemptionFeeFloorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_paused","type":"bool"}],"name":"RedemptionsPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_updTimestamp","type":"uint256"}],"name":"StEthIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_stakingRewardSplit","type":"uint256"}],"name":"StakingRewardSplitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_systemDebtRedistributionIndex","type":"uint256"}],"name":"SystemDebtRedistributionIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_totalStakesSnapshot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalCollateralSnapshot","type":"uint256"}],"name":"SystemSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"TCR","type":"uint256"}],"name":"TCRNotified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newTotalStakes","type":"uint256"}],"name":"TotalStakesUpdated","type":"event"},{"inputs":[],"name":"BORROWING_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"Cdps","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint128","name":"liquidatorRewardShares","type":"uint128"},{"internalType":"enum ICdpManagerData.Status","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATOR_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINUTE_DECAY_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARD_SPLIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_GRACE_PERIOD","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CHANGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_MINUTE_DECAY_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_NET_STETH_BALANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_REDEMPTION_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SECONDS_IN_ONE_MINUTE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKING_REWARD_SPLIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNSET_TIMESTAMP","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activePool","outputs":[{"internalType":"contract IActivePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorityInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_cdpArray","type":"bytes32[]"}],"name":"batchLiquidateCdps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"beta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOperationsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newIndex","type":"uint256"},{"internalType":"uint256","name":"_prevIndex","type":"uint256"}],"name":"calcFeeUponStakingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"icr","type":"uint256"},{"internalType":"uint256","name":"tcr","type":"uint256"}],"name":"canLiquidateRecoveryMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"cdpDebtRedistributionIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"cdpStEthFeePerUnitIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_systemCollShares","type":"uint256"},{"internalType":"uint256","name":"_systemDebt","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"checkPotentialRecoveryMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"checkRecoveryMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_coll","type":"uint256"}],"name":"closeCdp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ICollateralToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ebtcToken","outputs":[{"internalType":"contract IEBTCToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_systemStEthFeePerUnitIndex","type":"uint256"}],"name":"getAccumulatedFeeSplitApplied","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveCdpsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getCachedICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCachedNominalICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getCachedTCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCdpCollShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCdpDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCdpLiquidatorRewardShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCdpStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getCdpStatus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDeploymentStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getPendingRedistributedDebt","outputs":[{"internalType":"uint256","name":"pendingEBTCDebtReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stETHToRedeem","type":"uint256"}],"name":"getRedemptionFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getSyncedCdpCollShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getSyncedCdpDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getSyncedDebtAndCollShares","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getSyncedICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getSyncedNominalICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSyncedSystemCollShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getSyncedTCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSystemCollShares","outputs":[{"internalType":"uint256","name":"entireSystemColl","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSystemDebt","outputs":[{"internalType":"uint256","name":"entireSystemDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"hasPendingRedistributedDebt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_coll","type":"uint256"},{"internalType":"uint256","name":"_liquidatorRewardShares","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"initializeCdp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastEBTCDebtErrorRedistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastGracePeriodStartTimestamp","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRedemptionTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationLibrary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"locked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minuteDecayFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tcr","type":"uint256"}],"name":"notifyEndGracePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tcr","type":"uint256"}],"name":"notifyStartGracePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_partialAmount","type":"uint256"},{"internalType":"bytes32","name":"_upperPartialHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerPartialHint","type":"bytes32"}],"name":"partiallyLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recoveryModeGracePeriodDuration","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"bytes32","name":"_firstRedemptionHint","type":"bytes32"},{"internalType":"bytes32","name":"_upperPartialRedemptionHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerPartialRedemptionHint","type":"bytes32"},{"internalType":"uint256","name":"_partialRedemptionHintNICR","type":"uint256"},{"internalType":"uint256","name":"_maxIterations","type":"uint256"},{"internalType":"uint256","name":"_maxFeePercentage","type":"uint256"}],"name":"redeemCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionFeeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redemptionsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_beta","type":"uint256"}],"name":"setBeta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_gracePeriod","type":"uint128"}],"name":"setGracePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minuteDecayFactor","type":"uint256"}],"name":"setMinuteDecayFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_redemptionFeeFloor","type":"uint256"}],"name":"setRedemptionFeeFloor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setRedemptionsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakingRewardSplit","type":"uint256"}],"name":"setStakingRewardSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedCdps","outputs":[{"internalType":"contract ISortedCdps","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stEthIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingRewardSplit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"syncAccounting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"syncGlobalAccounting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"syncGlobalAccountingAndGracePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"systemDebtRedistributionIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"systemStEthFeePerUnitIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"systemStEthFeePerUnitIndexError","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakesSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_coll","type":"uint256"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_newColl","type":"uint256"},{"internalType":"uint256","name":"_newDebt","type":"uint256"}],"name":"updateCdp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"updateStakeAndTotalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106105305760003560e01c8063882a7a37116102af578063b835f03211610172578063d8dfeb45116100d9578063e825665111610092578063e825665114610c74578063ea6932b314610c83578063ec93e2bc14610c8c578063f869817f14610cac578063f92d343314610cbf578063facc051114610cc757600080fd5b8063d8dfeb4514610c00578063d9901b9414610c27578063db4159ae14610c30578063e20217be14610c43578063e3bf120d14610c63578063e3f6838814610c6c57600080fd5b8063c52861f21161012b578063c52861f214610bad578063cd1d004014610bb5578063ce292b7614610bc8578063cf30901214610bd1578063d3a0b81014610bda578063d5b3563514610bed57600080fd5b8063b835f03214610b3d578063bc006c3814610b50578063be6b86af14610b59578063bf7e214f14610b6c578063bf9befb114610b7d578063c526a6b014610b8657600080fd5b8063a20baee611610216578063a9beb765116101cf578063a9beb76514610ae0578063adf6cd1614610ae9578063ae61734614610af1578063b5b9c01714610afa578063b6fe918a14610b03578063b7f8cf9b14610b1657600080fd5b8063a20baee6146109f6578063a33aacfd14610a05578063a3f4df7e14610a60578063a60b578d14610a96578063a839d19114610aa9578063a8b54b0914610ad757600080fd5b806397bc1e1b1161026857806397bc1e1b1461099e57806398ad5a7e146109b05780639ac2e9f0146109c35780639c77f4cf146109d15780639dc55330146109da5780639faa3c91146109ed57600080fd5b8063882a7a371461092657806389b4391e1461094e5780638d136e321461096157806392caa08b1461097457806394691d851461098257806396d711ff1461099557600080fd5b80633c84b7c2116103f7578063672dbf7a1161035e578063794e572411610317578063794e5724146108bc5780637e95d664146108cb5780637f3020c1146108d45780637f7dde4a146108e3578063807d138d1461090a57806386551e2d1461091357600080fd5b8063672dbf7a146108445780636b4f105b146108575780636e2d76d31461086a5780636ee4c8cf1461087d5780636ffd89421461088c578063741bef1a1461089557600080fd5b80634e443d9e116103b05780634e443d9e146107db578063533ed3b6146107ee5780635733d58f146108115780635f402a37146108205780636030cc8c1461082957806361ec893d1461083c57600080fd5b80633c84b7c2146107655780633d01f1371461078b5780634494bf4d1461079e5780634607e9be146107b15780634870dd9a146107b95780634cebd90a146107c157600080fd5b806317f43b861161049b57806324f2fe241161045457806324f2fe24146106c75780632b11551a146106d45780633198853e146106dc57806331b725be146106ef578063345f47e71461071b5780633c16805d1461073e57600080fd5b806317f43b861461063557806319d6c022146106555780631a5866501461065d5780631d50f532146106805780631f68f20a14610693578063208c260d1461069c57600080fd5b806307c2c41c116104ed57806307c2c41c146105d25780630a71096e146105e15780630c5805bc146105f45780630da254b4146105fc5780631776165b1461060f57806317acaf781461062257600080fd5b8063013380e71461053557806301b7d96314610579578063028dba891461059a57806305a42d99146105af57806305c4e46d146105c257806307902c55146105ca575b600080fd5b61055c7f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb81565b6040516001600160a01b0390911681526020015b60405180910390f35b61058c61058736600461530d565b610ccf565b604051908152602001610570565b6105ad6105a836600461530d565b610d02565b005b61058c6105bd36600461530d565b610d16565b61058c600181565b6105ad610d2a565b61058c670de0b6b3a763ffff81565b6105ad6105ef36600461530d565b610d3c565b61058c610d65565b6105ad61060a366004615334565b610dee565b6105ad61061d366004615351565b610e81565b6105ad61063036600461538f565b610f7c565b61058c61064336600461530d565b600f6020526000908152604090205481565b61058c610ff5565b61067061066b3660046153df565b611018565b6040519015158152602001610570565b61058c61068e36600461530d565b611034565b61058c60075481565b6002546106af906001600160801b031681565b6040516001600160801b039091168152602001610570565b6004546106709060ff1681565b61058c61103f565b61058c6106ea36600461530d565b61104c565b61058c6106fd36600461530d565b6000908152600a60205260409020600301546001600160801b031690565b61058c61072936600461530d565b6000908152600a602052604090206002015490565b61055c7f0000000000000000000000004ae990c3b2f7c3961c51483efba20760946a768181565b7f0000000000000000000000000000000000000000000000000000000065f3a7df61058c565b61058c6107993660046153df565b611062565b6105ad6107ac366004615401565b61108b565b61058c61116b565b61058c60c881565b6002546106af90600160801b90046001600160801b031681565b6106706107e936600461530d565b61122d565b61058c6107fc36600461530d565b6000908152600a602052604090206001015490565b61058c671158e460913d000081565b6106af61038481565b6105ad61083736600461530d565b611238565b61058c603c81565b6105ad61085236600461544e565b6113b9565b61058c61086536600461530d565b61234b565b61058c61087836600461530d565b612398565b61058c671bc16d674ec8000081565b61058c600e5481565b61055c7f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a81565b61058c670f43fc2c04ee000081565b61058c60035481565b61058c6702c68af0bb14000081565b61055c7f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc81565b61058c600c5481565b61067061092136600461549a565b6123bf565b6109396109343660046153df565b6123cc565b60408051928352602083019190915201610570565b6105ad61095c36600461530d565b612498565b6105ad61096f3660046154c6565b6124a9565b6106af6001600160801b0381565b6105ad61099036600461530d565b6124e1565b61058c600d5481565b600154600160a01b900460ff16610670565b61058c6109be36600461530d565b6124f2565b6105ad6105ef36600461550e565b61058c61138881565b6106706109e836600461530d565b612590565b61058c60065481565b61058c670de0b6b3a764000081565b610a4f610a1336600461530d565b600a602052600090815260409020805460018201546002830154600390930154919290916001600160801b03811690600160801b900460ff1685565b6040516105709594939291906155e2565b610a896040518060400160405280600a81526020016921b23826b0b730b3b2b960b11b81525081565b6040516105709190615626565b610939610aa436600461530d565b61259b565b610abc610ab73660046153df565b6125d1565b60408051938452602084019290925290820152606001610570565b61058c60125481565b61058c61271081565b6105ad612787565b61058c60135481565b61058c60085481565b6105ad610b1136600461530d565b612797565b61055c7f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad081565b6105ad610b4b36600461530d565b612871565b61058c60055481565b6105ad610b67366004615674565b612931565b6001546001600160a01b031661055c565b61058c600b5481565b61055c7f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be381565b61058c61298b565b61058c610bc336600461530d565b61299d565b61058c60095481565b61058c60005481565b6105ad610be836600461530d565b6129b0565b61058c610bfb36600461530d565b612a27565b61055c7f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b61058c6103e881565b61058c610c3e36600461530d565b612a3a565b61058c610c5136600461530d565b60146020526000908152604090205481565b61058c60105481565b61058c612a9f565b61058c670e4b4b8af6a7000081565b61058c60115481565b61058c610c9a36600461530d565b6000908152600a602052604090205490565b61058c610cba3660046153df565b612aff565b61058c600081565b61058c612b25565b6000818152600a6020526040812060030154600160801b900460ff166004811115610cfc57610cfc6155cc565b92915050565b610d0a612b2f565b610d1381612bcd565b50565b600080610d2283612c6a565b509392505050565b610d32612d0d565b610d3a612d6e565b565b610d137f0000000000000000000000004ae990c3b2f7c3961c51483efba20760946a7681612e35565b60007f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de991906156b1565b905090565b610e04336000356001600160e01b031916612e59565b610e295760405162461bcd60e51b8152600401610e20906156ca565b60405180910390fd5b610e31610d2a565b610e39612ef2565b6004805460ff19168215159081179091556040519081527f115ad0fece58825ec20640903934fbedfb746157e6d93b35ddc249fb9c82330b906020015b60405180910390a150565b610e97336000356001600160e01b031916612e59565b610eb35760405162461bcd60e51b8152600401610e20906156ca565b6103846001600160801b0382161015610f265760405162461bcd60e51b815260206004820152602f60248201527f4364704d616e616765723a20477261636520706572696f642062656c6f77206d60448201526e34b734b6bab690323ab930ba34b7b760891b6064820152608401610e20565b610f2e610d2a565b600280546001600160801b03908116600160801b918416918202179091556040519081527ffcb0de0cdd94ed0400cd374b4c81409ae22dd0bee82df2b0d09e0af6958684f290602001610e76565b610f84612b2f565b6000868152600a6020526040812060018101849055829055610fa587612f96565b9050336001600160a01b0316866001600160a01b0316886000805160206158f983398151915287898789886002604051610fe49695949392919061570a565b60405180910390a450505050505050565b6103e861100b670de0b6b3a7640000600561575b565b6110159190615788565b81565b600082821015801561102d575061102d613007565b9392505050565b6000610cfc82613052565b6000610de9600754613066565b60008061105883613087565b5090949350505050565b60008060006110708561259b565b9150915060006110818284876130ca565b9695505050505050565b611093612b2f565b6000858152600a6020526040902084815560018101849055600301805460ff60801b1916600160801b1790556110c882613165565b6000868152600a6020908152604080832060030180546001600160801b0319166001600160801b039590951694909417909355601254601490915291902055611110856131d6565b600061111b86612f96565b9050336001600160a01b0316826001600160a01b0316876000805160206158f98339815191526000808a8a88600060405161115b9695949392919061570a565b60405180910390a4505050505050565b6000806000611178613229565b91509150600061118882846132c9565b5050905060007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121091906156b1565b9050811561122557611222828261579c565b90505b949350505050565b6000610cfc8261332a565b61124e336000356001600160e01b031916612e59565b61126a5760405162461bcd60e51b8152600401610e20906156ca565b6103e8611280670de0b6b3a7640000600561575b565b61128a9190615788565b8110156112ff5760405162461bcd60e51b815260206004820152603a60248201527f4344504d616e616765723a206e657720726564656d7074696f6e20666565206660448201527f6c6f6f72206973206c6f776572207468616e206d696e696d756d0000000000006064820152608401610e20565b670de0b6b3a764000081111561137d5760405162461bcd60e51b815260206004820152603b60248201527f4344504d616e616765723a206e657720726564656d7074696f6e20666565206660448201527f6c6f6f7220697320686967686572207468616e206d6178696d756d00000000006064820152608401610e20565b611385610d2a565b60038190556040518181527e579e3ca70618629ddd2923c5cc1653ca1eca4f9c7d321ff6f61896adad891490602001610e76565b60016000541461141f5760405162461bcd60e51b815260206004820152602b60248201527f4364704d616e616765723a205265656e7472616e637920696e206e6f6e52656560448201526a1b9d1c985b9d0818d85b1b60aa1b6064820152608401610e20565b60017f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad06001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa15801561147f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a391906156b1565b1461150c5760405162461bcd60e51b815260206004820152603360248201527f426f72726f7765724f7065726174696f6e733a205265656e7472616e637920696044820152721b881b9bdb9499595b9d1c985b9d0818d85b1b606a1b6064820152608401610e20565b60026000819055506115786040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60045460ff16156115cb5760405162461bcd60e51b815260206004820152601e60248201527f4364704d616e616765723a20526564656d7074696f6e732050617573656400006044820152606401610e20565b6115d482613345565b6115dc612d0d565b7f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561163c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166091906156b1565b60e0820181905260009081908190611677906133d3565b9250925092508284610160018181525050818461014001818152505080846101000181815250507f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b0316638e0b2e0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172091906157af565b6117c7577f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b03166314fc78fc6040518163ffffffff1660e01b81526004016020604051808303816000875af19250505080156117a0575060408051601f3d908101601f1916820190925261179d918101906156b1565b60015b6117b15761012084018190526117d0565b6117bb8183613497565b610120860152506117d0565b61012084018190525b5050506117e68160e001518261016001516134ad565b6117ef88613516565b6117ff338983610100015161357c565b87815260e0810151600090889061181790829061370e565b156118aa57604051626a1f6360e11b8152600481018a90527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa15801561187f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118a391906157cc565b9150611b04565b7f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611908573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192c91906156b1565b604051626a1f6360e11b8152600481018290529091507f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa158015611992573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b691906157cc565b91505b6001600160a01b038216158015906119e55750670f43fc2c04ee00006119e3828560e00151612aff565b105b15611b0457604051630d0117ab60e31b8152600481018290527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031690636808bd5890602401602060405180830381865afa158015611a4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7391906156b1565b604051626a1f6360e11b8152600481018290529091507f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa158015611ad9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afd91906157cc565b91506119b9565b84600003611b125760001994505b80806000805b6001600160a01b03861615801590611b305750865115155b8015611b3c5750600089115b15611d5f57611b4a85613981565b60006040518060c00160405280878152602001896000015181526020018960e0015181526020018e81526020018d81526020018c81525090506000611b8e82613afa565b9050806080015115611ba1575050611d5f565b60c081015115611bb3578060c0015192505b805160208a0151611bc491906157e9565b6020808b019190915281015160408a0151611bdf91906157e9565b60408a015280518951611bf2919061579c565b8952604081015160608a0151611c0891906157e9565b60608a0152604051630d0117ab60e31b8152600481018890526000907f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031690636808bd5890602401602060405180830381865afa158015611c75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9991906156b1565b90508160a0015115611cb957879550611cb38560016157e9565b94508097505b604051626a1f6360e11b8152600481018290526000907f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa158015611d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d4391906157cc565b9050809950505050508880611d57906157fc565b995050611b18565b6000876040015111611dc35760405162461bcd60e51b815260206004820152602760248201527f4364704d616e616765723a20556e61626c6520746f2072656465656d20616e7960448201526608185b5bdd5b9d60ca1b6064820152608401610e20565b81600103611e4a576040516395bc267360e01b8152600481018590527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316906395bc267390602401600060405180830381600087803b158015611e2d57600080fd5b505af1158015611e41573d6000803e3d6000fd5b50505050611ee3565b6001821115611ee3576000611e60848487614091565b60405163bff0cecf60e01b81529091506001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be3169063bff0cecf90611eaf908490600401615813565b600060405180830381600087803b158015611ec957600080fd5b505af1158015611edd573d6000803e3d6000fd5b50505050505b8415801590611ef25750600081115b15611f8757604051637b30119360e01b81526004810186905260248101829052604481018d9052606481018c90526001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be31690637b30119390608401600060405180830381600087803b158015611f6e57600080fd5b505af1158015611f82573d6000803e3d6000fd5b505050505b611f9f87604001518860e001518961012001516142cb565b50611fad876040015161444b565b608088018190526040880151611fc491908a614458565b86608001518760400151611fd8919061579c565b60a0880152606087015160408801516101408901516120249291611ffb9161579c565b612005919061579c565b886020015189610100015161201a919061579c565b8960e001516144c9565b336001600160a01b03167fc17822d29d251285c0e18ca329b43dc8a172c747318754878a8dc90855f2a0828f89602001518a604001518b60800151604051612085949392919093845260208401929092526040830152606082015260800190565b60405180910390a26020870151604051632770a7eb60e21b815233600482015260248101919091527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb6001600160a01b031690639dc29fac90604401600060405180830381600087803b1580156120fb57600080fd5b505af115801561210f573d6000803e3d6000fd5b50505060208801516040516349f047bb60e01b81526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc1692506349f047bb916121669160040190815260200190565b600060405180830381600087803b15801561218057600080fd5b505af1158015612194573d6000803e3d6000fd5b5050506080880151604051631424027f60e01b81526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc169250631424027f916121eb9160040190815260200190565b600060405180830381600087803b15801561220557600080fd5b505af1158015612219573d6000803e3d6000fd5b5050505060a08701516040516338339d3160e11b815233600482015260248101919091527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b0316906370673a6290604401600060405180830381600087803b15801561228b57600080fd5b505af115801561229f573d6000803e3d6000fd5b505050506122b9876101600151671158e460913d00001190565b612336576122ca8760e0015161332a565b156123365760405162461bcd60e51b815260206004820152603660248201527f4364704d616e616765723a20726564656d7074696f6e2073686f756c64206e6f604482015275742074726967676572205265636f766572794d6f646560501b6064820152608401610e20565b50506001600055505050505050505050505050565b6000806000612358613229565b91509150600061236882846132c9565b50600087815260146020526040812054919350915061238990879084614589565b50929998505050505050505050565b60008060006123a68461259b565b9150915060006123b682846145f6565b95945050505050565b600061122584848461462c565b600082815260146020908152604080832054600a9092528220600101548291908115806123f7575080155b8061240157508482145b15612413576000935091506124919050565b600061241f838761579c565b6000888152600a602052604090206002015461243b919061575b565b90506000612451670de0b6b3a76400008461575b565b9050818111156124865781670de0b6b3a764000061246f828461579c565b6124799190615788565b9550955050505050612491565b506000945090925050505b9250929050565b6124a0612b2f565b610d1381613981565b6124b283613516565b6124db7f0000000000000000000000004ae990c3b2f7c3961c51483efba20760946a7681612e35565b50505050565b6124e9612b2f565b610d138161464d565b6000806124fd61116b565b905060007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663facc05116040518163ffffffff1660e01b8152600401602060405180830381865afa15801561255f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258391906156b1565b90506112258282866130ca565b6000610cfc826146d8565b6000806000806125c1856014600088815260200190815260200160002054601254614589565b5091989297509195505050505050565b600080600083851161263d5760405162461bcd60e51b815260206004820152602f60248201527f4344504d616e616765723a206f6e6c792074616b65206665652077697468206260448201526e0d2cececae440dccaee40d2dcc8caf608b1b6064820152608401610e20565b6000612649858761579c565b905060006127106008548361265e919061575b565b6126689190615788565b90506000612674612a9f565b61267e908361575b565b600b54604051631920845160e01b81526004810183905291925090600090670de0b6b3a7640000907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa1580156126f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061271b91906156b1565b6127259190615788565b90506000601354670de0b6b3a764000083612740919061575b565b61274a91906157e9565b905060006127588483615788565b90506000612766858361575b565b612770908461579c565b939a509098509196505050505050505b9250925092565b61278f612b2f565b610d3a612d0d565b6127ad336000356001600160e01b031916612e59565b6127c95760405162461bcd60e51b8152600401610e20906156ca565b6127108111156128345760405162461bcd60e51b815260206004820152603060248201527f4344504d616e616765723a206e6577207374616b696e6720726577617264207360448201526f0e0d8d2e840caf0c6cacac8e640dac2f60831b6064820152608401610e20565b61283c610d2a565b60088190556040518181527f3b7749f898809ba97d1ce1863e586ae0a7e17b14a07b48505564050c56c50a3a90602001610e76565b612887336000356001600160e01b031916612e59565b6128a35760405162461bcd60e51b8152600401610e20906156ca565b60018110156128c45760405162461bcd60e51b8152600401610e2090615857565b670de0b6b3a763ffff8111156128ec5760405162461bcd60e51b8152600401610e2090615857565b6128f4610d2a565b6128fc612ef2565b60058190556040518181527f9d8b7809b20b74114e61955710408360ea4ebd3a7198d8619386a3e7f35ff7ed90602001610e76565b612939612b2f565b336001600160a01b0316836001600160a01b0316856000805160206158f98339815191528585600080600060016040516129789695949392919061570a565b60405180910390a46124db84600261472e565b6000610de96129986147b6565b613066565b60006129a7612b2f565b610cfc82612f96565b6129c6336000356001600160e01b031916612e59565b6129e25760405162461bcd60e51b8152600401610e20906156ca565b6129ea610d2a565b6129f2612ef2565b60068190556040518181527f6f0010b70ec34f0385e553a35e949f59fdba468f90870e996f7492d4b8df433290602001610e76565b6000610cfc612a3461298b565b836147fb565b6000806000612a47613229565b915091506000612a5782846132c9565b509150506000806000612a7e88601460008b81526020019081526020016000205486614589565b50935050925092506000612a9284846145f6565b9998505050505050505050565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc5573d6000803e3d6000fd5b600080612b0b8461104c565b90506000612b188561234b565b90506123b68183866130ca565b6000610de9614889565b336001600160a01b037f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad01614610d3a5760405162461bcd60e51b815260206004820152603960248201527f4364704d616e616765723a2043616c6c6572206973206e6f742074686520426f60448201527f72726f7765724f7065726174696f6e7320636f6e7472616374000000000000006064820152608401610e20565b6040518181527f41a8f1fd29dc6218d58cbc134120f372f585f1f92225604e66c7eceaf08ccbbe9060200160405180910390a16002546001600160801b03166ffffffffffffffffffffffffffffffffe1901610d1357600280546001600160801b031916426001600160801b03161790556040517ff8c0d4edf35c388faff93d34bebba3447add915ecdd59cd3d9512d40a4dde70f90600090a150565b6000818152600a60205260408120819060016003820154600160801b900460ff166004811115612c9c57612c9c6155cc565b14612cad5750600093849350915050565b6000848152600f6020526040902054600e54612cc9919061579c565b91508115612cfb57670de0b6b3a7640000828260020154612cea919061575b565b612cf49190615788565b9250612d07565b50600093849350915050565b50915091565b600080612d18613229565b91509150612d2682826148e9565b8181118015612d3757506000600b54115b15612d6a576000806000612d4b84866132c9565b925092509250612d5c838383614935565b612d666000614adc565b5050505b5050565b60007f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612dd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df491906156b1565b90506000612e0182613052565b90506000612e1682671158e460913d00001190565b90508015612e2c57612e2782612bcd565b505050565b612e278261464d565b3660008037600080366000845af43d6000803e808015612e54573d6000f35b3d6000fd5b6001546000906001600160a01b03168015801590611225575060405163b700961360e01b81526001600160a01b0385811660048301523060248301526001600160e01b03198516604483015282169063b700961390606401602060405180830381865afa158015612ece573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122591906157af565b6000612efc6147b6565b9050670de0b6b3a7640000811115612f565760405162461bcd60e51b815260206004820152601f60248201527f4364704d616e616765723a20626173655261746520746f6f206c6172676521006044820152606401610e20565b60078190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610d13614bbf565b6000806000612fa484614c42565b9150915060008183600b54612fb991906157e9565b612fc3919061579c565b600b8190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b6002546000906001600160801b0390811690811480159061304c575060025461304090600160801b90046001600160801b0316826158a7565b6001600160801b031642115b91505090565b600061305d826133d3565b50909392505050565b6000610cfc8260035461307991906157e9565b670de0b6b3a7640000613497565b600080600080600061309886612c6a565b6000888152600a6020526040902054919350915082156130bf576130bc83826157e9565b90505b969195509350915050565b604051630f451f7160e31b81526004810184905260009081906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015613134573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315891906156b1565b90506123b6818585614c7a565b60006001600160801b038211156131d25760405162461bcd60e51b815260206004820152602b60248201527f456274634d6174683a20646f776e6361737420746f2075696e7431323820776960448201526a6c6c206f766572666c6f7760a81b6064820152608401610e20565b5090565b600e546000828152600f602090815260409182902083905581518481529081018390527f75a085b846bf9716e36c23a2654fa549def06c94f9e24590c1448f19137b2d3191015b60405180910390a15050565b601154604051630f451f7160e31b8152670de0b6b3a7640000600482015260009182916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa15801561329d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c191906156b1565b915091509091565b600080600083851180156132df57506000600b54115b156133195760008060006132f388886125d1565b92509250925060008260125461330991906157e9565b9396509294509250612780915050565b505060125460135460009250612780565b6000610cfc61333883613052565b671158e460913d00001190565b600354811015801561335f5750670de0b6b3a76400008111155b610d13576040805162461bcd60e51b81526020600482015260248101919091527f4d6178206665652070657263656e74616765206d75737420626520626574776560448201527f656e20726564656d7074696f6e2066656520666c6f6f7220616e6420313030256064820152608401610e20565b6000806000806133e1612a9f565b905060006133ed614889565b604051630f451f7160e31b8152600481018490529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015613458573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061347c91906156b1565b9050613489818389614c7a565b979296509094509092505050565b60008183106134a6578161102d565b5090919050565b670f43fc2c04ee0000811015612d6a5760405162461bcd60e51b815260206004820152602860248201527f4364704d616e616765723a2043616e6e6f742072656465656d207768656e205460448201526721a9101e1026a1a960c11b6064820152608401610e20565b6103e8811015610d135760405162461bcd60e51b815260206004820152602b60248201527f4364704d616e616765723a20416d6f756e74206d75737420626520677265617460448201526a32b9103a3430b71036b4b760a91b6064820152608401610e20565b6040516370a0823160e01b81526001600160a01b0384811660048301526000917f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb909116906370a0823190602401602060405180830381865afa1580156135e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061360b91906156b1565b9050828110156136985760405162461bcd60e51b815260206004820152604c60248201527f4364704d616e616765723a2052657175657374656420726564656d7074696f6e60448201527f20616d6f756e74206d757374206265203c3d207573657227732045425443207460648201526b6f6b656e2062616c616e636560a01b608482015260a401610e20565b818111156124db5760405162461bcd60e51b815260206004820152603960248201527f4364704d616e616765723a2072656465656d6572277320454254432062616c6160448201527f6e6365206578636565647320746f74616c20737570706c7921000000000000006064820152608401610e20565b60007f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663b0faee936040518163ffffffff1660e01b8152600401602060405180830381865afa15801561376e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379291906156b1565b8314806138255750604051631d1a696d60e01b8152600481018490527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031690631d1a696d90602401602060405180830381865afa1580156137ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061382391906157af565b155b806138405750670f43fc2c04ee000061383e8484612aff565b105b1561384d57506000610cfc565b6040516394dfe33f60e01b8152600481018490526000907f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316906394dfe33f90602401602060405180830381865afa1580156138b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d991906156b1565b90507f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663b0faee936040518163ffffffff1660e01b8152600401602060405180830381865afa158015613939573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061395d91906156b1565b8114806112255750670f43fc2c04ee00006139788285612aff565b10949350505050565b613989612d0d565b6000818152601460205260408120546012549091808080806139ac888888614589565b9450945094509450945060008311806139c55750600081115b15613ad7576000888152600a602052604090206001810154815485156139f2576139f28b89888d8d614cab565b8315613a0c57613a018b6131d6565b868114613a0c578683555b604051626a1f6360e11b8152600481018c905233906001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be3169062d43ec690602401602060405180830381865afa158015613a71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9591906157cc565b6001600160a01b03168c6000805160206158f983398151915284868c8e8a600201546003604051613acb9695949392919061570a565b60405180910390a45050505b858714613af05760008881526014602052604090208690555b5050505050505050565b613b3e6040518060e0016040528060008152602001600081526020016000815260200160008152602001600015158152602001600015158152602001600081525090565b60208083015183516000908152600a909252604090912054613b609190613497565b80825260408301516001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84169163192084519190613bad90670de0b6b3a76400009061575b565b613bb79190615788565b6040518263ffffffff1660e01b8152600401613bd591815260200190565b602060405180830381865afa158015613bf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c1691906156b1565b60208083019190915260408051808201825284516000908152600a808552838220548352865182528452918220600101549281019290925282518251613c5c919061579c565b9050600083602001518360200151613c74919061579c565b905081600003613da3578451604051626a1f6360e11b81526000916001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be3169162d43ec691613ccf9160040190815260200190565b602060405180830381865afa158015613cec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1091906157cc565b86516000908152600a6020526040808220600301549088018590526001600160801b031660608801819052600160a0890152885192935091613d5491858486614d16565b86518551602087015160405133936001600160a01b0387169390926000805160206158f983398151915292613d949291906000908190819060069061570a565b60405180910390a45050614089565b6000613daf82846145f6565b90508560a0015181141580613e545750604051630f451f7160e31b815260048101839052671bc16d674ec80000907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690637a28fb8890602401602060405180830381865afa158015613e2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e5291906156b1565b105b80613e6057506103e883105b15613f6b578551613e7090612f96565b508551604051626a1f6360e11b815233917f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169162d43ec691613ec19160040190815260200190565b602060405180830381865afa158015613ede573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f0291906157cc565b875186516020808901516000848152600a9092526040918290206002015491516001600160a01b0395909516946000805160206158f983398151915293613f52939092918391839160089061570a565b60405180910390a4505060016080840152509092915050565b60c0850181905285516000908152600a60205260408082208590558751825290206001018290558551613f9d90612f96565b508551604051626a1f6360e11b815233917f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169162d43ec691613fee9160040190815260200190565b602060405180830381865afa15801561400b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061402f91906157cc565b875186516020808901516000848152600a9092526040918290206002015491516001600160a01b0395909516946000805160206158f98339815191529361407f939092918b918b9160069061570a565b60405180910390a4505b505050919050565b6060828460008267ffffffffffffffff8111156140b0576140b06154f8565b6040519080825280602002602001820160405280156140d9578160200160208202803683370190505b5090505b6000831180156140ec57508115155b156141b65781816140fd858961579c565b8151811061410d5761410d6158ce565b602090810291909101015261412360018461579c565b6040516394dfe33f60e01b8152600481018490529093507f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316906394dfe33f90602401602060405180830381865afa15801561418b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141af91906156b1565b91506140dd565b86816000815181106141ca576141ca6158ce565b60200260200101511461423d5760405162461bcd60e51b815260206004820152603560248201527f4364704d616e616765723a20626174636852656d6f7665536f7274656443647060448201527424b2399031b432b1b59039ba30b93a1032b93937b960591b6064820152608401610e20565b848161424a60018961579c565b8151811061425a5761425a6158ce565b6020026020010151146110815760405162461bcd60e51b815260206004820152603360248201527f4364704d616e616765723a20626174636852656d6f7665536f7274656443647060448201527224b2399031b432b1b59032b7321032b93937b960691b6064820152608401610e20565b6000806142d66147b6565b9050600083857f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316637a28fb88896040518263ffffffff1660e01b815260040161432a91815260200190565b602060405180830381865afa158015614347573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061436b91906156b1565b614375919061575b565b61437f9190615788565b90506000600654826143919190615788565b61439b90846157e9565b90506143af81670de0b6b3a7640000613497565b90506000811161440b5760405162461bcd60e51b815260206004820152602160248201527f4364704d616e616765723a206e6577206261736552617465206973207a65726f6044820152602160f81b6064820152608401610e20565b60078190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1611081614bbf565b6000610cfc612a3461103f565b60008261446d670de0b6b3a76400008661575b565b6144779190615788565b9050818111156124db5760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610e20565b604051630f451f7160e31b815260048101849052600090614560906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015614535573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061455991906156b1565b8484614c7a565b9050671158e460913d00008110156145805761457b81612bcd565b6124db565b6124db8161464d565b6000838152600a6020526040812060010154819081908190819081908888148015906145b55750600089115b156145cf576000806145c78c8b6123cc565b909450925050505b60008060006145dd8d613087565b959f919e50959c50949a50929850929650505050505050565b60008115614623578161461268056bc75e2d631000008561575b565b61461c9190615788565b9050610cfc565b50600019610cfc565b60008061463a8585856130ca565b671158e460913d00001195945050505050565b6040518181527f41a8f1fd29dc6218d58cbc134120f372f585f1f92225604e66c7eceaf08ccbbe9060200160405180910390a16002546001600160801b0390811614610d1357600280546001600160801b0319166001600160801b031790556040517fa4884b11e6de87d7ea9a9d8c420198ec163777e456bd6b36a3acd5b4667fb3c590600090a150565b600060016000838152600a6020526040902060030154600160801b900460ff166004811115614709576147096155cc565b1461471657506000919050565b50600e546000918252600f6020526040909120541090565b6147388282614edb565b6040516395bc267360e01b8152600481018390527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316906395bc267390602401600060405180830381600087803b15801561479a57600080fd5b505af11580156147ae573d6000803e3d6000fd5b505050505050565b6000806147c1615012565b905060006147d16005548361503d565b9050670de0b6b3a7640000816007546147ea919061575b565b6147f49190615788565b9250505090565b600080670de0b6b3a7640000614811848661575b565b61481b9190615788565b905082811061102d5760405162461bcd60e51b815260206004820152603460248201527f4364704d616e616765723a2046656520776f756c642065617420757020616c6c604482015273081c995d1d5c9b99590818dbdb1b185d195c985b60621b6064820152608401610e20565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663facc05116040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc5573d6000803e3d6000fd5b818114612d6a576011819055604080518381526020810183905242918101919091527fc3fdd35fd80bc508237115f23bf8e423d1cb1ac6e75cc2e32eb938c193e7ea379060600161321d565b600060125490508260128190555081601381905550837f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149cd91906156b1565b11614a1a5760405162461bcd60e51b815260206004820181905260248201527f4344504d616e616765723a206665652073706c697420697320746f6f206269676044820152606401610e20565b604051631424027f60e01b8152600481018590527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031690631424027f90602401600060405180830381600087803b158015614a7c57600080fd5b505af1158015614a90573d6000803e3d6000fd5b505060408051848152602081018790529081018790527f4b636d6be3a9993af0c45fcb447e094dfb35d8229c2c1ac75fd6576b0109c0319250606001905060405180910390a150505050565b6000600b54905080600c819055506000827f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa158015614b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b6f91906156b1565b614b79919061579c565b600d81905560408051848152602081018390529192507f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf60910160405180910390a1505050565b60006009544211614bd1576000614bde565b600954614bde904261579c565b9050603c8110610d1357603c614bf2615012565b614bfc919061575b565b60096000828254614c0d91906157e9565b90915550506040514281527fa7d7ab658247352c73489a979233e310e048e0c6fb1548c4239d6a8aaecc7f4690602001610e76565b6000818152600a6020526040812060018101548291908290614c63906150e8565b600290920180549083905591959194509092505050565b60008215614ca257600083614c8f848761575b565b614c999190615788565b915061102d9050565b5060001961102d565b6000858152600a6020908152604091829020600101869055815187815290810184905290810182905260608101849052608081018590527f74f8f18ebc63a5cfcb205b885cb004fe379a24ba2549e9dbb34a1aef72cd38209060a00160405180910390a15050505050565b614d21856004614edb565b6040516349f047bb60e01b8152600481018590527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b0316906349f047bb90602401600060405180830381600087803b158015614d8357600080fd5b505af1158015614d97573d6000803e3d6000fd5b5050604051639fcb252760e01b8152600481018890526001600160a01b03848116602483015260448201879052606482018690527f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de169250639fcb25279150608401600060405180830381600087803b158015614e1357600080fd5b505af1158015614e27573d6000803e3d6000fd5b5050604051634667d4f360e11b81526001600160a01b037f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de8116600483015260248201879052604482018690527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc169250638ccfa9e69150606401600060405180830381600087803b158015614ebc57600080fd5b505af1158015614ed0573d6000803e3d6000fd5b505050505050505050565b6000816004811115614eef57614eef6155cc565b14158015614f0f57506001816004811115614f0c57614f0c6155cc565b14155b614f795760405162461bcd60e51b815260206004820152603560248201527f4364704d616e6167657253746f726167653a20636c6f7365206e6f6e2d6578696044820152747374206f72206e6f6e2d616374697665204344502160581b6064820152608401610e20565b6000614f83610d65565b9050614f8e8161517d565b614f9783615269565b6000838152600a60205260409020600301805483919060ff60801b1916600160801b836004811115614fcb57614fcb6155cc565b021790555050506000908152600a602090815260408083206001810184905583815560030180546001600160801b0319169055600f82528083208390556014909152812055565b600060095442116150235750600090565b603c60095442615033919061579c565b610de99190615788565b6000631f54050082111561505357631f54050091505b8160000361506a5750670de0b6b3a7640000610cfc565b670de0b6b3a764000083835b60018111156150de5761508a6002826158e4565b6000036150af5761509b82836152d6565b91506150a8600282615788565b9050615076565b6150b982846152d6565b92506150c582836152d6565b915060026150d460018361579c565b6150a89190615788565b61108182846152d6565b600080600d546000036150fc575081610cfc565b6000600c54116151635760405162461bcd60e51b815260206004820152602c60248201527f4364704d616e6167657253746f726167653a207a65726f20746f74616c53746160448201526b6b6573536e617073686f742160a01b6064820152608401610e20565b600d54600c54615173908561575b565b61102d9190615788565b60018111801561520e575060017f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156151e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061520c91906156b1565b115b610d135760405162461bcd60e51b815260206004820152602660248201527f4364704d616e616765723a204f6e6c79206f6e652063647020696e207468652060448201526573797374656d60d01b6064820152608401610e20565b6000818152600a6020526040812060020154600b54615288919061579c565b600b8190556000838152600a602052604080822060020191909155519091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829061321d9083815260200190565b6000806152e3838561575b565b9050670de0b6b3a76400006152f9600282615788565b61530390836157e9565b6112259190615788565b60006020828403121561531f57600080fd5b5035919050565b8015158114610d1357600080fd5b60006020828403121561534657600080fd5b813561102d81615326565b60006020828403121561536357600080fd5b81356001600160801b038116811461102d57600080fd5b6001600160a01b0381168114610d1357600080fd5b60008060008060008060c087890312156153a857600080fd5b8635955060208701356153ba8161537a565b95989597505050506040840135936060810135936080820135935060a0909101359150565b600080604083850312156153f257600080fd5b50508035926020909101359150565b600080600080600060a0868803121561541957600080fd5b8535945060208601359350604086013592506060860135915060808601356154408161537a565b809150509295509295909350565b600080600080600080600060e0888a03121561546957600080fd5b505085359760208701359750604087013596606081013596506080810135955060a0810135945060c0013592509050565b6000806000606084860312156154af57600080fd5b505081359360208301359350604090920135919050565b600080600080608085870312156154dc57600080fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b600052604160045260246000fd5b6000602080838503121561552157600080fd5b823567ffffffffffffffff8082111561553957600080fd5b818501915085601f83011261554d57600080fd5b81358181111561555f5761555f6154f8565b8060051b604051601f19603f83011681018181108582111715615584576155846154f8565b6040529182528482019250838101850191888311156155a257600080fd5b938501935b828510156155c0578435845293850193928501926155a7565b98975050505050505050565b634e487b7160e01b600052602160045260246000fd5b85815260208101859052604081018490526001600160801b038316606082015260a0810160058310615616576156166155cc565b8260808301529695505050505050565b600060208083528351808285015260005b8181101561565357858101830151858201604001528201615637565b506000604082860101526040601f19601f8301168501019250505092915050565b6000806000806080858703121561568a57600080fd5b84359350602085013561569c8161537a565b93969395505050506040820135916060013590565b6000602082840312156156c357600080fd5b5051919050565b602080825260129082015271105d5d1a0e8815539055551213d49256915160721b604082015260600190565b60098110615706576157066155cc565b9052565b600060c08201905087825286602083015285604083015284606083015283608083015261573a60a08301846156f6565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610cfc57610cfc615745565b634e487b7160e01b600052601260045260246000fd5b60008261579757615797615772565b500490565b81810381811115610cfc57610cfc615745565b6000602082840312156157c157600080fd5b815161102d81615326565b6000602082840312156157de57600080fd5b815161102d8161537a565b80820180821115610cfc57610cfc615745565b60008161580b5761580b615745565b506000190190565b6020808252825182820181905260009190848201906040850190845b8181101561584b5783518352928401929184019160010161582f565b50909695505050505050565b60208082526030908201527f4344504d616e616765723a206e6577206d696e7574652064656361792066616360408201526f746f72206f7574206f662072616e676560801b606082015260800190565b6001600160801b038181168382160190808211156158c7576158c7615745565b5092915050565b634e487b7160e01b600052603260045260246000fd5b6000826158f3576158f3615772565b50069056fe94bbf0bce1cd1f8f3842d4a02225a01ed47c14e2cece80bfc4fa9a66308a5f7ea26469706673582212207dc2f92b2a05a3d7e4831b48392a42d574e4dad9a1a9c29978325835cb1b868f64736f6c63430008110033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.