Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19437153 | 337 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x80B7807e...c50A5543e The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
LiquidationLibrary
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/ICdpManagerData.sol"; import "./Interfaces/ICollSurplusPool.sol"; import "./Interfaces/IEBTCToken.sol"; import "./Interfaces/ISortedCdps.sol"; import "./Dependencies/ICollateralTokenOracle.sol"; import "./CdpManagerStorage.sol"; /// @title LiquidationLibrary mainly provide necessary logic to fulfill liquidation for eBTC Cdps. /// @dev This contract shares same base and storage layout with CdpManager and is the delegatecall destination from CdpManager contract LiquidationLibrary is CdpManagerStorage { constructor( address _borrowerOperationsAddress, address _collSurplusPool, address _ebtcToken, address _sortedCdps, address _activePool, address _priceFeed, address _collateral ) CdpManagerStorage( address(0), address(0), _borrowerOperationsAddress, _collSurplusPool, _ebtcToken, _sortedCdps, _activePool, _priceFeed, _collateral ) {} /// @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 nonReentrantSelfAndBOps { _liquidateIndividualCdpSetup(_cdpId, 0, _cdpId, _cdpId); } /// @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 nonReentrantSelfAndBOps { require(_partialAmount != 0, "LiquidationLibrary: use `liquidate` for 100%"); _liquidateIndividualCdpSetup(_cdpId, _partialAmount, _upperPartialHint, _lowerPartialHint); } // Single CDP liquidation function. function _liquidateIndividualCdpSetup( bytes32 _cdpId, uint256 _partialAmount, bytes32 _upperPartialHint, bytes32 _lowerPartialHint ) internal { _requireCdpIsActive(_cdpId); _syncAccounting(_cdpId); uint256 _price = priceFeed.fetchPrice(); // prepare local variables uint256 _ICR = getCachedICR(_cdpId, _price); // @audit syncAccounting already called, guarenteed to be synced (uint256 _TCR, uint256 systemColl, uint256 systemDebt) = _getTCRWithSystemDebtAndCollShares( _price ); // If CDP is above MCR if (_ICR >= MCR) { // We must be in RM require( _checkICRAgainstLiqThreshold(_ICR, _TCR), "LiquidationLibrary: ICR is not below liquidation threshold in current mode" ); // == Grace Period == // uint128 cachedLastGracePeriodStartTimestamp = lastGracePeriodStartTimestamp; require( cachedLastGracePeriodStartTimestamp != UNSET_TIMESTAMP, "LiquidationLibrary: Recovery Mode grace period not started" ); require( block.timestamp > cachedLastGracePeriodStartTimestamp + recoveryModeGracePeriodDuration, "LiquidationLibrary: Recovery mode grace period still in effect" ); } // Implicit Else Case, Implies ICR < MRC, meaning the CDP is liquidatable bool _recoveryModeAtStart = _TCR < CCR ? true : false; LiquidationLocals memory _liqState = LiquidationLocals( _cdpId, _partialAmount, _price, _ICR, _upperPartialHint, _lowerPartialHint, (_recoveryModeAtStart), _TCR, 0, 0, 0, 0, 0 ); LiquidationRecoveryModeLocals memory _rs = LiquidationRecoveryModeLocals( systemDebt, systemColl, 0, 0, 0, _cdpId, _price, _ICR, 0, 0 ); _liquidateIndividualCdpSetupCDP(_liqState, _rs); } // liquidate given CDP by repaying debt in full or partially if its ICR is below MCR or TCR in recovery mode. // For partial liquidation, caller should use HintHelper smart contract to get correct hints for reinsertion into sorted CDP list function _liquidateIndividualCdpSetupCDP( LiquidationLocals memory _liqState, LiquidationRecoveryModeLocals memory _recoveryState ) internal { LiquidationValues memory liquidationValues; uint256 startingSystemDebt = _recoveryState.entireSystemDebt; uint256 startingSystemColl = _recoveryState.entireSystemColl; if (_liqState.partialAmount == 0) { ( liquidationValues.debtToBurn, liquidationValues.totalCollToSendToLiquidator, liquidationValues.debtToRedistribute, liquidationValues.liquidatorCollSharesReward, liquidationValues.collSurplus ) = _liquidateCdpInGivenMode(_liqState, _recoveryState); } else { ( liquidationValues.debtToBurn, liquidationValues.totalCollToSendToLiquidator ) = _liquidateCDPPartially(_liqState); if ( liquidationValues.totalCollToSendToLiquidator == 0 && liquidationValues.debtToBurn == 0 ) { // retry with fully liquidation ( liquidationValues.debtToBurn, liquidationValues.totalCollToSendToLiquidator, liquidationValues.debtToRedistribute, liquidationValues.liquidatorCollSharesReward, liquidationValues.collSurplus ) = _liquidateCdpInGivenMode(_liqState, _recoveryState); } } _finalizeLiquidation( liquidationValues.debtToBurn, liquidationValues.totalCollToSendToLiquidator, liquidationValues.debtToRedistribute, liquidationValues.liquidatorCollSharesReward, liquidationValues.collSurplus, startingSystemColl, startingSystemDebt, _liqState.price ); } // liquidate (and close) the CDP from an external liquidator // this function would return the liquidated debt and collateral of the given CDP function _liquidateCdpInGivenMode( LiquidationLocals memory _liqState, LiquidationRecoveryModeLocals memory _recoveryState ) private returns (uint256, uint256, uint256, uint256, uint256) { if (_liqState.recoveryModeAtStart) { LiquidationRecoveryModeLocals memory _outputState = _liquidateIndividualCdpSetupCDPInRecoveryMode(_recoveryState); // housekeeping leftover collateral for liquidated CDP if (_outputState.totalSurplusCollShares > 0) { activePool.transferSystemCollShares( address(collSurplusPool), _outputState.totalSurplusCollShares ); } return ( _outputState.totalDebtToBurn, _outputState.totalCollSharesToSend, _outputState.totalDebtToRedistribute, _outputState.totalLiquidatorRewardCollShares, _outputState.totalSurplusCollShares ); } else { LiquidationLocals memory _outputState = _liquidateIndividualCdpSetupCDPInNormalMode( _liqState ); return ( _outputState.totalDebtToBurn, _outputState.totalCollSharesToSend, _outputState.totalDebtToRedistribute, _outputState.totalLiquidatorRewardCollShares, _outputState.totalSurplusCollShares ); } } function _liquidateIndividualCdpSetupCDPInNormalMode( LiquidationLocals memory _liqState ) private returns (LiquidationLocals memory) { // liquidate entire debt ( uint256 _totalDebtToBurn, uint256 _totalColToSend, uint256 _liquidatorReward ) = _closeCdpByLiquidation(_liqState.cdpId); uint256 _cappedColPortion; uint256 _collSurplus; uint256 _debtToRedistribute; address _borrower = sortedCdps.getOwnerAddress(_liqState.cdpId); // I don't see an issue emitting the CdpUpdated() event up here and avoiding this extra cache, any objections? emit CdpUpdated( _liqState.cdpId, _borrower, msg.sender, _totalDebtToBurn, _totalColToSend, 0, 0, 0, CdpOperation.liquidateInNormalMode ); { ( _cappedColPortion, _collSurplus, _debtToRedistribute ) = _calculateFullLiquidationSurplusAndCap( _liqState.ICR, _liqState.price, _totalDebtToBurn, _totalColToSend ); if (_collSurplus > 0) { // due to division precision loss, should be zero surplus in normal mode _cappedColPortion = _cappedColPortion + _collSurplus; _collSurplus = 0; } if (_debtToRedistribute > 0) { _totalDebtToBurn = _totalDebtToBurn - _debtToRedistribute; } } _liqState.totalDebtToBurn = _liqState.totalDebtToBurn + _totalDebtToBurn; _liqState.totalCollSharesToSend = _liqState.totalCollSharesToSend + _cappedColPortion; _liqState.totalDebtToRedistribute = _liqState.totalDebtToRedistribute + _debtToRedistribute; _liqState.totalLiquidatorRewardCollShares = _liqState.totalLiquidatorRewardCollShares + _liquidatorReward; // Emit events uint _debtToColl = (_totalDebtToBurn * DECIMAL_PRECISION) / _liqState.price; uint _cappedColl = collateral.getPooledEthByShares(_cappedColPortion + _liquidatorReward); emit CdpLiquidated( _liqState.cdpId, _borrower, _totalDebtToBurn, // please note this is the collateral share of the liquidated CDP _cappedColPortion, CdpOperation.liquidateInNormalMode, msg.sender, _cappedColl > _debtToColl ? (_cappedColl - _debtToColl) : 0 ); return _liqState; } function _liquidateIndividualCdpSetupCDPInRecoveryMode( LiquidationRecoveryModeLocals memory _recoveryState ) private returns (LiquidationRecoveryModeLocals memory) { // liquidate entire debt ( uint256 _totalDebtToBurn, uint256 _totalColToSend, uint256 _liquidatorReward ) = _closeCdpByLiquidation(_recoveryState.cdpId); // cap the liquidated collateral if required uint256 _cappedColPortion; uint256 _collSurplus; uint256 _debtToRedistribute; address _borrower = sortedCdps.getOwnerAddress(_recoveryState.cdpId); // I don't see an issue emitting the CdpUpdated() event up here and avoiding an extra cache of the values, any objections? emit CdpUpdated( _recoveryState.cdpId, _borrower, msg.sender, _totalDebtToBurn, _totalColToSend, 0, 0, 0, CdpOperation.liquidateInRecoveryMode ); // avoid stack too deep { ( _cappedColPortion, _collSurplus, _debtToRedistribute ) = _calculateFullLiquidationSurplusAndCap( _recoveryState.ICR, _recoveryState.price, _totalDebtToBurn, _totalColToSend ); if (_collSurplus > 0) { if (_checkICRAgainstMCR(_recoveryState.ICR)) { _cappedColPortion = _collSurplus + _cappedColPortion; _collSurplus = 0; } else { collSurplusPool.increaseSurplusCollShares( _recoveryState.cdpId, _borrower, _collSurplus, 0 ); _recoveryState.totalSurplusCollShares = _recoveryState.totalSurplusCollShares + _collSurplus; } } if (_debtToRedistribute > 0) { _totalDebtToBurn = _totalDebtToBurn - _debtToRedistribute; } } _recoveryState.totalDebtToBurn = _recoveryState.totalDebtToBurn + _totalDebtToBurn; _recoveryState.totalCollSharesToSend = _recoveryState.totalCollSharesToSend + _cappedColPortion; _recoveryState.totalDebtToRedistribute = _recoveryState.totalDebtToRedistribute + _debtToRedistribute; _recoveryState.totalLiquidatorRewardCollShares = _recoveryState.totalLiquidatorRewardCollShares + _liquidatorReward; // check if system back to normal mode _recoveryState.entireSystemDebt = _recoveryState.entireSystemDebt > _totalDebtToBurn ? _recoveryState.entireSystemDebt - _totalDebtToBurn : 0; _recoveryState.entireSystemColl = _recoveryState.entireSystemColl > _totalColToSend ? _recoveryState.entireSystemColl - _totalColToSend : 0; uint _debtToColl = (_totalDebtToBurn * DECIMAL_PRECISION) / _recoveryState.price; uint _cappedColl = collateral.getPooledEthByShares(_cappedColPortion + _liquidatorReward); emit CdpLiquidated( _recoveryState.cdpId, _borrower, _totalDebtToBurn, // please note this is the collateral share of the liquidated CDP _cappedColPortion, CdpOperation.liquidateInRecoveryMode, msg.sender, _cappedColl > _debtToColl ? (_cappedColl - _debtToColl) : 0 ); return _recoveryState; } // liquidate (and close) the CDP from an external liquidator // this function would return the liquidated debt and collateral of the given CDP // without emmiting events function _closeCdpByLiquidation(bytes32 _cdpId) private returns (uint256, uint256, uint256) { // calculate entire debt to repay (uint256 entireDebt, uint256 entireColl) = getSyncedDebtAndCollShares(_cdpId); // housekeeping after liquidation by closing the CDP uint256 _liquidatorReward = uint256(Cdps[_cdpId].liquidatorRewardShares); _closeCdp(_cdpId, Status.closedByLiquidation); return (entireDebt, entireColl, _liquidatorReward); } // Liquidate partially the CDP by an external liquidator // This function would return the liquidated debt and collateral of the given CDP function _liquidateCDPPartially( LiquidationLocals memory _partialState ) private returns (uint256, uint256) { bytes32 _cdpId = _partialState.cdpId; uint256 _partialDebt = _partialState.partialAmount; // calculate entire debt to repay CdpDebtAndCollShares memory _debtAndColl = _getSyncedDebtAndCollShares(_cdpId); _requirePartialLiqDebtSize(_partialDebt, _debtAndColl.debt, _partialState.price); uint256 newDebt = _debtAndColl.debt - _partialDebt; // credit to https://arxiv.org/pdf/2212.07306.pdf for details (uint256 _partialColl, uint256 newColl, ) = _calculatePartialLiquidationSurplusAndCap( _partialState.ICR, _partialState.price, _partialDebt, _debtAndColl.collShares ); // early return: if new collateral is zero, we have a full liqudiation if (newColl == 0) { return (0, 0); } // If we have coll remaining, it must meet minimum CDP size requirements _requirePartialLiqCollSize(collateral.getPooledEthByShares(newColl)); // updating the CDP accounting for partial liquidation _partiallyReduceCdpDebt(_cdpId, _partialDebt, _partialColl); // reInsert into sorted CDP list after partial liquidation { _reInsertPartialLiquidation( _partialState, EbtcMath._computeNominalCR(newColl, newDebt), _debtAndColl.debt, _debtAndColl.collShares ); uint _debtToColl = (_partialDebt * DECIMAL_PRECISION) / _partialState.price; uint _cappedColl = collateral.getPooledEthByShares(_partialColl); emit CdpPartiallyLiquidated( _cdpId, sortedCdps.getOwnerAddress(_cdpId), _partialDebt, _partialColl, CdpOperation.partiallyLiquidate, msg.sender, _cappedColl > _debtToColl ? (_cappedColl - _debtToColl) : 0 ); } return (_partialDebt, _partialColl); } function _partiallyReduceCdpDebt( bytes32 _cdpId, uint256 _partialDebt, uint256 _partialColl ) internal { Cdp storage _cdp = Cdps[_cdpId]; uint256 _coll = _cdp.coll; uint256 _debt = _cdp.debt; uint256 newDebt = _debt - _partialDebt; _requireMinDebt(newDebt); _cdp.coll = _coll - _partialColl; _cdp.debt = newDebt; _updateStakeAndTotalStakes(_cdpId); } // Re-Insertion into SortedCdp list after partial liquidation function _reInsertPartialLiquidation( LiquidationLocals memory _partialState, uint256 _newNICR, uint256 _oldDebt, uint256 _oldColl ) internal { bytes32 _cdpId = _partialState.cdpId; // ensure new ICR does NOT decrease due to partial liquidation // if original ICR is above LICR if (_partialState.ICR > LICR) { require( getCachedICR(_cdpId, _partialState.price) >= _partialState.ICR, "LiquidationLibrary: !_newICR>=_ICR" ); } // reInsert into sorted CDP list sortedCdps.reInsert( _cdpId, _newNICR, _partialState.upperPartialHint, _partialState.lowerPartialHint ); emit CdpUpdated( _cdpId, sortedCdps.getOwnerAddress(_cdpId), msg.sender, _oldDebt, _oldColl, Cdps[_cdpId].debt, Cdps[_cdpId].coll, Cdps[_cdpId].stake, CdpOperation.partiallyLiquidate ); } function _finalizeLiquidation( uint256 totalDebtToBurn, uint256 totalCollSharesToSend, uint256 totalDebtToRedistribute, uint256 totalLiquidatorRewardCollShares, uint256 totalSurplusCollShares, uint256 systemInitialCollShares, uint256 systemInitialDebt, uint256 price ) internal { // update the staking and collateral snapshots _updateSystemSnapshotsExcludeCollRemainder(totalCollSharesToSend); emit Liquidation(totalDebtToBurn, totalCollSharesToSend, totalLiquidatorRewardCollShares); _syncGracePeriodForGivenValues( systemInitialCollShares - totalCollSharesToSend - totalSurplusCollShares, systemInitialDebt - totalDebtToBurn, price ); // redistribute debt if any if (totalDebtToRedistribute > 0) { _redistributeDebt(totalDebtToRedistribute); } // burn the debt from liquidator ebtcToken.burn(msg.sender, totalDebtToBurn); // offset debt from Active Pool activePool.decreaseSystemDebt(totalDebtToBurn); // CEI: ensure sending back collateral to liquidator is last thing to do activePool.transferSystemCollSharesAndLiquidatorReward( msg.sender, totalCollSharesToSend, totalLiquidatorRewardCollShares ); } // Partial Liquidation Cap Logic function _calculatePartialLiquidationSurplusAndCap( uint256 _ICR, uint256 _price, uint256 _totalDebtToBurn, uint256 _totalColToSend ) private view returns (uint256 toLiquidator, uint256 collSurplus, uint256 debtToRedistribute) { uint256 _incentiveColl; // CLAMP if (_ICR > LICR) { // Cap at 10% _incentiveColl = (_totalDebtToBurn * (_ICR > MCR ? MCR : _ICR)) / _price; } else { // Min 103% _incentiveColl = (_totalDebtToBurn * LICR) / _price; } toLiquidator = collateral.getSharesByPooledEth(_incentiveColl); /// @audit MUST be like so, else we have debt redistribution, which we assume cannot happen in partial assert(toLiquidator < _totalColToSend); // Assert is correct here for Echidna /// Because of above we can subtract collSurplus = _totalColToSend - toLiquidator; // Can use unchecked but w/e } function _calculateFullLiquidationSurplusAndCap( uint256 _ICR, uint256 _price, uint256 _totalDebtToBurn, uint256 _totalColToSend ) private view returns (uint256 toLiquidator, uint256 collSurplus, uint256 debtToRedistribute) { uint256 _incentiveColl; if (_ICR > LICR) { _incentiveColl = (_totalDebtToBurn * (_ICR > MCR ? MCR : _ICR)) / _price; // Convert back to shares toLiquidator = collateral.getSharesByPooledEth(_incentiveColl); } else { // for full liquidation, there would be some bad debt to redistribute _incentiveColl = collateral.getPooledEthByShares(_totalColToSend); // Since it's full and there's bad debt we use spot conversion to // Determine the amount of debt that willl be repaid after adding the LICR discount // Basically this is buying underwater Coll // By repaying debt at 3% discount // Can there be a rounding error where the _debtToRepay > debtToBurn? uint256 _debtToRepay = (_incentiveColl * _price) / LICR; debtToRedistribute = _debtToRepay < _totalDebtToBurn ? _totalDebtToBurn - _debtToRepay // Bad Debt (to be redistributed) is (CdpDebt - Repaid) : 0; // Else 0 (note we may underpay per the comment above, althought that may be imaginary) // now CDP owner should have zero surplus to claim toLiquidator = _totalColToSend; } toLiquidator = toLiquidator < _totalColToSend ? toLiquidator : _totalColToSend; collSurplus = (toLiquidator == _totalColToSend) ? 0 : _totalColToSend - toLiquidator; } // --- Batch liquidation functions --- function _getLiquidationValuesNormalMode( uint256 _price, uint256 _TCR, LocalVariables_LiquidationSequence memory vars, LiquidationValues memory singleLiquidation ) internal { LiquidationLocals memory _liqState = LiquidationLocals( vars.cdpId, 0, _price, vars.ICR, vars.cdpId, vars.cdpId, (false), _TCR, 0, 0, 0, 0, 0 ); LiquidationLocals memory _outputState = _liquidateIndividualCdpSetupCDPInNormalMode( _liqState ); singleLiquidation.entireCdpDebt = _outputState.totalDebtToBurn; singleLiquidation.debtToBurn = _outputState.totalDebtToBurn; singleLiquidation.totalCollToSendToLiquidator = _outputState.totalCollSharesToSend; singleLiquidation.collSurplus = _outputState.totalSurplusCollShares; singleLiquidation.debtToRedistribute = _outputState.totalDebtToRedistribute; singleLiquidation.liquidatorCollSharesReward = _outputState.totalLiquidatorRewardCollShares; } function _getLiquidationValuesRecoveryMode( uint256 _price, uint256 _systemDebt, uint256 _systemCollShares, LocalVariables_LiquidationSequence memory vars, LiquidationValues memory singleLiquidation ) internal { LiquidationRecoveryModeLocals memory _recState = LiquidationRecoveryModeLocals( _systemDebt, _systemCollShares, 0, 0, 0, vars.cdpId, _price, vars.ICR, 0, 0 ); LiquidationRecoveryModeLocals memory _outputState = _liquidateIndividualCdpSetupCDPInRecoveryMode(_recState); singleLiquidation.entireCdpDebt = _outputState.totalDebtToBurn; singleLiquidation.debtToBurn = _outputState.totalDebtToBurn; singleLiquidation.totalCollToSendToLiquidator = _outputState.totalCollSharesToSend; singleLiquidation.collSurplus = _outputState.totalSurplusCollShares; singleLiquidation.debtToRedistribute = _outputState.totalDebtToRedistribute; singleLiquidation.liquidatorCollSharesReward = _outputState.totalLiquidatorRewardCollShares; } /// @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 nonReentrantSelfAndBOps { require( _cdpArray.length != 0, "LiquidationLibrary: Calldata address array must not be empty" ); LocalVariables_OuterLiquidationFunction memory vars; LiquidationTotals memory totals; // taking fee to avoid accounted for the calculation of the TCR _syncGlobalAccounting(); vars.price = priceFeed.fetchPrice(); (uint256 _TCR, uint256 systemColl, uint256 systemDebt) = _getTCRWithSystemDebtAndCollShares( vars.price ); vars.recoveryModeAtStart = _TCR < CCR ? true : false; // Perform the appropriate batch liquidation - tally values and obtain their totals. if (vars.recoveryModeAtStart) { totals = _getTotalFromBatchLiquidate_RecoveryMode( vars.price, systemColl, systemDebt, _cdpArray ); } else { // if !vars.recoveryModeAtStart totals = _getTotalsFromBatchLiquidate_NormalMode(vars.price, _TCR, _cdpArray); } require(totals.totalDebtInSequence > 0, "LiquidationLibrary: nothing to liquidate"); // housekeeping leftover collateral for liquidated CDPs if (totals.totalCollSurplus > 0) { activePool.transferSystemCollShares(address(collSurplusPool), totals.totalCollSurplus); } _finalizeLiquidation( totals.totalDebtToBurn, totals.totalCollToSendToLiquidator, totals.totalDebtToRedistribute, totals.totalCollReward, totals.totalCollSurplus, systemColl, systemDebt, vars.price ); } /* * This function is used when the batch liquidation starts during Recovery Mode. However, it * handle the case where the system *leaves* Recovery Mode, part way through the liquidation processing */ function _getTotalFromBatchLiquidate_RecoveryMode( uint256 _price, uint256 _systemCollShares, uint256 _systemDebt, bytes32[] memory _cdpArray ) internal returns (LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; vars.backToNormalMode = false; vars.entireSystemDebt = _systemDebt; vars.entireSystemColl = _systemCollShares; uint256 _TCR = _computeTCRWithGivenSystemValues( vars.entireSystemColl, vars.entireSystemDebt, _price ); uint256 _cnt = _cdpArray.length; bool[] memory _liqFlags = new bool[](_cnt); uint256 _start; for (vars.i = _start; ; ) { vars.cdpId = _cdpArray[vars.i]; // only for active cdps if (vars.cdpId != bytes32(0) && Cdps[vars.cdpId].status == Status.active) { vars.ICR = getSyncedICR(vars.cdpId, _price); if ( !vars.backToNormalMode && (_checkICRAgainstMCR(vars.ICR) || canLiquidateRecoveryMode(vars.ICR, _TCR)) ) { vars.price = _price; _syncAccounting(vars.cdpId); _getLiquidationValuesRecoveryMode( _price, vars.entireSystemDebt, vars.entireSystemColl, vars, singleLiquidation ); // Update aggregate trackers vars.entireSystemDebt = vars.entireSystemDebt - singleLiquidation.debtToBurn; vars.entireSystemColl = vars.entireSystemColl - singleLiquidation.totalCollToSendToLiquidator - singleLiquidation.collSurplus; // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); _TCR = _computeTCRWithGivenSystemValues( vars.entireSystemColl, vars.entireSystemDebt, _price ); vars.backToNormalMode = _TCR < CCR ? false : true; _liqFlags[vars.i] = true; } else if (vars.backToNormalMode && _checkICRAgainstMCR(vars.ICR)) { _syncAccounting(vars.cdpId); _getLiquidationValuesNormalMode(_price, _TCR, vars, singleLiquidation); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); _liqFlags[vars.i] = true; } // In Normal Mode skip cdps with ICR >= MCR } ++vars.i; if (vars.i == _cnt) { break; } } } function _getTotalsFromBatchLiquidate_NormalMode( uint256 _price, uint256 _TCR, bytes32[] memory _cdpArray ) internal returns (LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; uint256 _cnt = _cdpArray.length; uint256 _start; for (vars.i = _start; ; ) { vars.cdpId = _cdpArray[vars.i]; // only for active cdps if (vars.cdpId != bytes32(0) && Cdps[vars.cdpId].status == Status.active) { vars.ICR = getSyncedICR(vars.cdpId, _price); if (_checkICRAgainstMCR(vars.ICR)) { _syncAccounting(vars.cdpId); _getLiquidationValuesNormalMode(_price, _TCR, vars, singleLiquidation); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); } } ++vars.i; if (vars.i == _cnt) { break; } } } // --- Liquidation helper functions --- function _addLiquidationValuesToTotals( LiquidationTotals memory oldTotals, LiquidationValues memory singleLiquidation ) internal pure returns (LiquidationTotals memory newTotals) { // Tally all the values with their respective running totals newTotals.totalDebtInSequence = oldTotals.totalDebtInSequence + singleLiquidation.entireCdpDebt; newTotals.totalDebtToBurn = oldTotals.totalDebtToBurn + singleLiquidation.debtToBurn; newTotals.totalCollToSendToLiquidator = oldTotals.totalCollToSendToLiquidator + singleLiquidation.totalCollToSendToLiquidator; newTotals.totalDebtToRedistribute = oldTotals.totalDebtToRedistribute + singleLiquidation.debtToRedistribute; newTotals.totalCollSurplus = oldTotals.totalCollSurplus + singleLiquidation.collSurplus; newTotals.totalCollReward = oldTotals.totalCollReward + singleLiquidation.liquidatorCollSharesReward; return newTotals; } function _redistributeDebt(uint256 _debt) internal { if (_debt == 0) { return; } /* * Add distributed debt rewards-per-unit-staked to the running totals. Division uses a "feedback" * error correction, to keep the cumulative error low in the running totals systemDebtRedistributionIndex: * * 1) Form numerators which compensate for the floor division errors that occurred the last time this * function was called. * 2) Calculate "per-unit-staked" ratios. * 3) Multiply each ratio back by its denominator, to reveal the current floor division error. * 4) Store these errors for use in the next correction when this function is called. * 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended. */ uint256 EBTCDebtNumerator = (_debt * DECIMAL_PRECISION) + lastEBTCDebtErrorRedistribution; // Get the per-unit-staked terms uint256 _totalStakes = totalStakes; uint256 EBTCDebtRewardPerUnitStaked = EBTCDebtNumerator / _totalStakes; lastEBTCDebtErrorRedistribution = EBTCDebtNumerator - (EBTCDebtRewardPerUnitStaked * _totalStakes); // Add per-unit-staked terms to the running totals systemDebtRedistributionIndex = systemDebtRedistributionIndex + EBTCDebtRewardPerUnitStaked; emit SystemDebtRedistributionIndexUpdated(systemDebtRedistributionIndex); } // --- 'require' wrapper functions --- function _requirePartialLiqDebtSize( uint256 _partialDebt, uint256 _entireDebt, uint256 _price ) internal view { require( (_partialDebt + _convertDebtDenominationToBtc(MIN_NET_STETH_BALANCE, _price)) <= _entireDebt, "LiquidationLibrary: Partial debt liquidated must be less than total debt" ); } function _requirePartialLiqCollSize(uint256 _entireColl) internal pure { require( _entireColl >= MIN_NET_STETH_BALANCE, "LiquidationLibrary: Coll remaining in partially liquidated CDP must be >= minimum" ); } function _requireMinDebt(uint256 _debt) internal pure { require(_debt >= MIN_CHANGE, "LiquidationLibrary: Debt must be above min"); } }
// 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: 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
[{"inputs":[{"internalType":"address","name":"_borrowerOperationsAddress","type":"address"},{"internalType":"address","name":"_collSurplusPool","type":"address"},{"internalType":"address","name":"_ebtcToken","type":"address"},{"internalType":"address","name":"_sortedCdps","type":"address"},{"internalType":"address","name":"_activePool","type":"address"},{"internalType":"address","name":"_priceFeed","type":"address"},{"internalType":"address","name":"_collateral","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":[],"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":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"getPendingRedistributedDebt","outputs":[{"internalType":"uint256","name":"pendingEBTCDebtReward","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":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"hasPendingRedistributedDebt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[],"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":"uint128","name":"_gracePeriod","type":"uint128"}],"name":"setGracePeriod","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":[],"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"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104285760003560e01c806392caa08b1161022b578063b5b9c01711610130578063d9901b94116100b8578063e3f6838811610087578063e3f6838814610991578063e825665114610999578063ea6932b3146109a8578063f869817f146109b1578063f92d3433146109c457600080fd5b8063d9901b941461094c578063db4159ae14610955578063e20217be14610968578063e3bf120d1461098857600080fd5b8063bf9befb1116100ff578063bf9befb1146108e3578063c526a6b0146108ec578063ce292b7614610913578063cf3090121461091c578063d8dfeb451461092557600080fd5b8063b5b9c01714610899578063b7f8cf9b146108a2578063bc006c38146108c9578063bf7e214f146108d257600080fd5b8063a20baee6116101b3578063a839d19111610182578063a839d19114610848578063a8b54b0914610876578063a9beb7651461087f578063adf6cd1614610888578063ae6173461461089057600080fd5b8063a20baee614610795578063a33aacfd146107a4578063a3f4df7e146107ff578063a60b578d1461083557600080fd5b806398ad5a7e116101fa57806398ad5a7e1461074a5780639ac2e9f01461075d5780639c77f4cf146107705780639dc55330146107795780639faa3c911461078c57600080fd5b806392caa08b1461070e57806394691d851461071c57806396d711ff1461072f57806397bc1e1b1461073857600080fd5b80634607e9be116103315780636ffd8942116102b95780637f3020c1116102885780637f3020c1146106945780637f7dde4a146106a3578063807d138d146106ca578063882a7a37146106d35780638d136e32146106fb57600080fd5b80636ffd89421461064c578063741bef1a14610655578063794e57241461067c5780637e95d6641461068b57600080fd5b80635f402a37116103005780635f402a371461060657806361ec893d1461060f5780636b4f105b146106175780636e2d76d31461062a5780636ee4c8cf1461063d57600080fd5b80634607e9be146105cd5780634870dd9a146105d55780634cebd90a146105dd5780635733d58f146105f757600080fd5b806317f43b86116103b4578063208c260d11610383578063208c260d1461054857806324f2fe24146105735780633198853e146105805780633c16805d146105935780633d01f137146105ba57600080fd5b806317f43b86146104f457806319d6c022146105145780631a5866501461051c5780631f68f20a1461053f57600080fd5b806307902c55116103fb57806307902c55146104af57806307c2c41c146104b75780630a71096e146104c65780630c5805bc146104d95780631776165b146104e157600080fd5b8063013380e71461042d578063028dba891461047157806305a42d991461048657806305c4e46d146104a7575b600080fd5b6104547f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb81565b6040516001600160a01b0390911681526020015b60405180910390f35b61048461047f366004614b55565b6109cc565b005b610499610494366004614b55565b6109e0565b604051908152602001610468565b610499600181565b6104846109f4565b610499670de0b6b3a763ffff81565b6104846104d4366004614b55565b610a06565b610499610aef565b6104846104ef366004614b6e565b610b78565b610499610502366004614b55565b600f6020526000908152604090205481565b610499610c9e565b61052f61052a366004614b97565b610cc1565b6040519015158152602001610468565b61049960075481565b60025461055b906001600160801b031681565b6040516001600160801b039091168152602001610468565b60045461052f9060ff1681565b61049961058e366004614b55565b610cdf565b6104547f000000000000000000000000000000000000000000000000000000000000000081565b6104996105c8366004614b97565b610cf5565b610499610d1e565b61049960c881565b60025461055b90600160801b90046001600160801b031681565b610499671158e460913d000081565b61055b61038481565b610499603c81565b610499610625366004614b55565b610de0565b610499610638366004614b55565b610e2d565b610499671bc16d674ec8000081565b610499600e5481565b6104547f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a81565b610499670f43fc2c04ee000081565b61049960035481565b6104996702c68af0bb14000081565b6104547f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc81565b610499600c5481565b6106e66106e1366004614b97565b610e54565b60408051928352602083019190915201610468565b610484610709366004614bb9565b610f20565b61055b6001600160801b0381565b61048461072a366004614b55565b611065565b610499600d5481565b600154600160a01b900460ff1661052f565b610499610758366004614b55565b611076565b61048461076b366004614c01565b611114565b61049961138881565b61052f610787366004614b55565b6114ce565b61049960065481565b610499670de0b6b3a764000081565b6107ee6107b2366004614b55565b600a602052600090815260409020805460018201546002830154600390930154919290916001600160801b03811690600160801b900460ff1685565b604051610468959493929190614cd5565b6108286040518060400160405280600a81526020016921b23826b0b730b3b2b960b11b81525081565b6040516104689190614d19565b6106e6610843366004614b55565b6114d9565b61085b610856366004614b97565b61150f565b60408051938452602084019290925290820152606001610468565b61049960125481565b61049961271081565b6104846116c5565b61049960135481565b61049960085481565b6104547f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad081565b61049960055481565b6001546001600160a01b0316610454565b610499600b5481565b6104547f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be381565b61049960095481565b61049960005481565b6104547f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b6104996103e881565b610499610963366004614b55565b6116d5565b610499610976366004614b55565b60146020526000908152604090205481565b61049960105481565b61049961173a565b610499670e4b4b8af6a7000081565b61049960115481565b6104996109bf366004614b97565b61179a565b610499600081565b6109d46117c0565b6109dd8161185e565b50565b6000806109ec836118f2565b509392505050565b6109fc611995565b610a046119f6565b565b600160005414610a315760405162461bcd60e51b8152600401610a2890614d67565b60405180910390fd5b60017f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad06001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab59190614db2565b14610ad25760405162461bcd60e51b8152600401610a2890614dcb565b6002600081905550610ae78160008384611abd565b506001600055565b60007f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b739190614db2565b905090565b610b8e336000356001600160e01b031916611e23565b610bcf5760405162461bcd60e51b8152602060048201526012602482015271105d5d1a0e8815539055551213d49256915160721b6044820152606401610a28565b6103846001600160801b0382161015610c425760405162461bcd60e51b815260206004820152602f60248201527f4364704d616e616765723a20477261636520706572696f642062656c6f77206d60448201526e34b734b6bab690323ab930ba34b7b760891b6064820152608401610a28565b610c4a6109f4565b600280546001600160801b03908116600160801b918416918202179091556040519081527ffcb0de0cdd94ed0400cd374b4c81409ae22dd0bee82df2b0d09e0af6958684f29060200160405180910390a150565b6103e8610cb4670de0b6b3a76400006005614e34565b610cbe9190614e4b565b81565b6000828210158015610cd65750610cd6611ebc565b90505b92915050565b600080610ceb83611f07565b5090949350505050565b6000806000610d03856114d9565b915091506000610d14828487611f4a565b9695505050505050565b6000806000610d2b611ff0565b915091506000610d3b8284612090565b5050905060007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc39190614db2565b90508115610dd857610dd58282614e6d565b90505b949350505050565b6000806000610ded611ff0565b915091506000610dfd8284612090565b506000878152601460205260408120549193509150610e1e908790846120f1565b50929998505050505050505050565b6000806000610e3b846114d9565b915091506000610e4b828461215e565b95945050505050565b600082815260146020908152604080832054600a909252822060010154829190811580610e7f575080155b80610e8957508482145b15610e9b57600093509150610f199050565b6000610ea78387614e6d565b6000888152600a6020526040902060020154610ec39190614e34565b90506000610ed9670de0b6b3a764000084614e34565b905081811115610f0e5781670de0b6b3a7640000610ef78284614e6d565b610f019190614e4b565b9550955050505050610f19565b506000945090925050505b9250929050565b600160005414610f425760405162461bcd60e51b8152600401610a2890614d67565b60017f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad06001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc69190614db2565b14610fe35760405162461bcd60e51b8152600401610a2890614dcb565b6002600090815583900361104e5760405162461bcd60e51b815260206004820152602c60248201527f4c69717569646174696f6e4c6962726172793a2075736520606c69717569646160448201526b74656020666f72203130302560a01b6064820152608401610a28565b61105a84848484611abd565b505060016000555050565b61106d6117c0565b6109dd81612194565b600080611081610d1e565b905060007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663facc05116040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111079190614db2565b9050610dd8828286611f4a565b6001600054146111365760405162461bcd60e51b8152600401610a2890614d67565b60017f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad06001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190614db2565b146111d75760405162461bcd60e51b8152600401610a2890614dcb565b60026000908155815190036112545760405162461bcd60e51b815260206004820152603c60248201527f4c69717569646174696f6e4c6962726172793a2043616c6c646174612061646460448201527f72657373206172726179206d757374206e6f7420626520656d707479000000006064820152608401610a28565b61128160405180608001604052806000815260200160001515815260200160008152602001600081525090565b611289614ad5565b611291611995565b7f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156112f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113159190614db2565b808352600090819081906113289061221f565b925092509250671158e460913d00008310611344576000611347565b60015b1580156020870152611368578451611361908383896122e3565b9350611378565b8451611375908488612590565b93505b83516113d75760405162461bcd60e51b815260206004820152602860248201527f4c69717569646174696f6e4c6962726172793a206e6f7468696e6720746f206c604482015267697175696461746560c01b6064820152608401610a28565b6080840151156114995760808401516040516338339d3160e11b81526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc16916370673a6291611466917f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de916004016001600160a01b03929092168252602082015260400190565b600060405180830381600087803b15801561148057600080fd5b505af1158015611494573d6000803e3d6000fd5b505050505b6114c18460200151856040015186606001518760a00151886080015187878c60000151612686565b5050600160005550505050565b6000610cd982612884565b6000806000806114ff8560146000888152602001908152602001600020546012546120f1565b5091989297509195505050505050565b600080600083851161157b5760405162461bcd60e51b815260206004820152602f60248201527f4344504d616e616765723a206f6e6c792074616b65206665652077697468206260448201526e0d2cececae440dccaee40d2dcc8caf608b1b6064820152608401610a28565b60006115878587614e6d565b905060006127106008548361159c9190614e34565b6115a69190614e4b565b905060006115b261173a565b6115bc9083614e34565b600b54604051631920845160e01b81526004810183905291925090600090670de0b6b3a7640000907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa158015611635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116599190614db2565b6116639190614e4b565b90506000601354670de0b6b3a76400008361167e9190614e34565b6116889190614e80565b905060006116968483614e4b565b905060006116a48583614e34565b6116ae9084614e6d565b939a509098509196505050505050505b9250925092565b6116cd6117c0565b610a04611995565b60008060006116e2611ff0565b9150915060006116f28284612090565b50915050600080600061171988601460008b815260200190815260200160002054866120f1565b5093505092509250600061172d848461215e565b9998505050505050505050565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b6000806117a684610cdf565b905060006117b385610de0565b9050610e4b818386611f4a565b336001600160a01b037f000000000000000000000000d366e016ae0677cdce93472e603b75051e022ad01614610a045760405162461bcd60e51b815260206004820152603960248201527f4364704d616e616765723a2043616c6c6572206973206e6f742074686520426f60448201527f72726f7765724f7065726174696f6e7320636f6e7472616374000000000000006064820152608401610a28565b6040518181527f41a8f1fd29dc6218d58cbc134120f372f585f1f92225604e66c7eceaf08ccbbe9060200160405180910390a16002546001600160801b03166002600160801b0319016109dd57600280546001600160801b031916426001600160801b03161790556040517ff8c0d4edf35c388faff93d34bebba3447add915ecdd59cd3d9512d40a4dde70f90600090a150565b6000818152600a60205260408120819060016003820154600160801b900460ff16600481111561192457611924614cbf565b146119355750600093849350915050565b6000848152600f6020526040902054600e546119519190614e6d565b9150811561198357670de0b6b3a76400008282600201546119729190614e34565b61197c9190614e4b565b925061198f565b50600093849350915050565b50915091565b6000806119a0611ff0565b915091506119ae82826128da565b81811180156119bf57506000600b54115b156119f25760008060006119d38486612090565b9250925092506119e483838361292e565b6119ee6000612ad6565b5050505b5050565b60007f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611a58573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7c9190614db2565b90506000611a8982612bb9565b90506000611a9e82671158e460913d00001190565b90508015611ab457611aaf8261185e565b505050565b611aaf82612194565b611ac684612bcd565b611acf84612c5d565b60007f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611b31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b559190614db2565b90506000611b638683610cf5565b90506000806000611b738561221f565b925092509250670f43fc2c04ee00008410611d3c57611b928484612dd6565b611c175760405162461bcd60e51b815260206004820152604a60248201527f4c69717569646174696f6e4c6962726172793a20494352206973206e6f74206260448201527f656c6f77206c69717569646174696f6e207468726573686f6c6420696e2063756064820152697272656e74206d6f646560b01b608482015260a401610a28565b6002546001600160801b03166002600160801b03198101611ca05760405162461bcd60e51b815260206004820152603a60248201527f4c69717569646174696f6e4c6962726172793a205265636f76657279204d6f6460448201527f6520677261636520706572696f64206e6f7420737461727465640000000000006064820152608401610a28565b600254611cbd90600160801b90046001600160801b031682614e93565b6001600160801b03164211611d3a5760405162461bcd60e51b815260206004820152603e60248201527f4c69717569646174696f6e4c6962726172793a205265636f76657279206d6f6460448201527f6520677261636520706572696f64207374696c6c20696e2065666665637400006064820152608401610a28565b505b6000671158e460913d00008410611d54576000611d57565b60015b90506000604051806101a001604052808c81526020018b81526020018881526020018781526020018a81526020018981526020018315158152602001868152602001600081526020016000815260200160008152602001600081526020016000815250905060006040518061014001604052808581526020018681526020016000815260200160008152602001600081526020018d81526020018981526020018881526020016000815260200160008152509050611e158282612e0b565b505050505050505050505050565b6001546000906001600160a01b03168015801590610dd8575060405163b700961360e01b81526001600160a01b0385811660048301523060248301526001600160e01b03198516604483015282169063b700961390606401602060405180830381865afa158015611e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd89190614eba565b6002546000906001600160801b03908116908114801590611f015750600254611ef590600160801b90046001600160801b031682614e93565b6001600160801b031642115b91505090565b6000806000806000611f18866118f2565b6000888152600a602052604090205491935091508215611f3f57611f3c8382614e80565b90505b969195509350915050565b604051630f451f7160e31b81526004810184905260009081906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015611fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd89190614db2565b9050611fe5818585612ec6565b9150505b9392505050565b601154604051630f451f7160e31b8152670de0b6b3a7640000600482015260009182916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015612064573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120889190614db2565b915091509091565b600080600083851180156120a657506000600b54115b156120e05760008060006120ba888861150f565b9250925092506000826012546120d09190614e80565b93965092945092506116be915050565b5050601254601354600092506116be565b6000838152600a60205260408120600101548190819081908190819088881480159061211d5750600089115b156121375760008061212f8c8b610e54565b909450925050505b60008060006121458d611f07565b959f919e50959c50949a50929850929650505050505050565b6000811561218b578161217a68056bc75e2d6310000085614e34565b6121849190614e4b565b9050610cd9565b50600019610cd9565b6040518181527f41a8f1fd29dc6218d58cbc134120f372f585f1f92225604e66c7eceaf08ccbbe9060200160405180910390a16002546001600160801b03908116146109dd57600280546001600160801b0319166001600160801b031790556040517fa4884b11e6de87d7ea9a9d8c420198ec163777e456bd6b36a3acd5b4667fb3c590600090a150565b60008060008061222d61173a565b90506000612239612ef7565b604051630f451f7160e31b8152600481018490529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa1580156122a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c89190614db2565b90506122d5818389612ec6565b979296509094509092505050565b6122eb614ad5565b6122f3614b0b565b6122fb614ad5565b6000606083018190526080830186905260a0830187905261231d87878a611f4a565b855190915060008167ffffffffffffffff81111561233d5761233d614beb565b604051908082528060200260200182016040528015612366578160200160208202803683370190505b5060008087529091505b8786600001518151811061238657612386614edc565b602090810291909101015160408701819052158015906123d5575060016040878101516000908152600a6020522060030154600160801b900460ff1660048111156123d3576123d3614cbf565b145b1561256b576123e886604001518c61179a565b6020870152606086015115801561241d57506020860151670f43fc2c04ee0000118061241d575061241d866020015185610cc1565b156124fc5760c086018b9052604086015161243790612c5d565b61244c8b87608001518860a001518989612f57565b846020015186608001516124609190614e6d565b608080880191909152850151604086015160a08801516124809190614e6d565b61248a9190614e6d565b60a08701526124998786612fff565b96506124ae8660a0015187608001518d611f4a565b9350671158e460913d000084106124c65760016124c9565b60005b1515606087015285518251600191849181106124e7576124e7614edc565b9115156020928302919091019091015261256b565b8560600151801561251857506020860151670f43fc2c04ee0000115b1561256b5761252a8660400151612c5d565b6125368b85888861309a565b6125408786612fff565b965060018287600001518151811061255a5761255a614edc565b911515602092830291909101909101525b8551869061257890614ef2565b90528551830361237057505050505050949350505050565b612598614ad5565b6125a0614b0b565b6125a8614ad5565b835160008084525b858460000151815181106125c6576125c6614edc565b60209081029190910101516040850181905215801590612615575060016040858101516000908152600a6020522060030154600160801b900460ff16600481111561261357612613614cbf565b145b156126645761262884604001518961179a565b60208501819052670f43fc2c04ee000011156126645761264b8460400151612c5d565b6126578888868661309a565b6126618584612fff565b94505b8351849061267190614ef2565b9052835182036125b057505050509392505050565b61268f87612ad6565b60408051898152602081018990529081018690527fc5cb827645ffa2009bf4c45a58df393302651bf85a1f49ba3bd75c05032bcde29060600160405180910390a16126f8846126de8986614e6d565b6126e89190614e6d565b6126f28a85614e6d565b8361315f565b85156127075761270786613225565b604051632770a7eb60e21b8152336004820152602481018990527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb6001600160a01b031690639dc29fac90604401600060405180830381600087803b15801561276f57600080fd5b505af1158015612783573d6000803e3d6000fd5b50506040516349f047bb60e01b8152600481018b90527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031692506349f047bb9150602401600060405180830381600087803b1580156127e957600080fd5b505af11580156127fd573d6000803e3d6000fd5b5050604051634667d4f360e11b8152336004820152602481018a9052604481018890527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b03169250638ccfa9e69150606401600060405180830381600087803b15801561287057600080fd5b505af1158015611e15573d6000803e3d6000fd5b600060016000838152600a6020526040902060030154600160801b900460ff1660048111156128b5576128b5614cbf565b146128c257506000919050565b50600e546000918252600f6020526040909120541090565b8181146119f2576011819055604080518381526020810183905242918101919091527fc3fdd35fd80bc508237115f23bf8e423d1cb1ac6e75cc2e32eb938c193e7ea37906060015b60405180910390a15050565b600060125490508260128190555081601381905550837f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c69190614db2565b11612a135760405162461bcd60e51b815260206004820181905260248201527f4344504d616e616765723a206665652073706c697420697320746f6f206269676044820152606401610a28565b604051631424027f60e01b8152600481018590527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031690631424027f90602401600060405180830381600087803b158015612a7557600080fd5b505af1158015612a89573d6000803e3d6000fd5b505060408051848152602081018790529081018790527f4b636d6be3a9993af0c45fcb447e094dfb35d8229c2c1ac75fd6576b0109c031925060600190505b60405180910390a150505050565b6000600b54905080600c819055506000827f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b699190614db2565b612b739190614e6d565b600d81905560408051848152602081018390529192507f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf60910160405180910390a1505050565b6000612bc48261221f565b50909392505050565b60016000828152600a6020526040902060030154600160801b900460ff166004811115612bfc57612bfc614cbf565b146109dd5760405162461bcd60e51b815260206004820152602b60248201527f4364704d616e616765723a2043647020646f6573206e6f74206578697374206f60448201526a1c881a5cc818db1bdcd95960aa1b6064820152608401610a28565b612c65611995565b600081815260146020526040812054601254909180808080612c888888886120f1565b945094509450945094506000831180612ca15750600081115b15612db3576000888152600a60205260409020600181015481548515612cce57612cce8b89888d8d6132c0565b8315612ce857612cdd8b61332b565b868114612ce8578683555b604051626a1f6360e11b8152600481018c905233906001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be3169062d43ec690602401602060405180830381865afa158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190614f0b565b6001600160a01b03168c600080516020614fc483398151915284868c8e8a600201546003604051612da796959493929190614f48565b60405180910390a45050505b858714612dcc5760008881526014602052604090208690555b5050505050505050565b6000612de983670f43fc2c04ee00001190565b80610cd65750828210158015610cd65750610cd682671158e460913d00001190565b612e13614ad5565b815160208084015190850151600003612e4e57612e308585613376565b608088015260a0870152606086015260408501526020840152612e9e565b612e57856134c4565b604085018190526020850191909152158015612e7557506020830151155b15612e9e57612e848585613376565b608088015260a08701526060860152604085015260208401525b6119ee8360200151846040015185606001518660a00151876080015186888c60400151612686565b60008215612eee57600083612edb8487614e34565b612ee59190614e4b565b9150611fe99050565b50600019611fe9565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663facc05116040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b6000604051806101400160405280868152602001858152602001600081526020016000815260200160008152602001846040015181526020018781526020018460200151815260200160008152602001600081525090506000612fb9826137a0565b604080820180518652516020860152606080830151918601919091526080808301519086015261010082015190850152610120015160a090930192909252505050505050565b613007614ad5565b815183516130159190614e80565b81526020808301519084015161302b9190614e80565b6020820152604080830151908401516130449190614e80565b60408201526060808301519084015161305d9190614e80565b6060820152608080830151908401516130769190614e80565b608082015260a0808301519084015161308f9190614e80565b60a082015292915050565b6000604051806101a0016040528084604001518152602001600081526020018681526020018460200151815260200184604001518152602001846040015181526020016000151581526020018581526020016000815260200160008152602001600081526020016000815260200160008152509050600061311a82613bdb565b610140810180518552516020850152610120810151604085015261010081015160808501526101608101516060850152610180015160a0909301929092525050505050565b604051630f451f7160e31b8152600481018490526000906131f6906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa1580156131cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ef9190614db2565b8484612ec6565b9050671158e460913d0000811015613216576132118161185e565b61321f565b61321f81612194565b50505050565b806000036132305750565b601054600090613248670de0b6b3a764000084614e34565b6132529190614e80565b600b5490915060006132648284614e4b565b90506132708282614e34565b61327a9084614e6d565b601055600e5461328b908290614e80565b600e8190556040519081527f05e6f8af4804325bb77b06cfe6aa6d8fa37cf2ee9ee165ca94e1bad877966a1790602001612ac8565b6000858152600a6020908152604091829020600101869055815187815290810184905290810182905260608101849052608081018590527f74f8f18ebc63a5cfcb205b885cb004fe379a24ba2549e9dbb34a1aef72cd38209060a00160405180910390a15050505050565b600e546000828152600f602090815260409182902083905581518481529081018390527f75a085b846bf9716e36c23a2654fa549def06c94f9e24590c1448f19137b2d319101612922565b60008060008060008660c0015115613483576000613393876137a0565b6080810151909150156134585760808101516040516338339d3160e11b81526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc16916370673a6291613425917f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de916004016001600160a01b03929092168252602082015260400190565b600060405180830381600087803b15801561343f57600080fd5b505af1158015613453573d6000803e3d6000fd5b505050505b80604001518160600151826101000151836101200151846080015195509550955095509550506134ba565b600061348e88613bdb565b905080610140015181610120015182610160015183610180015184610100015195509550955095509550505b9295509295909350565b805160208201516000918291826134da83613ef4565b90506134ef8282600001518860400151613f2e565b80516000906134ff908490614e6d565b905060008061351c89606001518a60400151878760200151613fd0565b50915091508060000361353a57506000988998509650505050505050565b604051630f451f7160e31b8152600481018290526135cc907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690637a28fb8890602401602060405180830381865afa1580156135a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135c79190614db2565b6140f2565b6135d786868461418a565b6135f1896135e5838661215e565b865160208801516141d4565b604089015160009061360b670de0b6b3a764000088614e34565b6136159190614e4b565b604051630f451f7160e31b8152600481018590529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015613680573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a49190614db2565b604051626a1f6360e11b8152600481018a905290915033906001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be3169062d43ec690602401602060405180830381865afa15801561370c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137309190614f0b565b6001600160a01b0316897f955c32472631ff0a9f8122002fbb08254175ed4bbb64b32fb3c5ea67b7594eaa8a88600788881161376d576000613777565b6137778989614e6d565b6040516137879493929190614f83565b60405180910390a4509499919850909650505050505050565b6137f9604051806101400160405280600081526020016000815260200160008152602001600081526020016000815260200160008019168152602001600081526020016000815260200160008152602001600081525090565b600080600061380b8560a001516143d8565b9250925092506000806000807f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031662d43ec68a60a001516040518263ffffffff1660e01b815260040161386891815260200190565b602060405180830381865afa158015613885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138a99190614f0b565b9050336001600160a01b0316816001600160a01b03168a60a00151600080516020614fc48339815191528a8a600080600060056040516138ee96959493929190614f48565b60405180910390a461390a8960e001518a60c001518989614427565b9195509350915082156139ec5760e0890151670f43fc2c04ee00001115613940576139358484614e80565b9350600092506139ec565b60a0890151604051639fcb252760e01b815260048101919091526001600160a01b03828116602483015260448201859052600060648301527f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de1690639fcb252790608401600060405180830381600087803b1580156139be57600080fd5b505af11580156139d2573d6000803e3d6000fd5b505050508289608001516139e69190614e80565b60808a01525b81156139ff576139fc8288614e6d565b96505b868960400151613a0f9190614e80565b60408a01526060890151613a24908590614e80565b60608a0152610100890151613a3a908390614e80565b6101008a0152610120890151613a51908690614e80565b6101208a015288518710613a66576000613a73565b8851613a73908890614e6d565b895260208901518610613a87576000613a97565b858960200151613a979190614e6d565b60208a015260c0890151600090613ab6670de0b6b3a76400008a614e34565b613ac09190614e4b565b905060006001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416637a28fb88613afd8989614e80565b6040518263ffffffff1660e01b8152600401613b1b91815260200190565b602060405180830381865afa158015613b38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b5c9190614db2565b9050336001600160a01b0316836001600160a01b03168c60a001517f4aa47c64a602b8aac095cf86852713d35b22e7c8a7164d168b6cb1d37e05e8438c8a6005888811613baa576000613bb4565b613bb48989614e6d565b604051613bc49493929190614f83565b60405180910390a450989998505050505050505050565b613c51604051806101a001604052806000801916815260200160008152602001600081526020016000815260200160008019168152602001600080191681526020016000151581526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000806000613c6385600001516143d8565b9250925092506000806000807f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031662d43ec68a600001516040518263ffffffff1660e01b8152600401613cc091815260200190565b602060405180830381865afa158015613cdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d019190614f0b565b9050336001600160a01b0316816001600160a01b03168a60000151600080516020614fc48339815191528a8a60008060006004604051613d4696959493929190614f48565b60405180910390a4613d6289606001518a604001518989614427565b919550935091508215613d8057613d798385614e80565b9350600092505b8115613d9357613d908288614e6d565b96505b86896101400151613da49190614e80565b6101408a0152610120890151613dbb908590614e80565b6101208a0152610160890151613dd2908390614e80565b6101608a0152610180890151613de9908690614e80565b6101808a01526040890151600090613e09670de0b6b3a76400008a614e34565b613e139190614e4b565b905060006001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416637a28fb88613e508989614e80565b6040518263ffffffff1660e01b8152600401613e6e91815260200190565b602060405180830381865afa158015613e8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613eaf9190614db2565b8b5190915033906001600160a01b038516907f4aa47c64a602b8aac095cf86852713d35b22e7c8a7164d168b6cb1d37e05e8438c8a6004888811613baa576000613bb4565b6040805180820190915260008082526020820152600080613f14846114d9565b604080518082019091529182526020820152949350505050565b81613f41671bc16d674ec8000083614605565b613f4b9085614e80565b1115611aaf5760405162461bcd60e51b815260206004820152604860248201527f4c69717569646174696f6e4c6962726172793a205061727469616c206465627460448201527f206c697175696461746564206d757374206265206c657373207468616e20746f6064820152671d185b081919589d60c21b608482015260a401610a28565b600080600080670e4b4b8af6a700008811156140215786670f43fc2c04ee00008911613ffc5788614006565b670f43fc2c04ee00005b6140109088614e34565b61401a9190614e4b565b9050614041565b86614034670e4b4b8af6a7000088614e34565b61403e9190614e4b565b90505b604051631920845160e01b8152600481018290527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa1580156140a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140ca9190614db2565b93508484106140db576140db614fad565b6140e58486614e6d565b9250509450945094915050565b671bc16d674ec800008110156109dd5760405162461bcd60e51b815260206004820152605160248201527f4c69717569646174696f6e4c6962726172793a20436f6c6c2072656d61696e6960448201527f6e6720696e207061727469616c6c79206c69717569646174656420434450206d606482015270757374206265203e3d206d696e696d756d60781b608482015260a401610a28565b6000838152600a602052604081206001810154815491929091906141ae8683614e6d565b90506141b981614624565b6141c38584614e6d565b6001850155808455612dcc87614689565b83516060850151670e4b4b8af6a7000010156142565784606001516141fd828760400151610cf5565b10156142565760405162461bcd60e51b815260206004820152602260248201527f4c69717569646174696f6e4c6962726172793a20215f6e65774943523e3d5f4960448201526121a960f11b6064820152608401610a28565b608085015160a0860151604051637b30119360e01b81526004810184905260248101879052604481019290925260648201527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031690637b30119390608401600060405180830381600087803b1580156142d657600080fd5b505af11580156142ea573d6000803e3d6000fd5b5050604051626a1f6360e11b8152600481018490523392506001600160a01b037f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be316915062d43ec690602401602060405180830381865afa158015614353573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143779190614f0b565b6000838152600a6020526040908190208054600182015460029092015492516001600160a01b0394909416938693600080516020614fc4833981519152936143c9938b938b9391929190600790614f48565b60405180910390a45050505050565b60008060008060006143e9866114d9565b6000888152600a602052604090206003908101549294509092506001600160801b039091169061441a9088906146fa565b9196909550909350915050565b600080600080670e4b4b8af6a700008811156145045786670f43fc2c04ee00008911614453578861445d565b670f43fc2c04ee00005b6144679088614e34565b6144719190614e4b565b604051631920845160e01b8152600481018290529091507f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa1580156144d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144fd9190614db2565b93506145cf565b604051630f451f7160e31b8152600481018690527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690637a28fb8890602401602060405180830381865afa158015614569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061458d9190614db2565b90506000670e4b4b8af6a700006145a48984614e34565b6145ae9190614e4b565b90508681106145be5760006145c8565b6145c88188614e6d565b9250859450505b8484106145dc57846145de565b835b93508484146145f6576145f18486614e6d565b6140e5565b60009250509450945094915050565b6000670de0b6b3a764000061461a8385614e34565b610cd69190614e4b565b6103e88110156109dd5760405162461bcd60e51b815260206004820152602a60248201527f4c69717569646174696f6e4c6962726172793a2044656274206d7573742062656044820152691030b137bb329036b4b760b11b6064820152608401610a28565b600080600061469784614782565b9150915060008183600b546146ac9190614e80565b6146b69190614e6d565b600b8190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b61470482826147ba565b6040516395bc267360e01b8152600481018390527f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b0316906395bc267390602401600060405180830381600087803b15801561476657600080fd5b505af115801561477a573d6000803e3d6000fd5b505050505050565b6000818152600a60205260408120600181015482919082906147a3906148f1565b600290920180549083905591959194509092505050565b60008160048111156147ce576147ce614cbf565b141580156147ee575060018160048111156147eb576147eb614cbf565b14155b6148585760405162461bcd60e51b815260206004820152603560248201527f4364704d616e6167657253746f726167653a20636c6f7365206e6f6e2d6578696044820152747374206f72206e6f6e2d616374697665204344502160581b6064820152608401610a28565b6000614862610aef565b905061486d8161497c565b61487683614a68565b6000838152600a60205260409020600301805483919060ff60801b1916600160801b8360048111156148aa576148aa614cbf565b021790555050506000908152600a602090815260408083206001810184905583815560030180546001600160801b0319169055600f82528083208390556014909152812055565b600080600d54600003614905575081610cd9565b6000600c541161496c5760405162461bcd60e51b815260206004820152602c60248201527f4364704d616e6167657253746f726167653a207a65726f20746f74616c53746160448201526b6b6573536e617073686f742160a01b6064820152608401610a28565b600d54600c5461461a9085614e34565b600181118015614a0d575060017f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149e7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a0b9190614db2565b115b6109dd5760405162461bcd60e51b815260206004820152602660248201527f4364704d616e616765723a204f6e6c79206f6e652063647020696e207468652060448201526573797374656d60d01b6064820152608401610a28565b6000818152600a6020526040812060020154600b54614a879190614e6d565b600b8190556000838152600a602052604080822060020191909155519091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae520382906129229083815260200190565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806101000160405280600081526020016000815260200160008019168152602001600015158152602001600081526020016000815260200160008152602001600081525090565b600060208284031215614b6757600080fd5b5035919050565b600060208284031215614b8057600080fd5b81356001600160801b0381168114611fe957600080fd5b60008060408385031215614baa57600080fd5b50508035926020909101359150565b60008060008060808587031215614bcf57600080fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215614c1457600080fd5b823567ffffffffffffffff80821115614c2c57600080fd5b818501915085601f830112614c4057600080fd5b813581811115614c5257614c52614beb565b8060051b604051601f19603f83011681018181108582111715614c7757614c77614beb565b604052918252848201925083810185019188831115614c9557600080fd5b938501935b82851015614cb357843584529385019392850192614c9a565b98975050505050505050565b634e487b7160e01b600052602160045260246000fd5b85815260208101859052604081018490526001600160801b038316606082015260a0810160058310614d0957614d09614cbf565b8260808301529695505050505050565b600060208083528351808285015260005b81811015614d4657858101830151858201604001528201614d2a565b506000604082860101526040601f19601f8301168501019250505092915050565b6020808252602b908201527f4364704d616e616765723a205265656e7472616e637920696e206e6f6e52656560408201526a1b9d1c985b9d0818d85b1b60aa1b606082015260800190565b600060208284031215614dc457600080fd5b5051919050565b60208082526033908201527f426f72726f7765724f7065726174696f6e733a205265656e7472616e637920696040820152721b881b9bdb9499595b9d1c985b9d0818d85b1b606a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610cd957610cd9614e1e565b600082614e6857634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115610cd957610cd9614e1e565b80820180821115610cd957610cd9614e1e565b6001600160801b03818116838216019080821115614eb357614eb3614e1e565b5092915050565b600060208284031215614ecc57600080fd5b81518015158114611fe957600080fd5b634e487b7160e01b600052603260045260246000fd5b600060018201614f0457614f04614e1e565b5060010190565b600060208284031215614f1d57600080fd5b81516001600160a01b0381168114611fe957600080fd5b60098110614f4457614f44614cbf565b9052565b600060c082019050878252866020830152856040830152846060830152836080830152614f7860a0830184614f34565b979650505050505050565b8481526020810184905260808101614f9e6040830185614f34565b82606083015295945050505050565b634e487b7160e01b600052600160045260246000fdfe94bbf0bce1cd1f8f3842d4a02225a01ed47c14e2cece80bfc4fa9a66308a5f7ea264697066735822122038ce0ef3c0ab7e322758d96ded97a4b6af8f677fda304e928e5821688197ef9b64736f6c63430008110033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.