Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
MultiTroveGetter
Compiler Version
v0.6.11+commit.5ef660b1
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-04-06 */ // SPDX-License-Identifier: MIT // File contracts/Interfaces/IPriceFeed.sol pragma solidity 0.6.11; interface IPriceFeed { // --- Events --- event LastGoodPriceUpdated(uint _lastGoodPrice); // --- Function --- function fetchPrice() external returns (uint); } // File contracts/Interfaces/ILiquityBase.sol pragma solidity 0.6.11; interface ILiquityBase { function priceFeed() external view returns (IPriceFeed); } // File contracts/Interfaces/IStabilityPool.sol pragma solidity 0.6.11; /* * The Stability Pool holds LUSD tokens deposited by Stability Pool depositors. * * When a trove is liquidated, then depending on system conditions, some of its LUSD debt gets offset with * LUSD in the Stability Pool: that is, the offset debt evaporates, and an equal amount of LUSD tokens in the Stability Pool is burned. * * Thus, a liquidation causes each depositor to receive a LUSD loss, in proportion to their deposit as a share of total deposits. * They also receive an ETH gain, as the ETH collateral of the liquidated trove is distributed among Stability depositors, * in the same proportion. * * When a liquidation occurs, it depletes every deposit by the same fraction: for example, a liquidation that depletes 40% * of the total LUSD in the Stability Pool, depletes 40% of each deposit. * * A deposit that has experienced a series of liquidations is termed a "compounded deposit": each liquidation depletes the deposit, * multiplying it by some factor in range ]0,1[ * * Please see the implementation spec in the proof document, which closely follows on from the compounded deposit / ETH gain derivations: * https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf * * --- LQTY ISSUANCE TO STABILITY POOL DEPOSITORS --- * * An LQTY issuance event occurs at every deposit operation, and every liquidation. * * Each deposit is tagged with the address of the front end through which it was made. * * All deposits earn a share of the issued LQTY in proportion to the deposit as a share of total deposits. The LQTY earned * by a given deposit, is split between the depositor and the front end through which the deposit was made, based on the front end's kickbackRate. * * Please see the system Readme for an overview: * https://github.com/liquity/dev/blob/main/README.md#lqty-issuance-to-stability-providers */ interface IStabilityPool { // --- Events --- event StabilityPoolETHBalanceUpdated(uint _newBalance); event StabilityPoolLUSDBalanceUpdated(uint _newBalance); event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event TroveManagerAddressChanged(address _newTroveManagerAddress); event ActivePoolAddressChanged(address _newActivePoolAddress); event DefaultPoolAddressChanged(address _newDefaultPoolAddress); event LUSDTokenAddressChanged(address _newLUSDTokenAddress); event SortedTrovesAddressChanged(address _newSortedTrovesAddress); event PriceFeedAddressChanged(address _newPriceFeedAddress); event CommunityIssuanceAddressChanged(address _newCommunityIssuanceAddress); event P_Updated(uint _P); event S_Updated(uint _S, uint128 _epoch, uint128 _scale); event G_Updated(uint _G, uint128 _epoch, uint128 _scale); event EpochUpdated(uint128 _currentEpoch); event ScaleUpdated(uint128 _currentScale); event FrontEndRegistered(address indexed _frontEnd, uint _kickbackRate); event FrontEndTagSet(address indexed _depositor, address indexed _frontEnd); event DepositSnapshotUpdated(address indexed _depositor, uint _P, uint _S, uint _G); event FrontEndSnapshotUpdated(address indexed _frontEnd, uint _P, uint _G); event UserDepositChanged(address indexed _depositor, uint _newDeposit); event FrontEndStakeChanged(address indexed _frontEnd, uint _newFrontEndStake, address _depositor); event ETHGainWithdrawn(address indexed _depositor, uint _ETH, uint _LUSDLoss); event LQTYPaidToDepositor(address indexed _depositor, uint _LQTY); event LQTYPaidToFrontEnd(address indexed _frontEnd, uint _LQTY); event EtherSent(address _to, uint _amount); // --- Functions --- /* * Called only once on init, to set addresses of other Liquity contracts * Callable only by owner, renounces ownership at the end */ function setAddresses( address _borrowerOperationsAddress, address _troveManagerAddress, address _activePoolAddress, address _lusdTokenAddress, address _sortedTrovesAddress, address _priceFeedAddress, address _communityIssuanceAddress ) external; /* * Initial checks: * - Frontend is registered or zero address * - Sender is not a registered frontend * - _amount is not zero * --- * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends * - Tags the deposit with the provided front end tag param, if it's a new deposit * - Sends depositor's accumulated gains (LQTY, ETH) to depositor * - Sends the tagged front end's accumulated LQTY gains to the tagged front end * - Increases deposit and tagged front end's stake, and takes new snapshots for each. */ function provideToSP(uint _amount, address _frontEndTag) external; /* * Initial checks: * - _amount is zero or there are no under collateralized troves left in the system * - User has a non zero deposit * --- * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends * - Removes the deposit's front end tag if it is a full withdrawal * - Sends all depositor's accumulated gains (LQTY, ETH) to depositor * - Sends the tagged front end's accumulated LQTY gains to the tagged front end * - Decreases deposit and tagged front end's stake, and takes new snapshots for each. * * If _amount > userDeposit, the user withdraws all of their compounded deposit. */ function withdrawFromSP(uint _amount) external; /* * Initial checks: * - User has a non zero deposit * - User has an open trove * - User has some ETH gain * --- * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends * - Sends all depositor's LQTY gain to depositor * - Sends all tagged front end's LQTY gain to the tagged front end * - Transfers the depositor's entire ETH gain from the Stability Pool to the caller's trove * - Leaves their compounded deposit in the Stability Pool * - Updates snapshots for deposit and tagged front end stake */ function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external; /* * Initial checks: * - Frontend (sender) not already registered * - User (sender) has no deposit * - _kickbackRate is in the range [0, 100%] * --- * Front end makes a one-time selection of kickback rate upon registering */ function registerFrontEnd(uint _kickbackRate) external; /* * Initial checks: * - Caller is TroveManager * --- * Cancels out the specified debt against the LUSD contained in the Stability Pool (as far as possible) * and transfers the Trove's ETH collateral from ActivePool to StabilityPool. * Only called by liquidation functions in the TroveManager. */ function offset(uint _debt, uint _coll) external; /* * Returns the total amount of ETH held by the pool, accounted in an internal variable instead of `balance`, * to exclude edge cases like ETH received from a self-destruct. */ function getETH() external view returns (uint); /* * Returns LUSD held in the pool. Changes when users deposit/withdraw, and when Trove debt is offset. */ function getTotalLUSDDeposits() external view returns (uint); /* * Calculates the ETH gain earned by the deposit since its last snapshots were taken. */ function getDepositorETHGain(address _depositor) external view returns (uint); /* * Calculate the LQTY gain earned by a deposit since its last snapshots were taken. * If not tagged with a front end, the depositor gets a 100% cut of what their deposit earned. * Otherwise, their cut of the deposit's earnings is equal to the kickbackRate, set by the front end through * which they made their deposit. */ function getDepositorLQTYGain(address _depositor) external view returns (uint); /* * Return the LQTY gain earned by the front end. */ function getFrontEndLQTYGain(address _frontEnd) external view returns (uint); /* * Return the user's compounded deposit. */ function getCompoundedLUSDDeposit(address _depositor) external view returns (uint); /* * Return the front end's compounded stake. * * The front end's compounded stake is equal to the sum of its depositors' compounded deposits. */ function getCompoundedFrontEndStake(address _frontEnd) external view returns (uint); /* * Fallback function * Only callable by Active Pool, it just accounts for ETH received * receive() external payable; */ } // File contracts/Dependencies/IERC20.sol pragma solidity 0.6.11; /** * Based on the OpenZeppelin IER20 interface: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol * * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); function increaseAllowance(address spender, uint256 addedValue) external returns (bool); function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File contracts/Dependencies/IERC2612.sol pragma solidity 0.6.11; /** * @dev Interface of the ERC2612 standard as defined in the EIP. * * Adds the {permit} method, which can be used to change one's * {IERC20-allowance} without having to send a transaction, by signing a * message. This allows users to spend tokens without having to hold Ether. * * See https://eips.ethereum.org/EIPS/eip-2612. * * Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/ */ interface IERC2612 { /** * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, * given `owner`'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; /** * @dev Returns the current ERC2612 nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases `owner`'s nonce by one. This * prevents a signature from being used multiple times. * * `owner` can limit the time a Permit is valid for by setting `deadline` to * a value in the near future. The deadline argument can be set to uint(-1) to * create Permits that effectively never expire. */ function nonces(address owner) external view returns (uint256); function version() external view returns (string memory); function permitTypeHash() external view returns (bytes32); function domainSeparator() external view returns (bytes32); } // File contracts/Interfaces/ILUSDToken.sol pragma solidity 0.6.11; interface ILUSDToken is IERC20, IERC2612 { // --- Events --- event TroveManagerAddressChanged(address _troveManagerAddress); event StabilityPoolAddressChanged(address _newStabilityPoolAddress); event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event LUSDTokenBalanceUpdated(address _user, uint _amount); // --- Functions --- function mint(address _account, uint256 _amount) external; function burn(address _account, uint256 _amount) external; function sendToPool(address _sender, address poolAddress, uint256 _amount) external; function returnFromPool(address poolAddress, address user, uint256 _amount ) external; } // File contracts/Interfaces/ILQTYToken.sol pragma solidity 0.6.11; interface ILQTYToken is IERC20, IERC2612 { // --- Events --- event CommunityIssuanceAddressSet(address _communityIssuanceAddress); event LQTYStakingAddressSet(address _lqtyStakingAddress); event LockupContractFactoryAddressSet(address _lockupContractFactoryAddress); // --- Functions --- function sendToLQTYStaking(address _sender, uint256 _amount) external; function getDeploymentStartTime() external view returns (uint256); function getLpRewardsEntitlement() external view returns (uint256); } // File contracts/Interfaces/ILQTYStaking.sol pragma solidity 0.6.11; interface ILQTYStaking { // --- Events -- event LQTYTokenAddressSet(address _lqtyTokenAddress); event LUSDTokenAddressSet(address _lusdTokenAddress); event TroveManagerAddressSet(address _troveManager); event BorrowerOperationsAddressSet(address _borrowerOperationsAddress); event ActivePoolAddressSet(address _activePoolAddress); event StakeChanged(address indexed staker, uint newStake); event StakingGainsWithdrawn(address indexed staker, uint LUSDGain, uint ETHGain); event F_ETHUpdated(uint _F_ETH); event F_LUSDUpdated(uint _F_LUSD); event TotalLQTYStakedUpdated(uint _totalLQTYStaked); event EtherSent(address _account, uint _amount); event StakerSnapshotsUpdated(address _staker, uint _F_ETH, uint _F_LUSD); // --- Functions --- function setAddresses ( address _lqtyTokenAddress, address _lusdTokenAddress, address _troveManagerAddress, address _borrowerOperationsAddress, address _activePoolAddress ) external; function stake(uint _LQTYamount) external; function unstake(uint _LQTYamount) external; function increaseF_ETH(uint _ETHFee) external; function increaseF_LUSD(uint _LQTYFee) external; function getPendingETHGain(address _user) external view returns (uint); function getPendingLUSDGain(address _user) external view returns (uint); } // File contracts/Interfaces/ITroveManager.sol pragma solidity 0.6.11; // Common interface for the Trove Manager. interface ITroveManager is ILiquityBase { // --- Events --- event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event PriceFeedAddressChanged(address _newPriceFeedAddress); event LUSDTokenAddressChanged(address _newLUSDTokenAddress); event ActivePoolAddressChanged(address _activePoolAddress); event DefaultPoolAddressChanged(address _defaultPoolAddress); event StabilityPoolAddressChanged(address _stabilityPoolAddress); event GasPoolAddressChanged(address _gasPoolAddress); event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress); event SortedTrovesAddressChanged(address _sortedTrovesAddress); event LQTYTokenAddressChanged(address _lqtyTokenAddress); event LQTYStakingAddressChanged(address _lqtyStakingAddress); event Liquidation(uint _liquidatedDebt, uint _liquidatedColl, uint _collGasCompensation, uint _LUSDGasCompensation); event Redemption(uint _attemptedLUSDAmount, uint _actualLUSDAmount, uint _ETHSent, uint _ETHFee); event TroveUpdated(address indexed _borrower, uint _debt, uint _coll, uint stake, uint8 operation); event TroveLiquidated(address indexed _borrower, uint _debt, uint _coll, uint8 operation); event BaseRateUpdated(uint _baseRate); event LastFeeOpTimeUpdated(uint _lastFeeOpTime); event TotalStakesUpdated(uint _newTotalStakes); event SystemSnapshotsUpdated(uint _totalStakesSnapshot, uint _totalCollateralSnapshot); event LTermsUpdated(uint _L_ETH, uint _L_LUSDDebt); event TroveSnapshotsUpdated(uint _L_ETH, uint _L_LUSDDebt); event TroveIndexUpdated(address _borrower, uint _newIndex); // --- Functions --- function setAddresses( address _borrowerOperationsAddress, address _activePoolAddress, address _defaultPoolAddress, address _stabilityPoolAddress, address _gasPoolAddress, address _collSurplusPoolAddress, address _priceFeedAddress, address _lusdTokenAddress, address _sortedTrovesAddress, address _lqtyTokenAddress, address _lqtyStakingAddress ) external; function stabilityPool() external view returns (IStabilityPool); function lusdToken() external view returns (ILUSDToken); function lqtyToken() external view returns (ILQTYToken); function lqtyStaking() external view returns (ILQTYStaking); function getTroveOwnersCount() external view returns (uint); function getTroveFromTroveOwnersArray(uint _index) external view returns (address); function getNominalICR(address _borrower) external view returns (uint); function getCurrentICR(address _borrower, uint _price) external view returns (uint); function liquidate(address _borrower) external; function liquidateTroves(uint _n) external; function batchLiquidateTroves(address[] calldata _troveArray) external; function redeemCollateral( uint _LUSDAmount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFee ) external; function updateStakeAndTotalStakes(address _borrower) external returns (uint); function updateTroveRewardSnapshots(address _borrower) external; function addTroveOwnerToArray(address _borrower) external returns (uint index); function applyPendingRewards(address _borrower) external; function getPendingETHReward(address _borrower) external view returns (uint); function getPendingLUSDDebtReward(address _borrower) external view returns (uint); function hasPendingRewards(address _borrower) external view returns (bool); function getEntireDebtAndColl(address _borrower) external view returns ( uint debt, uint coll, uint pendingLUSDDebtReward, uint pendingETHReward ); function closeTrove(address _borrower) external; function removeStake(address _borrower) external; function getRedemptionRate() external view returns (uint); function getRedemptionRateWithDecay() external view returns (uint); function getRedemptionFeeWithDecay(uint _ETHDrawn) external view returns (uint); function getBorrowingRate() external view returns (uint); function getBorrowingRateWithDecay() external view returns (uint); function getBorrowingFee(uint LUSDDebt) external view returns (uint); function getBorrowingFeeWithDecay(uint _LUSDDebt) external view returns (uint); function decayBaseRateFromBorrowing() external; function getTroveStatus(address _borrower) external view returns (uint); function getTroveStake(address _borrower) external view returns (uint); function getTroveDebt(address _borrower) external view returns (uint); function getTroveColl(address _borrower) external view returns (uint); function setTroveStatus(address _borrower, uint num) external; function increaseTroveColl(address _borrower, uint _collIncrease) external returns (uint); function decreaseTroveColl(address _borrower, uint _collDecrease) external returns (uint); function increaseTroveDebt(address _borrower, uint _debtIncrease) external returns (uint); function decreaseTroveDebt(address _borrower, uint _collDecrease) external returns (uint); function getTCR(uint _price) external view returns (uint); function checkRecoveryMode(uint _price) external view returns (bool); } // File contracts/Interfaces/ICollSurplusPool.sol pragma solidity 0.6.11; interface ICollSurplusPool { // --- Events --- event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event TroveManagerAddressChanged(address _newTroveManagerAddress); event ActivePoolAddressChanged(address _newActivePoolAddress); event CollBalanceUpdated(address indexed _account, uint _newBalance); event EtherSent(address _to, uint _amount); // --- Contract setters --- function setAddresses( address _borrowerOperationsAddress, address _troveManagerAddress, address _activePoolAddress ) external; function getETH() external view returns (uint); function getCollateral(address _account) external view returns (uint); function accountSurplus(address _account, uint _amount) external; function claimColl(address _account) external; } // File contracts/Interfaces/ISortedTroves.sol pragma solidity 0.6.11; // Common interface for the SortedTroves Doubly Linked List. interface ISortedTroves { // --- Events --- event SortedTrovesAddressChanged(address _sortedDoublyLLAddress); event BorrowerOperationsAddressChanged(address _borrowerOperationsAddress); event NodeAdded(address _id, uint _NICR); event NodeRemoved(address _id); // --- Functions --- function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress) external; function insert(address _id, uint256 _ICR, address _prevId, address _nextId) external; function remove(address _id) external; function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) external; function contains(address _id) external view returns (bool); function isFull() external view returns (bool); function isEmpty() external view returns (bool); function getSize() external view returns (uint256); function getMaxSize() external view returns (uint256); function getFirst() external view returns (address); function getLast() external view returns (address); function getNext(address _id) external view returns (address); function getPrev(address _id) external view returns (address); function validInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (bool); function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address); } // File contracts/Dependencies/BaseMath.sol pragma solidity 0.6.11; contract BaseMath { uint constant public DECIMAL_PRECISION = 1e18; } // File contracts/Dependencies/SafeMath.sol pragma solidity 0.6.11; /** * Based on OpenZeppelin's SafeMath: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol * * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. * * _Available since v2.4.0._ */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File contracts/Dependencies/console.sol pragma solidity 0.6.11; // Buidler's helper contract for console logging library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); function log() internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log()")); ignored; } function logInt(int p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(int)", p0)); ignored; } function logUint(uint p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint)", p0)); ignored; } function logString(string memory p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string)", p0)); ignored; } function logBool(bool p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool)", p0)); ignored; } function logAddress(address p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address)", p0)); ignored; } function logBytes(bytes memory p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes)", p0)); ignored; } function logByte(byte p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(byte)", p0)); ignored; } function logBytes1(bytes1 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes1)", p0)); ignored; } function logBytes2(bytes2 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes2)", p0)); ignored; } function logBytes3(bytes3 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes3)", p0)); ignored; } function logBytes4(bytes4 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes4)", p0)); ignored; } function logBytes5(bytes5 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes5)", p0)); ignored; } function logBytes6(bytes6 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes6)", p0)); ignored; } function logBytes7(bytes7 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes7)", p0)); ignored; } function logBytes8(bytes8 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes8)", p0)); ignored; } function logBytes9(bytes9 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes9)", p0)); ignored; } function logBytes10(bytes10 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes10)", p0)); ignored; } function logBytes11(bytes11 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes11)", p0)); ignored; } function logBytes12(bytes12 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes12)", p0)); ignored; } function logBytes13(bytes13 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes13)", p0)); ignored; } function logBytes14(bytes14 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes14)", p0)); ignored; } function logBytes15(bytes15 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes15)", p0)); ignored; } function logBytes16(bytes16 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes16)", p0)); ignored; } function logBytes17(bytes17 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes17)", p0)); ignored; } function logBytes18(bytes18 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes18)", p0)); ignored; } function logBytes19(bytes19 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes19)", p0)); ignored; } function logBytes20(bytes20 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes20)", p0)); ignored; } function logBytes21(bytes21 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes21)", p0)); ignored; } function logBytes22(bytes22 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes22)", p0)); ignored; } function logBytes23(bytes23 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes23)", p0)); ignored; } function logBytes24(bytes24 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes24)", p0)); ignored; } function logBytes25(bytes25 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes25)", p0)); ignored; } function logBytes26(bytes26 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes26)", p0)); ignored; } function logBytes27(bytes27 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes27)", p0)); ignored; } function logBytes28(bytes28 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes28)", p0)); ignored; } function logBytes29(bytes29 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes29)", p0)); ignored; } function logBytes30(bytes30 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes30)", p0)); ignored; } function logBytes31(bytes31 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes31)", p0)); ignored; } function logBytes32(bytes32 p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bytes32)", p0)); ignored; } function log(uint p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint)", p0)); ignored; } function log(string memory p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string)", p0)); ignored; } function log(bool p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool)", p0)); ignored; } function log(address p0) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address)", p0)); ignored; } function log(uint p0, uint p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint)", p0, p1)); ignored; } function log(uint p0, string memory p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string)", p0, p1)); ignored; } function log(uint p0, bool p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool)", p0, p1)); ignored; } function log(uint p0, address p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address)", p0, p1)); ignored; } function log(string memory p0, uint p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint)", p0, p1)); ignored; } function log(string memory p0, string memory p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string)", p0, p1)); ignored; } function log(string memory p0, bool p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool)", p0, p1)); ignored; } function log(string memory p0, address p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address)", p0, p1)); ignored; } function log(bool p0, uint p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint)", p0, p1)); ignored; } function log(bool p0, string memory p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string)", p0, p1)); ignored; } function log(bool p0, bool p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool)", p0, p1)); ignored; } function log(bool p0, address p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address)", p0, p1)); ignored; } function log(address p0, uint p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint)", p0, p1)); ignored; } function log(address p0, string memory p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string)", p0, p1)); ignored; } function log(address p0, bool p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool)", p0, p1)); ignored; } function log(address p0, address p1) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address)", p0, p1)); ignored; } function log(uint p0, uint p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); ignored; } function log(uint p0, uint p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); ignored; } function log(uint p0, uint p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); ignored; } function log(uint p0, uint p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); ignored; } function log(uint p0, string memory p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); ignored; } function log(uint p0, string memory p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); ignored; } function log(uint p0, string memory p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); ignored; } function log(uint p0, string memory p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); ignored; } function log(uint p0, bool p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); ignored; } function log(uint p0, bool p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); ignored; } function log(uint p0, bool p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); ignored; } function log(uint p0, bool p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); ignored; } function log(uint p0, address p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); ignored; } function log(uint p0, address p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); ignored; } function log(uint p0, address p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); ignored; } function log(uint p0, address p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); ignored; } function log(string memory p0, uint p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); ignored; } function log(string memory p0, uint p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); ignored; } function log(string memory p0, uint p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); ignored; } function log(string memory p0, uint p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); ignored; } function log(string memory p0, string memory p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); ignored; } function log(string memory p0, string memory p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); ignored; } function log(string memory p0, string memory p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); ignored; } function log(string memory p0, string memory p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); ignored; } function log(string memory p0, bool p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); ignored; } function log(string memory p0, bool p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); ignored; } function log(string memory p0, bool p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); ignored; } function log(string memory p0, bool p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); ignored; } function log(string memory p0, address p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); ignored; } function log(string memory p0, address p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); ignored; } function log(string memory p0, address p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); ignored; } function log(string memory p0, address p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); ignored; } function log(bool p0, uint p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); ignored; } function log(bool p0, uint p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); ignored; } function log(bool p0, uint p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); ignored; } function log(bool p0, uint p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); ignored; } function log(bool p0, string memory p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); ignored; } function log(bool p0, string memory p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); ignored; } function log(bool p0, string memory p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); ignored; } function log(bool p0, string memory p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); ignored; } function log(bool p0, bool p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); ignored; } function log(bool p0, bool p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); ignored; } function log(bool p0, bool p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); ignored; } function log(bool p0, bool p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); ignored; } function log(bool p0, address p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); ignored; } function log(bool p0, address p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); ignored; } function log(bool p0, address p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); ignored; } function log(bool p0, address p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); ignored; } function log(address p0, uint p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); ignored; } function log(address p0, uint p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); ignored; } function log(address p0, uint p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); ignored; } function log(address p0, uint p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); ignored; } function log(address p0, string memory p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); ignored; } function log(address p0, string memory p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); ignored; } function log(address p0, string memory p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); ignored; } function log(address p0, string memory p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); ignored; } function log(address p0, bool p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); ignored; } function log(address p0, bool p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); ignored; } function log(address p0, bool p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); ignored; } function log(address p0, bool p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); ignored; } function log(address p0, address p1, uint p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); ignored; } function log(address p0, address p1, string memory p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); ignored; } function log(address p0, address p1, bool p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); ignored; } function log(address p0, address p1, address p2) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); ignored; } function log(uint p0, uint p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, uint p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, string memory p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, bool p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); ignored; } function log(uint p0, address p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, uint p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, string memory p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, bool p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); ignored; } function log(string memory p0, address p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, uint p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, string memory p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, bool p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); ignored; } function log(bool p0, address p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, uint p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, string memory p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, bool p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, uint p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, uint p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, uint p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, uint p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, string memory p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, string memory p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, string memory p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, string memory p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, bool p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, bool p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, bool p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, bool p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, address p2, uint p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, address p2, string memory p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, address p2, bool p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); ignored; } function log(address p0, address p1, address p2, address p3) internal view { (bool ignored, ) = CONSOLE_ADDRESS.staticcall(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); ignored; } } // File contracts/Dependencies/LiquityMath.sol pragma solidity 0.6.11; library LiquityMath { using SafeMath for uint; uint 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 ETH, * and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator. * */ uint internal constant NICR_PRECISION = 1e20; function _min(uint _a, uint _b) internal pure returns (uint) { return (_a < _b) ? _a : _b; } function _max(uint _a, uint _b) internal pure returns (uint) { 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(uint x, uint y) internal pure returns (uint decProd) { uint prod_xy = x.mul(y); decProd = prod_xy.add(DECIMAL_PRECISION / 2).div(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) TroveManager._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(uint _base, uint _minutes) internal pure returns (uint) { if (_minutes > 525600000) {_minutes = 525600000;} // cap to avoid overflow if (_minutes == 0) {return DECIMAL_PRECISION;} uint y = DECIMAL_PRECISION; uint x = _base; uint n = _minutes; // Exponentiation-by-squaring while (n > 1) { if (n % 2 == 0) { x = decMul(x, x); n = n.div(2); } else { // if (n % 2 != 0) y = decMul(x, y); x = decMul(x, x); n = (n.sub(1)).div(2); } } return decMul(x, y); } function _getAbsoluteDifference(uint _a, uint _b) internal pure returns (uint) { return (_a >= _b) ? _a.sub(_b) : _b.sub(_a); } function _computeNominalCR(uint _coll, uint _debt) internal pure returns (uint) { if (_debt > 0) { return _coll.mul(NICR_PRECISION).div(_debt); } // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR. else { // if (_debt == 0) return 2**256 - 1; } } function _computeCR(uint _coll, uint _debt, uint _price) internal pure returns (uint) { if (_debt > 0) { uint newCollRatio = _coll.mul(_price).div(_debt); return newCollRatio; } // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR. else { // if (_debt == 0) return 2**256 - 1; } } } // File contracts/Interfaces/IPool.sol pragma solidity 0.6.11; // Common interface for the Pools. interface IPool { // --- Events --- event ETHBalanceUpdated(uint _newBalance); event LUSDBalanceUpdated(uint _newBalance); event ActivePoolAddressChanged(address _newActivePoolAddress); event DefaultPoolAddressChanged(address _newDefaultPoolAddress); event StabilityPoolAddressChanged(address _newStabilityPoolAddress); event EtherSent(address _to, uint _amount); // --- Functions --- function getETH() external view returns (uint); function getLUSDDebt() external view returns (uint); function increaseLUSDDebt(uint _amount) external; function decreaseLUSDDebt(uint _amount) external; } // File contracts/Interfaces/IActivePool.sol pragma solidity 0.6.11; interface IActivePool is IPool { // --- Events --- event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event TroveManagerAddressChanged(address _newTroveManagerAddress); event ActivePoolLUSDDebtUpdated(uint _LUSDDebt); event ActivePoolETHBalanceUpdated(uint _ETH); // --- Functions --- function sendETH(address _account, uint _amount) external; } // File contracts/Interfaces/IDefaultPool.sol pragma solidity 0.6.11; interface IDefaultPool is IPool { // --- Events --- event TroveManagerAddressChanged(address _newTroveManagerAddress); event DefaultPoolLUSDDebtUpdated(uint _LUSDDebt); event DefaultPoolETHBalanceUpdated(uint _ETH); // --- Functions --- function sendETHToActivePool(uint _amount) external; } // File contracts/Dependencies/LiquityBase.sol pragma solidity 0.6.11; /* * Base contract for TroveManager, BorrowerOperations and StabilityPool. Contains global system constants and * common functions. */ contract LiquityBase is BaseMath, ILiquityBase { using SafeMath for uint; uint constant public _100pct = 1000000000000000000; // 1e18 == 100% // Minimum collateral ratio for individual troves uint constant public MCR = 1100000000000000000; // 110% // Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered. uint constant public CCR = 1500000000000000000; // 150% // Amount of LUSD to be locked in gas pool on opening troves uint constant public LUSD_GAS_COMPENSATION = 200e18; // Minimum amount of net LUSD debt a trove must have uint constant public MIN_NET_DEBT = 1800e18; // uint constant public MIN_NET_DEBT = 0; uint constant public PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5% uint constant public BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5% IActivePool public activePool; IDefaultPool public defaultPool; IPriceFeed public override priceFeed; // --- Gas compensation functions --- // Returns the composite debt (drawn debt + gas compensation) of a trove, for the purpose of ICR calculation function _getCompositeDebt(uint _debt) internal pure returns (uint) { return _debt.add(LUSD_GAS_COMPENSATION); } function _getNetDebt(uint _debt) internal pure returns (uint) { return _debt.sub(LUSD_GAS_COMPENSATION); } // Return the amount of ETH to be drawn from a trove's collateral and sent as gas compensation. function _getCollGasCompensation(uint _entireColl) internal pure returns (uint) { return _entireColl / PERCENT_DIVISOR; } function getEntireSystemColl() public view returns (uint entireSystemColl) { uint activeColl = activePool.getETH(); uint liquidatedColl = defaultPool.getETH(); return activeColl.add(liquidatedColl); } function getEntireSystemDebt() public view returns (uint entireSystemDebt) { uint activeDebt = activePool.getLUSDDebt(); uint closedDebt = defaultPool.getLUSDDebt(); return activeDebt.add(closedDebt); } function _getTCR(uint _price) internal view returns (uint TCR) { uint entireSystemColl = getEntireSystemColl(); uint entireSystemDebt = getEntireSystemDebt(); TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt, _price); return TCR; } function _checkRecoveryMode(uint _price) internal view returns (bool) { uint TCR = _getTCR(_price); return TCR < CCR; } function _requireUserAcceptsFee(uint _fee, uint _amount, uint _maxFeePercentage) internal pure { uint feePercentage = _fee.mul(DECIMAL_PRECISION).div(_amount); require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum"); } } // File contracts/Dependencies/Ownable.sol pragma solidity 0.6.11; /** * Based on OpenZeppelin's Ownable contract: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol * * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. * * NOTE: This function is not safe, as it doesn’t check owner is calling it. * Make sure you check it before calling it. */ function _renounceOwnership() internal { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } } // File contracts/Dependencies/CheckContract.sol pragma solidity 0.6.11; contract CheckContract { /** * Check that the account is an already deployed non-destroyed contract. * See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12 */ function checkContract(address _account) internal view { require(_account != address(0), "Account cannot be zero address"); uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(_account) } require(size > 0, "Account code size cannot be zero"); } } // File contracts/TroveManager.sol pragma solidity 0.6.11; contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager { string constant public NAME = "TroveManager"; // --- Connected contract declarations --- address public borrowerOperationsAddress; IStabilityPool public override stabilityPool; address gasPoolAddress; ICollSurplusPool collSurplusPool; ILUSDToken public override lusdToken; ILQTYToken public override lqtyToken; ILQTYStaking public override lqtyStaking; // A doubly linked list of Troves, sorted by their sorted by their collateral ratios ISortedTroves public sortedTroves; // --- Data structures --- uint constant public SECONDS_IN_ONE_MINUTE = 60; /* * Half-life of 12h. 12h = 720 min * (1/2) = d^720 => d = (1/2)^(1/720) */ uint constant public MINUTE_DECAY_FACTOR = 999037758833783000; uint constant public REDEMPTION_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5% uint constant public MAX_BORROWING_FEE = DECIMAL_PRECISION / 100 * 5; // 5% // During bootsrap period redemptions are not allowed uint constant public BOOTSTRAP_PERIOD = 14 days; /* * 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. */ uint constant public BETA = 2; uint public baseRate; // The timestamp of the latest fee operation (redemption or new LUSD issuance) uint public lastFeeOperationTime; enum Status { nonExistent, active, closedByOwner, closedByLiquidation, closedByRedemption } // Store the necessary data for a trove struct Trove { uint debt; uint coll; uint stake; Status status; uint128 arrayIndex; } mapping (address => Trove) public Troves; uint public totalStakes; // Snapshot of the value of totalStakes, taken immediately after the latest liquidation uint public totalStakesSnapshot; // Snapshot of the total collateral across the ActivePool and DefaultPool, immediately after the latest liquidation. uint public totalCollateralSnapshot; /* * L_ETH and L_LUSDDebt track the sums of accumulated liquidation rewards per unit staked. During its lifetime, each stake earns: * * An ETH gain of ( stake * [L_ETH - L_ETH(0)] ) * A LUSDDebt increase of ( stake * [L_LUSDDebt - L_LUSDDebt(0)] ) * * Where L_ETH(0) and L_LUSDDebt(0) are snapshots of L_ETH and L_LUSDDebt for the active Trove taken at the instant the stake was made */ uint public L_ETH; uint public L_LUSDDebt; // Map addresses with active troves to their RewardSnapshot mapping (address => RewardSnapshot) public rewardSnapshots; // Object containing the ETH and LUSD snapshots for a given active trove struct RewardSnapshot { uint ETH; uint LUSDDebt;} // Array of all active trove addresses - used to to compute an approximate hint off-chain, for the sorted list insertion address[] public TroveOwners; // Error trackers for the trove redistribution calculation uint public lastETHError_Redistribution; uint public lastLUSDDebtError_Redistribution; /* * --- Variable container structs for liquidations --- * * These structs are used to hold, return and assign variables inside the liquidation functions, * in order to avoid the error: "CompilerError: Stack too deep". **/ struct LocalVariables_OuterLiquidationFunction { uint price; uint LUSDInStabPool; bool recoveryModeAtStart; uint liquidatedDebt; uint liquidatedColl; } struct LocalVariables_InnerSingleLiquidateFunction { uint collToLiquidate; uint pendingDebtReward; uint pendingCollReward; } struct LocalVariables_LiquidationSequence { uint remainingLUSDInStabPool; uint i; uint ICR; address user; bool backToNormalMode; uint entireSystemDebt; uint entireSystemColl; } struct LiquidationValues { uint entireTroveDebt; uint entireTroveColl; uint collGasCompensation; uint LUSDGasCompensation; uint debtToOffset; uint collToSendToSP; uint debtToRedistribute; uint collToRedistribute; uint collSurplus; } struct LiquidationTotals { uint totalCollInSequence; uint totalDebtInSequence; uint totalCollGasCompensation; uint totalLUSDGasCompensation; uint totalDebtToOffset; uint totalCollToSendToSP; uint totalDebtToRedistribute; uint totalCollToRedistribute; uint totalCollSurplus; } struct ContractsCache { IActivePool activePool; IDefaultPool defaultPool; ILUSDToken lusdToken; ILQTYStaking lqtyStaking; ISortedTroves sortedTroves; ICollSurplusPool collSurplusPool; address gasPoolAddress; } // --- Variable container structs for redemptions --- struct RedemptionTotals { uint remainingLUSD; uint totalLUSDToRedeem; uint totalETHDrawn; uint ETHFee; uint ETHToSendToRedeemer; uint decayedBaseRate; uint price; uint totalLUSDSupplyAtStart; } struct SingleRedemptionValues { uint LUSDLot; uint ETHLot; bool cancelledPartial; } // --- Events --- event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress); event PriceFeedAddressChanged(address _newPriceFeedAddress); event LUSDTokenAddressChanged(address _newLUSDTokenAddress); event ActivePoolAddressChanged(address _activePoolAddress); event DefaultPoolAddressChanged(address _defaultPoolAddress); event StabilityPoolAddressChanged(address _stabilityPoolAddress); event GasPoolAddressChanged(address _gasPoolAddress); event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress); event SortedTrovesAddressChanged(address _sortedTrovesAddress); event LQTYTokenAddressChanged(address _lqtyTokenAddress); event LQTYStakingAddressChanged(address _lqtyStakingAddress); event Liquidation(uint _liquidatedDebt, uint _liquidatedColl, uint _collGasCompensation, uint _LUSDGasCompensation); event Redemption(uint _attemptedLUSDAmount, uint _actualLUSDAmount, uint _ETHSent, uint _ETHFee); event TroveUpdated(address indexed _borrower, uint _debt, uint _coll, uint _stake, TroveManagerOperation _operation); event TroveLiquidated(address indexed _borrower, uint _debt, uint _coll, TroveManagerOperation _operation); event BaseRateUpdated(uint _baseRate); event LastFeeOpTimeUpdated(uint _lastFeeOpTime); event TotalStakesUpdated(uint _newTotalStakes); event SystemSnapshotsUpdated(uint _totalStakesSnapshot, uint _totalCollateralSnapshot); event LTermsUpdated(uint _L_ETH, uint _L_LUSDDebt); event TroveSnapshotsUpdated(uint _L_ETH, uint _L_LUSDDebt); event TroveIndexUpdated(address _borrower, uint _newIndex); enum TroveManagerOperation { applyPendingRewards, liquidateInNormalMode, liquidateInRecoveryMode, redeemCollateral } // --- Dependency setter --- function setAddresses( address _borrowerOperationsAddress, address _activePoolAddress, address _defaultPoolAddress, address _stabilityPoolAddress, address _gasPoolAddress, address _collSurplusPoolAddress, address _priceFeedAddress, address _lusdTokenAddress, address _sortedTrovesAddress, address _lqtyTokenAddress, address _lqtyStakingAddress ) external override onlyOwner { checkContract(_borrowerOperationsAddress); checkContract(_activePoolAddress); checkContract(_defaultPoolAddress); checkContract(_stabilityPoolAddress); checkContract(_gasPoolAddress); checkContract(_collSurplusPoolAddress); checkContract(_priceFeedAddress); checkContract(_lusdTokenAddress); checkContract(_sortedTrovesAddress); checkContract(_lqtyTokenAddress); checkContract(_lqtyStakingAddress); borrowerOperationsAddress = _borrowerOperationsAddress; activePool = IActivePool(_activePoolAddress); defaultPool = IDefaultPool(_defaultPoolAddress); stabilityPool = IStabilityPool(_stabilityPoolAddress); gasPoolAddress = _gasPoolAddress; collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress); priceFeed = IPriceFeed(_priceFeedAddress); lusdToken = ILUSDToken(_lusdTokenAddress); sortedTroves = ISortedTroves(_sortedTrovesAddress); lqtyToken = ILQTYToken(_lqtyTokenAddress); lqtyStaking = ILQTYStaking(_lqtyStakingAddress); emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress); emit ActivePoolAddressChanged(_activePoolAddress); emit DefaultPoolAddressChanged(_defaultPoolAddress); emit StabilityPoolAddressChanged(_stabilityPoolAddress); emit GasPoolAddressChanged(_gasPoolAddress); emit CollSurplusPoolAddressChanged(_collSurplusPoolAddress); emit PriceFeedAddressChanged(_priceFeedAddress); emit LUSDTokenAddressChanged(_lusdTokenAddress); emit SortedTrovesAddressChanged(_sortedTrovesAddress); emit LQTYTokenAddressChanged(_lqtyTokenAddress); emit LQTYStakingAddressChanged(_lqtyStakingAddress); _renounceOwnership(); } // --- Getters --- function getTroveOwnersCount() external view override returns (uint) { return TroveOwners.length; } function getTroveFromTroveOwnersArray(uint _index) external view override returns (address) { return TroveOwners[_index]; } // --- Trove Liquidation functions --- // Single liquidation function. Closes the trove if its ICR is lower than the minimum collateral ratio. function liquidate(address _borrower) external override { _requireTroveIsActive(_borrower); address[] memory borrowers = new address[](1); borrowers[0] = _borrower; batchLiquidateTroves(borrowers); } // --- Inner single liquidation functions --- // Liquidate one trove, in Normal Mode. function _liquidateNormalMode( IActivePool _activePool, IDefaultPool _defaultPool, address _borrower, uint _LUSDInStabPool ) internal returns (LiquidationValues memory singleLiquidation) { LocalVariables_InnerSingleLiquidateFunction memory vars; (singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, vars.pendingDebtReward, vars.pendingCollReward) = getEntireDebtAndColl(_borrower); _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward); _removeStake(_borrower); singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl); singleLiquidation.LUSDGasCompensation = LUSD_GAS_COMPENSATION; uint collToLiquidate = singleLiquidation.entireTroveColl.sub(singleLiquidation.collGasCompensation); (singleLiquidation.debtToOffset, singleLiquidation.collToSendToSP, singleLiquidation.debtToRedistribute, singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, collToLiquidate, _LUSDInStabPool); _closeTrove(_borrower, Status.closedByLiquidation); emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInNormalMode); emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInNormalMode); return singleLiquidation; } // Liquidate one trove, in Recovery Mode. function _liquidateRecoveryMode( IActivePool _activePool, IDefaultPool _defaultPool, address _borrower, uint _ICR, uint _LUSDInStabPool, uint _TCR, uint _price ) internal returns (LiquidationValues memory singleLiquidation) { LocalVariables_InnerSingleLiquidateFunction memory vars; if (TroveOwners.length <= 1) {return singleLiquidation;} // don't liquidate if last trove (singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, vars.pendingDebtReward, vars.pendingCollReward) = getEntireDebtAndColl(_borrower); singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl); singleLiquidation.LUSDGasCompensation = LUSD_GAS_COMPENSATION; vars.collToLiquidate = singleLiquidation.entireTroveColl.sub(singleLiquidation.collGasCompensation); // If ICR <= 100%, purely redistribute the Trove across all active Troves if (_ICR <= _100pct) { _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward); _removeStake(_borrower); singleLiquidation.debtToOffset = 0; singleLiquidation.collToSendToSP = 0; singleLiquidation.debtToRedistribute = singleLiquidation.entireTroveDebt; singleLiquidation.collToRedistribute = vars.collToLiquidate; _closeTrove(_borrower, Status.closedByLiquidation); emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode); emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode); // If 100% < ICR < MCR, offset as much as possible, and redistribute the remainder } else if ((_ICR > _100pct) && (_ICR < MCR)) { _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward); _removeStake(_borrower); (singleLiquidation.debtToOffset, singleLiquidation.collToSendToSP, singleLiquidation.debtToRedistribute, singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, vars.collToLiquidate, _LUSDInStabPool); _closeTrove(_borrower, Status.closedByLiquidation); emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode); emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode); /* * If 110% <= ICR < current TCR (accounting for the preceding liquidations in the current sequence) * and there is LUSD in the Stability Pool, only offset, with no redistribution, * but at a capped rate of 1.1 and only if the whole debt can be liquidated. * The remainder due to the capped rate will be claimable as collateral surplus. */ } else if ((_ICR >= MCR) && (_ICR < _TCR) && (singleLiquidation.entireTroveDebt <= _LUSDInStabPool)) { _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward); assert(_LUSDInStabPool != 0); _removeStake(_borrower); singleLiquidation = _getCappedOffsetVals(singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, _price); _closeTrove(_borrower, Status.closedByLiquidation); if (singleLiquidation.collSurplus > 0) { collSurplusPool.accountSurplus(_borrower, singleLiquidation.collSurplus); } emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.collToSendToSP, TroveManagerOperation.liquidateInRecoveryMode); emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode); } else { // if (_ICR >= MCR && ( _ICR >= _TCR || singleLiquidation.entireTroveDebt > _LUSDInStabPool)) LiquidationValues memory zeroVals; return zeroVals; } return singleLiquidation; } /* In a full liquidation, returns the values for a trove's coll and debt to be offset, and coll and debt to be * redistributed to active troves. */ function _getOffsetAndRedistributionVals ( uint _debt, uint _coll, uint _LUSDInStabPool ) internal pure returns (uint debtToOffset, uint collToSendToSP, uint debtToRedistribute, uint collToRedistribute) { if (_LUSDInStabPool > 0) { /* * Offset as much debt & collateral as possible against the Stability Pool, and redistribute the remainder * between all active troves. * * If the trove's debt is larger than the deposited LUSD in the Stability Pool: * * - Offset an amount of the trove's debt equal to the LUSD in the Stability Pool * - Send a fraction of the trove's collateral to the Stability Pool, equal to the fraction of its offset debt * */ debtToOffset = LiquityMath._min(_debt, _LUSDInStabPool); collToSendToSP = _coll.mul(debtToOffset).div(_debt); debtToRedistribute = _debt.sub(debtToOffset); collToRedistribute = _coll.sub(collToSendToSP); } else { debtToOffset = 0; collToSendToSP = 0; debtToRedistribute = _debt; collToRedistribute = _coll; } } /* * Get its offset coll/debt and ETH gas comp, and close the trove. */ function _getCappedOffsetVals ( uint _entireTroveDebt, uint _entireTroveColl, uint _price ) internal pure returns (LiquidationValues memory singleLiquidation) { singleLiquidation.entireTroveDebt = _entireTroveDebt; singleLiquidation.entireTroveColl = _entireTroveColl; uint collToOffset = _entireTroveDebt.mul(MCR).div(_price); singleLiquidation.collGasCompensation = _getCollGasCompensation(collToOffset); singleLiquidation.LUSDGasCompensation = LUSD_GAS_COMPENSATION; singleLiquidation.debtToOffset = _entireTroveDebt; singleLiquidation.collToSendToSP = collToOffset.sub(singleLiquidation.collGasCompensation); singleLiquidation.collSurplus = _entireTroveColl.sub(collToOffset); singleLiquidation.debtToRedistribute = 0; singleLiquidation.collToRedistribute = 0; } /* * Liquidate a sequence of troves. Closes a maximum number of n under-collateralized Troves, * starting from the one with the lowest collateral ratio in the system, and moving upwards */ function liquidateTroves(uint _n) external override { ContractsCache memory contractsCache = ContractsCache( activePool, defaultPool, ILUSDToken(address(0)), ILQTYStaking(address(0)), sortedTroves, ICollSurplusPool(address(0)), address(0) ); IStabilityPool stabilityPoolCached = stabilityPool; LocalVariables_OuterLiquidationFunction memory vars; LiquidationTotals memory totals; vars.price = priceFeed.fetchPrice(); vars.LUSDInStabPool = stabilityPoolCached.getTotalLUSDDeposits(); vars.recoveryModeAtStart = _checkRecoveryMode(vars.price); // Perform the appropriate liquidation sequence - tally the values, and obtain their totals if (vars.recoveryModeAtStart) { totals = _getTotalsFromLiquidateTrovesSequence_RecoveryMode(contractsCache, vars.price, vars.LUSDInStabPool, _n); } else { // if !vars.recoveryModeAtStart totals = _getTotalsFromLiquidateTrovesSequence_NormalMode(contractsCache.activePool, contractsCache.defaultPool, vars.price, vars.LUSDInStabPool, _n); } require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate"); // Move liquidated ETH and LUSD to the appropriate pools stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP); _redistributeDebtAndColl(contractsCache.activePool, contractsCache.defaultPool, totals.totalDebtToRedistribute, totals.totalCollToRedistribute); if (totals.totalCollSurplus > 0) { contractsCache.activePool.sendETH(address(collSurplusPool), totals.totalCollSurplus); } // Update system snapshots _updateSystemSnapshots_excludeCollRemainder(contractsCache.activePool, totals.totalCollGasCompensation); vars.liquidatedDebt = totals.totalDebtInSequence; vars.liquidatedColl = totals.totalCollInSequence.sub(totals.totalCollGasCompensation).sub(totals.totalCollSurplus); emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalLUSDGasCompensation); // Send gas compensation to caller _sendGasCompensation(contractsCache.activePool, msg.sender, totals.totalLUSDGasCompensation, totals.totalCollGasCompensation); } /* * This function is used when the liquidateTroves sequence starts during Recovery Mode. However, it * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence */ function _getTotalsFromLiquidateTrovesSequence_RecoveryMode ( ContractsCache memory _contractsCache, uint _price, uint _LUSDInStabPool, uint _n ) internal returns(LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; vars.remainingLUSDInStabPool = _LUSDInStabPool; vars.backToNormalMode = false; vars.entireSystemDebt = getEntireSystemDebt(); vars.entireSystemColl = getEntireSystemColl(); vars.user = _contractsCache.sortedTroves.getLast(); address firstUser = _contractsCache.sortedTroves.getFirst(); for (vars.i = 0; vars.i < _n && vars.user != firstUser; vars.i++) { // we need to cache it, because current user is likely going to be deleted address nextUser = _contractsCache.sortedTroves.getPrev(vars.user); vars.ICR = getCurrentICR(vars.user, _price); if (!vars.backToNormalMode) { // Break the loop if ICR is greater than MCR and Stability Pool is empty if (vars.ICR >= MCR && vars.remainingLUSDInStabPool == 0) { break; } uint TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price); singleLiquidation = _liquidateRecoveryMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.ICR, vars.remainingLUSDInStabPool, TCR, _price); // Update aggregate trackers vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); vars.entireSystemDebt = vars.entireSystemDebt.sub(singleLiquidation.debtToOffset); vars.entireSystemColl = vars.entireSystemColl.sub(singleLiquidation.collToSendToSP).sub(singleLiquidation.collSurplus); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price); } else if (vars.backToNormalMode && vars.ICR < MCR) { singleLiquidation = _liquidateNormalMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.remainingLUSDInStabPool); vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); } else break; // break if the loop reaches a Trove with ICR >= MCR vars.user = nextUser; } } function _getTotalsFromLiquidateTrovesSequence_NormalMode ( IActivePool _activePool, IDefaultPool _defaultPool, uint _price, uint _LUSDInStabPool, uint _n ) internal returns(LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; ISortedTroves sortedTrovesCached = sortedTroves; vars.remainingLUSDInStabPool = _LUSDInStabPool; for (vars.i = 0; vars.i < _n; vars.i++) { vars.user = sortedTrovesCached.getLast(); vars.ICR = getCurrentICR(vars.user, _price); if (vars.ICR < MCR) { singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingLUSDInStabPool); vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); } else break; // break if the loop reaches a Trove with ICR >= MCR } } /* * Attempt to liquidate a custom list of troves provided by the caller. */ function batchLiquidateTroves(address[] memory _troveArray) public override { require(_troveArray.length != 0, "TroveManager: Calldata address array must not be empty"); IActivePool activePoolCached = activePool; IDefaultPool defaultPoolCached = defaultPool; IStabilityPool stabilityPoolCached = stabilityPool; LocalVariables_OuterLiquidationFunction memory vars; LiquidationTotals memory totals; vars.price = priceFeed.fetchPrice(); vars.LUSDInStabPool = stabilityPoolCached.getTotalLUSDDeposits(); vars.recoveryModeAtStart = _checkRecoveryMode(vars.price); // Perform the appropriate liquidation sequence - tally values and obtain their totals. if (vars.recoveryModeAtStart) { totals = _getTotalFromBatchLiquidate_RecoveryMode(activePoolCached, defaultPoolCached, vars.price, vars.LUSDInStabPool, _troveArray); } else { // if !vars.recoveryModeAtStart totals = _getTotalsFromBatchLiquidate_NormalMode(activePoolCached, defaultPoolCached, vars.price, vars.LUSDInStabPool, _troveArray); } require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate"); // Move liquidated ETH and LUSD to the appropriate pools stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP); _redistributeDebtAndColl(activePoolCached, defaultPoolCached, totals.totalDebtToRedistribute, totals.totalCollToRedistribute); if (totals.totalCollSurplus > 0) { activePoolCached.sendETH(address(collSurplusPool), totals.totalCollSurplus); } // Update system snapshots _updateSystemSnapshots_excludeCollRemainder(activePoolCached, totals.totalCollGasCompensation); vars.liquidatedDebt = totals.totalDebtInSequence; vars.liquidatedColl = totals.totalCollInSequence.sub(totals.totalCollGasCompensation).sub(totals.totalCollSurplus); emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalLUSDGasCompensation); // Send gas compensation to caller _sendGasCompensation(activePoolCached, msg.sender, totals.totalLUSDGasCompensation, totals.totalCollGasCompensation); } /* * This function is used when the batch liquidation sequence starts during Recovery Mode. However, it * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence */ function _getTotalFromBatchLiquidate_RecoveryMode ( IActivePool _activePool, IDefaultPool _defaultPool, uint _price, uint _LUSDInStabPool, address[] memory _troveArray ) internal returns(LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; vars.remainingLUSDInStabPool = _LUSDInStabPool; vars.backToNormalMode = false; vars.entireSystemDebt = getEntireSystemDebt(); vars.entireSystemColl = getEntireSystemColl(); for (vars.i = 0; vars.i < _troveArray.length; vars.i++) { vars.user = _troveArray[vars.i]; // Skip non-active troves if (Troves[vars.user].status != Status.active) { continue; } vars.ICR = getCurrentICR(vars.user, _price); if (!vars.backToNormalMode) { // Skip this trove if ICR is greater than MCR and Stability Pool is empty if (vars.ICR >= MCR && vars.remainingLUSDInStabPool == 0) { continue; } uint TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price); singleLiquidation = _liquidateRecoveryMode(_activePool, _defaultPool, vars.user, vars.ICR, vars.remainingLUSDInStabPool, TCR, _price); // Update aggregate trackers vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); vars.entireSystemDebt = vars.entireSystemDebt.sub(singleLiquidation.debtToOffset); vars.entireSystemColl = vars.entireSystemColl.sub(singleLiquidation.collToSendToSP); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price); } else if (vars.backToNormalMode && vars.ICR < MCR) { singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingLUSDInStabPool); vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); } else continue; // In Normal Mode skip troves with ICR >= MCR } } function _getTotalsFromBatchLiquidate_NormalMode ( IActivePool _activePool, IDefaultPool _defaultPool, uint _price, uint _LUSDInStabPool, address[] memory _troveArray ) internal returns(LiquidationTotals memory totals) { LocalVariables_LiquidationSequence memory vars; LiquidationValues memory singleLiquidation; vars.remainingLUSDInStabPool = _LUSDInStabPool; for (vars.i = 0; vars.i < _troveArray.length; vars.i++) { vars.user = _troveArray[vars.i]; vars.ICR = getCurrentICR(vars.user, _price); if (vars.ICR < MCR) { singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingLUSDInStabPool); vars.remainingLUSDInStabPool = vars.remainingLUSDInStabPool.sub(singleLiquidation.debtToOffset); // Add liquidation values to their respective running totals totals = _addLiquidationValuesToTotals(totals, singleLiquidation); } } } // --- Liquidation helper functions --- function _addLiquidationValuesToTotals(LiquidationTotals memory oldTotals, LiquidationValues memory singleLiquidation) internal pure returns(LiquidationTotals memory newTotals) { // Tally all the values with their respective running totals newTotals.totalCollGasCompensation = oldTotals.totalCollGasCompensation.add(singleLiquidation.collGasCompensation); newTotals.totalLUSDGasCompensation = oldTotals.totalLUSDGasCompensation.add(singleLiquidation.LUSDGasCompensation); newTotals.totalDebtInSequence = oldTotals.totalDebtInSequence.add(singleLiquidation.entireTroveDebt); newTotals.totalCollInSequence = oldTotals.totalCollInSequence.add(singleLiquidation.entireTroveColl); newTotals.totalDebtToOffset = oldTotals.totalDebtToOffset.add(singleLiquidation.debtToOffset); newTotals.totalCollToSendToSP = oldTotals.totalCollToSendToSP.add(singleLiquidation.collToSendToSP); newTotals.totalDebtToRedistribute = oldTotals.totalDebtToRedistribute.add(singleLiquidation.debtToRedistribute); newTotals.totalCollToRedistribute = oldTotals.totalCollToRedistribute.add(singleLiquidation.collToRedistribute); newTotals.totalCollSurplus = oldTotals.totalCollSurplus.add(singleLiquidation.collSurplus); return newTotals; } function _sendGasCompensation(IActivePool _activePool, address _liquidator, uint _LUSD, uint _ETH) internal { if (_LUSD > 0) { lusdToken.returnFromPool(gasPoolAddress, _liquidator, _LUSD); } if (_ETH > 0) { _activePool.sendETH(_liquidator, _ETH); } } // Move a Trove's pending debt and collateral rewards from distributions, from the Default Pool to the Active Pool function _movePendingTroveRewardsToActivePool(IActivePool _activePool, IDefaultPool _defaultPool, uint _LUSD, uint _ETH) internal { _defaultPool.decreaseLUSDDebt(_LUSD); _activePool.increaseLUSDDebt(_LUSD); _defaultPool.sendETHToActivePool(_ETH); } // --- Redemption functions --- // Redeem as much collateral as possible from _borrower's Trove in exchange for LUSD up to _maxLUSDamount function _redeemCollateralFromTrove( ContractsCache memory _contractsCache, address _borrower, uint _maxLUSDamount, uint _price, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR ) internal returns (SingleRedemptionValues memory singleRedemption) { // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Trove minus the liquidation reserve singleRedemption.LUSDLot = LiquityMath._min(_maxLUSDamount, Troves[_borrower].debt.sub(LUSD_GAS_COMPENSATION)); // Get the ETHLot of equivalent value in USD singleRedemption.ETHLot = singleRedemption.LUSDLot.mul(DECIMAL_PRECISION).div(_price); // Decrease the debt and collateral of the current Trove according to the LUSD lot and corresponding ETH to send uint newDebt = (Troves[_borrower].debt).sub(singleRedemption.LUSDLot); uint newColl = (Troves[_borrower].coll).sub(singleRedemption.ETHLot); if (newDebt == LUSD_GAS_COMPENSATION) { // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed _removeStake(_borrower); _closeTrove(_borrower, Status.closedByRedemption); _redeemCloseTrove(_contractsCache, _borrower, LUSD_GAS_COMPENSATION, newColl); emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.redeemCollateral); } else { uint newNICR = LiquityMath._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. */ if (newNICR != _partialRedemptionHintNICR || _getNetDebt(newDebt) < MIN_NET_DEBT) { singleRedemption.cancelledPartial = true; return singleRedemption; } _contractsCache.sortedTroves.reInsert(_borrower, newNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint); Troves[_borrower].debt = newDebt; Troves[_borrower].coll = newColl; _updateStakeAndTotalStakes(_borrower); emit TroveUpdated( _borrower, newDebt, newColl, Troves[_borrower].stake, TroveManagerOperation.redeemCollateral ); } return singleRedemption; } /* * Called when a full redemption occurs, and closes the trove. * The redeemer swaps (debt - liquidation reserve) LUSD for (debt - liquidation reserve) worth of ETH, so the LUSD liquidation reserve left corresponds to the remaining debt. * In order to close the trove, the LUSD liquidation reserve is burned, and the corresponding debt is removed from the active pool. * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove. * Any surplus ETH left in the trove, is sent to the Coll surplus pool, and can be later claimed by the borrower. */ function _redeemCloseTrove(ContractsCache memory _contractsCache, address _borrower, uint _LUSD, uint _ETH) internal { _contractsCache.lusdToken.burn(gasPoolAddress, _LUSD); // Update Active Pool LUSD, and send ETH to account _contractsCache.activePool.decreaseLUSDDebt(_LUSD); // send ETH from Active Pool to CollSurplus Pool _contractsCache.collSurplusPool.accountSurplus(_borrower, _ETH); _contractsCache.activePool.sendETH(address(_contractsCache.collSurplusPool), _ETH); } function _isValidFirstRedemptionHint(ISortedTroves _sortedTroves, address _firstRedemptionHint, uint _price) internal view returns (bool) { if (_firstRedemptionHint == address(0) || !_sortedTroves.contains(_firstRedemptionHint) || getCurrentICR(_firstRedemptionHint, _price) < MCR ) { return false; } address nextTrove = _sortedTroves.getNext(_firstRedemptionHint); return nextTrove == address(0) || getCurrentICR(nextTrove, _price) < MCR; } /* Send _LUSDamount LUSD to the system and redeem the corresponding amount of collateral from as many Troves as are needed to fill the redemption * request. Applies pending rewards to a Trove before reducing its debt and coll. * * Note that if _amount is very large, this function can run out of gas, specially if traversed troves 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 Troves 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 trove 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 Troves 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 Trove 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 Trove will be after redemption, and pass a hint for its position * in the sortedTroves 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 Trove 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 Trove and the sender will keep the remaining LUSD amount, which they can attempt * to redeem later. */ function redeemCollateral( uint _LUSDamount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFeePercentage ) external override { ContractsCache memory contractsCache = ContractsCache( activePool, defaultPool, lusdToken, lqtyStaking, sortedTroves, collSurplusPool, gasPoolAddress ); RedemptionTotals memory totals; _requireValidMaxFeePercentage(_maxFeePercentage); _requireAfterBootstrapPeriod(); totals.price = priceFeed.fetchPrice(); _requireTCRoverMCR(totals.price); _requireAmountGreaterThanZero(_LUSDamount); _requireLUSDBalanceCoversRedemption(contractsCache.lusdToken, msg.sender, _LUSDamount); totals.totalLUSDSupplyAtStart = getEntireSystemDebt(); // Confirm redeemer's balance is less than total LUSD supply assert(contractsCache.lusdToken.balanceOf(msg.sender) <= totals.totalLUSDSupplyAtStart); totals.remainingLUSD = _LUSDamount; address currentBorrower; if (_isValidFirstRedemptionHint(contractsCache.sortedTroves, _firstRedemptionHint, totals.price)) { currentBorrower = _firstRedemptionHint; } else { currentBorrower = contractsCache.sortedTroves.getLast(); // Find the first trove with ICR >= MCR while (currentBorrower != address(0) && getCurrentICR(currentBorrower, totals.price) < MCR) { currentBorrower = contractsCache.sortedTroves.getPrev(currentBorrower); } } // Loop through the Troves starting from the one with lowest collateral ratio until _amount of LUSD is exchanged for collateral if (_maxIterations == 0) { _maxIterations = uint(-1); } while (currentBorrower != address(0) && totals.remainingLUSD > 0 && _maxIterations > 0) { _maxIterations--; // Save the address of the Trove preceding the current one, before potentially modifying the list address nextUserToCheck = contractsCache.sortedTroves.getPrev(currentBorrower); _applyPendingRewards(contractsCache.activePool, contractsCache.defaultPool, currentBorrower); SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove( contractsCache, currentBorrower, totals.remainingLUSD, 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 Trove totals.totalLUSDToRedeem = totals.totalLUSDToRedeem.add(singleRedemption.LUSDLot); totals.totalETHDrawn = totals.totalETHDrawn.add(singleRedemption.ETHLot); totals.remainingLUSD = totals.remainingLUSD.sub(singleRedemption.LUSDLot); currentBorrower = nextUserToCheck; } require(totals.totalETHDrawn > 0, "TroveManager: 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 LUSD supply value, from before it was reduced by the redemption. _updateBaseRateFromRedemption(totals.totalETHDrawn, totals.price, totals.totalLUSDSupplyAtStart); // Calculate the ETH fee totals.ETHFee = _getRedemptionFee(totals.totalETHDrawn); _requireUserAcceptsFee(totals.ETHFee, totals.totalETHDrawn, _maxFeePercentage); // Send the ETH fee to the LQTY staking contract contractsCache.activePool.sendETH(address(contractsCache.lqtyStaking), totals.ETHFee); contractsCache.lqtyStaking.increaseF_ETH(totals.ETHFee); totals.ETHToSendToRedeemer = totals.totalETHDrawn.sub(totals.ETHFee); emit Redemption(_LUSDamount, totals.totalLUSDToRedeem, totals.totalETHDrawn, totals.ETHFee); // Burn the total LUSD that is cancelled with debt, and send the redeemed ETH to msg.sender contractsCache.lusdToken.burn(msg.sender, totals.totalLUSDToRedeem); // Update Active Pool LUSD, and send ETH to account contractsCache.activePool.decreaseLUSDDebt(totals.totalLUSDToRedeem); contractsCache.activePool.sendETH(msg.sender, totals.ETHToSendToRedeemer); } // --- Helper functions --- // Return the nominal collateral ratio (ICR) of a given Trove, without the price. Takes a trove's pending coll and debt rewards from redistributions into account. function getNominalICR(address _borrower) public view override returns (uint) { (uint currentETH, uint currentLUSDDebt) = _getCurrentTroveAmounts(_borrower); uint NICR = LiquityMath._computeNominalCR(currentETH, currentLUSDDebt); return NICR; } // Return the current collateral ratio (ICR) of a given Trove. Takes a trove's pending coll and debt rewards from redistributions into account. function getCurrentICR(address _borrower, uint _price) public view override returns (uint) { (uint currentETH, uint currentLUSDDebt) = _getCurrentTroveAmounts(_borrower); uint ICR = LiquityMath._computeCR(currentETH, currentLUSDDebt, _price); return ICR; } function _getCurrentTroveAmounts(address _borrower) internal view returns (uint, uint) { uint pendingETHReward = getPendingETHReward(_borrower); uint pendingLUSDDebtReward = getPendingLUSDDebtReward(_borrower); uint currentETH = Troves[_borrower].coll.add(pendingETHReward); uint currentLUSDDebt = Troves[_borrower].debt.add(pendingLUSDDebtReward); return (currentETH, currentLUSDDebt); } function applyPendingRewards(address _borrower) external override { _requireCallerIsBorrowerOperations(); return _applyPendingRewards(activePool, defaultPool, _borrower); } // Add the borrowers's coll and debt rewards earned from redistributions, to their Trove function _applyPendingRewards(IActivePool _activePool, IDefaultPool _defaultPool, address _borrower) internal { if (hasPendingRewards(_borrower)) { _requireTroveIsActive(_borrower); // Compute pending rewards uint pendingETHReward = getPendingETHReward(_borrower); uint pendingLUSDDebtReward = getPendingLUSDDebtReward(_borrower); // Apply pending rewards to trove's state Troves[_borrower].coll = Troves[_borrower].coll.add(pendingETHReward); Troves[_borrower].debt = Troves[_borrower].debt.add(pendingLUSDDebtReward); _updateTroveRewardSnapshots(_borrower); // Transfer from DefaultPool to ActivePool _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, pendingLUSDDebtReward, pendingETHReward); emit TroveUpdated( _borrower, Troves[_borrower].debt, Troves[_borrower].coll, Troves[_borrower].stake, TroveManagerOperation.applyPendingRewards ); } } // Update borrower's snapshots of L_ETH and L_LUSDDebt to reflect the current values function updateTroveRewardSnapshots(address _borrower) external override { _requireCallerIsBorrowerOperations(); return _updateTroveRewardSnapshots(_borrower); } function _updateTroveRewardSnapshots(address _borrower) internal { rewardSnapshots[_borrower].ETH = L_ETH; rewardSnapshots[_borrower].LUSDDebt = L_LUSDDebt; emit TroveSnapshotsUpdated(L_ETH, L_LUSDDebt); } // Get the borrower's pending accumulated ETH reward, earned by their stake function getPendingETHReward(address _borrower) public view override returns (uint) { uint snapshotETH = rewardSnapshots[_borrower].ETH; uint rewardPerUnitStaked = L_ETH.sub(snapshotETH); if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; } uint stake = Troves[_borrower].stake; uint pendingETHReward = stake.mul(rewardPerUnitStaked).div(DECIMAL_PRECISION); return pendingETHReward; } // Get the borrower's pending accumulated LUSD reward, earned by their stake function getPendingLUSDDebtReward(address _borrower) public view override returns (uint) { uint snapshotLUSDDebt = rewardSnapshots[_borrower].LUSDDebt; uint rewardPerUnitStaked = L_LUSDDebt.sub(snapshotLUSDDebt); if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; } uint stake = Troves[_borrower].stake; uint pendingLUSDDebtReward = stake.mul(rewardPerUnitStaked).div(DECIMAL_PRECISION); return pendingLUSDDebtReward; } function hasPendingRewards(address _borrower) public view override returns (bool) { /* * A Trove 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 (Troves[_borrower].status != Status.active) {return false;} return (rewardSnapshots[_borrower].ETH < L_ETH); } // Return the Troves entire debt and coll, including pending rewards from redistributions. function getEntireDebtAndColl( address _borrower ) public view override returns (uint debt, uint coll, uint pendingLUSDDebtReward, uint pendingETHReward) { debt = Troves[_borrower].debt; coll = Troves[_borrower].coll; pendingLUSDDebtReward = getPendingLUSDDebtReward(_borrower); pendingETHReward = getPendingETHReward(_borrower); debt = debt.add(pendingLUSDDebtReward); coll = coll.add(pendingETHReward); } function removeStake(address _borrower) external override { _requireCallerIsBorrowerOperations(); return _removeStake(_borrower); } // Remove borrower's stake from the totalStakes sum, and set their stake to 0 function _removeStake(address _borrower) internal { uint stake = Troves[_borrower].stake; totalStakes = totalStakes.sub(stake); Troves[_borrower].stake = 0; } function updateStakeAndTotalStakes(address _borrower) external override returns (uint) { _requireCallerIsBorrowerOperations(); return _updateStakeAndTotalStakes(_borrower); } // Update borrower's stake based on their latest collateral value function _updateStakeAndTotalStakes(address _borrower) internal returns (uint) { uint newStake = _computeNewStake(Troves[_borrower].coll); uint oldStake = Troves[_borrower].stake; Troves[_borrower].stake = newStake; totalStakes = totalStakes.sub(oldStake).add(newStake); emit TotalStakesUpdated(totalStakes); return newStake; } // Calculate a new stake based on the snapshots of the totalStakes and totalCollateral taken at the last liquidation function _computeNewStake(uint _coll) internal view returns (uint) { uint stake; if (totalCollateralSnapshot == 0) { stake = _coll; } else { /* * The following assert() holds true because: * - The system always contains >= 1 trove * - When we close or liquidate a trove, we redistribute the pending rewards, so if all troves were closed/liquidated, * rewards would’ve been emptied and totalCollateralSnapshot would be zero too. */ assert(totalStakesSnapshot > 0); stake = _coll.mul(totalStakesSnapshot).div(totalCollateralSnapshot); } return stake; } function _redistributeDebtAndColl(IActivePool _activePool, IDefaultPool _defaultPool, uint _debt, uint _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_ETH and L_LUSDDebt: * * 1) Form numerators which compensate for the floor division errors that occurred the last time this * function was called. * 2) Calculate "per-unit-staked" ratios. * 3) Multiply each ratio back by its denominator, to reveal the current floor division error. * 4) Store these errors for use in the next correction when this function is called. * 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended. */ uint ETHNumerator = _coll.mul(DECIMAL_PRECISION).add(lastETHError_Redistribution); uint LUSDDebtNumerator = _debt.mul(DECIMAL_PRECISION).add(lastLUSDDebtError_Redistribution); // Get the per-unit-staked terms uint ETHRewardPerUnitStaked = ETHNumerator.div(totalStakes); uint LUSDDebtRewardPerUnitStaked = LUSDDebtNumerator.div(totalStakes); lastETHError_Redistribution = ETHNumerator.sub(ETHRewardPerUnitStaked.mul(totalStakes)); lastLUSDDebtError_Redistribution = LUSDDebtNumerator.sub(LUSDDebtRewardPerUnitStaked.mul(totalStakes)); // Add per-unit-staked terms to the running totals L_ETH = L_ETH.add(ETHRewardPerUnitStaked); L_LUSDDebt = L_LUSDDebt.add(LUSDDebtRewardPerUnitStaked); emit LTermsUpdated(L_ETH, L_LUSDDebt); // Transfer coll and debt from ActivePool to DefaultPool _activePool.decreaseLUSDDebt(_debt); _defaultPool.increaseLUSDDebt(_debt); _activePool.sendETH(address(_defaultPool), _coll); } function closeTrove(address _borrower) external override { _requireCallerIsBorrowerOperations(); return _closeTrove(_borrower, Status.closedByOwner); } function _closeTrove(address _borrower, Status closedStatus) internal { assert(closedStatus != Status.nonExistent && closedStatus != Status.active); uint TroveOwnersArrayLength = TroveOwners.length; _requireMoreThanOneTroveInSystem(TroveOwnersArrayLength); Troves[_borrower].status = closedStatus; Troves[_borrower].coll = 0; Troves[_borrower].debt = 0; rewardSnapshots[_borrower].ETH = 0; rewardSnapshots[_borrower].LUSDDebt = 0; _removeTroveOwner(_borrower, TroveOwnersArrayLength); sortedTroves.remove(_borrower); } /* * Updates snapshots of system total stakes and total collateral, excluding a given collateral remainder from the calculation. * Used in a liquidation sequence. * * The calculation excludes a portion of collateral that is in the ActivePool: * * the total ETH gas compensation from the liquidation sequence * * The ETH as compensation must be excluded as it is always sent out at the very end of the liquidation sequence. */ function _updateSystemSnapshots_excludeCollRemainder(IActivePool _activePool, uint _collRemainder) internal { totalStakesSnapshot = totalStakes; uint activeColl = _activePool.getETH(); uint liquidatedColl = defaultPool.getETH(); totalCollateralSnapshot = activeColl.sub(_collRemainder).add(liquidatedColl); emit SystemSnapshotsUpdated(totalStakesSnapshot, totalCollateralSnapshot); } // Push the owner's address to the Trove owners list, and record the corresponding array index on the Trove struct function addTroveOwnerToArray(address _borrower) external override returns (uint index) { _requireCallerIsBorrowerOperations(); return _addTroveOwnerToArray(_borrower); } function _addTroveOwnerToArray(address _borrower) internal returns (uint128 index) { /* Max array size is 2**128 - 1, i.e. ~3e30 troves. No risk of overflow, since troves have minimum LUSD debt of liquidation reserve plus MIN_NET_DEBT. 3e30 LUSD dwarfs the value of all wealth in the world ( which is < 1e15 USD). */ // Push the Troveowner to the array TroveOwners.push(_borrower); // Record the index of the new Troveowner on their Trove struct index = uint128(TroveOwners.length.sub(1)); Troves[_borrower].arrayIndex = index; return index; } /* * Remove a Trove owner from the TroveOwners array, not preserving array order. Removing owner 'B' does the following: * [A B C D E] => [A E C D], and updates E's Trove struct to point to its new array index. */ function _removeTroveOwner(address _borrower, uint TroveOwnersArrayLength) internal { Status troveStatus = Troves[_borrower].status; // It’s set in caller function `_closeTrove` assert(troveStatus != Status.nonExistent && troveStatus != Status.active); uint128 index = Troves[_borrower].arrayIndex; uint length = TroveOwnersArrayLength; uint idxLast = length.sub(1); assert(index <= idxLast); address addressToMove = TroveOwners[idxLast]; TroveOwners[index] = addressToMove; Troves[addressToMove].arrayIndex = index; emit TroveIndexUpdated(addressToMove, index); TroveOwners.pop(); } // --- Recovery Mode and TCR functions --- function getTCR(uint _price) external view override returns (uint) { return _getTCR(_price); } function checkRecoveryMode(uint _price) external view override returns (bool) { return _checkRecoveryMode(_price); } // Check whether or not the system *would be* in Recovery Mode, given an ETH:USD price, and the entire system coll and debt. function _checkPotentialRecoveryMode( uint _entireSystemColl, uint _entireSystemDebt, uint _price ) internal pure returns (bool) { uint TCR = LiquityMath._computeCR(_entireSystemColl, _entireSystemDebt, _price); return TCR < CCR; } // --- Redemption fee functions --- /* * This function has two impacts on the baseRate state variable: * 1) decays the baseRate based on time passed since last redemption or LUSD borrowing operation. * then, * 2) increases the baseRate based on the amount redeemed, as a proportion of total supply */ function _updateBaseRateFromRedemption(uint _ETHDrawn, uint _price, uint _totalLUSDSupply) internal returns (uint) { uint decayedBaseRate = _calcDecayedBaseRate(); /* Convert the drawn ETH back to LUSD at face value rate (1 LUSD:1 USD), in order to get * the fraction of total supply that was redeemed at face value. */ uint redeemedLUSDFraction = _ETHDrawn.mul(_price).div(_totalLUSDSupply); uint newBaseRate = decayedBaseRate.add(redeemedLUSDFraction.div(BETA)); newBaseRate = LiquityMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100% //assert(newBaseRate <= DECIMAL_PRECISION); // This is already enforced in the line above assert(newBaseRate > 0); // Base rate is always non-zero after redemption // Update the baseRate state variable baseRate = newBaseRate; emit BaseRateUpdated(newBaseRate); _updateLastFeeOpTime(); return newBaseRate; } function getRedemptionRate() public view override returns (uint) { return _calcRedemptionRate(baseRate); } function getRedemptionRateWithDecay() public view override returns (uint) { return _calcRedemptionRate(_calcDecayedBaseRate()); } function _calcRedemptionRate(uint _baseRate) internal pure returns (uint) { return LiquityMath._min( REDEMPTION_FEE_FLOOR.add(_baseRate), DECIMAL_PRECISION // cap at a maximum of 100% ); } function _getRedemptionFee(uint _ETHDrawn) internal view returns (uint) { return _calcRedemptionFee(getRedemptionRate(), _ETHDrawn); } function getRedemptionFeeWithDecay(uint _ETHDrawn) external view override returns (uint) { return _calcRedemptionFee(getRedemptionRateWithDecay(), _ETHDrawn); } function _calcRedemptionFee(uint _redemptionRate, uint _ETHDrawn) internal pure returns (uint) { uint redemptionFee = _redemptionRate.mul(_ETHDrawn).div(DECIMAL_PRECISION); require(redemptionFee < _ETHDrawn, "TroveManager: Fee would eat up all returned collateral"); return redemptionFee; } // --- Borrowing fee functions --- function getBorrowingRate() public view override returns (uint) { return _calcBorrowingRate(baseRate); } function getBorrowingRateWithDecay() public view override returns (uint) { return _calcBorrowingRate(_calcDecayedBaseRate()); } function _calcBorrowingRate(uint _baseRate) internal pure returns (uint) { return LiquityMath._min( BORROWING_FEE_FLOOR.add(_baseRate), MAX_BORROWING_FEE ); } function getBorrowingFee(uint _LUSDDebt) external view override returns (uint) { return _calcBorrowingFee(getBorrowingRate(), _LUSDDebt); } function getBorrowingFeeWithDecay(uint _LUSDDebt) external view override returns (uint) { return _calcBorrowingFee(getBorrowingRateWithDecay(), _LUSDDebt); } function _calcBorrowingFee(uint _borrowingRate, uint _LUSDDebt) internal pure returns (uint) { return _borrowingRate.mul(_LUSDDebt).div(DECIMAL_PRECISION); } // Updates the baseRate state variable based on time elapsed since the last redemption or LUSD borrowing operation. function decayBaseRateFromBorrowing() external override { _requireCallerIsBorrowerOperations(); uint decayedBaseRate = _calcDecayedBaseRate(); assert(decayedBaseRate <= DECIMAL_PRECISION); // The baseRate can decay to 0 baseRate = decayedBaseRate; emit BaseRateUpdated(decayedBaseRate); _updateLastFeeOpTime(); } // --- Internal fee functions --- // Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing. function _updateLastFeeOpTime() internal { uint timePassed = block.timestamp.sub(lastFeeOperationTime); if (timePassed >= SECONDS_IN_ONE_MINUTE) { lastFeeOperationTime = block.timestamp; emit LastFeeOpTimeUpdated(block.timestamp); } } function _calcDecayedBaseRate() internal view returns (uint) { uint minutesPassed = _minutesPassedSinceLastFeeOp(); uint decayFactor = LiquityMath._decPow(MINUTE_DECAY_FACTOR, minutesPassed); return baseRate.mul(decayFactor).div(DECIMAL_PRECISION); } function _minutesPassedSinceLastFeeOp() internal view returns (uint) { return (block.timestamp.sub(lastFeeOperationTime)).div(SECONDS_IN_ONE_MINUTE); } // --- 'require' wrapper functions --- function _requireCallerIsBorrowerOperations() internal view { require(msg.sender == borrowerOperationsAddress, "TroveManager: Caller is not the BorrowerOperations contract"); } function _requireTroveIsActive(address _borrower) internal view { require(Troves[_borrower].status == Status.active, "TroveManager: Trove does not exist or is closed"); } function _requireLUSDBalanceCoversRedemption(ILUSDToken _lusdToken, address _redeemer, uint _amount) internal view { require(_lusdToken.balanceOf(_redeemer) >= _amount, "TroveManager: Requested redemption amount must be <= user's LUSD token balance"); } function _requireMoreThanOneTroveInSystem(uint TroveOwnersArrayLength) internal view { require (TroveOwnersArrayLength > 1 && sortedTroves.getSize() > 1, "TroveManager: Only one trove in the system"); } function _requireAmountGreaterThanZero(uint _amount) internal pure { require(_amount > 0, "TroveManager: Amount must be greater than zero"); } function _requireTCRoverMCR(uint _price) internal view { require(_getTCR(_price) >= MCR, "TroveManager: Cannot redeem when TCR < MCR"); } function _requireAfterBootstrapPeriod() internal view { uint systemDeploymentTime = lqtyToken.getDeploymentStartTime(); require(block.timestamp >= systemDeploymentTime.add(BOOTSTRAP_PERIOD), "TroveManager: Redemptions are not allowed during bootstrap phase"); } function _requireValidMaxFeePercentage(uint _maxFeePercentage) internal pure { require(_maxFeePercentage >= REDEMPTION_FEE_FLOOR && _maxFeePercentage <= DECIMAL_PRECISION, "Max fee percentage must be between 0.5% and 100%"); } // --- Trove property getters --- function getTroveStatus(address _borrower) external view override returns (uint) { return uint(Troves[_borrower].status); } function getTroveStake(address _borrower) external view override returns (uint) { return Troves[_borrower].stake; } function getTroveDebt(address _borrower) external view override returns (uint) { return Troves[_borrower].debt; } function getTroveColl(address _borrower) external view override returns (uint) { return Troves[_borrower].coll; } // --- Trove property setters, called by BorrowerOperations --- function setTroveStatus(address _borrower, uint _num) external override { _requireCallerIsBorrowerOperations(); Troves[_borrower].status = Status(_num); } function increaseTroveColl(address _borrower, uint _collIncrease) external override returns (uint) { _requireCallerIsBorrowerOperations(); uint newColl = Troves[_borrower].coll.add(_collIncrease); Troves[_borrower].coll = newColl; return newColl; } function decreaseTroveColl(address _borrower, uint _collDecrease) external override returns (uint) { _requireCallerIsBorrowerOperations(); uint newColl = Troves[_borrower].coll.sub(_collDecrease); Troves[_borrower].coll = newColl; return newColl; } function increaseTroveDebt(address _borrower, uint _debtIncrease) external override returns (uint) { _requireCallerIsBorrowerOperations(); uint newDebt = Troves[_borrower].debt.add(_debtIncrease); Troves[_borrower].debt = newDebt; return newDebt; } function decreaseTroveDebt(address _borrower, uint _debtDecrease) external override returns (uint) { _requireCallerIsBorrowerOperations(); uint newDebt = Troves[_borrower].debt.sub(_debtDecrease); Troves[_borrower].debt = newDebt; return newDebt; } } // File contracts/Interfaces/IBorrowerOperations.sol pragma solidity 0.6.11; // Common interface for the Trove Manager. interface IBorrowerOperations { // --- Events --- event TroveManagerAddressChanged(address _newTroveManagerAddress); event ActivePoolAddressChanged(address _activePoolAddress); event DefaultPoolAddressChanged(address _defaultPoolAddress); event StabilityPoolAddressChanged(address _stabilityPoolAddress); event GasPoolAddressChanged(address _gasPoolAddress); event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress); event PriceFeedAddressChanged(address _newPriceFeedAddress); event SortedTrovesAddressChanged(address _sortedTrovesAddress); event LUSDTokenAddressChanged(address _lusdTokenAddress); event LQTYStakingAddressChanged(address _lqtyStakingAddress); event TroveCreated(address indexed _borrower, uint arrayIndex); event TroveUpdated(address indexed _borrower, uint _debt, uint _coll, uint stake, uint8 operation); event LUSDBorrowingFeePaid(address indexed _borrower, uint _LUSDFee); // --- Functions --- function setAddresses( address _troveManagerAddress, address _activePoolAddress, address _defaultPoolAddress, address _stabilityPoolAddress, address _gasPoolAddress, address _collSurplusPoolAddress, address _priceFeedAddress, address _sortedTrovesAddress, address _lusdTokenAddress, address _lqtyStakingAddress ) external; function openTrove(uint _maxFee, uint _LUSDAmount, address _upperHint, address _lowerHint) external payable; function addColl(address _upperHint, address _lowerHint) external payable; function moveETHGainToTrove(address _user, address _upperHint, address _lowerHint) external payable; function withdrawColl(uint _amount, address _upperHint, address _lowerHint) external; function withdrawLUSD(uint _maxFee, uint _amount, address _upperHint, address _lowerHint) external; function repayLUSD(uint _amount, address _upperHint, address _lowerHint) external; function closeTrove() external; function adjustTrove(uint _maxFee, uint _collWithdrawal, uint _debtChange, bool isDebtIncrease, address _upperHint, address _lowerHint) external payable; function claimCollateral() external; function getCompositeDebt(uint _debt) external pure returns (uint); } // File contracts/SortedTroves.sol pragma solidity 0.6.11; /* * A sorted doubly linked list with nodes sorted in descending order. * * Nodes map to active Troves in the system - the ID property is the address of a Trove owner. * Nodes are ordered according to their current nominal individual collateral ratio (NICR), * which is like the ICR but without the price, i.e., just collateral / debt. * * The list optionally accepts insert position hints. * * NICRs are computed dynamically at runtime, and not stored on the Node. This is because NICRs of active Troves * change dynamically as liquidation events occur. * * The list relies on the fact that liquidation events preserve ordering: a liquidation decreases the NICRs of all active Troves, * but maintains their order. A node inserted based on current NICR will maintain the correct position, * relative to it's peers, as rewards accumulate, as long as it's raw collateral and debt have not changed. * Thus, Nodes remain sorted by current NICR. * * Nodes need only be re-inserted upon a Trove operation - when the owner adds or removes collateral or debt * to their position. * * The list is a modification of the following audited SortedDoublyLinkedList: * https://github.com/livepeer/protocol/blob/master/contracts/libraries/SortedDoublyLL.sol * * * Changes made in the Liquity implementation: * * - Keys have been removed from nodes * * - Ordering checks for insertion are performed by comparing an NICR argument to the current NICR, calculated at runtime. * The list relies on the property that ordering by ICR is maintained as the ETH:USD price varies. * * - Public functions with parameters have been made internal to save gas, and given an external wrapper function for external access */ contract SortedTroves is Ownable, CheckContract, ISortedTroves { using SafeMath for uint256; string constant public NAME = "SortedTroves"; event TroveManagerAddressChanged(address _troveManagerAddress); event BorrowerOperationsAddressChanged(address _borrowerOperationsAddress); event NodeAdded(address _id, uint _NICR); event NodeRemoved(address _id); address public borrowerOperationsAddress; ITroveManager public troveManager; // Information for a node in the list struct Node { bool exists; address nextId; // Id of next node (smaller NICR) in the list address prevId; // Id of previous node (larger NICR) in the list } // Information for the list struct Data { address head; // Head of the list. Also the node in the list with the largest NICR address tail; // Tail of the list. Also the node in the list with the smallest NICR uint256 maxSize; // Maximum size of the list uint256 size; // Current size of the list mapping (address => Node) nodes; // Track the corresponding ids for each node in the list } Data public data; // --- Dependency setters --- function setParams(uint256 _size, address _troveManagerAddress, address _borrowerOperationsAddress) external override onlyOwner { require(_size > 0, "SortedTroves: Size can’t be zero"); checkContract(_troveManagerAddress); checkContract(_borrowerOperationsAddress); data.maxSize = _size; troveManager = ITroveManager(_troveManagerAddress); borrowerOperationsAddress = _borrowerOperationsAddress; emit TroveManagerAddressChanged(_troveManagerAddress); emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress); _renounceOwnership(); } /* * @dev Add a node to the list * @param _id Node's id * @param _NICR Node's NICR * @param _prevId Id of previous node for the insert position * @param _nextId Id of next node for the insert position */ function insert (address _id, uint256 _NICR, address _prevId, address _nextId) external override { ITroveManager troveManagerCached = troveManager; _requireCallerIsBOorTroveM(troveManagerCached); _insert(troveManagerCached, _id, _NICR, _prevId, _nextId); } function _insert(ITroveManager _troveManager, address _id, uint256 _NICR, address _prevId, address _nextId) internal { // List must not be full require(!isFull(), "SortedTroves: List is full"); // List must not already contain node require(!contains(_id), "SortedTroves: List already contains the node"); // Node id must not be null require(_id != address(0), "SortedTroves: Id cannot be zero"); // NICR must be non-zero require(_NICR > 0, "SortedTroves: NICR must be positive"); address prevId = _prevId; address nextId = _nextId; if (!_validInsertPosition(_troveManager, _NICR, prevId, nextId)) { // Sender's hint was not a valid insert position // Use sender's hint to find a valid insert position (prevId, nextId) = _findInsertPosition(_troveManager, _NICR, prevId, nextId); } data.nodes[_id].exists = true; if (prevId == address(0) && nextId == address(0)) { // Insert as head and tail data.head = _id; data.tail = _id; } else if (prevId == address(0)) { // Insert before `prevId` as the head data.nodes[_id].nextId = data.head; data.nodes[data.head].prevId = _id; data.head = _id; } else if (nextId == address(0)) { // Insert after `nextId` as the tail data.nodes[_id].prevId = data.tail; data.nodes[data.tail].nextId = _id; data.tail = _id; } else { // Insert at insert position between `prevId` and `nextId` data.nodes[_id].nextId = nextId; data.nodes[_id].prevId = prevId; data.nodes[prevId].nextId = _id; data.nodes[nextId].prevId = _id; } data.size = data.size.add(1); emit NodeAdded(_id, _NICR); } function remove(address _id) external override { _requireCallerIsTroveManager(); _remove(_id); } /* * @dev Remove a node from the list * @param _id Node's id */ function _remove(address _id) internal { // List must contain the node require(contains(_id), "SortedTroves: List does not contain the id"); if (data.size > 1) { // List contains more than a single node if (_id == data.head) { // The removed node is the head // Set head to next node data.head = data.nodes[_id].nextId; // Set prev pointer of new head to null data.nodes[data.head].prevId = address(0); } else if (_id == data.tail) { // The removed node is the tail // Set tail to previous node data.tail = data.nodes[_id].prevId; // Set next pointer of new tail to null data.nodes[data.tail].nextId = address(0); } else { // The removed node is neither the head nor the tail // Set next pointer of previous node to the next node data.nodes[data.nodes[_id].prevId].nextId = data.nodes[_id].nextId; // Set prev pointer of next node to the previous node data.nodes[data.nodes[_id].nextId].prevId = data.nodes[_id].prevId; } } else { // List contains a single node // Set the head and tail to null data.head = address(0); data.tail = address(0); } delete data.nodes[_id]; data.size = data.size.sub(1); NodeRemoved(_id); } /* * @dev Re-insert the node at a new position, based on its new NICR * @param _id Node's id * @param _newNICR Node's new NICR * @param _prevId Id of previous node for the new insert position * @param _nextId Id of next node for the new insert position */ function reInsert(address _id, uint256 _newNICR, address _prevId, address _nextId) external override { ITroveManager troveManagerCached = troveManager; _requireCallerIsBOorTroveM(troveManagerCached); // List must contain the node require(contains(_id), "SortedTroves: List does not contain the id"); // NICR must be non-zero require(_newNICR > 0, "SortedTroves: NICR must be positive"); // Remove node from the list _remove(_id); _insert(troveManagerCached, _id, _newNICR, _prevId, _nextId); } /* * @dev Checks if the list contains a node */ function contains(address _id) public view override returns (bool) { return data.nodes[_id].exists; } /* * @dev Checks if the list is full */ function isFull() public view override returns (bool) { return data.size == data.maxSize; } /* * @dev Checks if the list is empty */ function isEmpty() public view override returns (bool) { return data.size == 0; } /* * @dev Returns the current size of the list */ function getSize() external view override returns (uint256) { return data.size; } /* * @dev Returns the maximum size of the list */ function getMaxSize() external view override returns (uint256) { return data.maxSize; } /* * @dev Returns the first node in the list (node with the largest NICR) */ function getFirst() external view override returns (address) { return data.head; } /* * @dev Returns the last node in the list (node with the smallest NICR) */ function getLast() external view override returns (address) { return data.tail; } /* * @dev Returns the next node (with a smaller NICR) in the list for a given node * @param _id Node's id */ function getNext(address _id) external view override returns (address) { return data.nodes[_id].nextId; } /* * @dev Returns the previous node (with a larger NICR) in the list for a given node * @param _id Node's id */ function getPrev(address _id) external view override returns (address) { return data.nodes[_id].prevId; } /* * @dev Check if a pair of nodes is a valid insertion point for a new node with the given NICR * @param _NICR Node's NICR * @param _prevId Id of previous node for the insert position * @param _nextId Id of next node for the insert position */ function validInsertPosition(uint256 _NICR, address _prevId, address _nextId) external view override returns (bool) { return _validInsertPosition(troveManager, _NICR, _prevId, _nextId); } function _validInsertPosition(ITroveManager _troveManager, uint256 _NICR, address _prevId, address _nextId) internal view returns (bool) { if (_prevId == address(0) && _nextId == address(0)) { // `(null, null)` is a valid insert position if the list is empty return isEmpty(); } else if (_prevId == address(0)) { // `(null, _nextId)` is a valid insert position if `_nextId` is the head of the list return data.head == _nextId && _NICR >= _troveManager.getNominalICR(_nextId); } else if (_nextId == address(0)) { // `(_prevId, null)` is a valid insert position if `_prevId` is the tail of the list return data.tail == _prevId && _NICR <= _troveManager.getNominalICR(_prevId); } else { // `(_prevId, _nextId)` is a valid insert position if they are adjacent nodes and `_NICR` falls between the two nodes' NICRs return data.nodes[_prevId].nextId == _nextId && _troveManager.getNominalICR(_prevId) >= _NICR && _NICR >= _troveManager.getNominalICR(_nextId); } } /* * @dev Descend the list (larger NICRs to smaller NICRs) to find a valid insert position * @param _troveManager TroveManager contract, passed in as param to save SLOAD’s * @param _NICR Node's NICR * @param _startId Id of node to start descending the list from */ function _descendList(ITroveManager _troveManager, uint256 _NICR, address _startId) internal view returns (address, address) { // If `_startId` is the head, check if the insert position is before the head if (data.head == _startId && _NICR >= _troveManager.getNominalICR(_startId)) { return (address(0), _startId); } address prevId = _startId; address nextId = data.nodes[prevId].nextId; // Descend the list until we reach the end or until we find a valid insert position while (prevId != address(0) && !_validInsertPosition(_troveManager, _NICR, prevId, nextId)) { prevId = data.nodes[prevId].nextId; nextId = data.nodes[prevId].nextId; } return (prevId, nextId); } /* * @dev Ascend the list (smaller NICRs to larger NICRs) to find a valid insert position * @param _troveManager TroveManager contract, passed in as param to save SLOAD’s * @param _NICR Node's NICR * @param _startId Id of node to start ascending the list from */ function _ascendList(ITroveManager _troveManager, uint256 _NICR, address _startId) internal view returns (address, address) { // If `_startId` is the tail, check if the insert position is after the tail if (data.tail == _startId && _NICR <= _troveManager.getNominalICR(_startId)) { return (_startId, address(0)); } address nextId = _startId; address prevId = data.nodes[nextId].prevId; // Ascend the list until we reach the end or until we find a valid insertion point while (nextId != address(0) && !_validInsertPosition(_troveManager, _NICR, prevId, nextId)) { nextId = data.nodes[nextId].prevId; prevId = data.nodes[nextId].prevId; } return (prevId, nextId); } /* * @dev Find the insert position for a new node with the given NICR * @param _NICR Node's NICR * @param _prevId Id of previous node for the insert position * @param _nextId Id of next node for the insert position */ function findInsertPosition(uint256 _NICR, address _prevId, address _nextId) external view override returns (address, address) { return _findInsertPosition(troveManager, _NICR, _prevId, _nextId); } function _findInsertPosition(ITroveManager _troveManager, uint256 _NICR, address _prevId, address _nextId) internal view returns (address, address) { address prevId = _prevId; address nextId = _nextId; if (prevId != address(0)) { if (!contains(prevId) || _NICR > _troveManager.getNominalICR(prevId)) { // `prevId` does not exist anymore or now has a smaller NICR than the given NICR prevId = address(0); } } if (nextId != address(0)) { if (!contains(nextId) || _NICR < _troveManager.getNominalICR(nextId)) { // `nextId` does not exist anymore or now has a larger NICR than the given NICR nextId = address(0); } } if (prevId == address(0) && nextId == address(0)) { // No hint - descend list starting from head return _descendList(_troveManager, _NICR, data.head); } else if (prevId == address(0)) { // No `prevId` for hint - ascend list starting from `nextId` return _ascendList(_troveManager, _NICR, nextId); } else if (nextId == address(0)) { // No `nextId` for hint - descend list starting from `prevId` return _descendList(_troveManager, _NICR, prevId); } else { // Descend list starting from `prevId` return _descendList(_troveManager, _NICR, prevId); } } // --- 'require' functions --- function _requireCallerIsTroveManager() internal view { require(msg.sender == address(troveManager), "SortedTroves: Caller is not the TroveManager"); } function _requireCallerIsBOorTroveM(ITroveManager _troveManager) internal view { require(msg.sender == borrowerOperationsAddress || msg.sender == address(_troveManager), "SortedTroves: Caller is neither BO nor TroveM"); } } // File contracts/MultiTroveGetter.sol pragma solidity 0.6.11; pragma experimental ABIEncoderV2; /* Helper contract for grabbing Trove data for the front end. Not part of the core Liquity system. */ contract MultiTroveGetter { struct CombinedTroveData { address owner; uint debt; uint coll; uint stake; uint snapshotETH; uint snapshotLUSDDebt; } TroveManager public troveManager; // XXX Troves missing from ITroveManager? ISortedTroves public sortedTroves; constructor(TroveManager _troveManager, ISortedTroves _sortedTroves) public { troveManager = _troveManager; sortedTroves = _sortedTroves; } function getMultipleSortedTroves(int _startIdx, uint _count) external view returns (CombinedTroveData[] memory _troves) { uint startIdx; bool descend; if (_startIdx >= 0) { startIdx = uint(_startIdx); descend = true; } else { startIdx = uint(-(_startIdx + 1)); descend = false; } uint sortedTrovesSize = sortedTroves.getSize(); if (startIdx >= sortedTrovesSize) { _troves = new CombinedTroveData[](0); } else { uint maxCount = sortedTrovesSize - startIdx; if (_count > maxCount) { _count = maxCount; } if (descend) { _troves = _getMultipleSortedTrovesFromHead(startIdx, _count); } else { _troves = _getMultipleSortedTrovesFromTail(startIdx, _count); } } } function _getMultipleSortedTrovesFromHead(uint _startIdx, uint _count) internal view returns (CombinedTroveData[] memory _troves) { address currentTroveowner = sortedTroves.getFirst(); for (uint idx = 0; idx < _startIdx; ++idx) { currentTroveowner = sortedTroves.getNext(currentTroveowner); } _troves = new CombinedTroveData[](_count); for (uint idx = 0; idx < _count; ++idx) { _troves[idx].owner = currentTroveowner; ( _troves[idx].debt, _troves[idx].coll, _troves[idx].stake, /* status */, /* arrayIndex */ ) = troveManager.Troves(currentTroveowner); ( _troves[idx].snapshotETH, _troves[idx].snapshotLUSDDebt ) = troveManager.rewardSnapshots(currentTroveowner); currentTroveowner = sortedTroves.getNext(currentTroveowner); } } function _getMultipleSortedTrovesFromTail(uint _startIdx, uint _count) internal view returns (CombinedTroveData[] memory _troves) { address currentTroveowner = sortedTroves.getLast(); for (uint idx = 0; idx < _startIdx; ++idx) { currentTroveowner = sortedTroves.getPrev(currentTroveowner); } _troves = new CombinedTroveData[](_count); for (uint idx = 0; idx < _count; ++idx) { _troves[idx].owner = currentTroveowner; ( _troves[idx].debt, _troves[idx].coll, _troves[idx].stake, /* status */, /* arrayIndex */ ) = troveManager.Troves(currentTroveowner); ( _troves[idx].snapshotETH, _troves[idx].snapshotLUSDDebt ) = troveManager.rewardSnapshots(currentTroveowner); currentTroveowner = sortedTroves.getPrev(currentTroveowner); } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract TroveManager","name":"_troveManager","type":"address"},{"internalType":"contract ISortedTroves","name":"_sortedTroves","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"int256","name":"_startIdx","type":"int256"},{"internalType":"uint256","name":"_count","type":"uint256"}],"name":"getMultipleSortedTroves","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"snapshotETH","type":"uint256"},{"internalType":"uint256","name":"snapshotLUSDDebt","type":"uint256"}],"internalType":"struct MultiTroveGetter.CombinedTroveData[]","name":"_troves","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sortedTroves","outputs":[{"internalType":"contract ISortedTroves","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"troveManager","outputs":[{"internalType":"contract TroveManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50604051610beb380380610beb83398101604081905261002f91610060565b600080546001600160a01b039384166001600160a01b031991821617909155600180549290931691161790556100b1565b60008060408385031215610072578182fd5b825161007d81610099565b602084015190925061008e81610099565b809150509250929050565b6001600160a01b03811681146100ae57600080fd5b50565b610b2b806100c06000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80633d83908a14610046578063ae91875414610064578063b90bce451461006c575b600080fd5b61004e61008c565b60405161005b9190610a60565b60405180910390f35b61004e61009b565b61007f61007a3660046109a0565b6100aa565b60405161005b9190610a74565b6000546001600160a01b031681565b6001546001600160a01b031681565b6060600080600085126100c2575083905060016100d0565b846001016000039150600090505b6001546040805163de8fa43160e01b815290516000926001600160a01b03169163de8fa431916004808301926020929190829003018186803b15801561011557600080fd5b505afa158015610129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014d91906109c1565b905080831061018f576040805160008082526020820190925290610187565b610174610933565b81526020019060019003908161016c5790505b5093506101c4565b8281038086111561019e578095505b82156101b5576101ae84876101cd565b94506101c2565b6101bf8487610584565b94505b505b50505092915050565b60606000600160009054906101000a90046001600160a01b03166001600160a01b0316631e2231436040518163ffffffff1660e01b815260040160206040518083038186803b15801561021f57600080fd5b505afa158015610233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102579190610972565b905060005b848110156102ee5760015460405163765e015960e01b81526001600160a01b039091169063765e015990610294908590600401610a60565b60206040518083038186803b1580156102ac57600080fd5b505afa1580156102c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102e49190610972565b915060010161025c565b508267ffffffffffffffff8111801561030657600080fd5b5060405190808252806020026020018201604052801561034057816020015b61032d610933565b8152602001906001900390816103255790505b50915060005b8381101561057c578183828151811061035b57fe5b60209081029190910101516001600160a01b039182169052600054604051630ddec86760e31b8152911690636ef643389061039a908590600401610a60565b60a06040518083038186803b1580156103b257600080fd5b505afa1580156103c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ea91906109fc565b9050508584815181106103f957fe5b602002602001015160200186858151811061041057fe5b602002602001015160400187868151811061042757fe5b60209081029190910101516060019290925291905252600054604051630b39e3cd60e11b81526001600160a01b0390911690631673c79a9061046d908590600401610a60565b604080518083038186803b15801561048457600080fd5b505afa158015610498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104bc91906109d9565b8483815181106104c857fe5b60200260200101516080018584815181106104df57fe5b602090810291909101015160a001919091525260015460405163765e015960e01b81526001600160a01b039091169063765e015990610522908590600401610a60565b60206040518083038186803b15801561053a57600080fd5b505afa15801561054e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105729190610972565b9150600101610346565b505092915050565b60606000600160009054906101000a90046001600160a01b03166001600160a01b0316634d6228316040518163ffffffff1660e01b815260040160206040518083038186803b1580156105d657600080fd5b505afa1580156105ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060e9190610972565b905060005b848110156106a557600154604051632dc9c0eb60e21b81526001600160a01b039091169063b72703ac9061064b908590600401610a60565b60206040518083038186803b15801561066357600080fd5b505afa158015610677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069b9190610972565b9150600101610613565b508267ffffffffffffffff811180156106bd57600080fd5b506040519080825280602002602001820160405280156106f757816020015b6106e4610933565b8152602001906001900390816106dc5790505b50915060005b8381101561057c578183828151811061071257fe5b60209081029190910101516001600160a01b039182169052600054604051630ddec86760e31b8152911690636ef6433890610751908590600401610a60565b60a06040518083038186803b15801561076957600080fd5b505afa15801561077d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a191906109fc565b9050508584815181106107b057fe5b60200260200101516020018685815181106107c757fe5b60200260200101516040018786815181106107de57fe5b60209081029190910101516060019290925291905252600054604051630b39e3cd60e11b81526001600160a01b0390911690631673c79a90610824908590600401610a60565b604080518083038186803b15801561083b57600080fd5b505afa15801561084f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087391906109d9565b84838151811061087f57fe5b602002602001015160800185848151811061089657fe5b602090810291909101015160a0019190915252600154604051632dc9c0eb60e21b81526001600160a01b039091169063b72703ac906108d9908590600401610a60565b60206040518083038186803b1580156108f157600080fd5b505afa158015610905573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109299190610972565b91506001016106fd565b6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060208284031215610983578081fd5b81516001600160a01b0381168114610999578182fd5b9392505050565b600080604083850312156109b2578081fd5b50508035926020909101359150565b6000602082840312156109d2578081fd5b5051919050565b600080604083850312156109eb578182fd5b505080516020909101519092909150565b600080600080600060a08688031215610a13578081fd5b855194506020860151935060408601519250606086015160058110610a36578182fd5b60808701519092506001600160801b0381168114610a52578182fd5b809150509295509295909350565b6001600160a01b0391909116815260200190565b602080825282518282018190526000919060409081850190868401855b82811015610ae857815180516001600160a01b0316855286810151878601528581015186860152606080820151908601526080808201519086015260a0908101519085015260c09093019290850190600101610a91565b509197965050505050505056fea2646970667358221220f7a87fd7b211695e21a65b3577c3e55e1725ae3f66d9aed8cafbf4c865fffefa64736f6c634300060b0033000000000000000000000000a39739ef8b0231dbfa0dcda07d7e29faabcf4bb20000000000000000000000008fdd3fbfeb32b28fb73555518f8b361bcea741a6
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100415760003560e01c80633d83908a14610046578063ae91875414610064578063b90bce451461006c575b600080fd5b61004e61008c565b60405161005b9190610a60565b60405180910390f35b61004e61009b565b61007f61007a3660046109a0565b6100aa565b60405161005b9190610a74565b6000546001600160a01b031681565b6001546001600160a01b031681565b6060600080600085126100c2575083905060016100d0565b846001016000039150600090505b6001546040805163de8fa43160e01b815290516000926001600160a01b03169163de8fa431916004808301926020929190829003018186803b15801561011557600080fd5b505afa158015610129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014d91906109c1565b905080831061018f576040805160008082526020820190925290610187565b610174610933565b81526020019060019003908161016c5790505b5093506101c4565b8281038086111561019e578095505b82156101b5576101ae84876101cd565b94506101c2565b6101bf8487610584565b94505b505b50505092915050565b60606000600160009054906101000a90046001600160a01b03166001600160a01b0316631e2231436040518163ffffffff1660e01b815260040160206040518083038186803b15801561021f57600080fd5b505afa158015610233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102579190610972565b905060005b848110156102ee5760015460405163765e015960e01b81526001600160a01b039091169063765e015990610294908590600401610a60565b60206040518083038186803b1580156102ac57600080fd5b505afa1580156102c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102e49190610972565b915060010161025c565b508267ffffffffffffffff8111801561030657600080fd5b5060405190808252806020026020018201604052801561034057816020015b61032d610933565b8152602001906001900390816103255790505b50915060005b8381101561057c578183828151811061035b57fe5b60209081029190910101516001600160a01b039182169052600054604051630ddec86760e31b8152911690636ef643389061039a908590600401610a60565b60a06040518083038186803b1580156103b257600080fd5b505afa1580156103c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ea91906109fc565b9050508584815181106103f957fe5b602002602001015160200186858151811061041057fe5b602002602001015160400187868151811061042757fe5b60209081029190910101516060019290925291905252600054604051630b39e3cd60e11b81526001600160a01b0390911690631673c79a9061046d908590600401610a60565b604080518083038186803b15801561048457600080fd5b505afa158015610498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104bc91906109d9565b8483815181106104c857fe5b60200260200101516080018584815181106104df57fe5b602090810291909101015160a001919091525260015460405163765e015960e01b81526001600160a01b039091169063765e015990610522908590600401610a60565b60206040518083038186803b15801561053a57600080fd5b505afa15801561054e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105729190610972565b9150600101610346565b505092915050565b60606000600160009054906101000a90046001600160a01b03166001600160a01b0316634d6228316040518163ffffffff1660e01b815260040160206040518083038186803b1580156105d657600080fd5b505afa1580156105ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060e9190610972565b905060005b848110156106a557600154604051632dc9c0eb60e21b81526001600160a01b039091169063b72703ac9061064b908590600401610a60565b60206040518083038186803b15801561066357600080fd5b505afa158015610677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069b9190610972565b9150600101610613565b508267ffffffffffffffff811180156106bd57600080fd5b506040519080825280602002602001820160405280156106f757816020015b6106e4610933565b8152602001906001900390816106dc5790505b50915060005b8381101561057c578183828151811061071257fe5b60209081029190910101516001600160a01b039182169052600054604051630ddec86760e31b8152911690636ef6433890610751908590600401610a60565b60a06040518083038186803b15801561076957600080fd5b505afa15801561077d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a191906109fc565b9050508584815181106107b057fe5b60200260200101516020018685815181106107c757fe5b60200260200101516040018786815181106107de57fe5b60209081029190910101516060019290925291905252600054604051630b39e3cd60e11b81526001600160a01b0390911690631673c79a90610824908590600401610a60565b604080518083038186803b15801561083b57600080fd5b505afa15801561084f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087391906109d9565b84838151811061087f57fe5b602002602001015160800185848151811061089657fe5b602090810291909101015160a0019190915252600154604051632dc9c0eb60e21b81526001600160a01b039091169063b72703ac906108d9908590600401610a60565b60206040518083038186803b1580156108f157600080fd5b505afa158015610905573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109299190610972565b91506001016106fd565b6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060208284031215610983578081fd5b81516001600160a01b0381168114610999578182fd5b9392505050565b600080604083850312156109b2578081fd5b50508035926020909101359150565b6000602082840312156109d2578081fd5b5051919050565b600080604083850312156109eb578182fd5b505080516020909101519092909150565b600080600080600060a08688031215610a13578081fd5b855194506020860151935060408601519250606086015160058110610a36578182fd5b60808701519092506001600160801b0381168114610a52578182fd5b809150509295509295909350565b6001600160a01b0391909116815260200190565b602080825282518282018190526000919060409081850190868401855b82811015610ae857815180516001600160a01b0316855286810151878601528581015186860152606080820151908601526080808201519086015260a0908101519085015260c09093019290850190600101610a91565b509197965050505050505056fea2646970667358221220f7a87fd7b211695e21a65b3577c3e55e1725ae3f66d9aed8cafbf4c865fffefa64736f6c634300060b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a39739ef8b0231dbfa0dcda07d7e29faabcf4bb20000000000000000000000008fdd3fbfeb32b28fb73555518f8b361bcea741a6
-----Decoded View---------------
Arg [0] : _troveManager (address): 0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2
Arg [1] : _sortedTroves (address): 0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000a39739ef8b0231dbfa0dcda07d7e29faabcf4bb2
Arg [1] : 0000000000000000000000008fdd3fbfeb32b28fb73555518f8b361bcea741a6
Deployed Bytecode Sourcemap
212451:3530:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;212673:32;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;212754:33;;;:::i;212966:959::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;212673:32::-;;;-1:-1:-1;;;;;212673:32:0;;:::o;212754:33::-;;;-1:-1:-1;;;;;212754:33:0;;:::o;212966:959::-;213059:34;213111:13;213135:12;213177:1;213164:9;:14;213160:198;;-1:-1:-1;213211:9:0;;-1:-1:-1;213246:4:0;213160:198;;;213301:9;213313:1;213301:13;213299:16;;213283:33;;213341:5;213331:15;;213160:198;213394:12;;:22;;;-1:-1:-1;;;213394:22:0;;;;213370:21;;-1:-1:-1;;;;;213394:12:0;;:20;;:22;;;;;;;;;;;;;;:12;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;213370:46;;213445:16;213433:8;:28;213429:489;;213488:26;;;213512:1;213488:26;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;213478:36;;213429:489;;;213563:27;;;213611:17;;;213607:75;;;213658:8;213649:17;;213607:75;213702:7;213698:209;;;213740:50;213773:8;213783:6;213740:32;:50::i;:::-;213730:60;;213698:209;;;213841:50;213874:8;213884:6;213841:32;:50::i;:::-;213831:60;;213698:209;213429:489;;212966:959;;;;;;;:::o;213933:1019::-;214036:34;214088:25;214116:12;;;;;;;;;-1:-1:-1;;;;;214116:12:0;-1:-1:-1;;;;;214116:21:0;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;214088:51;-1:-1:-1;214157:8:0;214152:129;214177:9;214171:3;:15;214152:129;;;214230:12;;:39;;-1:-1:-1;;;214230:39:0;;-1:-1:-1;;;;;214230:12:0;;;;:20;;:39;;214251:17;;214230:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;214210:59;-1:-1:-1;214188:5:0;;214152:129;;;;214327:6;214303:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;214293:41:0;-1:-1:-1;214352:8:0;214347:598;214372:6;214366:3;:12;214347:598;;;214423:17;214402:7;214410:3;214402:12;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;214402:38:0;;;;;:18;214648:12;:38;;-1:-1:-1;;;214648:38:0;;:12;;;:19;;:38;;214668:17;;214648:38;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;214455:231;;;214474:7;214482:3;214474:12;;;;;;;;;;;;;;:17;;214510:7;214518:3;214510:12;;;;;;;;;;;;;;:17;;214546:7;214554:3;214546:12;;;;;;;;;;;;;;;;;;:18;;214455:231;;;;;;;;214810:12;;:47;;-1:-1:-1;;;214810:47:0;;-1:-1:-1;;;;;214810:12:0;;;;:28;;:47;;214839:17;;214810:47;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;214720:7;214728:3;214720:12;;;;;;;;;;;;;;:24;;214763:7;214771:3;214763:12;;;;;;;;;;;;;;;;;;:29;;214701:156;;;;;214894:12;;:39;;-1:-1:-1;;;214894:39:0;;-1:-1:-1;;;;;214894:12:0;;;;:20;;:39;;214915:17;;214894:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;214874:59;-1:-1:-1;214380:5:0;;214347:598;;;;213933:1019;;;;;:::o;214960:1018::-;215063:34;215115:25;215143:12;;;;;;;;;-1:-1:-1;;;;;215143:12:0;-1:-1:-1;;;;;215143:20:0;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;215115:50;-1:-1:-1;215183:8:0;215178:129;215203:9;215197:3;:15;215178:129;;;215256:12;;:39;;-1:-1:-1;;;215256:39:0;;-1:-1:-1;;;;;215256:12:0;;;;:20;;:39;;215277:17;;215256:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;215236:59;-1:-1:-1;215214:5:0;;215178:129;;;;215353:6;215329:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;215319:41:0;-1:-1:-1;215378:8:0;215373:598;215398:6;215392:3;:12;215373:598;;;215449:17;215428:7;215436:3;215428:12;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;215428:38:0;;;;;:18;215674:12;:38;;-1:-1:-1;;;215674:38:0;;:12;;;:19;;:38;;215694:17;;215674:38;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;215481:231;;;215500:7;215508:3;215500:12;;;;;;;;;;;;;;:17;;215536:7;215544:3;215536:12;;;;;;;;;;;;;;:17;;215572:7;215580:3;215572:12;;;;;;;;;;;;;;;;;;:18;;215481:231;;;;;;;;215836:12;;:47;;-1:-1:-1;;;215836:47:0;;-1:-1:-1;;;;;215836:12:0;;;;:28;;:47;;215865:17;;215836:47;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;215746:7;215754:3;215746:12;;;;;;;;;;;;;;:24;;215789:7;215797:3;215789:12;;;;;;;;;;;;;;;;;;:29;;215727:156;;;;;215920:12;;:39;;-1:-1:-1;;;215920:39:0;;-1:-1:-1;;;;;215920:12:0;;;;:20;;:39;;215941:17;;215920:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;215900:59;-1:-1:-1;215406:5:0;;215373:598;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;865:263::-;;980:2;968:9;959:7;955:23;951:32;948:2;;;-1:-1;;986:12;948:2;83:13;;-1:-1;;;;;8754:54;;9576:35;;9566:2;;-1:-1;;9615:12;9566:2;1038:74;942:186;-1:-1;;;942:186::o;1135:364::-;;;1255:2;1243:9;1234:7;1230:23;1226:32;1223:2;;;-1:-1;;1261:12;1223:2;-1:-1;;377:20;;;1412:2;1451:22;;;654:20;;-1:-1;1217:282::o;1506:263::-;;1621:2;1609:9;1600:7;1596:23;1592:32;1589:2;;;-1:-1;;1627:12;1589:2;-1:-1;802:13;;1583:186;-1:-1;1583:186::o;1776:399::-;;;1908:2;1896:9;1887:7;1883:23;1879:32;1876:2;;;-1:-1;;1914:12;1876:2;-1:-1;;802:13;;2077:2;2127:22;;;802:13;;;;;-1:-1;1870:305::o;2182:833::-;;;;;;2377:3;2365:9;2356:7;2352:23;2348:33;2345:2;;;-1:-1;;2384:12;2345:2;808:6;802:13;2436:74;;2547:2;2601:9;2597:22;802:13;2555:74;;2666:2;2720:9;2716:22;802:13;2674:74;;2785:2;2851:9;2847:22;236:13;9722:1;9715:5;9712:12;9702:2;;-1:-1;;9728:12;9702:2;2916:3;2967:22;;524:13;2793:86;;-1:-1;;;;;;8634:46;;9935:35;;9925:2;;-1:-1;;9974:12;9925:2;2925:74;;;;2339:676;;;;;;;;:::o;6352:222::-;-1:-1;;;;;8754:54;;;;3409:37;;6479:2;6464:18;;6450:124::o;6581:514::-;6830:2;6844:47;;;7981:12;;6815:18;;;8328:19;;;6581:514;;6830:2;8368:14;;;;;;7799;;;6581:514;4265:368;4290:6;4287:1;4284:13;4265:368;;;4351:13;;5332:23;;-1:-1;;;;;8754:54;3409:37;;5492:16;;;5486:23;5563:14;;;6303:37;5646:16;;;5640:23;5717:14;;;6303:37;5812:4;5801:16;;;5795:23;5872:14;;;6303:37;5973:4;5962:16;;;5956:23;6033:14;;;6303:37;8765:42;6128:16;;;6122:23;6199:14;;;6303:37;3329:4;3320:14;;;;8147;;;;8765:42;4305:9;4265:368;;;-1:-1;6897:188;;6801:294;-1:-1;;;;;;;6801:294::o
Swarm Source
ipfs://f7a87fd7b211695e21a65b3577c3e55e1725ae3f66d9aed8cafbf4c865fffefa
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.