Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
PositionManager
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 1 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import { IERC3156FlashBorrower } from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import "../interfaces/core/IBorrowerOperations.sol";
import "../interfaces/core/IDebtToken.sol";
import "../interfaces/core/ISortedPositions.sol";
import "../interfaces/core/IPriceFeed.sol";
import "../interfaces/core/IFactory.sol";
import "../interfaces/core/ILiquidationManager.sol";
import "../dependencies/SystemStart.sol";
import "../dependencies/PropBase.sol";
import "../dependencies/PropMath.sol";
import "../dependencies/PropOwnable.sol";
/**
@title Position Manager
@notice Based on Liquity's `TroveManager`
https://github.com/liquity/dev/blob/main/packages/contracts/contracts/TroveManager.sol
Implementation is modified so that multiple `PositionManager` and `SortedPositions`
contracts are deployed in tandem, with each pair managing positions of a single collateral
type.
Functionality related to liquidations has been moved to `LiquidationManager`. This was
necessary to avoid the restriction on deployed bytecode size.
*/
contract PositionManager is PropBase, PropOwnable, SystemStart {
using SafeERC20 for IERC20;
address public immutable borrowerOperations;
address public immutable liquidationManager;
address immutable gasPoolAddress;
IDebtToken public immutable debtToken;
address public collVaultRouter;
IPriceFeed public priceFeed;
IERC20 public collateralToken;
// A doubly linked list of Positions, sorted by their collateral ratios
ISortedPositions public sortedPositions;
// Minimum collateral ratio for individual positions
uint256 public MCR;
uint256 constant SECONDS_IN_ONE_MINUTE = 60;
uint256 constant INTEREST_PRECISION = 1e27;
uint256 constant SECONDS_IN_YEAR = 365 days;
uint256 public constant SUNSETTING_INTEREST_RATE = (INTEREST_PRECISION * 5000) / (BP * SECONDS_IN_YEAR); // 50%
uint256 constant _100pct = 1000000000000000000; // 1e18 == 100%, below this CR it's considered undercollateralized
/*
* 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 white paper.
*/
uint256 constant BETA = 2;
uint16 constant BP = 1e4;
// --- ERC 3156 Data ---
bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
// commented values are Liquity's fixed settings for each parameter
uint256 public minuteDecayFactor; // 999037758833783000 (half-life of 12 hours)
/// @dev Redemption fee floor should be higher than deviation threshold of oracle supporting this collateral
uint256 public redemptionFeeFloor; // DECIMAL_PRECISION / 1000 * 5 (0.5%)
uint256 public maxRedemptionFee; // DECIMAL_PRECISION (100%)
uint256 public borrowingFeeFloor; // DECIMAL_PRECISION / 1000 * 5 (0.5%)
uint256 public maxBorrowingFee; // DECIMAL_PRECISION / 100 * 5 (5%)
uint256 public maxSystemDebt;
uint256 public interestRate;
uint256 public activeInterestIndex;
uint256 public lastActiveIndexUpdate;
uint256 public systemDeploymentTime;
bool public sunsetting;
bool public paused;
uint256 public baseRate;
// The timestamp of the latest fee operation (redemption or new debt issuance)
uint256 public lastFeeOperationTime;
uint256 public totalStakes;
// Snapshot of the value of totalStakes, taken immediately after the latest liquidation
uint256 public totalStakesSnapshot;
// Snapshot of the total collateral taken immediately after the latest liquidation.
uint256 public totalCollateralSnapshot;
/*
* L_collateral and L_debt track the sums of accumulated liquidation rewards per unit staked. During its lifetime, each stake earns:
*
* An collateral gain of ( stake * [L_collateral - L_collateral(0)] )
* A debt increase of ( stake * [L_debt - L_debt(0)] )
*
* Where L_collateral(0) and L_debt(0) are snapshots of L_collateral and L_debt for the active Position taken at the instant the stake was made
*/
uint256 public L_collateral;
uint256 public L_debt;
// Error trackers for the position redistribution calculation
uint256 public lastCollateralError_Redistribution;
uint256 public lastDebtError_Redistribution;
uint256 internal totalActiveCollateral;
uint256 internal totalActiveDebt;
uint256 public interestPayable;
address public interestReceiver;
uint256 public defaultedCollateral;
uint256 public defaultedDebt;
mapping(address => Position) public Positions;
mapping(address => uint256) public surplusBalances;
// Map addresses with active positions to their RewardSnapshot
mapping(address => RewardSnapshot) public rewardSnapshots;
// Array of all active position addresses - used to to compute an approximate hint off-chain, for the sorted list insertion
address[] PositionOwners;
// Store the necessary data for a position
struct Position {
uint256 debt;
uint256 coll;
uint256 stake;
Status status;
uint128 arrayIndex;
uint256 activeInterestIndex;
}
struct RedemptionTotals {
uint256 remainingDebt;
uint256 totalDebtToRedeem;
uint256 totalCollateralDrawn;
uint256 collateralFee;
uint256 collateralToSendToRedeemer;
uint256 decayedBaseRate;
uint256 price;
uint256 totalDebtSupplyAtStart;
}
struct SingleRedemptionValues {
uint256 debtLot;
uint256 collateralLot;
bool cancelledPartial;
}
// Object containing the collateral and debt snapshots for a given active position
struct RewardSnapshot {
uint256 collateral;
uint256 debt;
}
enum PositionManagerOperation {
applyPendingRewards,
liquidateInNormalMode,
liquidateInRecoveryMode,
redeemCollateral
}
enum Status {
nonExistent,
active,
closedByOwner,
closedByLiquidation,
closedByRedemption
}
event NewParameters(IFactory.DeploymentParams params);
event PriceFeedUpdated(address _priceFeed);
event PositionUpdated(
address indexed _borrower,
uint256 _debt,
uint256 _coll,
uint256 _stake,
PositionManagerOperation _operation
);
event Redemption(
address indexed _redeemer,
uint256 _attemptedDebtAmount,
uint256 _actualDebtAmount,
uint256 _collateralSent,
uint256 _collateralFee
);
event BaseRateUpdated(uint256 _baseRate);
event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);
event TotalStakesUpdated(uint256 _newTotalStakes);
event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);
event LTermsUpdated(uint256 _L_collateral, uint256 _L_debt);
event PositionSnapshotsUpdated(uint256 _L_collateral, uint256 _L_debt);
event PositionIndexUpdated(address _borrower, uint256 _newIndex);
event CollateralSent(address _to, uint256 _amount);
event InterestReceiverSet(address indexed _interestReceiver);
modifier whenNotPaused() {
require(!paused, "Collateral Paused");
_;
}
/// @dev Overrided by PermissionedPositionManager to enforce only one position
function _isPermissionedCheck(address _borrower) internal virtual {}
constructor(
address _core,
address _gasPoolAddress,
address _debtTokenAddress,
address _borrowerOperations,
address _liquidationManager,
uint256 _gasCompensation
) PropOwnable(_core) PropBase(_gasCompensation) SystemStart(_core) {
if (_core == address(0) || _gasPoolAddress == address(0) || _debtTokenAddress == address(0) || _borrowerOperations == address(0) || _liquidationManager == address(0)) {
revert("PositionManager: 0 address");
}
gasPoolAddress = _gasPoolAddress;
debtToken = IDebtToken(_debtTokenAddress);
borrowerOperations = _borrowerOperations;
liquidationManager = _liquidationManager;
}
function setAddresses(address _priceFeedAddress, address _sortedPositionsAddress, address _collateralToken) external {
require(address(sortedPositions) == address(0));
priceFeed = IPriceFeed(_priceFeedAddress);
sortedPositions = ISortedPositions(_sortedPositionsAddress);
collateralToken = IERC20(_collateralToken);
systemDeploymentTime = block.timestamp;
sunsetting = false;
activeInterestIndex = INTEREST_PRECISION;
lastActiveIndexUpdate = block.timestamp;
}
/**
* @notice Sets the pause state for this position manager
* Pausing is used to mitigate risks in exceptional circumstances
* Functionalities affected by pausing are:
* - New borrowing is not possible
* - New collateral deposits are not possible
* @param _paused If true the protocol is paused
*/
function setPaused(bool _paused) external {
require((_paused && msg.sender == guardian()) || msg.sender == owner(), "Unauthorized");
paused = _paused;
}
/**
* @notice Sets a custom price feed for this position manager
* @param _priceFeedAddress Price feed address
*/
function setPriceFeed(address _priceFeedAddress) external onlyOwner {
priceFeed = IPriceFeed(_priceFeedAddress);
emit PriceFeedUpdated(_priceFeedAddress);
}
function setCollVaultRouter(address _collVaultRouter) external onlyOwner {
collVaultRouter = _collVaultRouter;
}
function setInterestReceiver(address _interestReceiver) external onlyOwner {
interestReceiver = _interestReceiver;
emit InterestReceiverSet(_interestReceiver);
}
/**
* @notice Starts sunsetting a collateral
* During sunsetting only the following are possible:
1) Disable collateral handoff to SP
2) Greatly Increase interest rate to incentivize redemptions
3) Remove redemptions fees
4) Disable new loans
@dev IMPORTANT: When sunsetting a collateral altogether this function should be called on
all PM linked to that collateral as well as `StabilityPool.startCollateralSunset`
@dev IMPORTANT: A peripheral system will ensure users aren't MEVed due to redemptions fees being removed
*/
function startSunset() external onlyOwner {
sunsetting = true;
_accrueActiveInterests();
interestRate = SUNSETTING_INTEREST_RATE;
// accrual function doesn't update timestamp if interest was 0
lastActiveIndexUpdate = block.timestamp;
redemptionFeeFloor = 0;
maxSystemDebt = 0;
baseRate = 0;
maxRedemptionFee = 0;
}
/*
_minuteDecayFactor is calculated as
10**18 * (1/2)**(1/n)
where n = the half-life in minutes
*/
function setParameters(IFactory.DeploymentParams calldata params) public {
require(!sunsetting, "Cannot change after sunset");
require(params.MCR >= 1e18, "MCR cannot < 100%");
if (minuteDecayFactor != 0) {
require(msg.sender == owner(), "Only owner");
}
require(
params.minuteDecayFactor >= 977159968434245900 && // half-life of 30 minutes
params.minuteDecayFactor <= 999931237762985000 // half-life of 1 week
);
require(params.redemptionFeeFloor <= params.maxRedemptionFee && params.maxRedemptionFee <= DECIMAL_PRECISION);
require(params.borrowingFeeFloor <= params.maxBorrowingFee && params.maxBorrowingFee <= DECIMAL_PRECISION);
_decayBaseRate();
minuteDecayFactor = params.minuteDecayFactor;
redemptionFeeFloor = params.redemptionFeeFloor;
maxRedemptionFee = params.maxRedemptionFee;
borrowingFeeFloor = params.borrowingFeeFloor;
maxBorrowingFee = params.maxBorrowingFee;
maxSystemDebt = params.maxDebt;
collVaultRouter = params.collVaultRouter;
uint256 newInterestRate = (INTEREST_PRECISION * params.interestRateInBps) / (BP * SECONDS_IN_YEAR);
if (newInterestRate != interestRate) {
_accrueActiveInterests();
// accrual function doesn't update timestamp if interest was 0
lastActiveIndexUpdate = block.timestamp;
interestRate = newInterestRate;
}
MCR = params.MCR;
emit NewParameters(params);
}
function collectInterests() external {
_accrueActiveInterests();
uint256 interestPayableCached = interestPayable;
require(interestPayableCached > 0, "Nothing to collect");
uint256 interestPayableToTreasury = Math.mulDiv(interestPayableCached, CORE.interestProtocolShare(), BP);
uint256 interestPayableToProtocol = interestPayableCached - interestPayableToTreasury;
address protocolReceiver = interestReceiver != address(0) ? interestReceiver : CORE.defaultInterestReceiver();
if (interestPayableToTreasury != 0) debtToken.mint(CORE.feeReceiver(), interestPayableToTreasury);
if (interestPayableToProtocol != 0) debtToken.mint(protocolReceiver, interestPayableToProtocol);
interestPayable = 0;
}
// --- Getters ---
function fetchPrice() public view returns (uint256) {
IPriceFeed _priceFeed = priceFeed;
if (address(_priceFeed) == address(0)) {
_priceFeed = IPriceFeed(CORE.priceFeed());
}
return _priceFeed.fetchPrice(address(collateralToken));
}
function getPositionOwnersCount() external view returns (uint256) {
return PositionOwners.length;
}
function getPositionFromPositionOwnersArray(uint256 _index) external view returns (address) {
return PositionOwners[_index];
}
function getPositionStatus(address _borrower) external view returns (uint256) {
return uint256(Positions[_borrower].status);
}
function getPositionStake(address _borrower) external view returns (uint256) {
return Positions[_borrower].stake;
}
/**
@notice Get the current total collateral and debt amounts for a position
@dev Also includes pending rewards from redistribution
*/
function getPositionCollAndDebt(address _borrower) public view returns (uint256 coll, uint256 debt) {
(debt, coll, , ) = getEntireDebtAndColl(_borrower);
return (coll, debt);
}
/**
@notice Get the total and pending collateral and debt amounts for a position
@dev Used by the liquidation manager
*/
function getEntireDebtAndColl(
address _borrower
) public view returns (uint256 debt, uint256 coll, uint256 pendingDebtReward, uint256 pendingCollateralReward) {
Position storage t = Positions[_borrower];
debt = t.debt;
coll = t.coll;
(pendingCollateralReward, pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);
// Accrued position interest for correct liquidation values. This assumes the index to be updated.
uint256 positionInterestIndex = t.activeInterestIndex;
if (positionInterestIndex > 0) {
(uint256 currentIndex, ) = _calculateInterestIndex();
debt = (debt * currentIndex) / positionInterestIndex;
}
debt = debt + pendingDebtReward;
coll = coll + pendingCollateralReward;
}
function getEntireSystemColl() public view returns (uint256) {
return totalActiveCollateral + defaultedCollateral;
}
function getEntireSystemDebt() public view returns (uint256) {
uint256 currentActiveDebt = totalActiveDebt;
(, uint256 interestFactor) = _calculateInterestIndex();
if (interestFactor > 0) {
uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);
currentActiveDebt = currentActiveDebt + activeInterests;
}
return currentActiveDebt + defaultedDebt;
}
function getEntireSystemBalances() external view returns (uint256, uint256, uint256) {
return (getEntireSystemColl(), getEntireSystemDebt(), fetchPrice());
}
// --- Helper functions ---
// Return the nominal collateral ratio (ICR) of a given Position, without the price. Takes a positions pending coll and debt rewards from redistributions into account.
function getNominalICR(address _borrower) public view returns (uint256) {
(uint256 currentCollateral, uint256 currentDebt) = getPositionCollAndDebt(_borrower);
uint256 NICR = PropMath._computeNominalCR(currentCollateral, currentDebt);
return NICR;
}
// Return the current collateral ratio (ICR) of a given Position. Takes a positions pending coll and debt rewards from redistributions into account.
function getCurrentICR(address _borrower, uint256 _price) public view returns (uint256) {
(uint256 currentCollateral, uint256 currentDebt) = getPositionCollAndDebt(_borrower);
uint256 ICR = PropMath._computeCR(currentCollateral, currentDebt, _price);
return ICR;
}
function getTotalActiveCollateral() public view returns (uint256) {
return totalActiveCollateral;
}
function getTotalActiveDebt() public view returns (uint256) {
uint256 currentActiveDebt = totalActiveDebt;
(, uint256 interestFactor) = _calculateInterestIndex();
if (interestFactor > 0) {
uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);
currentActiveDebt = currentActiveDebt + activeInterests;
}
return currentActiveDebt;
}
// Get the borrower's pending accumulated collateral and debt rewards, earned by their stake
function getPendingCollAndDebtRewards(address _borrower) public view returns (uint256, uint256) {
RewardSnapshot memory snapshot = rewardSnapshots[_borrower];
uint256 coll = L_collateral - snapshot.collateral;
uint256 debt = L_debt - snapshot.debt;
if (coll + debt == 0 || Positions[_borrower].status != Status.active) return (0, 0);
uint256 stake = Positions[_borrower].stake;
return ((stake * coll) / DECIMAL_PRECISION, (stake * debt) / DECIMAL_PRECISION);
}
function hasPendingRewards(address _borrower) public view returns (bool) {
/*
* A Position has pending rewards if its snapshot is less than the current rewards per-unit-staked sum:
* this indicates that rewards have occured since the snapshot was made, and the user therefore has
* pending rewards
*/
if (Positions[_borrower].status != Status.active) {
return false;
}
return (rewardSnapshots[_borrower].collateral < L_collateral);
}
// --- Redemption fee functions ---
/*
* This function has two impacts on the baseRate state variable:
* 1) decays the baseRate based on time passed since last redemption or debt borrowing operation.
* then,
* 2) increases the baseRate based on the amount redeemed, as a proportion of total supply
*/
function _updateBaseRateFromRedemption(
uint256 _collateralDrawn,
uint256 _price,
uint256 _totalDebtSupply
) internal returns (uint256) {
uint256 decayedBaseRate = _calcDecayedBaseRate();
/* Convert the drawn collateral back to debt at face value rate (1 debt:1 USD), in order to get
* the fraction of total supply that was redeemed at face value. */
uint256 redeemedDebtFraction = (_collateralDrawn * _price) / _totalDebtSupply;
uint256 newBaseRate = decayedBaseRate + (redeemedDebtFraction / BETA);
newBaseRate = PropMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100%
// Update the baseRate state variable
baseRate = newBaseRate;
emit BaseRateUpdated(newBaseRate);
_updateLastFeeOpTime();
return newBaseRate;
}
function getRedemptionRate() public view returns (uint256) {
return _calcRedemptionRate(baseRate);
}
function getRedemptionRateWithDecay() public view returns (uint256) {
return _calcRedemptionRate(_calcDecayedBaseRate());
}
function _calcRedemptionRate(uint256 _baseRate) internal view returns (uint256) {
return
PropMath._min(
redemptionFeeFloor + _baseRate,
maxRedemptionFee
);
}
function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint256) {
return _calcRedemptionFee(getRedemptionRateWithDecay(), _collateralDrawn);
}
function _calcRedemptionFee(uint256 _redemptionRate, uint256 _collateralDrawn) internal pure returns (uint256) {
uint256 redemptionFee = (_redemptionRate * _collateralDrawn) / DECIMAL_PRECISION;
require(redemptionFee < _collateralDrawn, "Fee exceeds returned collateral");
return redemptionFee;
}
// --- Borrowing fee functions ---
function getBorrowingRate() public view returns (uint256) {
return _calcBorrowingRate(baseRate);
}
function getBorrowingRateWithDecay() public view returns (uint256) {
return _calcBorrowingRate(_calcDecayedBaseRate());
}
function _calcBorrowingRate(uint256 _baseRate) internal view returns (uint256) {
return PropMath._min(borrowingFeeFloor + _baseRate, maxBorrowingFee);
}
function getBorrowingFee(uint256 _debt) external view returns (uint256) {
return _calcBorrowingFee(getBorrowingRate(), _debt);
}
function getBorrowingFeeWithDecay(uint256 _debt) external view returns (uint256) {
return _calcBorrowingFee(getBorrowingRateWithDecay(), _debt);
}
function _calcBorrowingFee(uint256 _borrowingRate, uint256 _debt) internal pure returns (uint256) {
return (_borrowingRate * _debt) / DECIMAL_PRECISION;
}
// --- Internal fee functions ---
// Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing.
function _updateLastFeeOpTime() internal {
uint256 timePassed = block.timestamp - lastFeeOperationTime;
if (timePassed >= SECONDS_IN_ONE_MINUTE) {
lastFeeOperationTime += _minutesPassedSinceLastFeeOp() * SECONDS_IN_ONE_MINUTE;
emit LastFeeOpTimeUpdated(block.timestamp);
}
}
function _calcDecayedBaseRate() internal view returns (uint256) {
uint256 minutesPassed = _minutesPassedSinceLastFeeOp();
uint256 decayFactor = PropMath._decPow(minuteDecayFactor, minutesPassed);
return (baseRate * decayFactor) / DECIMAL_PRECISION;
}
function _minutesPassedSinceLastFeeOp() internal view returns (uint256) {
return (block.timestamp - lastFeeOperationTime) / SECONDS_IN_ONE_MINUTE;
}
// --- Redemption functions ---
/* Send _debtAmount debt to the system and redeem the corresponding amount of collateral from as many Positions as are needed to fill the redemption
* request. Applies pending rewards to a Position before reducing its debt and coll.
*
* Note that if _amount is very large, this function can run out of gas, specially if traversed positions are small. This can be easily avoided by
* splitting the total _amount in appropriate chunks and calling the function multiple times.
*
* Param `_maxIterations` can also be provided, so the loop through Positions is capped (if it’s zero, it will be ignored).This makes it easier to
* avoid OOG for the frontend, as only knowing approximately the average cost of an iteration is enough, without needing to know the “topology”
* of the position list. It also avoids the need to set the cap in stone in the contract, nor doing gas calculations, as both gas price and opcode
* costs can vary.
*
* All Positions that are redeemed from -- with the likely exception of the last one -- will end up with no debt left, therefore they will be closed.
* If the last Position does have some remaining debt, it has a finite ICR, and the reinsertion could be anywhere in the list, therefore it requires a hint.
* A frontend should use getRedemptionHints() to calculate what the ICR of this Position will be after redemption, and pass a hint for its position
* in the sortedPositions list along with the ICR value that the hint was found for.
*
* If another transaction modifies the list between calling getRedemptionHints() and passing the hints to redeemCollateral(), it
* is very likely that the last (partially) redeemed Position would end up with a different ICR than what the hint is for. In this case the
* redemption will stop after the last completely redeemed Position and the sender will keep the remaining debt amount, which they can attempt
* to redeem later.
*/
function redeemCollateral(
uint256 _debtAmount,
address _firstRedemptionHint,
address _upperPartialRedemptionHint,
address _lowerPartialRedemptionHint,
uint256 _partialRedemptionHintNICR,
uint256 _maxIterations,
uint256 _maxFeePercentage
) external {
ISortedPositions _sortedPositionsCached = sortedPositions;
RedemptionTotals memory totals;
require(
_maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= maxRedemptionFee,
"Max fee not in bounds"
);
require(block.timestamp >= systemDeploymentTime + CORE.dmBootstrapPeriod(), "BOOTSTRAP_PERIOD");
totals.price = fetchPrice();
require(IBorrowerOperations(borrowerOperations).getTCR() >= MCR, "Cannot redeem when TCR < MCR");
require(_debtAmount > 0, "Amount must be greater than zero");
require(debtToken.balanceOf(msg.sender) >= _debtAmount, "Insufficient balance");
_updateBalances();
totals.totalDebtSupplyAtStart = getEntireSystemDebt();
totals.remainingDebt = _debtAmount;
address currentBorrower;
if (_isValidFirstRedemptionHint(_sortedPositionsCached, _firstRedemptionHint, totals.price)) {
currentBorrower = _firstRedemptionHint;
} else {
currentBorrower = _sortedPositionsCached.getLast();
// Find the first position with ICR >= MCR
while (currentBorrower != address(0) && getCurrentICR(currentBorrower, totals.price) < _100pct) {
currentBorrower = _sortedPositionsCached.getPrev(currentBorrower);
}
}
// Loop through the Positions starting from the one with lowest collateral ratio until _amount of debt is exchanged for collateral
if (_maxIterations == 0) {
_maxIterations = 100;
}
while (currentBorrower != address(0) && totals.remainingDebt > 0 && _maxIterations > 0) {
_maxIterations--;
// Save the address of the Position preceding the current one, before potentially modifying the list
address nextUserToCheck = _sortedPositionsCached.getPrev(currentBorrower);
_applyPendingRewards(currentBorrower);
SingleRedemptionValues memory singleRedemption = _redeemCollateralFromPosition(
_sortedPositionsCached,
currentBorrower,
totals.remainingDebt,
totals.price,
_upperPartialRedemptionHint,
_lowerPartialRedemptionHint,
_partialRedemptionHintNICR
);
if (singleRedemption.cancelledPartial) break; // Partial redemption was cancelled (out-of-date hint, or new net debt < minimum), therefore we could not redeem from the last Position
totals.totalDebtToRedeem = totals.totalDebtToRedeem + singleRedemption.debtLot;
totals.totalCollateralDrawn = totals.totalCollateralDrawn + singleRedemption.collateralLot;
totals.remainingDebt = totals.remainingDebt - singleRedemption.debtLot;
currentBorrower = nextUserToCheck;
}
require(totals.totalCollateralDrawn > 0, "Unable to redeem any amount");
// Decay the baseRate due to time passed, and then increase it according to the size of this redemption.
// Use the saved total debt supply value, from before it was reduced by the redemption.
// only callabe when not sunsetting
if (!sunsetting) {
_updateBaseRateFromRedemption(totals.totalCollateralDrawn, totals.price, totals.totalDebtSupplyAtStart);
}
// Calculate the collateral fee
totals.collateralFee = sunsetting ? 0 : _calcRedemptionFee(getRedemptionRate(), totals.totalCollateralDrawn);
_requireUserAcceptsFee(totals.collateralFee, totals.totalCollateralDrawn, _maxFeePercentage);
_sendCollateral(CORE.feeReceiver(), totals.collateralFee);
totals.collateralToSendToRedeemer = totals.totalCollateralDrawn - totals.collateralFee;
emit Redemption(msg.sender, _debtAmount, totals.totalDebtToRedeem, totals.totalCollateralDrawn, totals.collateralFee);
// Burn the total debt that is cancelled with debt, and send the redeemed collateral to msg.sender
debtToken.burn(msg.sender, totals.totalDebtToRedeem);
// Update Position Manager debt, and send collateral to account
totalActiveDebt = totalActiveDebt - totals.totalDebtToRedeem;
_sendCollateral(msg.sender, totals.collateralToSendToRedeemer);
_resetState();
}
// Redeem as much collateral as possible from _borrower's Position in exchange for debt up to _maxDebtAmount
function _redeemCollateralFromPosition(
ISortedPositions _sortedPositionsCached,
address _borrower,
uint256 _maxDebtAmount,
uint256 _price,
address _upperPartialRedemptionHint,
address _lowerPartialRedemptionHint,
uint256 _partialRedemptionHintNICR
) internal returns (SingleRedemptionValues memory singleRedemption) {
Position storage t = Positions[_borrower];
// Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Position minus the liquidation reserve
singleRedemption.debtLot = PropMath._min(_maxDebtAmount, t.debt - DEBT_GAS_COMPENSATION);
// Get the CollateralLot of equivalent value in USD
singleRedemption.collateralLot = (singleRedemption.debtLot * DECIMAL_PRECISION) / _price;
// Decrease the debt and collateral of the current Position according to the debt lot and corresponding collateral to send
uint256 newDebt = (t.debt) - singleRedemption.debtLot;
uint256 newColl = (t.coll) - singleRedemption.collateralLot;
if (newDebt == DEBT_GAS_COMPENSATION) {
// No debt left in the Position (except for the liquidation reserve), therefore the position gets closed
_removeStake(_borrower);
_closePosition(_borrower, Status.closedByRedemption);
_redeemClosePosition(_borrower, DEBT_GAS_COMPENSATION, newColl);
emit PositionUpdated(_borrower, 0, 0, 0, PositionManagerOperation.redeemCollateral);
} else {
uint256 newNICR = PropMath._computeNominalCR(newColl, newDebt);
/*
* If the provided hint is out of date, we bail since trying to reinsert without a good hint will almost
* certainly result in running out of gas.
*
* If the resultant net debt of the partial is less than the minimum, net debt we bail.
*/
{
// We check if the ICR hint is reasonable up to date, with continuous interest there might be slight differences (<1bps)
uint256 icrError = _partialRedemptionHintNICR > newNICR
? _partialRedemptionHintNICR - newNICR
: newNICR - _partialRedemptionHintNICR;
if (
icrError > 5e14 ||
_getNetDebt(newDebt) < IBorrowerOperations(borrowerOperations).minNetDebt()
) {
singleRedemption.cancelledPartial = true;
return singleRedemption;
}
}
_sortedPositionsCached.reInsert(_borrower, newNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint);
t.debt = newDebt;
t.coll = newColl;
_updateStakeAndTotalStakes(t);
emit PositionUpdated(_borrower, newDebt, newColl, t.stake, PositionManagerOperation.redeemCollateral);
}
return singleRedemption;
}
/*
* Called when a full redemption occurs, and closes the position.
* The redeemer swaps (debt - liquidation reserve) debt for (debt - liquidation reserve) worth of collateral, so the debt liquidation reserve left corresponds to the remaining debt.
* In order to close the position, the debt liquidation reserve is burned, and the corresponding debt is removed.
* The debt recorded on the positions struct is zero'd elswhere, in _closePosition.
* Any surplus collateral left in the position can be later claimed by the borrower.
*/
function _redeemClosePosition(address _borrower, uint256 _debt, uint256 _collateral) internal {
debtToken.burn(gasPoolAddress, _debt);
totalActiveDebt = totalActiveDebt - _debt;
surplusBalances[_borrower] += _collateral;
totalActiveCollateral -= _collateral;
}
function _isValidFirstRedemptionHint(
ISortedPositions _sortedPositions,
address _firstRedemptionHint,
uint256 _price
) internal view returns (bool) {
if (
_firstRedemptionHint == address(0) ||
!_sortedPositions.contains(_firstRedemptionHint) ||
getCurrentICR(_firstRedemptionHint, _price) < _100pct
) {
return false;
}
address nextPosition = _sortedPositions.getNext(_firstRedemptionHint);
return nextPosition == address(0) || getCurrentICR(nextPosition, _price) < _100pct;
}
/**
* Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode
*/
function claimCollateral(address borrower, address _receiver) external {
if (msg.sender != collVaultRouter) {
borrower = msg.sender;
}
uint256 claimableColl = surplusBalances[borrower];
require(claimableColl > 0, "No collateral available to claim");
surplusBalances[borrower] = 0;
collateralToken.safeTransfer(_receiver, claimableColl);
}
// --- Position Adjustment functions ---
function openPosition(
address _borrower,
uint256 _collateralAmount,
uint256 _compositeDebt,
uint256 NICR,
address _upperHint,
address _lowerHint
) external whenNotPaused returns (uint256 stake, uint256 arrayIndex) {
_requireCallerIsBO();
require(!sunsetting, "Cannot open while sunsetting");
_isPermissionedCheck(_borrower);
Position storage t = Positions[_borrower];
require(t.status != Status.active, "BorrowerOps: Position is active");
t.status = Status.active;
t.coll = _collateralAmount;
t.debt = _compositeDebt;
uint256 currentInterestIndex = _accrueActiveInterests();
t.activeInterestIndex = currentInterestIndex;
_updatePositionRewardSnapshots(_borrower);
stake = _updateStakeAndTotalStakes(t);
sortedPositions.insert(_borrower, NICR, _upperHint, _lowerHint);
PositionOwners.push(_borrower);
arrayIndex = PositionOwners.length - 1;
t.arrayIndex = uint128(arrayIndex);
totalActiveCollateral = totalActiveCollateral + _collateralAmount;
uint256 _newTotalDebt = totalActiveDebt + _compositeDebt;
require(_newTotalDebt + defaultedDebt <= maxSystemDebt, "Collateral debt limit reached");
totalActiveDebt = _newTotalDebt;
}
function updatePositionFromAdjustment(
bool _isDebtIncrease,
uint256 _debtChange,
uint256 _netDebtChange,
bool _isCollIncrease,
uint256 _collChange,
address _upperHint,
address _lowerHint,
address _borrower,
address _receiver
) external returns (uint256, uint256, uint256) {
_requireCallerIsBO();
if (_isCollIncrease || _isDebtIncrease) {
require(!paused, "Collateral Paused");
require(!sunsetting, "Cannot increase while sunsetting");
}
Position storage t = Positions[_borrower];
require(t.status == Status.active, "Position closed or does not exist");
uint256 newDebt = t.debt;
if (_debtChange > 0) {
if (_isDebtIncrease) {
newDebt = newDebt + _netDebtChange;
_increaseDebt(_receiver, _netDebtChange, _debtChange);
} else {
newDebt = newDebt - _netDebtChange;
_decreaseDebt(_receiver, _debtChange);
}
t.debt = newDebt;
}
uint256 newColl = t.coll;
if (_collChange > 0) {
if (_isCollIncrease) {
newColl = newColl + _collChange;
totalActiveCollateral = totalActiveCollateral + _collChange;
// trust that BorrowerOperations sent the collateral
} else {
newColl = newColl - _collChange;
_sendCollateral(_receiver, _collChange);
}
t.coll = newColl;
}
uint256 newNICR = PropMath._computeNominalCR(newColl, newDebt);
sortedPositions.reInsert(_borrower, newNICR, _upperHint, _lowerHint);
return (newColl, newDebt, _updateStakeAndTotalStakes(t));
}
function closePosition(address _borrower, address _receiver, uint256 collAmount, uint256 debtAmount) external {
_requireCallerIsBO();
require(Positions[_borrower].status == Status.active, "Position closed or does not exist");
_removeStake(_borrower);
_closePosition(_borrower, Status.closedByOwner);
totalActiveDebt = totalActiveDebt - debtAmount;
_sendCollateral(_receiver, collAmount);
_resetState();
}
/**
@dev Only called from `closePosition` because liquidating the final position is blocked in
`LiquidationManager`. Many liquidation paths involve redistributing debt and
collateral to existing positions. If the collateral is being sunset, the final position
must be closed by repaying the debt or via a redemption.
*/
function _resetState() private {
if (PositionOwners.length == 0) {
activeInterestIndex = INTEREST_PRECISION;
lastActiveIndexUpdate = block.timestamp;
totalStakes = 0;
totalStakesSnapshot = 0;
totalCollateralSnapshot = 0;
L_collateral = 0;
L_debt = 0;
lastCollateralError_Redistribution = 0;
lastDebtError_Redistribution = 0;
totalActiveCollateral = 0;
totalActiveDebt = 0;
defaultedCollateral = 0;
defaultedDebt = 0;
}
}
function _closePosition(address _borrower, Status closedStatus) internal {
uint256 PositionOwnersArrayLength = PositionOwners.length;
Position storage t = Positions[_borrower];
t.status = closedStatus;
t.coll = 0;
t.debt = 0;
t.activeInterestIndex = 0;
ISortedPositions sortedPositionsCached = sortedPositions;
rewardSnapshots[_borrower].collateral = 0;
rewardSnapshots[_borrower].debt = 0;
if (PositionOwnersArrayLength > 1 && sortedPositionsCached.getSize() > 1) {
// remove position owner from the PositionOwners array, not preserving array order
uint128 index = t.arrayIndex;
address addressToMove = PositionOwners[PositionOwnersArrayLength - 1];
PositionOwners[index] = addressToMove;
Positions[addressToMove].arrayIndex = index;
emit PositionIndexUpdated(addressToMove, index);
}
PositionOwners.pop();
sortedPositionsCached.remove(_borrower);
t.arrayIndex = 0;
}
// Updates the baseRate state variable based on time elapsed since the last redemption or debt borrowing operation.
function decayBaseRateAndGetBorrowingFee(uint256 _debt) external returns (uint256) {
_requireCallerIsBO();
uint256 rate = _decayBaseRate();
return _calcBorrowingFee(_calcBorrowingRate(rate), _debt);
}
function _decayBaseRate() internal returns (uint256) {
uint256 decayedBaseRate = _calcDecayedBaseRate();
baseRate = decayedBaseRate;
emit BaseRateUpdated(decayedBaseRate);
_updateLastFeeOpTime();
return decayedBaseRate;
}
function applyPendingRewards(address _borrower) external returns (uint256 coll, uint256 debt) {
_requireCallerIsBO();
return _applyPendingRewards(_borrower);
}
// Add the borrowers's coll and debt rewards earned from redistributions, to their Position
function _applyPendingRewards(address _borrower) internal returns (uint256 coll, uint256 debt) {
Position storage t = Positions[_borrower];
if (t.status == Status.active) {
uint256 positionInterestIndex = t.activeInterestIndex;
uint256 currentInterestIndex = _accrueActiveInterests();
debt = t.debt;
uint256 prevDebt = debt;
coll = t.coll;
// We accrue interests for this position if not already updated
if (positionInterestIndex < currentInterestIndex) {
debt = (debt * currentInterestIndex) / positionInterestIndex;
t.activeInterestIndex = currentInterestIndex;
}
if (rewardSnapshots[_borrower].collateral < L_collateral) {
// Compute pending rewards
(uint256 pendingCollateralReward, uint256 pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);
// Apply pending rewards to positions state
coll = coll + pendingCollateralReward;
t.coll = coll;
debt = debt + pendingDebtReward;
_updatePositionRewardSnapshots(_borrower);
_movePendingPositionRewardsToActiveBalance(pendingDebtReward, pendingCollateralReward);
emit PositionUpdated(_borrower, debt, coll, t.stake, PositionManagerOperation.applyPendingRewards);
}
if (prevDebt != debt) {
t.debt = debt;
}
}
return (coll, debt);
}
function _updatePositionRewardSnapshots(address _borrower) internal {
uint256 L_collateralCached = L_collateral;
uint256 L_debtCached = L_debt;
rewardSnapshots[_borrower] = RewardSnapshot(L_collateralCached, L_debtCached);
emit PositionSnapshotsUpdated(L_collateralCached, L_debtCached);
}
// Remove borrower's stake from the totalStakes sum, and set their stake to 0
function _removeStake(address _borrower) internal {
uint256 stake = Positions[_borrower].stake;
totalStakes = totalStakes - stake;
Positions[_borrower].stake = 0;
}
// Update borrower's stake based on their latest collateral value
function _updateStakeAndTotalStakes(Position storage t) internal returns (uint256) {
uint256 newStake = _computeNewStake(t.coll);
uint256 oldStake = t.stake;
t.stake = newStake;
uint256 newTotalStakes = totalStakes - oldStake + newStake;
totalStakes = newTotalStakes;
emit TotalStakesUpdated(newTotalStakes);
return newStake;
}
// 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;
uint256 totalCollateralSnapshotCached = totalCollateralSnapshot;
if (totalCollateralSnapshotCached == 0) {
stake = _coll;
} else {
/*
* The following assert() holds true because:
* - The system always contains >= 1 position
* - When we close or liquidate a position, we redistribute the pending rewards, so if all positions were closed/liquidated,
* rewards would’ve been emptied and totalCollateralSnapshot would be zero too.
*/
uint256 totalStakesSnapshotCached = totalStakesSnapshot;
assert(totalStakesSnapshotCached > 0);
stake = (_coll * totalStakesSnapshotCached) / totalCollateralSnapshotCached;
}
return stake;
}
// --- Liquidation Functions ---
function closePositionByLiquidation(address _borrower) external {
_requireCallerIsLM();
_removeStake(_borrower);
_closePosition(_borrower, Status.closedByLiquidation);
}
function movePendingPositionRewardsToActiveBalances(uint256 _debt, uint256 _collateral) external {
_requireCallerIsLM();
_movePendingPositionRewardsToActiveBalance(_debt, _collateral);
}
function _movePendingPositionRewardsToActiveBalance(uint256 _debt, uint256 _collateral) internal {
defaultedDebt -= _debt;
totalActiveDebt += _debt;
defaultedCollateral -= _collateral;
totalActiveCollateral += _collateral;
}
function addCollateralSurplus(address borrower, uint256 collSurplus) external {
_requireCallerIsLM();
surplusBalances[borrower] += collSurplus;
}
function finalizeLiquidation(
address _liquidator,
uint256 _debt,
uint256 _coll,
uint256 _collSurplus,
uint256 _debtGasComp,
uint256 _collGasComp
) external {
_requireCallerIsLM();
// redistribute debt and collateral
_redistributeDebtAndColl(_debt, _coll);
uint256 _activeColl = totalActiveCollateral;
if (_collSurplus > 0) {
_activeColl -= _collSurplus;
totalActiveCollateral = _activeColl;
}
// update system snapshot
totalStakesSnapshot = totalStakes;
totalCollateralSnapshot = _activeColl + defaultedCollateral - _collGasComp;
emit SystemSnapshotsUpdated(totalStakesSnapshot, totalCollateralSnapshot);
// Send collateral and debt compensation to liquidator
debtToken.returnFromPool(gasPoolAddress, _liquidator, _debtGasComp);
_sendCollateral(_liquidator, _collGasComp);
}
function _redistributeDebtAndColl(uint256 _debt, uint256 _coll) internal {
if (_debt == 0) {
return;
}
/*
* Add distributed coll and 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 L_collateral and L_debt:
*
* 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 positionominator, 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 collateralNumerator = (_coll * DECIMAL_PRECISION) + lastCollateralError_Redistribution;
uint256 debtNumerator = (_debt * DECIMAL_PRECISION) + lastDebtError_Redistribution;
uint256 totalStakesCached = totalStakes;
// Get the per-unit-staked terms
uint256 collateralRewardPerUnitStaked = collateralNumerator / totalStakesCached;
uint256 debtRewardPerUnitStaked = debtNumerator / totalStakesCached;
lastCollateralError_Redistribution = collateralNumerator - (collateralRewardPerUnitStaked * totalStakesCached);
lastDebtError_Redistribution = debtNumerator - (debtRewardPerUnitStaked * totalStakesCached);
// Add per-unit-staked terms to the running totals
uint256 new_L_collateral = L_collateral + collateralRewardPerUnitStaked;
uint256 new_L_debt = L_debt + debtRewardPerUnitStaked;
L_collateral = new_L_collateral;
L_debt = new_L_debt;
emit LTermsUpdated(new_L_collateral, new_L_debt);
totalActiveDebt -= _debt;
defaultedDebt += _debt;
defaultedCollateral += _coll;
totalActiveCollateral -= _coll;
}
// --- Position property setters ---
function _sendCollateral(address _account, uint256 _amount) private {
if (_amount > 0) {
totalActiveCollateral = totalActiveCollateral - _amount;
emit CollateralSent(_account, _amount);
collateralToken.safeTransfer(_account, _amount);
}
}
function _increaseDebt(address account, uint256 netDebtAmount, uint256 debtAmount) internal {
uint256 _newTotalDebt = totalActiveDebt + netDebtAmount;
require(_newTotalDebt + defaultedDebt <= maxSystemDebt, "Collateral debt limit reached");
totalActiveDebt = _newTotalDebt;
debtToken.mint(account, debtAmount);
}
function decreaseDebtAndSendCollateral(address account, uint256 debt, uint256 coll) external {
_requireCallerIsLM();
_decreaseDebt(account, debt);
_sendCollateral(account, coll);
}
function _decreaseDebt(address account, uint256 amount) internal {
debtToken.burn(account, amount);
totalActiveDebt = totalActiveDebt - amount;
}
// --- Balances and interest ---
function updateBalances() external {
_requireCallerIsLM();
_updateBalances();
}
function _updateBalances() private {
_accrueActiveInterests();
}
// This function must be called any time the debt or the interest changes
function _accrueActiveInterests() internal returns (uint256) {
(uint256 currentInterestIndex, uint256 interestFactor) = _calculateInterestIndex();
if (interestFactor > 0) {
uint256 currentDebt = totalActiveDebt;
uint256 activeInterests = Math.mulDiv(currentDebt, interestFactor, INTEREST_PRECISION);
totalActiveDebt = currentDebt + activeInterests;
interestPayable = interestPayable + activeInterests;
activeInterestIndex = currentInterestIndex;
lastActiveIndexUpdate = block.timestamp;
}
return currentInterestIndex;
}
function _calculateInterestIndex() internal view returns (uint256 currentInterestIndex, uint256 interestFactor) {
uint256 lastIndexUpdateCached = lastActiveIndexUpdate;
// Short circuit if we updated in the current block
if (lastIndexUpdateCached == block.timestamp) return (activeInterestIndex, 0);
uint256 currentInterest = interestRate;
currentInterestIndex = activeInterestIndex; // we need to return this if it's already up to date
if (currentInterest > 0) {
/*
* Calculate the interest accumulated and the new index:
* We compound the index and increase the debt accordingly
*/
uint256 deltaT = block.timestamp - lastIndexUpdateCached;
interestFactor = deltaT * currentInterest;
currentInterestIndex =
currentInterestIndex +
Math.mulDiv(currentInterestIndex, interestFactor, INTEREST_PRECISION);
}
}
// --- Requires ---
function _requireCallerIsBO() internal view {
require(msg.sender == borrowerOperations, "Caller not BO");
}
function _requireCallerIsLM() internal view {
require(msg.sender == liquidationManager, "Not Liquidation Manager");
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC3156 FlashBorrower, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*
* _Available since v4.1._
*/
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {ICore} from "./ICore.sol";
interface IBorrowerOperations {
struct Balances {
uint256[] collaterals;
uint256[] debts;
uint256[] prices;
}
event BorrowingFeePaid(address indexed borrower, uint256 amount);
event CollateralConfigured(address positionManager, address collateralToken);
event PositionCreated(address indexed _borrower, uint256 arrayIndex);
event PositionManagerRemoved(address positionManager);
event PositionUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
function addColl(
address positionManager,
address account,
uint256 _collateralAmount,
address _upperHint,
address _lowerHint
) external;
function adjustPosition(
address positionManager,
address account,
uint256 _maxFeePercentage,
uint256 _collDeposit,
uint256 _collWithdrawal,
uint256 _debtChange,
bool _isDebtIncrease,
address _upperHint,
address _lowerHint
) external;
function closePosition(address positionManager, address account) external;
function configureCollateral(address positionManager, address collateralToken) external;
function fetchBalances() external view returns (Balances memory balances);
function getGlobalSystemBalances() external view returns (uint256 totalPricedCollateral, uint256 totalDebt);
function getTCR() external view returns (uint256 globalTotalCollateralRatio);
function openPosition(
address positionManager,
address account,
uint256 _maxFeePercentage,
uint256 _collateralAmount,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
) external;
function removePositionManager(address positionManager) external;
function repayDebt(
address positionManager,
address account,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
) external;
function setDelegateApproval(address _delegate, bool _isApproved) external;
function setMinNetDebt(uint256 _minNetDebt) external;
function withdrawColl(
address positionManager,
address account,
uint256 _collWithdrawal,
address _upperHint,
address _lowerHint
) external;
function withdrawDebt(
address positionManager,
address account,
uint256 _maxFeePercentage,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
) external;
function positionManagers(uint256) external view returns (address);
function checkRecoveryMode(uint256 TCR) external view returns (bool);
function DEBT_GAS_COMPENSATION() external view returns (uint256);
function DECIMAL_PRECISION() external view returns (uint256);
function PERCENT_DIVISOR() external view returns (uint256);
function CORE() external view returns (ICore);
function debtToken() external view returns (address);
function factory() external view returns (address);
function getCompositeDebt(uint256 _debt) external view returns (uint256);
function guardian() external view returns (address);
function isApprovedDelegate(address owner, address caller) external view returns (bool isApproved);
function minNetDebt() external view returns (uint256);
function owner() external view returns (address);
function positionManagersData(address) external view returns (address collateralToken, uint16 index);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC3156FlashBorrower } from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import "./ICore.sol";
interface IDebtToken is IERC20 {
// --- Events ---
event FlashLoanFeeUpdated(uint256 newFee);
// --- Public constants ---
function version() external view returns (string memory);
function permitTypeHash() external view returns (bytes32);
// --- Public immutables ---
function gasPool() external view returns (address);
function DEBT_GAS_COMPENSATION() external view returns (uint256);
// --- Public mappings ---
function liquidStabilityPools(address) external view returns (bool);
function borrowerOperations(address) external view returns (bool);
function factories(address) external view returns (bool);
function peripheries(address) external view returns (bool);
function positionManagers(address) external view returns (bool);
// --- External functions ---
function enablePositionManager(address _positionManager) external;
function mintWithGasCompensation(address _account, uint256 _amount) external returns (bool);
function burnWithGasCompensation(address _account, uint256 _amount) external returns (bool);
function mint(address _account, uint256 _amount) external;
function burn(address _account, uint256 _amount) external;
function decimals() external view returns (uint8);
function sendToPeriphery(address _sender, uint256 _amount) external;
function sendToSP(address _sender, uint256 _amount) external;
function returnFromPool(address _poolAddress, address _receiver, uint256 _amount) external;
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function maxFlashLoan(address token) external view returns (uint256);
function flashFee(address token, uint256 amount) external view returns (uint256);
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
function whitelistLiquidStabilityPoolAddress(address _liquidStabilityPool, bool active) external;
function whitelistBorrowerOperationsAddress(address _borrowerOperations, bool active) external;
function whitelistFactoryAddress(address _factory, bool active) external;
function whitelistPeripheryAddress(address _periphery, bool active) external;
function whitelistPSM(address, bool) external;
function setDebtGasCompensation(uint256 _gasCompensation, bool _isFinalValue) external;
function setFlashLoanFee(uint256 _fee) external;
function DOMAIN_SEPARATOR() external view returns (bytes32);
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISortedPositions {
event NodeAdded(address _id, uint256 _NICR);
event NodeRemoved(address _id);
function insert(address _id, uint256 _NICR, address _prevId, address _nextId) external;
function reInsert(address _id, uint256 _newNICR, address _prevId, address _nextId) external;
function remove(address _id) external;
function setAddresses(address _positionManagerAddress) external;
function contains(address _id) external view returns (bool);
function data() external view returns (address head, address tail, uint256 size);
function findInsertPosition(
uint256 _NICR,
address _prevId,
address _nextId
) external view returns (address, address);
function getFirst() external view returns (address);
function getLast() external view returns (address);
function getNext(address _id) external view returns (address);
function getPrev(address _id) external view returns (address);
function getSize() external view returns (uint256);
function isEmpty() external view returns (bool);
function positionManager() external view returns (address);
function validInsertPosition(uint256 _NICR, address _prevId, address _nextId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPriceFeed {
struct FeedType {
address spotOracle;
bool isCollVault;
}
event NewOracleRegistered(address token, address chainlinkAggregator, address underlyingDerivative);
event PriceFeedStatusUpdated(address token, address oracle, bool isWorking);
event PriceRecordUpdated(address indexed token, uint256 _price);
event NewCollVaultRegistered(address collVault, bool enable);
event NewSpotOracleRegistered(address token, address spotOracle);
function fetchPrice(address _token) external view returns (uint256);
function getMultiplePrices(address[] memory _tokens) external view returns (uint256[] memory prices);
function setOracle(
address _token,
address _chainlinkOracle,
uint32 _heartbeat,
uint16 _staleThreshold,
address underlyingDerivative
) external;
function whitelistCollateralVault(address _collateralVaultShareToken, bool enable) external;
function setSpotOracle(address _token, address _spotOracle) external;
function MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND() external view returns (uint256);
function CORE() external view returns (address);
function RESPONSE_TIMEOUT() external view returns (uint256);
function TARGET_DIGITS() external view returns (uint256);
function guardian() external view returns (address);
function oracleRecords(
address
)
external
view
returns (
address chainLinkOracle,
uint8 decimals,
uint32 heartbeat,
uint16 staleThreshold,
address underlyingDerivative
);
function isCollVault(address _collateralVaultShareToken) external view returns (bool);
function isStableBPT(address _oracle) external view returns (bool);
function isWeightedBPT(address _oracle) external view returns (bool);
function getSpotOracle(address _token) external view returns (address);
function feedType(address _token) external view returns (FeedType memory);
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
interface IFactory {
// commented values are suggested default parameters
struct DeploymentParams {
uint256 minuteDecayFactor; // 999037758833783000 (half life of 12 hours)
uint256 redemptionFeeFloor; // 1e18 / 1000 * 5 (0.5%)
uint256 maxRedemptionFee; // 1e18 (100%)
uint256 borrowingFeeFloor; // 1e18 / 1000 * 5 (0.5%)
uint256 maxBorrowingFee; // 1e18 / 100 * 5 (5%)
uint256 interestRateInBps; // 100 (1%)
uint256 maxDebt;
uint256 MCR; // 12 * 1e17 (120%)
address collVaultRouter; // set to address(0) if PositionManager coll is not CollateralVault
}
event NewDeployment(address collateral, address priceFeed, address positionManager, address sortedPositions);
function deployNewInstance(
address collateral,
address priceFeed,
address customPositionManagerImpl,
address customSortedPositionsImpl,
DeploymentParams calldata params,
uint64 unlockRatePerSecond,
bool forceThroughLspBalanceCheck
) external;
function setImplementations(address _positionManagerImpl, address _sortedPositionsImpl) external;
function CORE() external view returns (address);
function borrowerOperations() external view returns (address);
function debtToken() external view returns (address);
function guardian() external view returns (address);
function liquidationManager() external view returns (address);
function owner() external view returns (address);
function sortedPositionsImpl() external view returns (address);
function liquidStabilityPool() external view returns (address);
function positionManagerCount() external view returns (uint256);
function positionManagerImpl() external view returns (address);
function positionManagers(uint256) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILiquidationManager {
function batchLiquidatePositions(address positionManager, address[] calldata _positionArray, address liquidator) external;
function enablePositionManager(address _positionManager) external;
function liquidate(address positionManager, address borrower, address liquidator) external;
function liquidatePositions(address positionManager, uint256 maxPositionsToLiquidate, uint256 maxICR, address liquidator) external;
function DEBT_GAS_COMPENSATION() external view returns (uint256);
function DECIMAL_PRECISION() external view returns (uint256);
function PERCENT_DIVISOR() external view returns (uint256);
function borrowerOperations() external view returns (address);
function factory() external view returns (address);
function liquidStabilityPool() external view returns (address);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "../interfaces/core/ICore.sol";
/**
@title System Start Time
@dev Provides a unified `startTime` and `getWeek`, used for emissions.
*/
contract SystemStart {
uint256 immutable startTime;
constructor(address core) {
startTime = ICore(core).startTime();
}
function getWeek() public view returns (uint256 week) {
return (block.timestamp - startTime) / 1 weeks;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/*
* Base contract for PositionManager, BorrowerOperations and StabilityPool. Contains global system constants and
* common functions.
*/
contract PropBase {
uint256 public constant DECIMAL_PRECISION = 1e18;
// Amount of debt to be locked in gas pool on opening positions
uint256 public immutable DEBT_GAS_COMPENSATION;
uint256 public constant PERCENT_DIVISOR = 1000; // dividing by 1000 yields 0.1%
constructor(uint256 _gasCompensation) {
DEBT_GAS_COMPENSATION = _gasCompensation;
}
// --- Gas compensation functions ---
// Returns the composite debt (drawn debt + gas compensation) of a position, for the purpose of ICR calculation
function _getCompositeDebt(uint256 _debt) internal view returns (uint256) {
return _debt + DEBT_GAS_COMPENSATION;
}
function _getNetDebt(uint256 _debt) internal view returns (uint256) {
return _debt - DEBT_GAS_COMPENSATION;
}
// Return the amount of collateral to be drawn from a position's collateral and sent as gas compensation.
function _getCollGasCompensation(uint256 _entireColl) internal pure returns (uint256) {
return _entireColl / PERCENT_DIVISOR;
}
function _requireUserAcceptsFee(uint256 _fee, uint256 _amount, uint256 _maxFeePercentage) internal pure {
uint256 feePercentage = _amount != 0 ? (_fee * DECIMAL_PRECISION) / _amount : 0;
require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum");
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
library PropMath {
uint256 internal constant DECIMAL_PRECISION = 1e18;
/* 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,
* 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;
}
/*
* 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) PositionManager._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 _coll, uint256 _debt) internal pure returns (uint256) {
if (_debt > 0) {
return (_coll * NICR_PRECISION) / _debt;
}
// Return the maximal value for uint256 if the Position has a debt of 0. Represents "infinite" CR.
else {
// if (_debt == 0)
return 2 ** 256 - 1;
}
}
function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint256) {
if (_debt > 0) {
uint256 newCollRatio = (_coll * _price) / _debt;
return newCollRatio;
}
// Return the maximal value for uint256 if the Position has a debt of 0. Represents "infinite" CR.
else {
// if (_debt == 0)
return 2 ** 256 - 1;
}
}
function _computeCR(uint256 _coll, uint256 _debt) internal pure returns (uint256) {
if (_debt > 0) {
uint256 newCollRatio = (_coll) / _debt;
return newCollRatio;
}
// Return the maximal value for uint256 if the Position has a debt of 0. Represents "infinite" CR.
else {
// if (_debt == 0)
return 2 ** 256 - 1;
}
}
function _isApproxEqAbs(uint256 a, uint256 b, uint256 tolerance) internal pure returns (bool) {
return a > b ? (a - b) <= tolerance : (b - a) <= tolerance;
}
function _isWithinToleranceAbove(
uint256 a,
uint256 b,
uint256 tolerance
) internal pure returns (bool) {
if (a < b) return false;
return (a - b) <= tolerance;
}
function _isWithinToleranceBelow(
uint256 a,
uint256 b,
uint256 tolerance
) internal pure returns (bool) {
if (a > b) return false;
return (b - a) <= tolerance;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "../interfaces/core/ICore.sol";
/**
@title Prop Ownable
@notice Contracts inheriting `PropOwnable` have the same owner as `PropCore`.
The ownership cannot be independently modified or renounced.
@dev In the contracts that use CORE to interact with protocol instance specific parameters,
the immutable will be instanced with PropCore.sol, eitherway, it will be MetaCore.sol
*/
contract PropOwnable {
ICore public immutable CORE;
constructor(address _core) {
CORE = ICore(_core);
}
modifier onlyOwner() {
require(msg.sender == CORE.owner(), "Only owner");
_;
}
function owner() public view returns (address) {
return CORE.owner();
}
function guardian() public view returns (address) {
return CORE.guardian();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` 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:
*
* - `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 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current 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.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IMetaCore} from "src/interfaces/core/IMetaCore.sol";
interface ICore {
// --- Public variables ---
function metaCore() external view returns (IMetaCore);
function startTime() external view returns (uint256);
function CCR() external view returns (uint256);
function dmBootstrapPeriod() external view returns (uint64);
function isPeriphery(address peripheryContract) external view returns (bool);
// --- External functions ---
function setPeripheryEnabled(address _periphery, bool _enabled) external;
function setPMBootstrapPeriod(address dm, uint64 _bootstrapPeriod) external;
function setNewCCR(uint256 _CCR) external;
function priceFeed() external view returns (address);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function guardian() external view returns (address);
function feeReceiver() external view returns (address);
function paused() external view returns (bool);
function lspBootstrapPeriod() external view returns (uint64);
function getLspEntryFee(address rebalancer) external view returns (uint16);
function getLspExitFee(address rebalancer) external view returns (uint16);
function interestProtocolShare() external view returns (uint16);
function defaultInterestReceiver() external view returns (address);
// --- Events ---
event CCRSet(uint256 initialCCR);
event PMBootstrapPeriodSet(address dm, uint64 bootstrapPeriod);
event PeripheryEnabled(address indexed periphery, bool enabled);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
interface IMetaCore {
// ---------------------------------
// Structures
// ---------------------------------
struct FeeInfo {
bool existsForDebtToken;
uint16 debtTokenFee;
}
struct RebalancerFeeInfo {
bool exists;
uint16 entryFee;
uint16 exitFee;
}
// ---------------------------------
// Public constants
// ---------------------------------
function OWNERSHIP_TRANSFER_DELAY() external view returns (uint256);
function DEFAULT_FLASH_LOAN_FEE() external view returns (uint16);
// ---------------------------------
// Public state variables
// ---------------------------------
function debtToken() external view returns (address);
function lspEntryFee() external view returns (uint16);
function lspExitFee() external view returns (uint16);
function interestProtocolShare() external view returns (uint16);
/// @dev Default interest receiver for all PositionManagers, unless overriden in the respective PM
function defaultInterestReceiver() external view returns (address);
function feeReceiver() external view returns (address);
function priceFeed() external view returns (address);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function ownershipTransferDeadline() external view returns (uint256);
function guardian() external view returns (address);
function paused() external view returns (bool);
function lspBootstrapPeriod() external view returns (uint64);
// ---------------------------------
// External functions
// ---------------------------------
function setFeeReceiver(address _feeReceiver) external;
function setPriceFeed(address _priceFeed) external;
function setGuardian(address _guardian) external;
/**
* @notice Global pause/unpause
* Pausing halts new deposits/borrowing across the protocol
*/
function setPaused(bool _paused) external;
/**
* @notice Extend or change the LSP bootstrap period,
* after which certain protocol mechanics change
*/
function setLspBootstrapPeriod(uint64 _bootstrapPeriod) external;
/**
* @notice Set a custom flash-loan fee for a given periphery contract
* @param _periphery Target contract that will get this custom fee
* @param _debtTokenFee Fee in basis points (bp)
* @param _existsForDebtToken Whether this custom fee is used when the caller = `debtToken`
*/
function setPeripheryFlashLoanFee(address _periphery, uint16 _debtTokenFee, bool _existsForDebtToken) external;
/**
* @notice Begin the ownership transfer process
* @param newOwner The address proposed to be the new owner
*/
function commitTransferOwnership(address newOwner) external;
/**
* @notice Finish the ownership transfer, after the mandatory delay
*/
function acceptTransferOwnership() external;
/**
* @notice Revoke a pending ownership transfer
*/
function revokeTransferOwnership() external;
/**
* @notice Look up a custom flash-loan fee for a specific periphery contract
* @param peripheryContract The contract that might have a custom fee
* @return The flash-loan fee in basis points
*/
function getPeripheryFlashLoanFee(address peripheryContract) external view returns (uint16);
/**
* @notice Set / override entry & exit fees for a special rebalancer contract
*/
function setRebalancerFee(address _rebalancer, uint16 _entryFee, uint16 _exitFee) external;
/**
* @notice Set the LSP entry fee globally
* @param _fee Fee in basis points
*/
function setEntryFee(uint16 _fee) external;
/**
* @notice Set the LSP exit fee globally
* @param _fee Fee in basis points
*/
function setExitFee(uint16 _fee) external;
/**
* @notice Set the interest protocol share globally to all PositionManagers
* @param _interestProtocolShare Share in basis points
*/
function setInterestProtocolShare(uint16 _interestProtocolShare) external;
/**
* @notice Look up the LSP entry fee for a rebalancer
* @param rebalancer Possibly has a special fee
* @return The entry fee in basis points
*/
function getLspEntryFee(address rebalancer) external view returns (uint16);
/**
* @notice Look up the LSP exit fee for a rebalancer
* @param rebalancer Possibly has a special fee
* @return The exit fee in basis points
*/
function getLspExitFee(address rebalancer) external view returns (uint16);
// ---------------------------------
// Events
// ---------------------------------
event NewOwnerCommitted(address indexed owner, address indexed pendingOwner, uint256 deadline);
event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);
event NewOwnerRevoked(address indexed owner, address indexed revokedOwner);
event FeeReceiverSet(address indexed feeReceiver);
event PriceFeedSet(address indexed priceFeed);
event GuardianSet(address indexed guardian);
event PeripheryFlashLoanFee(address indexed periphery, uint16 debtTokenFee);
event LSPBootstrapPeriodSet(uint64 bootstrapPeriod);
event RebalancerFees(address indexed rebalancer, uint16 entryFee, uint16 exitFee);
event EntryFeeSet(uint16 fee);
event ExitFeeSet(uint16 fee);
event InterestProtocolShareSet(uint16 interestProtocolShare);
event DefaultInterestReceiverSet(address indexed defaultInterestReceiver);
event Paused();
event Unpaused();
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin-upgradeable/contracts/=lib/openzeppelin-contracts-upgradeable/contracts/",
"solady/=lib/solady/src/",
"@solmate/=lib/solmate/src/",
"@chimera/=lib/chimera/src/",
"forge-std/=lib/forge-std/src/",
"@uniswap/v3-core/=lib/v3-core/",
"@uniswap/v3-periphery/=lib/v3-periphery/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"chimera/=lib/chimera/src/",
"ds-test/=lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"rewards/=lib/rewards/",
"solmate/=lib/solmate/src/",
"uniswap/=lib/uniswap/",
"v3-core/=lib/v3-core/contracts/",
"v3-periphery/=lib/v3-periphery/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 1
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_core","type":"address"},{"internalType":"address","name":"_gasPoolAddress","type":"address"},{"internalType":"address","name":"_debtTokenAddress","type":"address"},{"internalType":"address","name":"_borrowerOperations","type":"address"},{"internalType":"address","name":"_liquidationManager","type":"address"},{"internalType":"uint256","name":"_gasCompensation","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_baseRate","type":"uint256"}],"name":"BaseRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"CollateralSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_interestReceiver","type":"address"}],"name":"InterestReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_debt","type":"uint256"}],"name":"LTermsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastFeeOpTime","type":"uint256"}],"name":"LastFeeOpTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"minuteDecayFactor","type":"uint256"},{"internalType":"uint256","name":"redemptionFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxRedemptionFee","type":"uint256"},{"internalType":"uint256","name":"borrowingFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxBorrowingFee","type":"uint256"},{"internalType":"uint256","name":"interestRateInBps","type":"uint256"},{"internalType":"uint256","name":"maxDebt","type":"uint256"},{"internalType":"uint256","name":"MCR","type":"uint256"},{"internalType":"address","name":"collVaultRouter","type":"address"}],"indexed":false,"internalType":"struct IFactory.DeploymentParams","name":"params","type":"tuple"}],"name":"NewParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"}],"name":"PositionIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_debt","type":"uint256"}],"name":"PositionSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_coll","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_stake","type":"uint256"},{"indexed":false,"internalType":"enum PositionManager.PositionManagerOperation","name":"_operation","type":"uint8"}],"name":"PositionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_priceFeed","type":"address"}],"name":"PriceFeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"_attemptedDebtAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actualDebtAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collateralSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collateralFee","type":"uint256"}],"name":"Redemption","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":"_newTotalStakes","type":"uint256"}],"name":"TotalStakesUpdated","type":"event"},{"inputs":[],"name":"CORE","outputs":[{"internalType":"contract ICore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEBT_GAS_COMPENSATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"L_collateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"L_debt","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":"PERCENT_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"Positions","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"enum PositionManager.Status","name":"status","type":"uint8"},{"internalType":"uint128","name":"arrayIndex","type":"uint128"},{"internalType":"uint256","name":"activeInterestIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUNSETTING_INTEREST_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeInterestIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"collSurplus","type":"uint256"}],"name":"addCollateralSurplus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"applyPendingRewards","outputs":[{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOperations","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowingFeeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"claimCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"collAmount","type":"uint256"},{"internalType":"uint256","name":"debtAmount","type":"uint256"}],"name":"closePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"closePositionByLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collVaultRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectInterests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"debtToken","outputs":[{"internalType":"contract IDebtToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"}],"name":"decayBaseRateAndGetBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"}],"name":"decreaseDebtAndSendCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultedCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultedDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fetchPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_coll","type":"uint256"},{"internalType":"uint256","name":"_collSurplus","type":"uint256"},{"internalType":"uint256","name":"_debtGasComp","type":"uint256"},{"internalType":"uint256","name":"_collGasComp","type":"uint256"}],"name":"finalizeLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"}],"name":"getBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"}],"name":"getBorrowingFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getCurrentICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getEntireDebtAndColl","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"pendingDebtReward","type":"uint256"},{"internalType":"uint256","name":"pendingCollateralReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getNominalICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingCollAndDebtRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionCollAndDebt","outputs":[{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getPositionFromPositionOwnersArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionOwnersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionStatus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralDrawn","type":"uint256"}],"name":"getRedemptionFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWeek","outputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"hasPendingRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestPayable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastActiveIndexUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCollateralError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastDebtError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFeeOperationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedemptionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSystemDebt","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":"_debt","type":"uint256"},{"internalType":"uint256","name":"_collateral","type":"uint256"}],"name":"movePendingPositionRewardsToActiveBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"uint256","name":"_compositeDebt","type":"uint256"},{"internalType":"uint256","name":"NICR","type":"uint256"},{"internalType":"address","name":"_upperHint","type":"address"},{"internalType":"address","name":"_lowerHint","type":"address"}],"name":"openPosition","outputs":[{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"arrayIndex","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtAmount","type":"uint256"},{"internalType":"address","name":"_firstRedemptionHint","type":"address"},{"internalType":"address","name":"_upperPartialRedemptionHint","type":"address"},{"internalType":"address","name":"_lowerPartialRedemptionHint","type":"address"},{"internalType":"uint256","name":"_partialRedemptionHintNICR","type":"uint256"},{"internalType":"uint256","name":"_maxIterations","type":"uint256"},{"internalType":"uint256","name":"_maxFeePercentage","type":"uint256"}],"name":"redeemCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionFeeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardSnapshots","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeedAddress","type":"address"},{"internalType":"address","name":"_sortedPositionsAddress","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collVaultRouter","type":"address"}],"name":"setCollVaultRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_interestReceiver","type":"address"}],"name":"setInterestReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"minuteDecayFactor","type":"uint256"},{"internalType":"uint256","name":"redemptionFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxRedemptionFee","type":"uint256"},{"internalType":"uint256","name":"borrowingFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxBorrowingFee","type":"uint256"},{"internalType":"uint256","name":"interestRateInBps","type":"uint256"},{"internalType":"uint256","name":"maxDebt","type":"uint256"},{"internalType":"uint256","name":"MCR","type":"uint256"},{"internalType":"address","name":"collVaultRouter","type":"address"}],"internalType":"struct IFactory.DeploymentParams","name":"params","type":"tuple"}],"name":"setParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeedAddress","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedPositions","outputs":[{"internalType":"contract ISortedPositions","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startSunset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sunsetting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"surplusBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"systemDeploymentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakesSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"uint256","name":"_debtChange","type":"uint256"},{"internalType":"uint256","name":"_netDebtChange","type":"uint256"},{"internalType":"bool","name":"_isCollIncrease","type":"bool"},{"internalType":"uint256","name":"_collChange","type":"uint256"},{"internalType":"address","name":"_upperHint","type":"address"},{"internalType":"address","name":"_lowerHint","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"updatePositionFromAdjustment","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
610160604052348015610010575f80fd5b50604051614dd9380380614dd983398101604081905261002f9161018c565b60808190526001600160a01b03861660a0819052604080516378e9792560e01b815290518892916378e979259160048083019260209291908290030181865afa15801561007e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100a291906101fa565b60c052506001600160a01b03861615806100c357506001600160a01b038516155b806100d557506001600160a01b038416155b806100e757506001600160a01b038316155b806100f957506001600160a01b038216155b1561014a5760405162461bcd60e51b815260206004820152601a60248201527f506f736974696f6e4d616e616765723a20302061646472657373000000000000604482015260640160405180910390fd5b506001600160a01b039384166101205291831661014052821660e052166101005250610211565b80516001600160a01b0381168114610187575f80fd5b919050565b5f805f805f8060c087890312156101a1575f80fd5b6101aa87610171565b95506101b860208801610171565b94506101c660408801610171565b93506101d460608801610171565b92506101e260808801610171565b91505f60a08801519050809150509295509295509295565b5f6020828403121561020a575f80fd5b5051919050565b60805160a05160c05160e051610100516101205161014051614aac61032d5f395f81816109e20152818161164e0152818161176401528181611fcf015281816124bd01528181612a9d01528181613af501528181613b760152613ffa01525f8181612a66015261402701525f81816104b7015261311c01525f818161069a01528181611e9c01528181612b3601526137d001525f6117dd01525f818161062901528181610cbe01528181610db8015281816110d7015281816111690152818161132401528181611506015281816115b40152818161167d015281816118200152818161187c01528181611db001526123a901525f81816105b201528181613675015281816136f00152818161372f01526140dd0152614aac5ff3fe608060405234801561000f575f80fd5b50600436106103e6575f3560e01c806301c799fc146103ea5780630b076557146104125780630b216022146104335780630fdb11cf146104485780631289fefb1461045e5780631673c79a1461046657806316c38b3c1461048c5780631983fb401461049f5780631ef3a04c146104b25780631f68f20a146104e657806320d17d7b146104ef578063219efc46146104f85780632b11551a1461050b5780632d587e10146105135780632f41d9e11461051c5780632f5c83a81461052f57806331c7a2611461053857806333d53b5914610541578063363bf9641461054a5780633cf1b05b1461055d5780634056d9fc14610565578063452a93201461056e578063477d66cf146105765780634820fe56146105895780634870dd9a146105915780634af797341461059a5780634ba4a28b146105ad5780635728ddda146105d45780635c975abb146105e7578063631203b01461060957806366ca4a211461061c5780636b6c0774146106245780636c302c761461064b5780636f3fe4041461065e578063720fbe2c14610666578063724e78da1461066f578063741bef1a1461068257806377553ad414610695578063789c6b93146106bc578063794e5724146106cf578063795d26c3146106d85780637a83a34e146106e05780637c3a00fd146106e85780637e95d664146106f1578063807d138d146106fa578063874d6d8114610703578063887105d31461070b5780638bbb1de8146107135780638da5cb5b1461071c57806391245f9f146107245780639484fb8e1461072d57806396660b181461073a57806396d711ff146107435780639bf9b9611461074c5780639e994ca81461075e5780639ed7254214610771578063a20baee61461077a578063a27bcdaa14610789578063b0d8e1811461079c578063b2016bd4146107af578063b279906e146107c2578063b2f29d7b146107d5578063b91af97c146107e8578063bc006c381461080b578063bcd3752614610814578063be2c377b14610827578063bf88dd7114610830578063bf9befb114610892578063c045e47d1461089b578063c52861f2146108ae578063c77cf615146108b6578063c98eef62146108c9578063d128080c146108f4578063d12ae841146108fc578063d23ab4611461090f578063d293c71014610922578063d30612e114610935578063d380a37c1461093d578063d38b055814610946578063d5b356351461094f578063e2ac77b014610962578063e47cf0c314610975578063e9186c941461097e578063ea23a27a14610991578063eb7a7f7e146109a4578063ec38a05d146109b7578063f36b2425146109d5578063f8d89898146109dd575b5f80fd5b6103f2610a04565b604080519384526020840192909252908201526060015b60405180910390f35b6104256104203660046142c6565b610a2a565b6040516104099291906142e1565b6104466104413660046142ef565b610a46565b005b610450610ca9565b604051908152602001610409565b610446610db6565b6104256104743660046142c6565b60216020525f90815260409020805460019091015482565b61044661049a366004614316565b610ec9565b6104506104ad3660046142c6565b610f66565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b6040516104099190614331565b61045060105481565b610450601e5481565b610446610506366004614345565b610f9b565b61045061102e565b610450600d5481565b61044661052a3660046142c6565b61103f565b610450600a5481565b61045060085481565b61045060155481565b610446610558366004614388565b61105e565b602254610450565b61045060095481565b6104d96110d4565b6104506105843660046143d0565b611155565b601954610450565b6104506103e881565b6104466105a83660046142c6565b611167565b6104507f000000000000000000000000000000000000000000000000000000000000000081565b6104466105e23660046143e7565b611238565b600f546105f990610100900460ff1681565b6040519015158152602001610409565b6104506106173660046143d0565b6112dd565b6104506112e9565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b61044661065936600461441e565b6112fa565b610446611310565b610450600e5481565b61044661067d3660046142c6565b611322565b6001546104d9906001600160a01b031681565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b6104466106ca36600461443e565b611428565b61045060045481565b61045061145f565b6104466114b1565b610450600b5481565b61045060065481565b61045060135481565b6104506117d3565b61045061180c565b610450600c5481565b6104d961181d565b61045060075481565b600f546105f99060ff1681565b610450601b5481565b61045060145481565b5f546104d9906001600160a01b031681565b61044661076c3660046142c6565b61187a565b61045060185481565b610450670de0b6b3a764000081565b610425610797366004614468565b611973565b6104506107aa3660046142c6565b611bfb565b6002546104d9906001600160a01b031681565b6104d96107d03660046143d0565b611c1f565b6104506107e33660046143d0565b611c4d565b6107fb6107f63660046142c6565b611c7a565b60405161040994939291906144cb565b61045060055481565b6104466108223660046144e6565b611cfe565b610450601d5481565b61088061083e3660046142c6565b601f6020525f90815260409020805460018201546002830154600384015460049094015492939192909160ff8116916101009091046001600160801b03169086565b60405161040996959493929190614563565b61045060125481565b6003546104d9906001600160a01b031681565b610450612557565b601c546104d9906001600160a01b031681565b6104506108d73660046142c6565b6001600160a01b03165f908152601f602052604090206002015490565b610450612568565b61042561090a3660046142c6565b6125ac565b6103f261091d3660046145ae565b6126c1565b61045061093036600461443e565b6128dd565b610450612903565b61045060115481565b61045060175481565b61045061095d3660046143d0565b612937565b6105f96109703660046142c6565b612949565b61045060165481565b61044661098c366004614649565b6129a8565b61044661099f36600461467b565b6129c4565b6104256109b23660046142c6565b612b07565b6104506109c53660046142c6565b602080525f908152604090205481565b610450612b1f565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b5f805f610a0f61180c565b610a1761145f565b610a1f610ca9565b925092509250909192565b5f80610a34612b2b565b610a3d83612b93565b91509150915091565b600f5460ff1615610a9b5760405162461bcd60e51b815260206004820152601a60248201527910d85b9b9bdd0818da185b99d94818599d195c881cdd5b9cd95d60321b60448201526064015b60405180910390fd5b670de0b6b3a76400008160e001351015610aeb5760405162461bcd60e51b81526020600482015260116024820152704d43522063616e6e6f74203c203130302560781b6044820152606401610a92565b60055415610b2b57610afb61181d565b6001600160a01b0316336001600160a01b031614610b2b5760405162461bcd60e51b8152600401610a92906146c3565b670d8f91d06741510c813510801590610b4d5750670de07829b2adec28813511155b610b55575f80fd5b8060400135816020013511158015610b795750670de0b6b3a7640000816040013511155b610b81575f80fd5b8060800135816060013511158015610ba55750670de0b6b3a7640000816080013511155b610bad575f80fd5b610bb5612cc2565b508035600555602081013560065560408101356007556060810135600855608081013560095560c0810135600a55610bf5610120820161010083016142c6565b5f80546001600160a01b0319166001600160a01b0392909216919091178155610c246301e133806127106146fb565b610c3d60a0840135676765c793fa10079d601b1b6146fb565b610c479190614726565b9050600b548114610c6557610c5a612d01565b5042600d55600b8190555b60e08201356004556040517fdba82a425948402262be7f293af6e198241329ec2ff19e69724a0bcdcda31f4d90610c9d908490614739565b60405180910390a15050565b6001545f906001600160a01b031680610d3f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d3c91906147b0565b90505b600254604051635670bcc760e11b81526001600160a01b038381169263ace1798e92610d719290911690600401614331565b602060405180830381865afa158015610d8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db091906147cb565b91505090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3691906147b0565b6001600160a01b0316336001600160a01b031614610e665760405162461bcd60e51b8152600401610a92906146c3565b600f805460ff19166001179055610e7b612d01565b50610e8c6301e133806127106146fb565b610ea3676765c793fa10079d601b1b6113886146fb565b610ead9190614726565b600b5542600d555f6006819055600a8190556010819055600755565b808015610eee5750610ed96110d4565b6001600160a01b0316336001600160a01b0316145b80610f115750610efc61181d565b6001600160a01b0316336001600160a01b0316145b610f4c5760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606401610a92565b600f80549115156101000261ff0019909216919091179055565b6001600160a01b0381165f908152601f602052604081206003015460ff166004811115610f9557610f9561454f565b92915050565b610fa3612b2b565b60016001600160a01b0385165f908152601f602052604090206003015460ff166004811115610fd457610fd461454f565b14610ff15760405162461bcd60e51b8152600401610a92906147e2565b610ffa84612d62565b611005846002612dab565b80601a546110139190614823565b601a55611020838361303b565b6110286130a3565b50505050565b5f61103a6010546130f7565b905090565b611047613111565b61105081612d62565b61105b816003612dab565b50565b6003546001600160a01b031615611073575f80fd5b600180546001600160a01b039485166001600160a01b03199182161790915560038054938516938216939093179092556002805491909316911617905542600e819055600f805460ff19169055676765c793fa10079d601b1b600c55600d55565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061103a91906147b0565b5f610f956111616112e9565b83613183565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e791906147b0565b6001600160a01b0316336001600160a01b0316146112175760405162461bcd60e51b8152600401610a92906146c3565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f546001600160a01b0316331461124d573391505b6001600160a01b0382165f908152602080526040902054806112b15760405162461bcd60e51b815260206004820181905260248201527f4e6f20636f6c6c61746572616c20617661696c61626c6520746f20636c61696d6044820152606401610a92565b6001600160a01b038084165f9081526020805260408120556002546112d8911683836131a1565b505050565b5f610f95611161612b1f565b5f61103a6112f56131f7565b613233565b611302613111565b61130c828261324d565b5050565b611318613111565b6113206132a6565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a291906147b0565b6001600160a01b0316336001600160a01b0316146113d25760405162461bcd60e51b8152600401610a92906146c3565b600180546001600160a01b0319166001600160a01b0383161790556040517fe5b20b8497e4f3e2435ef9c20e2e26b47497ee13745ce1c681ad6640653119e69061141d908390614331565b60405180910390a150565b611430613111565b6001600160a01b0382165f90815260208052604081208054839290611456908490614836565b90915550505050565b601a545f908161146d6132ae565b915050801561149d575f61148d8383676765c793fa10079d601b1b613318565b90506114998184614836565b9250505b601e546114aa9083614836565b9250505090565b6114b9612d01565b50601b54806114ff5760405162461bcd60e51b8152602060048201526012602482015271139bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b6044820152606401610a92565b5f611590827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636d52dffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611560573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115849190614849565b61ffff16612710613318565b90505f61159d8284614823565b601c549091505f906001600160a01b0316611637577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166371f8424f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561160e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163291906147b0565b611644565b601c546001600160a01b03165b90508215611747577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f197f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116fb91906147b0565b856040518363ffffffff1660e01b815260040161171992919061486a565b5f604051808303815f87803b158015611730575f80fd5b505af1158015611742573d5f803e3d5ffd5b505050505b81156117c9576040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f199061179b908490869060040161486a565b5f604051808303815f87803b1580156117b2575f80fd5b505af11580156117c4573d5f803e3d5ffd5b505050505b50505f601b555050565b5f62093a806118027f000000000000000000000000000000000000000000000000000000000000000042614823565b61103a9190614726565b5f601d5460195461103a9190614836565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118fa91906147b0565b6001600160a01b0316336001600160a01b03161461192a5760405162461bcd60e51b8152600401610a92906146c3565b601c80546001600160a01b0319166001600160a01b0383169081179091556040517f085af60e444f2bfe4e6ad75d3227d2a8702123a3d663597ed1ca90a5f0c2c89c905f90a250565b600f545f908190610100900460ff161561199f5760405162461bcd60e51b8152600401610a9290614883565b6119a7612b2b565b600f5460ff16156119f95760405162461bcd60e51b815260206004820152601c60248201527b43616e6e6f74206f70656e207768696c652073756e73657474696e6760201b6044820152606401610a92565b6001600160a01b0388165f908152601f602052604090206001600382015460ff166004811115611a2b57611a2b61454f565b03611a785760405162461bcd60e51b815260206004820152601f60248201527f426f72726f7765724f70733a20506f736974696f6e20697320616374697665006044820152606401610a92565b60038101805460ff1916600190811790915581018890558681555f611a9b612d01565b600483018190559050611aad8a6133c0565b611ab682613439565b6003546040516346f7cf8760e01b81529195506001600160a01b0316906346f7cf8790611aed908d908b908b908b906004016148ae565b5f604051808303815f87803b158015611b04575f80fd5b505af1158015611b16573d5f803e3d5ffd5b5050505060228a908060018154018082558091505060019003905f5260205f20015f9091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506001602280549050611b729190614823565b600383018054610100600160881b0319166101006001600160801b03841602179055601954909350611ba5908a90614836565b601955601a545f90611bb8908a90614836565b9050600a54601e5482611bcb9190614836565b1115611be95760405162461bcd60e51b8152600401610a92906148d9565b601a5550919890975095505050505050565b5f805f611c0784612b07565b915091505f611c1683836134b6565b95945050505050565b5f60228281548110611c3357611c33614910565b5f918252602090912001546001600160a01b031692915050565b5f611c56612b2b565b5f611c5f612cc2565b9050611c73611c6d82613233565b84613183565b9392505050565b6001600160a01b0381165f908152601f6020526040812080546001820154909290918190611ca7866125ac565b60048301549094509092508015611cdd575f611cc16132ae565b50905081611ccf82896146fb565b611cd99190614726565b9650505b611ce78487614836565b9550611cf38386614836565b945050509193509193565b5f60035f9054906101000a90046001600160a01b03169050611d566040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6006548310158015611d6a57506007548311155b611dae5760405162461bcd60e51b81526020600482015260156024820152744d617820666565206e6f7420696e20626f756e647360581b6044820152606401610a92565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c0c39a6f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2e9190614924565b6001600160401b0316600e54611e449190614836565b421015611e865760405162461bcd60e51b815260206004820152601060248201526f1093d3d514d514905417d411549253d160821b6044820152606401610a92565b611e8e610ca9565b8160c00181815250506004547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b620115d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f1a91906147cb565b1015611f675760405162461bcd60e51b815260206004820152601c60248201527b21b0b73737ba103932b232b2b6903bb432b7102a21a9101e1026a1a960211b6044820152606401610a92565b5f8911611fb65760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610a92565b6040516370a0823160e01b815289906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190612004903390600401614331565b602060405180830381865afa15801561201f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061204391906147cb565b10156120885760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610a92565b6120906132a6565b61209861145f565b60e082015288815260c08101515f906120b49084908b906134ea565b156120c05750876121c6565b826001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212091906147b0565b90505b6001600160a01b0381161580159061214f5750670de0b6b3a764000061214d828460c001516128dd565b105b156121c657604051632dc9c0eb60e21b81526001600160a01b0384169063b72703ac90612180908490600401614331565b602060405180830381865afa15801561219b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121bf91906147b0565b9050612123565b845f036121d257606494505b6001600160a01b038116158015906121ea5750815115155b80156121f557505f85115b156122f357846122048161494a565b9550505f836001600160a01b031663b72703ac836040518263ffffffff1660e01b81526004016122349190614331565b602060405180830381865afa15801561224f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227391906147b0565b905061227e82612b93565b50505f6122978584865f01518760c001518e8e8e61362d565b90508060400151156122aa5750506122f3565b805160208501516122bb9190614836565b60208086019190915281015160408501516122d69190614836565b6040850152805184516122e99190614823565b84525090506121d2565b5f8260400151116123445760405162461bcd60e51b815260206004820152601b60248201527a155b98589b19481d1bc81c995919595b48185b9e48185b5bdd5b9d602a1b6044820152606401610a92565b600f5460ff166123675761236582604001518360c001518460e0015161392e565b505b600f5460ff1661238b5761238661237c61102e565b83604001516139ac565b61238d565b5f5b6060830181905260408301516123a4919086613a1c565b6124317f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa158015612403573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242791906147b0565b836060015161303b565b816060015182604001516124459190614823565b826080018181525050336001600160a01b03167f08b6f1ce3f9ab2722e8ea40c31a3e3a806a41702c5994f29af43dc0c1f2837df8b84602001518560400151866060015160405161249994939291906144cb565b60405180910390a26020820151604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691639dc29fac916124f291339160040161486a565b5f604051808303815f87803b158015612509575f80fd5b505af115801561251b573d5f803e3d5ffd5b5050506020830151601a546125309250614823565b601a55608082015161254390339061303b565b61254b6130a3565b50505050505050505050565b5f61103a6125636131f7565b6130f7565b601a545f90816125766132ae565b91505080156125a6575f6125968383676765c793fa10079d601b1b613318565b90506125a28184614836565b9250505b50919050565b6001600160a01b0381165f9081526021602090815260408083208151808301909252805480835260019091015492820192909252601554839283916125f19190614823565b90505f82602001516016546126069190614823565b90506126128183614836565b158061264d575060016001600160a01b0387165f908152601f602052604090206003015460ff16600481111561264a5761264a61454f565b14155b1561265f57505f958695509350505050565b6001600160a01b0386165f908152601f6020526040902060020154670de0b6b3a764000061268d84836146fb565b6126979190614726565b670de0b6b3a76400006126aa84846146fb565b6126b49190614726565b9550955050505050915091565b5f805f6126cc612b2b565b88806126d557508b5b1561275557600f54610100900460ff16156127025760405162461bcd60e51b8152600401610a9290614883565b600f5460ff16156127555760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420696e637265617365207768696c652073756e73657474696e676044820152606401610a92565b6001600160a01b0385165f908152601f602052604090206001600382015460ff1660048111156127875761278761454f565b146127a45760405162461bcd60e51b8152600401610a92906147e2565b80548c156127e8578d156127ce576127bc8c82614836565b90506127c9868d8f613a99565b6127e4565b6127d88c82614823565b90506127e4868e613b5f565b8082555b60018201548a15612839578b1561281b576128038b82614836565b90508a6019546128139190614836565b601955612831565b6128258b82614823565b9050612831878c61303b565b600183018190555b5f61284482846134b6565b905060035f9054906101000a90046001600160a01b03166001600160a01b0316632be212608a838e8e6040518563ffffffff1660e01b815260040161288c94939291906148ae565b5f604051808303815f87803b1580156128a3575f80fd5b505af11580156128b5573d5f803e3d5ffd5b5050505081836128c486613439565b9650965096505050505099509950999650505050505050565b5f805f6128e985612b07565b915091505f6128f9838387613bef565b9695505050505050565b6129136301e133806127106146fb565b61292a676765c793fa10079d601b1b6113886146fb565b6129349190614726565b81565b5f610f95612943612557565b836139ac565b5f60016001600160a01b0383165f908152601f602052604090206003015460ff16600481111561297b5761297b61454f565b1461298757505f919050565b506015546001600160a01b039091165f908152602160205260409020541090565b6129b0613111565b6129ba8383613b5f565b6112d8838261303b565b6129cc613111565b6129d68585613c1d565b60195483156129f1576129e98482614823565b601981905590505b601254601355601d548290612a069083614836565b612a109190614823565b60148190556013546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612a4792916142e1565b60405180910390a1604051631062c15f60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528881166024830152604482018590527f000000000000000000000000000000000000000000000000000000000000000016906320c582be906064015f604051808303815f87803b158015612ade575f80fd5b505af1158015612af0573d5f803e3d5ffd5b50505050612afe878361303b565b50505050505050565b5f80612b1283611c7a565b5090959194509092505050565b5f61103a601054613233565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113205760405162461bcd60e51b815260206004820152600d60248201526c43616c6c6572206e6f7420424f60981b6044820152606401610a92565b6001600160a01b0381165f908152601f6020526040812081906001600382015460ff166004811115612bc757612bc761454f565b03612cbc5760048101545f612bda612d01565b835460018501549650945090508381831015612c0f5782612bfb83876146fb565b612c059190614726565b6004850183905594505b6015546001600160a01b0388165f908152602160205260409020541015612cad575f80612c3b896125ac565b9092509050612c4a8289614836565b600187018190559750612c5d8188614836565b9650612c68896133c0565b612c72818361324d565b886001600160a01b03165f80516020614a37833981519152888a89600201545f604051612ca29493929190614973565b60405180910390a250505b848114612cb8578484555b5050505b50915091565b5f80612ccc6131f7565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a1612cfc613d8c565b919050565b5f805f612d0c6132ae565b909250905080156125a657601a545f612d318284676765c793fa10079d601b1b613318565b9050612d3d8183614836565b601a55601b54612d4e908290614836565b601b555050600c82905542600d5550919050565b6001600160a01b0381165f908152601f6020526040902060020154601254612d8b908290614823565b601255506001600160a01b03165f908152601f6020526040812060020155565b6022546001600160a01b0383165f908152601f6020526040902060038101805484919060ff19166001836004811115612de657612de661454f565b02179055505f6001808301829055818355600483018290556003546001600160a01b038781168452602160205260408420848155830193909355919091169083118015612e9257506001816001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9091906147cb565b115b15612f9757600382015461010090046001600160801b03165f6022612eb8600187614823565b81548110612ec857612ec8614910565b5f91825260209091200154602280546001600160a01b03909216925082916001600160801b038516908110612eff57612eff614910565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252601f83526040918290206003018054610100600160881b0319166101006001600160801b038816908102919091179091558251918252928101929092527f2391d278b0562640dd6add67aa715b4dab2327f1ec896819ff2a809adea29a36910160405180910390a150505b6022805480612fa857612fa8614995565b5f8281526020902081015f1990810180546001600160a01b0319169055019055604051631484968760e11b81526001600160a01b038216906329092d0e90612ff4908890600401614331565b5f604051808303815f87803b15801561300b575f80fd5b505af115801561301d573d5f803e3d5ffd5b50505060039092018054610100600160881b03191690555050505050565b801561130c578060195461304f9190614823565b6019556040517f342693d2465f6f44931e41128424a0227e0cbc69d1c3917a839e6de71696d44c90613084908490849061486a565b60405180910390a160025461130c906001600160a01b031683836131a1565b6022545f0361132057676765c793fa10079d601b1b600c5542600d555f60128190556013819055601481905560158190556016819055601781905560188190556019819055601a819055601d819055601e55565b5f610f95826006546131099190614836565b600754613dfe565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113205760405162461bcd60e51b81526020600482015260176024820152762737ba102634b8bab4b230ba34b7b71026b0b730b3b2b960491b6044820152606401610a92565b5f670de0b6b3a764000061319783856146fb565b611c739190614726565b6112d88363a9059cbb60e01b84846040516024016131c092919061486a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e13565b5f80613201613ee4565b90505f61321060055483613ef5565b9050670de0b6b3a76400008160105461322991906146fb565b6114aa9190614726565b5f610f95826008546132459190614836565b600954613dfe565b81601e5f82825461325e9190614823565b9250508190555081601a5f8282546132769190614836565b9250508190555080601d5f82825461328e9190614823565b925050819055508060195f8282546114569190614836565b61105b612d01565b600d545f9081904281036132c7575050600c54915f9150565b600b54600c5493508015613312575f6132e08342614823565b90506132ec82826146fb565b93506133048585676765c793fa10079d601b1b613318565b61330e9086614836565b9450505b50509091565b5f80805f19858709858702925082811083820303915050805f0361334f5783828161334557613345614712565b0492505050611c73565b80841161335a575f80fd5b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b60155460165460408051808201825283815260208082018481526001600160a01b0387165f9081526021909252908390209151825551600190910155517fc2910901d6342262f6857ac83c05564d215eef237fb5bcdf69013b47bbf4b8a89061342c90849084906142e1565b60405180910390a1505050565b5f806134488360010154613f9d565b60028401805490829055601254919250905f908390613468908490614823565b6134729190614836565b60128190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b5f81156134e257816134d168056bc75e2d63100000856146fb565b6134db9190614726565b9050610f95565b505f19610f95565b5f6001600160a01b03831615806135695750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e890613528908690600401614331565b602060405180830381865afa158015613543573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061356791906149a9565b155b806135845750670de0b6b3a764000061358284846128dd565b105b1561359057505f611c73565b60405163765e015960e01b81525f906001600160a01b0386169063765e0159906135be908790600401614331565b602060405180830381865afa1580156135d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135fd91906147b0565b90506001600160a01b0381161580611c165750670de0b6b3a764000061362382856128dd565b1095945050505050565b61365060405180606001604052805f81526020015f81526020015f151581525090565b6001600160a01b0387165f908152601f60205260409020805461369f90889061369a907f000000000000000000000000000000000000000000000000000000000000000090614823565b613dfe565b80835286906136b790670de0b6b3a7640000906146fb565b6136c19190614726565b6020830152815181545f916136d591614823565b90505f836020015183600101546136ec9190614823565b90507f0000000000000000000000000000000000000000000000000000000000000000820361378e5761371e8a612d62565b6137298a6004612dab565b6137548a7f000000000000000000000000000000000000000000000000000000000000000083613fe3565b896001600160a01b03165f80516020614a378339815191525f805f60036040516137819493929190614973565b60405180910390a261391f565b5f61379982846134b6565b90505f8187116137b2576137ad8783614823565b6137bc565b6137bc8288614823565b90506601c6bf5263400081118061385957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663969c24526040518163ffffffff1660e01b8152600401602060405180830381865afa15801561382a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384e91906147cb565b613857856140d7565b105b156138705750506001604085015250613923915050565b5060405163015f109360e51b81526001600160a01b038d1690632be21260906138a3908e9085908d908d906004016148ae565b5f604051808303815f87803b1580156138ba575f80fd5b505af11580156138cc573d5f803e3d5ffd5b50505083855550600184018290556138e384613439565b508a6001600160a01b03165f80516020614a378339815191528484876002015460036040516139159493929190614973565b60405180910390a2505b5050505b979650505050505050565b5f806139386131f7565b90505f8361394686886146fb565b6139509190614726565b90505f61395e600283614726565b6139689084614836565b905061397c81670de0b6b3a7640000613dfe565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a16128f9613d8c565b5f80670de0b6b3a76400006139c184866146fb565b6139cb9190614726565b9050828110611c735760405162461bcd60e51b815260206004820152601f60248201527f46656520657863656564732072657475726e656420636f6c6c61746572616c006044820152606401610a92565b5f825f03613a2a575f613a47565b82613a3d670de0b6b3a7640000866146fb565b613a479190614726565b9050818111156110285760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610a92565b5f82601a54613aa89190614836565b9050600a54601e5482613abb9190614836565b1115613ad95760405162461bcd60e51b8152600401610a92906148d9565b601a8190556040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990613b2c908790869060040161486a565b5f604051808303815f87803b158015613b43575f80fd5b505af1158015613b55573d5f803e3d5ffd5b5050505050505050565b604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac90613bad908590859060040161486a565b5f604051808303815f87803b158015613bc4575f80fd5b505af1158015613bd6573d5f803e3d5ffd5b5050505080601a54613be89190614823565b601a555050565b5f8215613c15575f83613c0284876146fb565b613c0c9190614726565b9150611c739050565b505f19611c73565b815f03613c28575050565b6017545f90613c3f670de0b6b3a7640000846146fb565b613c499190614836565b90505f601854670de0b6b3a764000085613c6391906146fb565b613c6d9190614836565b6012549091505f613c7e8285614726565b90505f613c8b8385614726565b9050613c9783836146fb565b613ca19086614823565b601755613cae83826146fb565b613cb89085614823565b6018556015545f90613ccb908490614836565b90505f82601654613cdc9190614836565b601583905560168190556040519091507f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e390613d1b90849084906142e1565b60405180910390a188601a5f828254613d349190614823565b9250508190555088601e5f828254613d4c9190614836565b9250508190555087601d5f828254613d649190614836565b925050819055508760195f828254613d7c9190614823565b9091555050505050505050505050565b5f60115442613d9b9190614823565b9050603c811061105b57603c613daf613ee4565b613db991906146fb565b60115f828254613dc99190614836565b90915550506040514281527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc9060200161141d565b5f818310613e0c5781611c73565b5090919050565b5f613e67826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141029092919063ffffffff16565b8051909150156112d85780806020019051810190613e8591906149a9565b6112d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a92565b5f603c601154426118029190614823565b5f631f540500821115613f0a57631f54050091505b815f03613f205750670de0b6b3a7640000610f95565b670de0b6b3a764000083835b6001811115613f9357613f406002826149c4565b5f03613f6457613f508283614118565b9150613f5d600282614726565b9050613f2c565b613f6e8284614118565b9250613f7a8283614118565b91506002613f89600183614823565b613f5d9190614726565b6128f98284614118565b6014545f908190808203613fb357839150613fdc565b60135480613fc357613fc36149d7565b81613fce82876146fb565b613fd89190614726565b9250505b5092915050565b604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac90614051907f000000000000000000000000000000000000000000000000000000000000000090869060040161486a565b5f604051808303815f87803b158015614068575f80fd5b505af115801561407a573d5f803e3d5ffd5b5050505081601a5461408c9190614823565b601a556001600160a01b0383165f908152602080526040812080548392906140b5908490614836565b925050819055508060195f8282546140cd9190614823565b9091555050505050565b5f610f957f000000000000000000000000000000000000000000000000000000000000000083614823565b606061411084845f8561414e565b949350505050565b5f8061412483856146fb565b9050670de0b6b3a764000061413a600282614726565b6141449083614836565b6141109190614726565b6060824710156141af5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a92565b5f80866001600160a01b031685876040516141ca91906149eb565b5f6040518083038185875af1925050503d805f8114614204576040519150601f19603f3d011682016040523d82523d5f602084013e614209565b606091505b509150915061392387838387606083156142835782515f0361427c576001600160a01b0385163b61427c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a92565b5081614110565b61411083838151156142985781518083602001fd5b8060405162461bcd60e51b8152600401610a929190614a01565b6001600160a01b038116811461105b575f80fd5b5f602082840312156142d6575f80fd5b8135611c73816142b2565b918252602082015260400190565b5f610120828403128015614301575f80fd5b509092915050565b801515811461105b575f80fd5b5f60208284031215614326575f80fd5b8135611c7381614309565b6001600160a01b0391909116815260200190565b5f805f8060808587031215614358575f80fd5b8435614363816142b2565b93506020850135614373816142b2565b93969395505050506040820135916060013590565b5f805f6060848603121561439a575f80fd5b83356143a5816142b2565b925060208401356143b5816142b2565b915060408401356143c5816142b2565b809150509250925092565b5f602082840312156143e0575f80fd5b5035919050565b5f80604083850312156143f8575f80fd5b8235614403816142b2565b91506020830135614413816142b2565b809150509250929050565b5f806040838503121561442f575f80fd5b50508035926020909101359150565b5f806040838503121561444f575f80fd5b823561445a816142b2565b946020939093013593505050565b5f805f805f8060c0878903121561447d575f80fd5b8635614488816142b2565b955060208701359450604087013593506060870135925060808701356144ad816142b2565b915060a08701356144bd816142b2565b809150509295509295509295565b93845260208401929092526040830152606082015260800190565b5f805f805f805f60e0888a0312156144fc575f80fd5b87359650602088013561450e816142b2565b9550604088013561451e816142b2565b9450606088013561452e816142b2565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b5f52602160045260245ffd5b868152602081018690526040810185905260c08101600585106145885761458861454f565b60608201949094526001600160801b0392909216608083015260a0909101529392505050565b5f805f805f805f805f6101208a8c0312156145c7575f80fd5b89356145d281614309565b985060208a0135975060408a0135965060608a01356145f081614309565b955060808a0135945060a08a0135614607816142b2565b935060c08a0135614617816142b2565b925060e08a0135614627816142b2565b91506101008a0135614638816142b2565b809150509295985092959850929598565b5f805f6060848603121561465b575f80fd5b8335614666816142b2565b95602085013595506040909401359392505050565b5f805f805f8060c08789031215614690575f80fd5b863561469b816142b2565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b6020808252600a908201526927b7363c9037bbb732b960b11b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f9557610f956146e7565b634e487b7160e01b5f52601260045260245ffd5b5f8261473457614734614712565b500490565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c0808301359082015260e080830135908201526101208101610100830135614797816142b2565b6001600160a01b03166101009290920191909152919050565b5f602082840312156147c0575f80fd5b8151611c73816142b2565b5f602082840312156147db575f80fd5b5051919050565b60208082526021908201527f506f736974696f6e20636c6f736564206f7220646f6573206e6f7420657869736040820152601d60fa1b606082015260800190565b81810381811115610f9557610f956146e7565b80820180821115610f9557610f956146e7565b5f60208284031215614859575f80fd5b815161ffff81168114611c73575f80fd5b6001600160a01b03929092168252602082015260400190565b60208082526011908201527010dbdb1b185d195c985b0814185d5cd959607a1b604082015260600190565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b6020808252601d908201527f436f6c6c61746572616c2064656274206c696d69742072656163686564000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614934575f80fd5b81516001600160401b0381168114611c73575f80fd5b5f81614958576149586146e7565b505f190190565b6004811061496f5761496f61454f565b9052565b848152602081018490526040810183905260808101611c16606083018461495f565b634e487b7160e01b5f52603160045260245ffd5b5f602082840312156149b9575f80fd5b8151611c7381614309565b5f826149d2576149d2614712565b500690565b634e487b7160e01b5f52600160045260245ffd5b5f82518060208501845e5f920191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f8301168401019150509291505056fe4fe7fb62190647b8a7596709832f68a365082b18206e55ae330b95593c369affc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600ca264697066735822122014b0870a961a31054827d231caff25708082a1a8f53366c14431671fb561448e64736f6c634300081a0033000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad40000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae08400000000000000000000000000000000000000000000000000de0b6b3a7640000
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106103e6575f3560e01c806301c799fc146103ea5780630b076557146104125780630b216022146104335780630fdb11cf146104485780631289fefb1461045e5780631673c79a1461046657806316c38b3c1461048c5780631983fb401461049f5780631ef3a04c146104b25780631f68f20a146104e657806320d17d7b146104ef578063219efc46146104f85780632b11551a1461050b5780632d587e10146105135780632f41d9e11461051c5780632f5c83a81461052f57806331c7a2611461053857806333d53b5914610541578063363bf9641461054a5780633cf1b05b1461055d5780634056d9fc14610565578063452a93201461056e578063477d66cf146105765780634820fe56146105895780634870dd9a146105915780634af797341461059a5780634ba4a28b146105ad5780635728ddda146105d45780635c975abb146105e7578063631203b01461060957806366ca4a211461061c5780636b6c0774146106245780636c302c761461064b5780636f3fe4041461065e578063720fbe2c14610666578063724e78da1461066f578063741bef1a1461068257806377553ad414610695578063789c6b93146106bc578063794e5724146106cf578063795d26c3146106d85780637a83a34e146106e05780637c3a00fd146106e85780637e95d664146106f1578063807d138d146106fa578063874d6d8114610703578063887105d31461070b5780638bbb1de8146107135780638da5cb5b1461071c57806391245f9f146107245780639484fb8e1461072d57806396660b181461073a57806396d711ff146107435780639bf9b9611461074c5780639e994ca81461075e5780639ed7254214610771578063a20baee61461077a578063a27bcdaa14610789578063b0d8e1811461079c578063b2016bd4146107af578063b279906e146107c2578063b2f29d7b146107d5578063b91af97c146107e8578063bc006c381461080b578063bcd3752614610814578063be2c377b14610827578063bf88dd7114610830578063bf9befb114610892578063c045e47d1461089b578063c52861f2146108ae578063c77cf615146108b6578063c98eef62146108c9578063d128080c146108f4578063d12ae841146108fc578063d23ab4611461090f578063d293c71014610922578063d30612e114610935578063d380a37c1461093d578063d38b055814610946578063d5b356351461094f578063e2ac77b014610962578063e47cf0c314610975578063e9186c941461097e578063ea23a27a14610991578063eb7a7f7e146109a4578063ec38a05d146109b7578063f36b2425146109d5578063f8d89898146109dd575b5f80fd5b6103f2610a04565b604080519384526020840192909252908201526060015b60405180910390f35b6104256104203660046142c6565b610a2a565b6040516104099291906142e1565b6104466104413660046142ef565b610a46565b005b610450610ca9565b604051908152602001610409565b610446610db6565b6104256104743660046142c6565b60216020525f90815260409020805460019091015482565b61044661049a366004614316565b610ec9565b6104506104ad3660046142c6565b610f66565b6104d97f000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae084081565b6040516104099190614331565b61045060105481565b610450601e5481565b610446610506366004614345565b610f9b565b61045061102e565b610450600d5481565b61044661052a3660046142c6565b61103f565b610450600a5481565b61045060085481565b61045060155481565b610446610558366004614388565b61105e565b602254610450565b61045060095481565b6104d96110d4565b6104506105843660046143d0565b611155565b601954610450565b6104506103e881565b6104466105a83660046142c6565b611167565b6104507f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6104466105e23660046143e7565b611238565b600f546105f990610100900460ff1681565b6040519015158152602001610409565b6104506106173660046143d0565b6112dd565b6104506112e9565b6104d97f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d5781565b61044661065936600461441e565b6112fa565b610446611310565b610450600e5481565b61044661067d3660046142c6565b611322565b6001546104d9906001600160a01b031681565b6104d97f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd481565b6104466106ca36600461443e565b611428565b61045060045481565b61045061145f565b6104466114b1565b610450600b5481565b61045060065481565b61045060135481565b6104506117d3565b61045061180c565b610450600c5481565b6104d961181d565b61045060075481565b600f546105f99060ff1681565b610450601b5481565b61045060145481565b5f546104d9906001600160a01b031681565b61044661076c3660046142c6565b61187a565b61045060185481565b610450670de0b6b3a764000081565b610425610797366004614468565b611973565b6104506107aa3660046142c6565b611bfb565b6002546104d9906001600160a01b031681565b6104d96107d03660046143d0565b611c1f565b6104506107e33660046143d0565b611c4d565b6107fb6107f63660046142c6565b611c7a565b60405161040994939291906144cb565b61045060055481565b6104466108223660046144e6565b611cfe565b610450601d5481565b61088061083e3660046142c6565b601f6020525f90815260409020805460018201546002830154600384015460049094015492939192909160ff8116916101009091046001600160801b03169086565b60405161040996959493929190614563565b61045060125481565b6003546104d9906001600160a01b031681565b610450612557565b601c546104d9906001600160a01b031681565b6104506108d73660046142c6565b6001600160a01b03165f908152601f602052604090206002015490565b610450612568565b61042561090a3660046142c6565b6125ac565b6103f261091d3660046145ae565b6126c1565b61045061093036600461443e565b6128dd565b610450612903565b61045060115481565b61045060175481565b61045061095d3660046143d0565b612937565b6105f96109703660046142c6565b612949565b61045060165481565b61044661098c366004614649565b6129a8565b61044661099f36600461467b565b6129c4565b6104256109b23660046142c6565b612b07565b6104506109c53660046142c6565b602080525f908152604090205481565b610450612b1f565b6104d97f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f81565b5f805f610a0f61180c565b610a1761145f565b610a1f610ca9565b925092509250909192565b5f80610a34612b2b565b610a3d83612b93565b91509150915091565b600f5460ff1615610a9b5760405162461bcd60e51b815260206004820152601a60248201527910d85b9b9bdd0818da185b99d94818599d195c881cdd5b9cd95d60321b60448201526064015b60405180910390fd5b670de0b6b3a76400008160e001351015610aeb5760405162461bcd60e51b81526020600482015260116024820152704d43522063616e6e6f74203c203130302560781b6044820152606401610a92565b60055415610b2b57610afb61181d565b6001600160a01b0316336001600160a01b031614610b2b5760405162461bcd60e51b8152600401610a92906146c3565b670d8f91d06741510c813510801590610b4d5750670de07829b2adec28813511155b610b55575f80fd5b8060400135816020013511158015610b795750670de0b6b3a7640000816040013511155b610b81575f80fd5b8060800135816060013511158015610ba55750670de0b6b3a7640000816080013511155b610bad575f80fd5b610bb5612cc2565b508035600555602081013560065560408101356007556060810135600855608081013560095560c0810135600a55610bf5610120820161010083016142c6565b5f80546001600160a01b0319166001600160a01b0392909216919091178155610c246301e133806127106146fb565b610c3d60a0840135676765c793fa10079d601b1b6146fb565b610c479190614726565b9050600b548114610c6557610c5a612d01565b5042600d55600b8190555b60e08201356004556040517fdba82a425948402262be7f293af6e198241329ec2ff19e69724a0bcdcda31f4d90610c9d908490614739565b60405180910390a15050565b6001545f906001600160a01b031680610d3f577f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d3c91906147b0565b90505b600254604051635670bcc760e11b81526001600160a01b038381169263ace1798e92610d719290911690600401614331565b602060405180830381865afa158015610d8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db091906147cb565b91505090565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3691906147b0565b6001600160a01b0316336001600160a01b031614610e665760405162461bcd60e51b8152600401610a92906146c3565b600f805460ff19166001179055610e7b612d01565b50610e8c6301e133806127106146fb565b610ea3676765c793fa10079d601b1b6113886146fb565b610ead9190614726565b600b5542600d555f6006819055600a8190556010819055600755565b808015610eee5750610ed96110d4565b6001600160a01b0316336001600160a01b0316145b80610f115750610efc61181d565b6001600160a01b0316336001600160a01b0316145b610f4c5760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606401610a92565b600f80549115156101000261ff0019909216919091179055565b6001600160a01b0381165f908152601f602052604081206003015460ff166004811115610f9557610f9561454f565b92915050565b610fa3612b2b565b60016001600160a01b0385165f908152601f602052604090206003015460ff166004811115610fd457610fd461454f565b14610ff15760405162461bcd60e51b8152600401610a92906147e2565b610ffa84612d62565b611005846002612dab565b80601a546110139190614823565b601a55611020838361303b565b6110286130a3565b50505050565b5f61103a6010546130f7565b905090565b611047613111565b61105081612d62565b61105b816003612dab565b50565b6003546001600160a01b031615611073575f80fd5b600180546001600160a01b039485166001600160a01b03199182161790915560038054938516938216939093179092556002805491909316911617905542600e819055600f805460ff19169055676765c793fa10079d601b1b600c55600d55565b5f7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061103a91906147b0565b5f610f956111616112e9565b83613183565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e791906147b0565b6001600160a01b0316336001600160a01b0316146112175760405162461bcd60e51b8152600401610a92906146c3565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f546001600160a01b0316331461124d573391505b6001600160a01b0382165f908152602080526040902054806112b15760405162461bcd60e51b815260206004820181905260248201527f4e6f20636f6c6c61746572616c20617661696c61626c6520746f20636c61696d6044820152606401610a92565b6001600160a01b038084165f9081526020805260408120556002546112d8911683836131a1565b505050565b5f610f95611161612b1f565b5f61103a6112f56131f7565b613233565b611302613111565b61130c828261324d565b5050565b611318613111565b6113206132a6565b565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a291906147b0565b6001600160a01b0316336001600160a01b0316146113d25760405162461bcd60e51b8152600401610a92906146c3565b600180546001600160a01b0319166001600160a01b0383161790556040517fe5b20b8497e4f3e2435ef9c20e2e26b47497ee13745ce1c681ad6640653119e69061141d908390614331565b60405180910390a150565b611430613111565b6001600160a01b0382165f90815260208052604081208054839290611456908490614836565b90915550505050565b601a545f908161146d6132ae565b915050801561149d575f61148d8383676765c793fa10079d601b1b613318565b90506114998184614836565b9250505b601e546114aa9083614836565b9250505090565b6114b9612d01565b50601b54806114ff5760405162461bcd60e51b8152602060048201526012602482015271139bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b6044820152606401610a92565b5f611590827f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316636d52dffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611560573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115849190614849565b61ffff16612710613318565b90505f61159d8284614823565b601c549091505f906001600160a01b0316611637577f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b03166371f8424f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561160e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163291906147b0565b611644565b601c546001600160a01b03165b90508215611747577f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f6001600160a01b03166340c10f197f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116fb91906147b0565b856040518363ffffffff1660e01b815260040161171992919061486a565b5f604051808303815f87803b158015611730575f80fd5b505af1158015611742573d5f803e3d5ffd5b505050505b81156117c9576040516340c10f1960e01b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906340c10f199061179b908490869060040161486a565b5f604051808303815f87803b1580156117b2575f80fd5b505af11580156117c4573d5f803e3d5ffd5b505050505b50505f601b555050565b5f62093a806118027f0000000000000000000000000000000000000000000000000000000069151f8042614823565b61103a9190614726565b5f601d5460195461103a9190614836565b5f7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118fa91906147b0565b6001600160a01b0316336001600160a01b03161461192a5760405162461bcd60e51b8152600401610a92906146c3565b601c80546001600160a01b0319166001600160a01b0383169081179091556040517f085af60e444f2bfe4e6ad75d3227d2a8702123a3d663597ed1ca90a5f0c2c89c905f90a250565b600f545f908190610100900460ff161561199f5760405162461bcd60e51b8152600401610a9290614883565b6119a7612b2b565b600f5460ff16156119f95760405162461bcd60e51b815260206004820152601c60248201527b43616e6e6f74206f70656e207768696c652073756e73657474696e6760201b6044820152606401610a92565b6001600160a01b0388165f908152601f602052604090206001600382015460ff166004811115611a2b57611a2b61454f565b03611a785760405162461bcd60e51b815260206004820152601f60248201527f426f72726f7765724f70733a20506f736974696f6e20697320616374697665006044820152606401610a92565b60038101805460ff1916600190811790915581018890558681555f611a9b612d01565b600483018190559050611aad8a6133c0565b611ab682613439565b6003546040516346f7cf8760e01b81529195506001600160a01b0316906346f7cf8790611aed908d908b908b908b906004016148ae565b5f604051808303815f87803b158015611b04575f80fd5b505af1158015611b16573d5f803e3d5ffd5b5050505060228a908060018154018082558091505060019003905f5260205f20015f9091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506001602280549050611b729190614823565b600383018054610100600160881b0319166101006001600160801b03841602179055601954909350611ba5908a90614836565b601955601a545f90611bb8908a90614836565b9050600a54601e5482611bcb9190614836565b1115611be95760405162461bcd60e51b8152600401610a92906148d9565b601a5550919890975095505050505050565b5f805f611c0784612b07565b915091505f611c1683836134b6565b95945050505050565b5f60228281548110611c3357611c33614910565b5f918252602090912001546001600160a01b031692915050565b5f611c56612b2b565b5f611c5f612cc2565b9050611c73611c6d82613233565b84613183565b9392505050565b6001600160a01b0381165f908152601f6020526040812080546001820154909290918190611ca7866125ac565b60048301549094509092508015611cdd575f611cc16132ae565b50905081611ccf82896146fb565b611cd99190614726565b9650505b611ce78487614836565b9550611cf38386614836565b945050509193509193565b5f60035f9054906101000a90046001600160a01b03169050611d566040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6006548310158015611d6a57506007548311155b611dae5760405162461bcd60e51b81526020600482015260156024820152744d617820666565206e6f7420696e20626f756e647360581b6044820152606401610a92565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663c0c39a6f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2e9190614924565b6001600160401b0316600e54611e449190614836565b421015611e865760405162461bcd60e51b815260206004820152601060248201526f1093d3d514d514905417d411549253d160821b6044820152606401610a92565b611e8e610ca9565b8160c00181815250506004547f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd46001600160a01b031663b620115d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f1a91906147cb565b1015611f675760405162461bcd60e51b815260206004820152601c60248201527b21b0b73737ba103932b232b2b6903bb432b7102a21a9101e1026a1a960211b6044820152606401610a92565b5f8911611fb65760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610a92565b6040516370a0823160e01b815289906001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906370a0823190612004903390600401614331565b602060405180830381865afa15801561201f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061204391906147cb565b10156120885760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610a92565b6120906132a6565b61209861145f565b60e082015288815260c08101515f906120b49084908b906134ea565b156120c05750876121c6565b826001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212091906147b0565b90505b6001600160a01b0381161580159061214f5750670de0b6b3a764000061214d828460c001516128dd565b105b156121c657604051632dc9c0eb60e21b81526001600160a01b0384169063b72703ac90612180908490600401614331565b602060405180830381865afa15801561219b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121bf91906147b0565b9050612123565b845f036121d257606494505b6001600160a01b038116158015906121ea5750815115155b80156121f557505f85115b156122f357846122048161494a565b9550505f836001600160a01b031663b72703ac836040518263ffffffff1660e01b81526004016122349190614331565b602060405180830381865afa15801561224f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227391906147b0565b905061227e82612b93565b50505f6122978584865f01518760c001518e8e8e61362d565b90508060400151156122aa5750506122f3565b805160208501516122bb9190614836565b60208086019190915281015160408501516122d69190614836565b6040850152805184516122e99190614823565b84525090506121d2565b5f8260400151116123445760405162461bcd60e51b815260206004820152601b60248201527a155b98589b19481d1bc81c995919595b48185b9e48185b5bdd5b9d602a1b6044820152606401610a92565b600f5460ff166123675761236582604001518360c001518460e0015161392e565b505b600f5460ff1661238b5761238661237c61102e565b83604001516139ac565b61238d565b5f5b6060830181905260408301516123a4919086613a1c565b6124317f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa158015612403573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242791906147b0565b836060015161303b565b816060015182604001516124459190614823565b826080018181525050336001600160a01b03167f08b6f1ce3f9ab2722e8ea40c31a3e3a806a41702c5994f29af43dc0c1f2837df8b84602001518560400151866060015160405161249994939291906144cb565b60405180910390a26020820151604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1691639dc29fac916124f291339160040161486a565b5f604051808303815f87803b158015612509575f80fd5b505af115801561251b573d5f803e3d5ffd5b5050506020830151601a546125309250614823565b601a55608082015161254390339061303b565b61254b6130a3565b50505050505050505050565b5f61103a6125636131f7565b6130f7565b601a545f90816125766132ae565b91505080156125a6575f6125968383676765c793fa10079d601b1b613318565b90506125a28184614836565b9250505b50919050565b6001600160a01b0381165f9081526021602090815260408083208151808301909252805480835260019091015492820192909252601554839283916125f19190614823565b90505f82602001516016546126069190614823565b90506126128183614836565b158061264d575060016001600160a01b0387165f908152601f602052604090206003015460ff16600481111561264a5761264a61454f565b14155b1561265f57505f958695509350505050565b6001600160a01b0386165f908152601f6020526040902060020154670de0b6b3a764000061268d84836146fb565b6126979190614726565b670de0b6b3a76400006126aa84846146fb565b6126b49190614726565b9550955050505050915091565b5f805f6126cc612b2b565b88806126d557508b5b1561275557600f54610100900460ff16156127025760405162461bcd60e51b8152600401610a9290614883565b600f5460ff16156127555760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420696e637265617365207768696c652073756e73657474696e676044820152606401610a92565b6001600160a01b0385165f908152601f602052604090206001600382015460ff1660048111156127875761278761454f565b146127a45760405162461bcd60e51b8152600401610a92906147e2565b80548c156127e8578d156127ce576127bc8c82614836565b90506127c9868d8f613a99565b6127e4565b6127d88c82614823565b90506127e4868e613b5f565b8082555b60018201548a15612839578b1561281b576128038b82614836565b90508a6019546128139190614836565b601955612831565b6128258b82614823565b9050612831878c61303b565b600183018190555b5f61284482846134b6565b905060035f9054906101000a90046001600160a01b03166001600160a01b0316632be212608a838e8e6040518563ffffffff1660e01b815260040161288c94939291906148ae565b5f604051808303815f87803b1580156128a3575f80fd5b505af11580156128b5573d5f803e3d5ffd5b5050505081836128c486613439565b9650965096505050505099509950999650505050505050565b5f805f6128e985612b07565b915091505f6128f9838387613bef565b9695505050505050565b6129136301e133806127106146fb565b61292a676765c793fa10079d601b1b6113886146fb565b6129349190614726565b81565b5f610f95612943612557565b836139ac565b5f60016001600160a01b0383165f908152601f602052604090206003015460ff16600481111561297b5761297b61454f565b1461298757505f919050565b506015546001600160a01b039091165f908152602160205260409020541090565b6129b0613111565b6129ba8383613b5f565b6112d8838261303b565b6129cc613111565b6129d68585613c1d565b60195483156129f1576129e98482614823565b601981905590505b601254601355601d548290612a069083614836565b612a109190614823565b60148190556013546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612a4792916142e1565b60405180910390a1604051631062c15f60e11b81526001600160a01b037f000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad4811660048301528881166024830152604482018590527f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906320c582be906064015f604051808303815f87803b158015612ade575f80fd5b505af1158015612af0573d5f803e3d5ffd5b50505050612afe878361303b565b50505050505050565b5f80612b1283611c7a565b5090959194509092505050565b5f61103a601054613233565b336001600160a01b037f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd416146113205760405162461bcd60e51b815260206004820152600d60248201526c43616c6c6572206e6f7420424f60981b6044820152606401610a92565b6001600160a01b0381165f908152601f6020526040812081906001600382015460ff166004811115612bc757612bc761454f565b03612cbc5760048101545f612bda612d01565b835460018501549650945090508381831015612c0f5782612bfb83876146fb565b612c059190614726565b6004850183905594505b6015546001600160a01b0388165f908152602160205260409020541015612cad575f80612c3b896125ac565b9092509050612c4a8289614836565b600187018190559750612c5d8188614836565b9650612c68896133c0565b612c72818361324d565b886001600160a01b03165f80516020614a37833981519152888a89600201545f604051612ca29493929190614973565b60405180910390a250505b848114612cb8578484555b5050505b50915091565b5f80612ccc6131f7565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a1612cfc613d8c565b919050565b5f805f612d0c6132ae565b909250905080156125a657601a545f612d318284676765c793fa10079d601b1b613318565b9050612d3d8183614836565b601a55601b54612d4e908290614836565b601b555050600c82905542600d5550919050565b6001600160a01b0381165f908152601f6020526040902060020154601254612d8b908290614823565b601255506001600160a01b03165f908152601f6020526040812060020155565b6022546001600160a01b0383165f908152601f6020526040902060038101805484919060ff19166001836004811115612de657612de661454f565b02179055505f6001808301829055818355600483018290556003546001600160a01b038781168452602160205260408420848155830193909355919091169083118015612e9257506001816001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9091906147cb565b115b15612f9757600382015461010090046001600160801b03165f6022612eb8600187614823565b81548110612ec857612ec8614910565b5f91825260209091200154602280546001600160a01b03909216925082916001600160801b038516908110612eff57612eff614910565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252601f83526040918290206003018054610100600160881b0319166101006001600160801b038816908102919091179091558251918252928101929092527f2391d278b0562640dd6add67aa715b4dab2327f1ec896819ff2a809adea29a36910160405180910390a150505b6022805480612fa857612fa8614995565b5f8281526020902081015f1990810180546001600160a01b0319169055019055604051631484968760e11b81526001600160a01b038216906329092d0e90612ff4908890600401614331565b5f604051808303815f87803b15801561300b575f80fd5b505af115801561301d573d5f803e3d5ffd5b50505060039092018054610100600160881b03191690555050505050565b801561130c578060195461304f9190614823565b6019556040517f342693d2465f6f44931e41128424a0227e0cbc69d1c3917a839e6de71696d44c90613084908490849061486a565b60405180910390a160025461130c906001600160a01b031683836131a1565b6022545f0361132057676765c793fa10079d601b1b600c5542600d555f60128190556013819055601481905560158190556016819055601781905560188190556019819055601a819055601d819055601e55565b5f610f95826006546131099190614836565b600754613dfe565b336001600160a01b037f000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae084016146113205760405162461bcd60e51b81526020600482015260176024820152762737ba102634b8bab4b230ba34b7b71026b0b730b3b2b960491b6044820152606401610a92565b5f670de0b6b3a764000061319783856146fb565b611c739190614726565b6112d88363a9059cbb60e01b84846040516024016131c092919061486a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e13565b5f80613201613ee4565b90505f61321060055483613ef5565b9050670de0b6b3a76400008160105461322991906146fb565b6114aa9190614726565b5f610f95826008546132459190614836565b600954613dfe565b81601e5f82825461325e9190614823565b9250508190555081601a5f8282546132769190614836565b9250508190555080601d5f82825461328e9190614823565b925050819055508060195f8282546114569190614836565b61105b612d01565b600d545f9081904281036132c7575050600c54915f9150565b600b54600c5493508015613312575f6132e08342614823565b90506132ec82826146fb565b93506133048585676765c793fa10079d601b1b613318565b61330e9086614836565b9450505b50509091565b5f80805f19858709858702925082811083820303915050805f0361334f5783828161334557613345614712565b0492505050611c73565b80841161335a575f80fd5b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b60155460165460408051808201825283815260208082018481526001600160a01b0387165f9081526021909252908390209151825551600190910155517fc2910901d6342262f6857ac83c05564d215eef237fb5bcdf69013b47bbf4b8a89061342c90849084906142e1565b60405180910390a1505050565b5f806134488360010154613f9d565b60028401805490829055601254919250905f908390613468908490614823565b6134729190614836565b60128190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b5f81156134e257816134d168056bc75e2d63100000856146fb565b6134db9190614726565b9050610f95565b505f19610f95565b5f6001600160a01b03831615806135695750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e890613528908690600401614331565b602060405180830381865afa158015613543573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061356791906149a9565b155b806135845750670de0b6b3a764000061358284846128dd565b105b1561359057505f611c73565b60405163765e015960e01b81525f906001600160a01b0386169063765e0159906135be908790600401614331565b602060405180830381865afa1580156135d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135fd91906147b0565b90506001600160a01b0381161580611c165750670de0b6b3a764000061362382856128dd565b1095945050505050565b61365060405180606001604052805f81526020015f81526020015f151581525090565b6001600160a01b0387165f908152601f60205260409020805461369f90889061369a907f0000000000000000000000000000000000000000000000000de0b6b3a764000090614823565b613dfe565b80835286906136b790670de0b6b3a7640000906146fb565b6136c19190614726565b6020830152815181545f916136d591614823565b90505f836020015183600101546136ec9190614823565b90507f0000000000000000000000000000000000000000000000000de0b6b3a7640000820361378e5761371e8a612d62565b6137298a6004612dab565b6137548a7f0000000000000000000000000000000000000000000000000de0b6b3a764000083613fe3565b896001600160a01b03165f80516020614a378339815191525f805f60036040516137819493929190614973565b60405180910390a261391f565b5f61379982846134b6565b90505f8187116137b2576137ad8783614823565b6137bc565b6137bc8288614823565b90506601c6bf5263400081118061385957507f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd46001600160a01b031663969c24526040518163ffffffff1660e01b8152600401602060405180830381865afa15801561382a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384e91906147cb565b613857856140d7565b105b156138705750506001604085015250613923915050565b5060405163015f109360e51b81526001600160a01b038d1690632be21260906138a3908e9085908d908d906004016148ae565b5f604051808303815f87803b1580156138ba575f80fd5b505af11580156138cc573d5f803e3d5ffd5b50505083855550600184018290556138e384613439565b508a6001600160a01b03165f80516020614a378339815191528484876002015460036040516139159493929190614973565b60405180910390a2505b5050505b979650505050505050565b5f806139386131f7565b90505f8361394686886146fb565b6139509190614726565b90505f61395e600283614726565b6139689084614836565b905061397c81670de0b6b3a7640000613dfe565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a16128f9613d8c565b5f80670de0b6b3a76400006139c184866146fb565b6139cb9190614726565b9050828110611c735760405162461bcd60e51b815260206004820152601f60248201527f46656520657863656564732072657475726e656420636f6c6c61746572616c006044820152606401610a92565b5f825f03613a2a575f613a47565b82613a3d670de0b6b3a7640000866146fb565b613a479190614726565b9050818111156110285760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610a92565b5f82601a54613aa89190614836565b9050600a54601e5482613abb9190614836565b1115613ad95760405162461bcd60e51b8152600401610a92906148d9565b601a8190556040516340c10f1960e01b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906340c10f1990613b2c908790869060040161486a565b5f604051808303815f87803b158015613b43575f80fd5b505af1158015613b55573d5f803e3d5ffd5b5050505050505050565b604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1690639dc29fac90613bad908590859060040161486a565b5f604051808303815f87803b158015613bc4575f80fd5b505af1158015613bd6573d5f803e3d5ffd5b5050505080601a54613be89190614823565b601a555050565b5f8215613c15575f83613c0284876146fb565b613c0c9190614726565b9150611c739050565b505f19611c73565b815f03613c28575050565b6017545f90613c3f670de0b6b3a7640000846146fb565b613c499190614836565b90505f601854670de0b6b3a764000085613c6391906146fb565b613c6d9190614836565b6012549091505f613c7e8285614726565b90505f613c8b8385614726565b9050613c9783836146fb565b613ca19086614823565b601755613cae83826146fb565b613cb89085614823565b6018556015545f90613ccb908490614836565b90505f82601654613cdc9190614836565b601583905560168190556040519091507f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e390613d1b90849084906142e1565b60405180910390a188601a5f828254613d349190614823565b9250508190555088601e5f828254613d4c9190614836565b9250508190555087601d5f828254613d649190614836565b925050819055508760195f828254613d7c9190614823565b9091555050505050505050505050565b5f60115442613d9b9190614823565b9050603c811061105b57603c613daf613ee4565b613db991906146fb565b60115f828254613dc99190614836565b90915550506040514281527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc9060200161141d565b5f818310613e0c5781611c73565b5090919050565b5f613e67826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141029092919063ffffffff16565b8051909150156112d85780806020019051810190613e8591906149a9565b6112d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a92565b5f603c601154426118029190614823565b5f631f540500821115613f0a57631f54050091505b815f03613f205750670de0b6b3a7640000610f95565b670de0b6b3a764000083835b6001811115613f9357613f406002826149c4565b5f03613f6457613f508283614118565b9150613f5d600282614726565b9050613f2c565b613f6e8284614118565b9250613f7a8283614118565b91506002613f89600183614823565b613f5d9190614726565b6128f98284614118565b6014545f908190808203613fb357839150613fdc565b60135480613fc357613fc36149d7565b81613fce82876146fb565b613fd89190614726565b9250505b5092915050565b604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1690639dc29fac90614051907f000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad490869060040161486a565b5f604051808303815f87803b158015614068575f80fd5b505af115801561407a573d5f803e3d5ffd5b5050505081601a5461408c9190614823565b601a556001600160a01b0383165f908152602080526040812080548392906140b5908490614836565b925050819055508060195f8282546140cd9190614823565b9091555050505050565b5f610f957f0000000000000000000000000000000000000000000000000de0b6b3a764000083614823565b606061411084845f8561414e565b949350505050565b5f8061412483856146fb565b9050670de0b6b3a764000061413a600282614726565b6141449083614836565b6141109190614726565b6060824710156141af5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a92565b5f80866001600160a01b031685876040516141ca91906149eb565b5f6040518083038185875af1925050503d805f8114614204576040519150601f19603f3d011682016040523d82523d5f602084013e614209565b606091505b509150915061392387838387606083156142835782515f0361427c576001600160a01b0385163b61427c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a92565b5081614110565b61411083838151156142985781518083602001fd5b8060405162461bcd60e51b8152600401610a929190614a01565b6001600160a01b038116811461105b575f80fd5b5f602082840312156142d6575f80fd5b8135611c73816142b2565b918252602082015260400190565b5f610120828403128015614301575f80fd5b509092915050565b801515811461105b575f80fd5b5f60208284031215614326575f80fd5b8135611c7381614309565b6001600160a01b0391909116815260200190565b5f805f8060808587031215614358575f80fd5b8435614363816142b2565b93506020850135614373816142b2565b93969395505050506040820135916060013590565b5f805f6060848603121561439a575f80fd5b83356143a5816142b2565b925060208401356143b5816142b2565b915060408401356143c5816142b2565b809150509250925092565b5f602082840312156143e0575f80fd5b5035919050565b5f80604083850312156143f8575f80fd5b8235614403816142b2565b91506020830135614413816142b2565b809150509250929050565b5f806040838503121561442f575f80fd5b50508035926020909101359150565b5f806040838503121561444f575f80fd5b823561445a816142b2565b946020939093013593505050565b5f805f805f8060c0878903121561447d575f80fd5b8635614488816142b2565b955060208701359450604087013593506060870135925060808701356144ad816142b2565b915060a08701356144bd816142b2565b809150509295509295509295565b93845260208401929092526040830152606082015260800190565b5f805f805f805f60e0888a0312156144fc575f80fd5b87359650602088013561450e816142b2565b9550604088013561451e816142b2565b9450606088013561452e816142b2565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b5f52602160045260245ffd5b868152602081018690526040810185905260c08101600585106145885761458861454f565b60608201949094526001600160801b0392909216608083015260a0909101529392505050565b5f805f805f805f805f6101208a8c0312156145c7575f80fd5b89356145d281614309565b985060208a0135975060408a0135965060608a01356145f081614309565b955060808a0135945060a08a0135614607816142b2565b935060c08a0135614617816142b2565b925060e08a0135614627816142b2565b91506101008a0135614638816142b2565b809150509295985092959850929598565b5f805f6060848603121561465b575f80fd5b8335614666816142b2565b95602085013595506040909401359392505050565b5f805f805f8060c08789031215614690575f80fd5b863561469b816142b2565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b6020808252600a908201526927b7363c9037bbb732b960b11b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f9557610f956146e7565b634e487b7160e01b5f52601260045260245ffd5b5f8261473457614734614712565b500490565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c0808301359082015260e080830135908201526101208101610100830135614797816142b2565b6001600160a01b03166101009290920191909152919050565b5f602082840312156147c0575f80fd5b8151611c73816142b2565b5f602082840312156147db575f80fd5b5051919050565b60208082526021908201527f506f736974696f6e20636c6f736564206f7220646f6573206e6f7420657869736040820152601d60fa1b606082015260800190565b81810381811115610f9557610f956146e7565b80820180821115610f9557610f956146e7565b5f60208284031215614859575f80fd5b815161ffff81168114611c73575f80fd5b6001600160a01b03929092168252602082015260400190565b60208082526011908201527010dbdb1b185d195c985b0814185d5cd959607a1b604082015260600190565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b6020808252601d908201527f436f6c6c61746572616c2064656274206c696d69742072656163686564000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614934575f80fd5b81516001600160401b0381168114611c73575f80fd5b5f81614958576149586146e7565b505f190190565b6004811061496f5761496f61454f565b9052565b848152602081018490526040810183905260808101611c16606083018461495f565b634e487b7160e01b5f52603160045260245ffd5b5f602082840312156149b9575f80fd5b8151611c7381614309565b5f826149d2576149d2614712565b500690565b634e487b7160e01b5f52600160045260245ffd5b5f82518060208501845e5f920191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f8301168401019150509291505056fe4fe7fb62190647b8a7596709832f68a365082b18206e55ae330b95593c369affc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600ca264697066735822122014b0870a961a31054827d231caff25708082a1a8f53366c14431671fb561448e64736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad40000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae08400000000000000000000000000000000000000000000000000de0b6b3a7640000
-----Decoded View---------------
Arg [0] : _core (address): 0xF6ec524105548C37D3C2eB482BA197AE19740d57
Arg [1] : _gasPoolAddress (address): 0x130541A39cfaA89c9eD8f387d479B277321D1ad4
Arg [2] : _debtTokenAddress (address): 0x0F26bBb8962d73bC891327F14dB5162D5279899F
Arg [3] : _borrowerOperations (address): 0x49FD0C4fb5172b20b7636b13c49fb15dA52D5bd4
Arg [4] : _liquidationManager (address): 0xfa2dE0DCAa6dfDc0D01aD2677258A641BdAe0840
Arg [5] : _gasCompensation (uint256): 1000000000000000000
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57
Arg [1] : 000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad4
Arg [2] : 0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f
Arg [3] : 00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4
Arg [4] : 000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae0840
Arg [5] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.