编译器
0.8.24+commit.e11b9ed9
文件 1 的 46:AddRemoveManagers.sol
pragma solidity 0.8.24;
import "../Interfaces/IAddRemoveManagers.sol";
import "../Interfaces/IAddressesRegistry.sol";
import "../Interfaces/ITroveNFT.sol";
contract AddRemoveManagers is IAddRemoveManagers {
ITroveNFT internal immutable troveNFT;
struct RemoveManagerReceiver {
address manager;
address receiver;
}
mapping(uint256 => address) public addManagerOf;
mapping(uint256 => RemoveManagerReceiver) public removeManagerReceiverOf;
error EmptyManager();
error NotBorrower();
error NotOwnerNorAddManager();
error NotOwnerNorRemoveManager();
event TroveNFTAddressChanged(address _newTroveNFTAddress);
event AddManagerUpdated(uint256 indexed _troveId, address _newAddManager);
event RemoveManagerAndReceiverUpdated(uint256 indexed _troveId, address _newRemoveManager, address _newReceiver);
constructor(IAddressesRegistry _addressesRegistry) {
troveNFT = _addressesRegistry.troveNFT();
emit TroveNFTAddressChanged(address(troveNFT));
}
function setAddManager(uint256 _troveId, address _manager) external {
_requireCallerIsBorrower(_troveId);
_setAddManager(_troveId, _manager);
}
function _setAddManager(uint256 _troveId, address _manager) internal {
addManagerOf[_troveId] = _manager;
emit AddManagerUpdated(_troveId, _manager);
}
function setRemoveManager(uint256 _troveId, address _manager) external {
setRemoveManagerWithReceiver(_troveId, _manager, troveNFT.ownerOf(_troveId));
}
function setRemoveManagerWithReceiver(uint256 _troveId, address _manager, address _receiver) public {
_requireCallerIsBorrower(_troveId);
_setRemoveManagerAndReceiver(_troveId, _manager, _receiver);
}
function _setRemoveManagerAndReceiver(uint256 _troveId, address _manager, address _receiver) internal {
_requireNonZeroManagerUnlessWiping(_manager, _receiver);
removeManagerReceiverOf[_troveId].manager = _manager;
removeManagerReceiverOf[_troveId].receiver = _receiver;
emit RemoveManagerAndReceiverUpdated(_troveId, _manager, _receiver);
}
function _wipeAddRemoveManagers(uint256 _troveId) internal {
delete addManagerOf[_troveId];
delete removeManagerReceiverOf[_troveId];
emit AddManagerUpdated(_troveId, address(0));
emit RemoveManagerAndReceiverUpdated(_troveId, address(0), address(0));
}
function _requireNonZeroManagerUnlessWiping(address _manager, address _receiver) internal pure {
if (_manager == address(0) && _receiver != address(0)) {
revert EmptyManager();
}
}
function _requireCallerIsBorrower(uint256 _troveId) internal view {
if (msg.sender != troveNFT.ownerOf(_troveId)) {
revert NotBorrower();
}
}
function _requireSenderIsOwnerOrAddManager(uint256 _troveId, address _owner) internal view {
address addManager = addManagerOf[_troveId];
if (msg.sender != _owner && addManager != address(0) && msg.sender != addManager) {
revert NotOwnerNorAddManager();
}
}
function _requireSenderIsOwnerOrRemoveManagerAndGetReceiver(uint256 _troveId, address _owner)
internal
view
returns (address)
{
address manager = removeManagerReceiverOf[_troveId].manager;
address receiver = removeManagerReceiverOf[_troveId].receiver;
if (msg.sender != _owner && msg.sender != manager) {
revert NotOwnerNorRemoveManager();
}
if (receiver == address(0) || msg.sender != manager) {
return _owner;
}
return receiver;
}
}
文件 2 的 46:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 3 的 46:BatchId.sol
pragma solidity 0.8.24;
type BatchId is address;
using {equals as ==, notEquals as !=, isZero, isNotZero} for BatchId global;
function equals(BatchId a, BatchId b) pure returns (bool) {
return BatchId.unwrap(a) == BatchId.unwrap(b);
}
function notEquals(BatchId a, BatchId b) pure returns (bool) {
return !(a == b);
}
function isZero(BatchId x) pure returns (bool) {
return x == BATCH_ID_ZERO;
}
function isNotZero(BatchId x) pure returns (bool) {
return !x.isZero();
}
BatchId constant BATCH_ID_ZERO = BatchId.wrap(address(0));
文件 4 的 46:BorrowerOperations.sol
pragma solidity 0.8.24;
import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import "./Interfaces/IBorrowerOperations.sol";
import "./Interfaces/IAddressesRegistry.sol";
import "./Interfaces/ITroveManager.sol";
import "./Interfaces/IBoldToken.sol";
import "./Interfaces/ICollSurplusPool.sol";
import "./Interfaces/ISortedTroves.sol";
import "./Dependencies/LiquityBase.sol";
import "./Dependencies/AddRemoveManagers.sol";
import "./Types/LatestTroveData.sol";
import "./Types/LatestBatchData.sol";
contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperations {
using SafeERC20 for IERC20;
IERC20 internal immutable collToken;
ITroveManager internal troveManager;
address internal gasPoolAddress;
ICollSurplusPool internal collSurplusPool;
IBoldToken internal boldToken;
ISortedTroves internal sortedTroves;
IWETH internal immutable WETH;
uint256 public immutable CCR;
uint256 public immutable SCR;
bool public hasBeenShutDown;
uint256 public immutable MCR;
mapping(uint256 => InterestIndividualDelegate) private interestIndividualDelegateOf;
mapping(uint256 => address) public interestBatchManagerOf;
mapping(address => InterestBatchManager) private interestBatchManagers;
struct OpenTroveVars {
ITroveManager troveManager;
uint256 troveId;
TroveChange change;
LatestBatchData batch;
}
struct LocalVariables_openTrove {
ITroveManager troveManager;
IActivePool activePool;
IBoldToken boldToken;
uint256 troveId;
uint256 price;
uint256 avgInterestRate;
uint256 entireDebt;
uint256 ICR;
uint256 newTCR;
bool newOracleFailureDetected;
}
struct LocalVariables_adjustTrove {
IActivePool activePool;
IBoldToken boldToken;
LatestTroveData trove;
uint256 price;
bool isBelowCriticalThreshold;
uint256 newICR;
uint256 newDebt;
uint256 newColl;
bool newOracleFailureDetected;
}
struct LocalVariables_setInterestBatchManager {
ITroveManager troveManager;
IActivePool activePool;
ISortedTroves sortedTroves;
address oldBatchManager;
LatestTroveData trove;
LatestBatchData oldBatch;
LatestBatchData newBatch;
}
struct LocalVariables_removeFromBatch {
ITroveManager troveManager;
ISortedTroves sortedTroves;
address batchManager;
LatestTroveData trove;
LatestBatchData batch;
uint256 newBatchDebt;
}
error IsShutDown();
error TCRNotBelowSCR();
error ZeroAdjustment();
error NotOwnerNorInterestManager();
error TroveInBatch();
error TroveNotInBatch();
error InterestNotInRange();
error BatchInterestRateChangePeriodNotPassed();
error DelegateInterestRateChangePeriodNotPassed();
error TroveExists();
error TroveNotOpen();
error TroveNotActive();
error TroveNotZombie();
error TroveWithZeroDebt();
error UpfrontFeeTooHigh();
error ICRBelowMCR();
error RepaymentNotMatchingCollWithdrawal();
error TCRBelowCCR();
error DebtBelowMin();
error CollWithdrawalTooHigh();
error NotEnoughBoldBalance();
error InterestRateTooLow();
error InterestRateTooHigh();
error InterestRateNotNew();
error InvalidInterestBatchManager();
error BatchManagerExists();
error BatchManagerNotNew();
error NewFeeNotLower();
error CallerNotTroveManager();
error CallerNotPriceFeed();
error MinGeMax();
error AnnualManagementFeeTooHigh();
error MinInterestRateChangePeriodTooLow();
error NewOracleFailureDetected();
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event GasPoolAddressChanged(address _gasPoolAddress);
event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
event SortedTrovesAddressChanged(address _sortedTrovesAddress);
event BoldTokenAddressChanged(address _boldTokenAddress);
event ShutDown(uint256 _tcr);
constructor(IAddressesRegistry _addressesRegistry)
AddRemoveManagers(_addressesRegistry)
LiquityBase(_addressesRegistry)
{
assert(MIN_DEBT > 0);
collToken = _addressesRegistry.collToken();
WETH = _addressesRegistry.WETH();
CCR = _addressesRegistry.CCR();
SCR = _addressesRegistry.SCR();
MCR = _addressesRegistry.MCR();
troveManager = _addressesRegistry.troveManager();
gasPoolAddress = _addressesRegistry.gasPoolAddress();
collSurplusPool = _addressesRegistry.collSurplusPool();
sortedTroves = _addressesRegistry.sortedTroves();
boldToken = _addressesRegistry.boldToken();
emit TroveManagerAddressChanged(address(troveManager));
emit GasPoolAddressChanged(gasPoolAddress);
emit CollSurplusPoolAddressChanged(address(collSurplusPool));
emit SortedTrovesAddressChanged(address(sortedTroves));
emit BoldTokenAddressChanged(address(boldToken));
collToken.approve(address(activePool), type(uint256).max);
}
function openTrove(
address _owner,
uint256 _ownerIndex,
uint256 _collAmount,
uint256 _boldAmount,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _annualInterestRate,
uint256 _maxUpfrontFee,
address _addManager,
address _removeManager,
address _receiver
) external override returns (uint256) {
_requireValidAnnualInterestRate(_annualInterestRate);
OpenTroveVars memory vars;
vars.troveId = _openTrove(
_owner,
_ownerIndex,
_collAmount,
_boldAmount,
_annualInterestRate,
address(0),
0,
0,
_maxUpfrontFee,
_addManager,
_removeManager,
_receiver,
vars.change
);
troveManager.onOpenTrove(_owner, vars.troveId, vars.change, _annualInterestRate);
sortedTroves.insert(vars.troveId, _annualInterestRate, _upperHint, _lowerHint);
return vars.troveId;
}
function openTroveAndJoinInterestBatchManager(OpenTroveAndJoinInterestBatchManagerParams calldata _params)
external
override
returns (uint256)
{
_requireValidInterestBatchManager(_params.interestBatchManager);
OpenTroveVars memory vars;
vars.troveManager = troveManager;
vars.batch = vars.troveManager.getLatestBatchData(_params.interestBatchManager);
vars.change.batchAccruedManagementFee = vars.batch.accruedManagementFee;
vars.change.oldWeightedRecordedDebt = vars.batch.weightedRecordedDebt;
vars.change.oldWeightedRecordedBatchManagementFee = vars.batch.weightedRecordedBatchManagementFee;
vars.troveId = _openTrove(
_params.owner,
_params.ownerIndex,
_params.collAmount,
_params.boldAmount,
vars.batch.annualInterestRate,
_params.interestBatchManager,
vars.batch.entireDebtWithoutRedistribution,
vars.batch.annualManagementFee,
_params.maxUpfrontFee,
_params.addManager,
_params.removeManager,
_params.receiver,
vars.change
);
interestBatchManagerOf[vars.troveId] = _params.interestBatchManager;
vars.troveManager.onOpenTroveAndJoinBatch(
_params.owner,
vars.troveId,
vars.change,
_params.interestBatchManager,
vars.batch.entireCollWithoutRedistribution,
vars.batch.entireDebtWithoutRedistribution
);
sortedTroves.insertIntoBatch(
vars.troveId,
BatchId.wrap(_params.interestBatchManager),
vars.batch.annualInterestRate,
_params.upperHint,
_params.lowerHint
);
return vars.troveId;
}
function _openTrove(
address _owner,
uint256 _ownerIndex,
uint256 _collAmount,
uint256 _boldAmount,
uint256 _annualInterestRate,
address _interestBatchManager,
uint256 _batchEntireDebt,
uint256 _batchManagementAnnualFee,
uint256 _maxUpfrontFee,
address _addManager,
address _removeManager,
address _receiver,
TroveChange memory _change
) internal returns (uint256) {
_requireIsNotShutDown();
LocalVariables_openTrove memory vars;
vars.troveManager = troveManager;
vars.activePool = activePool;
vars.boldToken = boldToken;
vars.price = _requireOraclesLive();
vars.troveId = uint256(keccak256(abi.encode(_owner, _ownerIndex)));
_requireTroveDoesNotExists(vars.troveManager, vars.troveId);
_change.collIncrease = _collAmount;
_change.debtIncrease = _boldAmount;
_change.newWeightedRecordedDebt = (_batchEntireDebt + _change.debtIncrease) * _annualInterestRate;
vars.avgInterestRate = vars.activePool.getNewApproxAvgInterestRateFromTroveChange(_change);
_change.upfrontFee = _calcUpfrontFee(_change.debtIncrease, vars.avgInterestRate);
_requireUserAcceptsUpfrontFee(_change.upfrontFee, _maxUpfrontFee);
vars.entireDebt = _change.debtIncrease + _change.upfrontFee;
_requireAtLeastMinDebt(vars.entireDebt);
if (_interestBatchManager == address(0)) {
_change.newWeightedRecordedDebt = vars.entireDebt * _annualInterestRate;
} else {
_change.newWeightedRecordedDebt = (_batchEntireDebt + vars.entireDebt) * _annualInterestRate;
_change.newWeightedRecordedBatchManagementFee =
(_batchEntireDebt + vars.entireDebt) * _batchManagementAnnualFee;
}
vars.ICR = LiquityMath._computeCR(_collAmount, vars.entireDebt, vars.price);
_requireICRisAboveMCR(vars.ICR);
vars.newTCR = _getNewTCRFromTroveChange(_change, vars.price);
_requireNewTCRisAboveCCR(vars.newTCR);
_setAddManager(vars.troveId, _addManager);
_setRemoveManagerAndReceiver(vars.troveId, _removeManager, _receiver);
vars.activePool.mintAggInterestAndAccountForTroveChange(_change, _interestBatchManager);
_pullCollAndSendToActivePool(vars.activePool, _collAmount);
vars.boldToken.mint(msg.sender, _boldAmount);
WETH.transferFrom(msg.sender, gasPoolAddress, ETH_GAS_COMPENSATION);
return vars.troveId;
}
function addColl(uint256 _troveId, uint256 _collAmount) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsActive(troveManagerCached, _troveId);
TroveChange memory troveChange;
troveChange.collIncrease = _collAmount;
_adjustTrove(
troveManagerCached,
_troveId,
troveChange,
0
);
}
function withdrawColl(uint256 _troveId, uint256 _collWithdrawal) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsActive(troveManagerCached, _troveId);
TroveChange memory troveChange;
troveChange.collDecrease = _collWithdrawal;
_adjustTrove(
troveManagerCached,
_troveId,
troveChange,
0
);
}
function withdrawBold(uint256 _troveId, uint256 _boldAmount, uint256 _maxUpfrontFee) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsActive(troveManagerCached, _troveId);
TroveChange memory troveChange;
troveChange.debtIncrease = _boldAmount;
_adjustTrove(troveManagerCached, _troveId, troveChange, _maxUpfrontFee);
}
function repayBold(uint256 _troveId, uint256 _boldAmount) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsActive(troveManagerCached, _troveId);
TroveChange memory troveChange;
troveChange.debtDecrease = _boldAmount;
_adjustTrove(
troveManagerCached,
_troveId,
troveChange,
0
);
}
function _initTroveChange(
TroveChange memory _troveChange,
uint256 _collChange,
bool _isCollIncrease,
uint256 _boldChange,
bool _isDebtIncrease
) internal pure {
if (_isCollIncrease) {
_troveChange.collIncrease = _collChange;
} else {
_troveChange.collDecrease = _collChange;
}
if (_isDebtIncrease) {
_troveChange.debtIncrease = _boldChange;
} else {
_troveChange.debtDecrease = _boldChange;
}
}
function adjustTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
uint256 _boldChange,
bool _isDebtIncrease,
uint256 _maxUpfrontFee
) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsActive(troveManagerCached, _troveId);
TroveChange memory troveChange;
_initTroveChange(troveChange, _collChange, _isCollIncrease, _boldChange, _isDebtIncrease);
_adjustTrove(troveManagerCached, _troveId, troveChange, _maxUpfrontFee);
}
function adjustZombieTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
uint256 _boldChange,
bool _isDebtIncrease,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsZombie(troveManagerCached, _troveId);
TroveChange memory troveChange;
_initTroveChange(troveChange, _collChange, _isCollIncrease, _boldChange, _isDebtIncrease);
_adjustTrove(troveManagerCached, _troveId, troveChange, _maxUpfrontFee);
troveManagerCached.setTroveStatusToActive(_troveId);
address batchManager = interestBatchManagerOf[_troveId];
uint256 batchAnnualInterestRate;
if (batchManager != address(0)) {
LatestBatchData memory batch = troveManagerCached.getLatestBatchData(batchManager);
batchAnnualInterestRate = batch.annualInterestRate;
}
_reInsertIntoSortedTroves(
_troveId,
troveManagerCached.getTroveAnnualInterestRate(_troveId),
_upperHint,
_lowerHint,
batchManager,
batchAnnualInterestRate
);
}
function adjustTroveInterestRate(
uint256 _troveId,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external {
_requireIsNotShutDown();
ITroveManager troveManagerCached = troveManager;
_requireValidAnnualInterestRate(_newAnnualInterestRate);
_requireIsNotInBatch(_troveId);
_requireSenderIsOwnerOrInterestManager(_troveId);
_requireTroveIsActive(troveManagerCached, _troveId);
LatestTroveData memory trove = troveManagerCached.getLatestTroveData(_troveId);
_requireValidDelegateAdustment(_troveId, trove.lastInterestRateAdjTime, _newAnnualInterestRate);
_requireAnnualInterestRateIsNew(trove.annualInterestRate, _newAnnualInterestRate);
uint256 newDebt = trove.entireDebt;
TroveChange memory troveChange;
troveChange.appliedRedistBoldDebtGain = trove.redistBoldDebtGain;
troveChange.appliedRedistCollGain = trove.redistCollGain;
troveChange.newWeightedRecordedDebt = newDebt * _newAnnualInterestRate;
troveChange.oldWeightedRecordedDebt = trove.weightedRecordedDebt;
if (
trove.annualInterestRate != _newAnnualInterestRate
&& block.timestamp < trove.lastInterestRateAdjTime + INTEREST_RATE_ADJ_COOLDOWN
) {
newDebt = _applyUpfrontFee(trove.entireColl, newDebt, troveChange, _maxUpfrontFee);
}
troveChange.newWeightedRecordedDebt = newDebt * _newAnnualInterestRate;
activePool.mintAggInterestAndAccountForTroveChange(troveChange, address(0));
sortedTroves.reInsert(_troveId, _newAnnualInterestRate, _upperHint, _lowerHint);
troveManagerCached.onAdjustTroveInterestRate(
_troveId, trove.entireColl, newDebt, _newAnnualInterestRate, troveChange
);
}
function _adjustTrove(
ITroveManager _troveManager,
uint256 _troveId,
TroveChange memory _troveChange,
uint256 _maxUpfrontFee
) internal {
_requireIsNotShutDown();
LocalVariables_adjustTrove memory vars;
vars.activePool = activePool;
vars.boldToken = boldToken;
vars.price = _requireOraclesLive();
vars.isBelowCriticalThreshold = _checkBelowCriticalThreshold(vars.price, CCR);
_requireTroveIsOpen(_troveManager, _troveId);
address owner = troveNFT.ownerOf(_troveId);
address receiver = owner;
if (_troveChange.collDecrease > 0 || _troveChange.debtIncrease > 0) {
receiver = _requireSenderIsOwnerOrRemoveManagerAndGetReceiver(_troveId, owner);
}
if (_troveChange.collIncrease > 0 || _troveChange.debtDecrease > 0) {
_requireSenderIsOwnerOrAddManager(_troveId, owner);
}
vars.trove = _troveManager.getLatestTroveData(_troveId);
if (_troveChange.debtDecrease > 0) {
uint256 maxRepayment = vars.trove.entireDebt > MIN_DEBT ? vars.trove.entireDebt - MIN_DEBT : 0;
if (_troveChange.debtDecrease > maxRepayment) {
_troveChange.debtDecrease = maxRepayment;
}
_requireSufficientBoldBalance(vars.boldToken, msg.sender, _troveChange.debtDecrease);
}
_requireNonZeroAdjustment(_troveChange);
if (_troveChange.collDecrease > 0) {
_requireValidCollWithdrawal(vars.trove.entireColl, _troveChange.collDecrease);
}
vars.newColl = vars.trove.entireColl + _troveChange.collIncrease - _troveChange.collDecrease;
vars.newDebt = vars.trove.entireDebt + _troveChange.debtIncrease - _troveChange.debtDecrease;
address batchManager = interestBatchManagerOf[_troveId];
bool isTroveInBatch = batchManager != address(0);
LatestBatchData memory batch;
uint256 batchFutureDebt;
if (isTroveInBatch) {
batch = _troveManager.getLatestBatchData(batchManager);
batchFutureDebt = batch.entireDebtWithoutRedistribution + vars.trove.redistBoldDebtGain
+ _troveChange.debtIncrease - _troveChange.debtDecrease;
_troveChange.appliedRedistBoldDebtGain = vars.trove.redistBoldDebtGain;
_troveChange.appliedRedistCollGain = vars.trove.redistCollGain;
_troveChange.batchAccruedManagementFee = batch.accruedManagementFee;
_troveChange.oldWeightedRecordedDebt = batch.weightedRecordedDebt;
_troveChange.newWeightedRecordedDebt = batchFutureDebt * batch.annualInterestRate;
_troveChange.oldWeightedRecordedBatchManagementFee = batch.weightedRecordedBatchManagementFee;
_troveChange.newWeightedRecordedBatchManagementFee = batchFutureDebt * batch.annualManagementFee;
} else {
_troveChange.appliedRedistBoldDebtGain = vars.trove.redistBoldDebtGain;
_troveChange.appliedRedistCollGain = vars.trove.redistCollGain;
_troveChange.oldWeightedRecordedDebt = vars.trove.weightedRecordedDebt;
_troveChange.newWeightedRecordedDebt = vars.newDebt * vars.trove.annualInterestRate;
}
if (_troveChange.debtIncrease > 0) {
uint256 avgInterestRate = vars.activePool.getNewApproxAvgInterestRateFromTroveChange(_troveChange);
_troveChange.upfrontFee = _calcUpfrontFee(_troveChange.debtIncrease, avgInterestRate);
_requireUserAcceptsUpfrontFee(_troveChange.upfrontFee, _maxUpfrontFee);
vars.newDebt += _troveChange.upfrontFee;
if (isTroveInBatch) {
batchFutureDebt += _troveChange.upfrontFee;
_troveChange.newWeightedRecordedDebt = batchFutureDebt * batch.annualInterestRate;
_troveChange.newWeightedRecordedBatchManagementFee = batchFutureDebt * batch.annualManagementFee;
} else {
_troveChange.newWeightedRecordedDebt = vars.newDebt * vars.trove.annualInterestRate;
}
}
_requireAtLeastMinDebt(vars.newDebt);
vars.newICR = LiquityMath._computeCR(vars.newColl, vars.newDebt, vars.price);
_requireValidAdjustmentInCurrentMode(_troveChange, vars);
if (isTroveInBatch) {
_troveManager.onAdjustTroveInsideBatch(
_troveId,
vars.newColl,
vars.newDebt,
_troveChange,
batchManager,
batch.entireCollWithoutRedistribution,
batch.entireDebtWithoutRedistribution
);
} else {
_troveManager.onAdjustTrove(_troveId, vars.newColl, vars.newDebt, _troveChange);
}
vars.activePool.mintAggInterestAndAccountForTroveChange(_troveChange, batchManager);
_moveTokensFromAdjustment(receiver, _troveChange, vars.boldToken, vars.activePool);
}
function closeTrove(uint256 _troveId) external override {
ITroveManager troveManagerCached = troveManager;
IActivePool activePoolCached = activePool;
IBoldToken boldTokenCached = boldToken;
address owner = troveNFT.ownerOf(_troveId);
address receiver = _requireSenderIsOwnerOrRemoveManagerAndGetReceiver(_troveId, owner);
_requireTroveIsOpen(troveManagerCached, _troveId);
LatestTroveData memory trove = troveManagerCached.getLatestTroveData(_troveId);
_requireSufficientBoldBalance(boldTokenCached, msg.sender, trove.entireDebt);
TroveChange memory troveChange;
troveChange.appliedRedistBoldDebtGain = trove.redistBoldDebtGain;
troveChange.appliedRedistCollGain = trove.redistCollGain;
troveChange.collDecrease = trove.entireColl;
troveChange.debtDecrease = trove.entireDebt;
address batchManager = interestBatchManagerOf[_troveId];
LatestBatchData memory batch;
if (batchManager != address(0)) {
batch = troveManagerCached.getLatestBatchData(batchManager);
uint256 batchFutureDebt =
batch.entireDebtWithoutRedistribution - (trove.entireDebt - trove.redistBoldDebtGain);
troveChange.batchAccruedManagementFee = batch.accruedManagementFee;
troveChange.oldWeightedRecordedDebt = batch.weightedRecordedDebt;
troveChange.newWeightedRecordedDebt = batchFutureDebt * batch.annualInterestRate;
troveChange.oldWeightedRecordedBatchManagementFee = batch.weightedRecordedBatchManagementFee;
troveChange.newWeightedRecordedBatchManagementFee = batchFutureDebt * batch.annualManagementFee;
} else {
troveChange.oldWeightedRecordedDebt = trove.weightedRecordedDebt;
}
(uint256 price,) = priceFeed.fetchPrice();
uint256 newTCR = _getNewTCRFromTroveChange(troveChange, price);
if (!hasBeenShutDown) _requireNewTCRisAboveCCR(newTCR);
troveManagerCached.onCloseTrove(
_troveId,
troveChange,
batchManager,
batch.entireCollWithoutRedistribution,
batch.entireDebtWithoutRedistribution
);
if (batchManager != address(0)) {
interestBatchManagerOf[_troveId] = address(0);
}
activePoolCached.mintAggInterestAndAccountForTroveChange(troveChange, batchManager);
WETH.transferFrom(gasPoolAddress, receiver, ETH_GAS_COMPENSATION);
boldTokenCached.burn(msg.sender, trove.entireDebt);
activePoolCached.sendColl(receiver, trove.entireColl);
_wipeTroveMappings(_troveId);
}
function applyPendingDebt(uint256 _troveId, uint256 _lowerHint, uint256 _upperHint) public {
_requireIsNotShutDown();
ITroveManager troveManagerCached = troveManager;
_requireTroveIsOpen(troveManagerCached, _troveId);
LatestTroveData memory trove = troveManagerCached.getLatestTroveData(_troveId);
_requireNonZeroDebt(trove.entireDebt);
TroveChange memory change;
change.appliedRedistBoldDebtGain = trove.redistBoldDebtGain;
change.appliedRedistCollGain = trove.redistCollGain;
address batchManager = interestBatchManagerOf[_troveId];
LatestBatchData memory batch;
if (batchManager == address(0)) {
change.oldWeightedRecordedDebt = trove.weightedRecordedDebt;
change.newWeightedRecordedDebt = trove.entireDebt * trove.annualInterestRate;
} else {
batch = troveManagerCached.getLatestBatchData(batchManager);
change.batchAccruedManagementFee = batch.accruedManagementFee;
change.oldWeightedRecordedDebt = batch.weightedRecordedDebt;
change.newWeightedRecordedDebt =
(batch.entireDebtWithoutRedistribution + trove.redistBoldDebtGain) * batch.annualInterestRate;
change.oldWeightedRecordedBatchManagementFee = batch.weightedRecordedBatchManagementFee;
change.newWeightedRecordedBatchManagementFee =
(batch.entireDebtWithoutRedistribution + trove.redistBoldDebtGain) * batch.annualManagementFee;
}
troveManagerCached.onApplyTroveInterest(
_troveId,
trove.entireColl,
trove.entireDebt,
batchManager,
batch.entireCollWithoutRedistribution,
batch.entireDebtWithoutRedistribution,
change
);
activePool.mintAggInterestAndAccountForTroveChange(change, batchManager);
if (_checkTroveIsZombie(troveManagerCached, _troveId) && trove.entireDebt >= MIN_DEBT) {
troveManagerCached.setTroveStatusToActive(_troveId);
_reInsertIntoSortedTroves(
_troveId, trove.annualInterestRate, _upperHint, _lowerHint, batchManager, batch.annualInterestRate
);
}
}
function getInterestIndividualDelegateOf(uint256 _troveId)
external
view
returns (InterestIndividualDelegate memory)
{
return interestIndividualDelegateOf[_troveId];
}
function setInterestIndividualDelegate(
uint256 _troveId,
address _delegate,
uint128 _minInterestRate,
uint128 _maxInterestRate,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee,
uint256 _minInterestRateChangePeriod
) external {
_requireIsNotShutDown();
_requireTroveIsActive(troveManager, _troveId);
_requireCallerIsBorrower(_troveId);
_requireValidAnnualInterestRate(_minInterestRate);
_requireValidAnnualInterestRate(_maxInterestRate);
_requireOrderedRange(_minInterestRate, _maxInterestRate);
interestIndividualDelegateOf[_troveId] =
InterestIndividualDelegate(_delegate, _minInterestRate, _maxInterestRate, _minInterestRateChangePeriod);
if (interestBatchManagerOf[_troveId] != address(0)) {
removeFromBatch(_troveId, _newAnnualInterestRate, _upperHint, _lowerHint, _maxUpfrontFee);
}
}
function removeInterestIndividualDelegate(uint256 _troveId) external {
_requireCallerIsBorrower(_troveId);
delete interestIndividualDelegateOf[_troveId];
}
function getInterestBatchManager(address _account) external view returns (InterestBatchManager memory) {
return interestBatchManagers[_account];
}
function registerBatchManager(
uint128 _minInterestRate,
uint128 _maxInterestRate,
uint128 _currentInterestRate,
uint128 _annualManagementFee,
uint128 _minInterestRateChangePeriod
) external {
_requireIsNotShutDown();
_requireNonExistentInterestBatchManager(msg.sender);
_requireValidAnnualInterestRate(_minInterestRate);
_requireValidAnnualInterestRate(_maxInterestRate);
_requireOrderedRange(_minInterestRate, _maxInterestRate);
_requireInterestRateInRange(_currentInterestRate, _minInterestRate, _maxInterestRate);
if (_annualManagementFee > MAX_ANNUAL_BATCH_MANAGEMENT_FEE) revert AnnualManagementFeeTooHigh();
if (_minInterestRateChangePeriod < MIN_INTEREST_RATE_CHANGE_PERIOD) revert MinInterestRateChangePeriodTooLow();
interestBatchManagers[msg.sender] =
InterestBatchManager(_minInterestRate, _maxInterestRate, _minInterestRateChangePeriod);
troveManager.onRegisterBatchManager(msg.sender, _currentInterestRate, _annualManagementFee);
}
function lowerBatchManagementFee(uint256 _newAnnualManagementFee) external {
_requireIsNotShutDown();
_requireValidInterestBatchManager(msg.sender);
ITroveManager troveManagerCached = troveManager;
LatestBatchData memory batch = troveManagerCached.getLatestBatchData(msg.sender);
if (_newAnnualManagementFee >= batch.annualManagementFee) {
revert NewFeeNotLower();
}
troveManagerCached.onLowerBatchManagerAnnualFee(
msg.sender,
batch.entireCollWithoutRedistribution,
batch.entireDebtWithoutRedistribution,
_newAnnualManagementFee
);
TroveChange memory batchChange;
batchChange.batchAccruedManagementFee = batch.accruedManagementFee;
batchChange.oldWeightedRecordedDebt = batch.weightedRecordedDebt;
batchChange.newWeightedRecordedDebt = batch.entireDebtWithoutRedistribution * batch.annualInterestRate;
batchChange.oldWeightedRecordedBatchManagementFee = batch.weightedRecordedBatchManagementFee;
batchChange.newWeightedRecordedBatchManagementFee =
batch.entireDebtWithoutRedistribution * _newAnnualManagementFee;
activePool.mintAggInterestAndAccountForTroveChange(batchChange, msg.sender);
}
function setBatchManagerAnnualInterestRate(
uint128 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external {
_requireIsNotShutDown();
_requireValidInterestBatchManager(msg.sender);
_requireInterestRateInBatchManagerRange(msg.sender, _newAnnualInterestRate);
ITroveManager troveManagerCached = troveManager;
IActivePool activePoolCached = activePool;
LatestBatchData memory batch = troveManagerCached.getLatestBatchData(msg.sender);
_requireBatchInterestRateChangePeriodPassed(msg.sender, uint256(batch.lastInterestRateAdjTime));
uint256 newDebt = batch.entireDebtWithoutRedistribution;
TroveChange memory batchChange;
batchChange.batchAccruedManagementFee = batch.accruedManagementFee;
batchChange.oldWeightedRecordedDebt = batch.weightedRecordedDebt;
batchChange.newWeightedRecordedDebt = newDebt * _newAnnualInterestRate;
batchChange.oldWeightedRecordedBatchManagementFee = batch.weightedRecordedBatchManagementFee;
batchChange.newWeightedRecordedBatchManagementFee = newDebt * batch.annualManagementFee;
if (
batch.annualInterestRate != _newAnnualInterestRate
&& block.timestamp < batch.lastInterestRateAdjTime + INTEREST_RATE_ADJ_COOLDOWN
) {
uint256 price = _requireOraclesLive();
uint256 avgInterestRate = activePoolCached.getNewApproxAvgInterestRateFromTroveChange(batchChange);
batchChange.upfrontFee = _calcUpfrontFee(newDebt, avgInterestRate);
_requireUserAcceptsUpfrontFee(batchChange.upfrontFee, _maxUpfrontFee);
newDebt += batchChange.upfrontFee;
batchChange.newWeightedRecordedDebt = newDebt * _newAnnualInterestRate;
batchChange.newWeightedRecordedBatchManagementFee = newDebt * batch.annualManagementFee;
uint256 newTCR = _getNewTCRFromTroveChange(batchChange, price);
_requireNewTCRisAboveCCR(newTCR);
}
activePoolCached.mintAggInterestAndAccountForTroveChange(batchChange, msg.sender);
if (!sortedTroves.isEmptyBatch(BatchId.wrap(msg.sender))) {
sortedTroves.reInsertBatch(BatchId.wrap(msg.sender), _newAnnualInterestRate, _upperHint, _lowerHint);
}
troveManagerCached.onSetBatchManagerAnnualInterestRate(
msg.sender, batch.entireCollWithoutRedistribution, newDebt, _newAnnualInterestRate, batchChange.upfrontFee
);
}
function setInterestBatchManager(
uint256 _troveId,
address _newBatchManager,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) public override {
_requireIsNotShutDown();
LocalVariables_setInterestBatchManager memory vars;
vars.troveManager = troveManager;
vars.activePool = activePool;
vars.sortedTroves = sortedTroves;
_requireTroveIsActive(vars.troveManager, _troveId);
_requireCallerIsBorrower(_troveId);
_requireValidInterestBatchManager(_newBatchManager);
_requireIsNotInBatch(_troveId);
interestBatchManagerOf[_troveId] = _newBatchManager;
if (interestIndividualDelegateOf[_troveId].account != address(0)) delete interestIndividualDelegateOf[_troveId];
vars.trove = vars.troveManager.getLatestTroveData(_troveId);
vars.newBatch = vars.troveManager.getLatestBatchData(_newBatchManager);
TroveChange memory newBatchTroveChange;
newBatchTroveChange.appliedRedistBoldDebtGain = vars.trove.redistBoldDebtGain;
newBatchTroveChange.appliedRedistCollGain = vars.trove.redistCollGain;
newBatchTroveChange.batchAccruedManagementFee = vars.newBatch.accruedManagementFee;
newBatchTroveChange.oldWeightedRecordedDebt =
vars.newBatch.weightedRecordedDebt + vars.trove.weightedRecordedDebt;
newBatchTroveChange.newWeightedRecordedDebt =
(vars.newBatch.entireDebtWithoutRedistribution + vars.trove.entireDebt) * vars.newBatch.annualInterestRate;
vars.trove.entireDebt =
_applyUpfrontFee(vars.trove.entireColl, vars.trove.entireDebt, newBatchTroveChange, _maxUpfrontFee);
newBatchTroveChange.newWeightedRecordedDebt =
(vars.newBatch.entireDebtWithoutRedistribution + vars.trove.entireDebt) * vars.newBatch.annualInterestRate;
newBatchTroveChange.oldWeightedRecordedBatchManagementFee = vars.newBatch.weightedRecordedBatchManagementFee;
newBatchTroveChange.newWeightedRecordedBatchManagementFee =
(vars.newBatch.entireDebtWithoutRedistribution + vars.trove.entireDebt) * vars.newBatch.annualManagementFee;
vars.activePool.mintAggInterestAndAccountForTroveChange(newBatchTroveChange, _newBatchManager);
vars.troveManager.onSetInterestBatchManager(
ITroveManager.OnSetInterestBatchManagerParams({
troveId: _troveId,
troveColl: vars.trove.entireColl,
troveDebt: vars.trove.entireDebt,
troveChange: newBatchTroveChange,
newBatchAddress: _newBatchManager,
newBatchColl: vars.newBatch.entireCollWithoutRedistribution,
newBatchDebt: vars.newBatch.entireDebtWithoutRedistribution
})
);
vars.sortedTroves.remove(_troveId);
vars.sortedTroves.insertIntoBatch(
_troveId, BatchId.wrap(_newBatchManager), vars.newBatch.annualInterestRate, _upperHint, _lowerHint
);
}
function removeFromBatch(
uint256 _troveId,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) public override {
_requireIsNotShutDown();
LocalVariables_removeFromBatch memory vars;
vars.troveManager = troveManager;
vars.sortedTroves = sortedTroves;
_requireTroveIsActive(vars.troveManager, _troveId);
_requireCallerIsBorrower(_troveId);
_requireValidAnnualInterestRate(_newAnnualInterestRate);
vars.batchManager = _requireIsInBatch(_troveId);
delete interestBatchManagerOf[_troveId];
vars.sortedTroves.removeFromBatch(_troveId);
vars.sortedTroves.insert(_troveId, _newAnnualInterestRate, _upperHint, _lowerHint);
vars.trove = vars.troveManager.getLatestTroveData(_troveId);
vars.batch = vars.troveManager.getLatestBatchData(vars.batchManager);
uint256 batchFutureDebt =
vars.batch.entireDebtWithoutRedistribution - (vars.trove.entireDebt - vars.trove.redistBoldDebtGain);
TroveChange memory batchChange;
batchChange.appliedRedistBoldDebtGain = vars.trove.redistBoldDebtGain;
batchChange.appliedRedistCollGain = vars.trove.redistCollGain;
batchChange.batchAccruedManagementFee = vars.batch.accruedManagementFee;
batchChange.oldWeightedRecordedDebt = vars.batch.weightedRecordedDebt;
batchChange.newWeightedRecordedDebt =
batchFutureDebt * vars.batch.annualInterestRate + vars.trove.entireDebt * _newAnnualInterestRate;
if (
vars.batch.annualInterestRate != _newAnnualInterestRate
&& block.timestamp < vars.trove.lastInterestRateAdjTime + INTEREST_RATE_ADJ_COOLDOWN
) {
vars.trove.entireDebt =
_applyUpfrontFee(vars.trove.entireColl, vars.trove.entireDebt, batchChange, _maxUpfrontFee);
}
batchChange.newWeightedRecordedDebt =
batchFutureDebt * vars.batch.annualInterestRate + vars.trove.entireDebt * _newAnnualInterestRate;
batchChange.oldWeightedRecordedBatchManagementFee = vars.batch.weightedRecordedBatchManagementFee;
batchChange.newWeightedRecordedBatchManagementFee = batchFutureDebt * vars.batch.annualManagementFee;
activePool.mintAggInterestAndAccountForTroveChange(batchChange, vars.batchManager);
vars.troveManager.onRemoveFromBatch(
_troveId,
vars.trove.entireColl,
vars.trove.entireDebt,
batchChange,
vars.batchManager,
vars.batch.entireCollWithoutRedistribution,
vars.batch.entireDebtWithoutRedistribution,
_newAnnualInterestRate
);
}
function switchBatchManager(
uint256 _troveId,
uint256 _removeUpperHint,
uint256 _removeLowerHint,
address _newBatchManager,
uint256 _addUpperHint,
uint256 _addLowerHint,
uint256 _maxUpfrontFee
) external override {
address oldBatchManager = _requireIsInBatch(_troveId);
_requireNewInterestBatchManager(oldBatchManager, _newBatchManager);
LatestBatchData memory oldBatch = troveManager.getLatestBatchData(oldBatchManager);
removeFromBatch(_troveId, oldBatch.annualInterestRate, _removeUpperHint, _removeLowerHint, 0);
setInterestBatchManager(_troveId, _newBatchManager, _addUpperHint, _addLowerHint, _maxUpfrontFee);
}
function _applyUpfrontFee(
uint256 _troveEntireColl,
uint256 _troveEntireDebt,
TroveChange memory _troveChange,
uint256 _maxUpfrontFee
) internal returns (uint256) {
uint256 price = _requireOraclesLive();
uint256 avgInterestRate = activePool.getNewApproxAvgInterestRateFromTroveChange(_troveChange);
_troveChange.upfrontFee = _calcUpfrontFee(_troveEntireDebt, avgInterestRate);
_requireUserAcceptsUpfrontFee(_troveChange.upfrontFee, _maxUpfrontFee);
_troveEntireDebt += _troveChange.upfrontFee;
uint256 newICR = LiquityMath._computeCR(_troveEntireColl, _troveEntireDebt, price);
_requireICRisAboveMCR(newICR);
uint256 newTCR = _getNewTCRFromTroveChange(_troveChange, price);
_requireNewTCRisAboveCCR(newTCR);
return _troveEntireDebt;
}
function _calcUpfrontFee(uint256 _debt, uint256 _avgInterestRate) internal pure returns (uint256) {
return _calcInterest(_debt * _avgInterestRate, UPFRONT_INTEREST_PERIOD);
}
function onLiquidateTrove(uint256 _troveId) external {
_requireCallerIsTroveManager();
_wipeTroveMappings(_troveId);
}
function _wipeTroveMappings(uint256 _troveId) internal {
delete interestIndividualDelegateOf[_troveId];
delete interestBatchManagerOf[_troveId];
_wipeAddRemoveManagers(_troveId);
}
function claimCollateral() external override {
collSurplusPool.claimColl(msg.sender);
}
function shutdown() external {
if (hasBeenShutDown) revert IsShutDown();
uint256 totalColl = getEntireSystemColl();
uint256 totalDebt = getEntireSystemDebt();
(uint256 price, bool newOracleFailureDetected) = priceFeed.fetchPrice();
if (newOracleFailureDetected) return;
uint256 TCR = LiquityMath._computeCR(totalColl, totalDebt, price);
if (TCR >= SCR) revert TCRNotBelowSCR();
_applyShutdown();
emit ShutDown(TCR);
}
function shutdownFromOracleFailure() external {
_requireCallerIsPriceFeed();
if (hasBeenShutDown) return;
_applyShutdown();
}
function _applyShutdown() internal {
activePool.mintAggInterest();
hasBeenShutDown = true;
troveManager.shutdown();
}
function _reInsertIntoSortedTroves(
uint256 _troveId,
uint256 _troveAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
address _batchManager,
uint256 _batchAnnualInterestRate
) internal {
if (_batchManager == address(0)) {
sortedTroves.insert(_troveId, _troveAnnualInterestRate, _upperHint, _lowerHint);
} else {
sortedTroves.insertIntoBatch(
_troveId, BatchId.wrap(_batchManager), _batchAnnualInterestRate, _upperHint, _lowerHint
);
}
}
function _moveTokensFromAdjustment(
address withdrawalReceiver,
TroveChange memory _troveChange,
IBoldToken _boldToken,
IActivePool _activePool
) internal {
if (_troveChange.debtIncrease > 0) {
_boldToken.mint(withdrawalReceiver, _troveChange.debtIncrease);
} else if (_troveChange.debtDecrease > 0) {
_boldToken.burn(msg.sender, _troveChange.debtDecrease);
}
if (_troveChange.collIncrease > 0) {
_pullCollAndSendToActivePool(_activePool, _troveChange.collIncrease);
} else if (_troveChange.collDecrease > 0) {
_activePool.sendColl(withdrawalReceiver, _troveChange.collDecrease);
}
}
function _pullCollAndSendToActivePool(IActivePool _activePool, uint256 _amount) internal {
collToken.safeTransferFrom(msg.sender, address(_activePool), _amount);
_activePool.accountForReceivedColl(_amount);
}
function checkBatchManagerExists(address _batchManager) external view returns (bool) {
return interestBatchManagers[_batchManager].maxInterestRate > 0;
}
function _requireIsNotShutDown() internal view {
if (hasBeenShutDown) {
revert IsShutDown();
}
}
function _requireNonZeroAdjustment(TroveChange memory _troveChange) internal pure {
if (
_troveChange.collIncrease == 0 && _troveChange.collDecrease == 0 && _troveChange.debtIncrease == 0
&& _troveChange.debtDecrease == 0
) {
revert ZeroAdjustment();
}
}
function _requireSenderIsOwnerOrInterestManager(uint256 _troveId) internal view {
address owner = troveNFT.ownerOf(_troveId);
if (msg.sender != owner && msg.sender != interestIndividualDelegateOf[_troveId].account) {
revert NotOwnerNorInterestManager();
}
}
function _requireValidDelegateAdustment(
uint256 _troveId,
uint256 _lastInterestRateAdjTime,
uint256 _annualInterestRate
) internal view {
InterestIndividualDelegate memory individualDelegate = interestIndividualDelegateOf[_troveId];
if (individualDelegate.account == msg.sender) {
_requireInterestRateInRange(
_annualInterestRate, individualDelegate.minInterestRate, individualDelegate.maxInterestRate
);
_requireDelegateInterestRateChangePeriodPassed(
_lastInterestRateAdjTime, individualDelegate.minInterestRateChangePeriod
);
}
}
function _requireIsNotInBatch(uint256 _troveId) internal view {
if (interestBatchManagerOf[_troveId] != address(0)) {
revert TroveInBatch();
}
}
function _requireIsInBatch(uint256 _troveId) internal view returns (address) {
address batchManager = interestBatchManagerOf[_troveId];
if (batchManager == address(0)) {
revert TroveNotInBatch();
}
return batchManager;
}
function _requireTroveDoesNotExists(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status != ITroveManager.Status.nonExistent) {
revert TroveExists();
}
}
function _requireTroveIsOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status != ITroveManager.Status.active && status != ITroveManager.Status.zombie) {
revert TroveNotOpen();
}
}
function _requireTroveIsActive(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status != ITroveManager.Status.active) {
revert TroveNotActive();
}
}
function _requireTroveIsZombie(ITroveManager _troveManager, uint256 _troveId) internal view {
if (!_checkTroveIsZombie(_troveManager, _troveId)) {
revert TroveNotZombie();
}
}
function _checkTroveIsZombie(ITroveManager _troveManager, uint256 _troveId) internal view returns (bool) {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
return status == ITroveManager.Status.zombie;
}
function _requireNonZeroDebt(uint256 _troveDebt) internal pure {
if (_troveDebt == 0) {
revert TroveWithZeroDebt();
}
}
function _requireUserAcceptsUpfrontFee(uint256 _fee, uint256 _maxFee) internal pure {
if (_fee > _maxFee) {
revert UpfrontFeeTooHigh();
}
}
function _requireValidAdjustmentInCurrentMode(
TroveChange memory _troveChange,
LocalVariables_adjustTrove memory _vars
) internal view {
_requireICRisAboveMCR(_vars.newICR);
uint256 newTCR = _getNewTCRFromTroveChange(_troveChange, _vars.price);
if (_vars.isBelowCriticalThreshold) {
_requireNoBorrowingUnlessNewTCRisAboveCCR(_troveChange.debtIncrease, newTCR);
_requireDebtRepaymentGeCollWithdrawal(_troveChange, _vars.price);
} else {
_requireNewTCRisAboveCCR(newTCR);
}
}
function _requireICRisAboveMCR(uint256 _newICR) internal view {
if (_newICR < MCR) {
revert ICRBelowMCR();
}
}
function _requireNoBorrowingUnlessNewTCRisAboveCCR(uint256 _debtIncrease, uint256 _newTCR) internal view {
if (_debtIncrease > 0 && _newTCR < CCR) {
revert TCRBelowCCR();
}
}
function _requireDebtRepaymentGeCollWithdrawal(TroveChange memory _troveChange, uint256 _price) internal pure {
if ((_troveChange.debtDecrease * DECIMAL_PRECISION < _troveChange.collDecrease * _price)) {
revert RepaymentNotMatchingCollWithdrawal();
}
}
function _requireNewTCRisAboveCCR(uint256 _newTCR) internal view {
if (_newTCR < CCR) {
revert TCRBelowCCR();
}
}
function _requireAtLeastMinDebt(uint256 _debt) internal pure {
if (_debt < MIN_DEBT) {
revert DebtBelowMin();
}
}
function _requireValidCollWithdrawal(uint256 _currentColl, uint256 _collWithdrawal) internal pure {
if (_collWithdrawal > _currentColl) {
revert CollWithdrawalTooHigh();
}
}
function _requireSufficientBoldBalance(IBoldToken _boldToken, address _borrower, uint256 _debtRepayment)
internal
view
{
if (_boldToken.balanceOf(_borrower) < _debtRepayment) {
revert NotEnoughBoldBalance();
}
}
function _requireValidAnnualInterestRate(uint256 _annualInterestRate) internal pure {
if (_annualInterestRate < MIN_ANNUAL_INTEREST_RATE) {
revert InterestRateTooLow();
}
if (_annualInterestRate > MAX_ANNUAL_INTEREST_RATE) {
revert InterestRateTooHigh();
}
}
function _requireAnnualInterestRateIsNew(uint256 _oldAnnualInterestRate, uint256 _newAnnualInterestRate)
internal
pure
{
if (_oldAnnualInterestRate == _newAnnualInterestRate) {
revert InterestRateNotNew();
}
}
function _requireOrderedRange(uint256 _minInterestRate, uint256 _maxInterestRate) internal pure {
if (_minInterestRate >= _maxInterestRate) revert MinGeMax();
}
function _requireInterestRateInBatchManagerRange(address _interestBatchManagerAddress, uint256 _annualInterestRate)
internal
view
{
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
_requireInterestRateInRange(
_annualInterestRate, interestBatchManager.minInterestRate, interestBatchManager.maxInterestRate
);
}
function _requireInterestRateInRange(
uint256 _annualInterestRate,
uint256 _minInterestRate,
uint256 _maxInterestRate
) internal pure {
if (_minInterestRate > _annualInterestRate || _annualInterestRate > _maxInterestRate) {
revert InterestNotInRange();
}
}
function _requireBatchInterestRateChangePeriodPassed(
address _interestBatchManagerAddress,
uint256 _lastInterestRateAdjTime
) internal view {
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
if (block.timestamp < _lastInterestRateAdjTime + uint256(interestBatchManager.minInterestRateChangePeriod)) {
revert BatchInterestRateChangePeriodNotPassed();
}
}
function _requireDelegateInterestRateChangePeriodPassed(
uint256 _lastInterestRateAdjTime,
uint256 _minInterestRateChangePeriod
) internal view {
if (block.timestamp < _lastInterestRateAdjTime + _minInterestRateChangePeriod) {
revert DelegateInterestRateChangePeriodNotPassed();
}
}
function _requireValidInterestBatchManager(address _interestBatchManagerAddress) internal view {
if (interestBatchManagers[_interestBatchManagerAddress].maxInterestRate == 0) {
revert InvalidInterestBatchManager();
}
}
function _requireNonExistentInterestBatchManager(address _interestBatchManagerAddress) internal view {
if (interestBatchManagers[_interestBatchManagerAddress].maxInterestRate > 0) {
revert BatchManagerExists();
}
}
function _requireNewInterestBatchManager(address _oldBatchManagerAddress, address _newBatchManagerAddress)
internal
pure
{
if (_oldBatchManagerAddress == _newBatchManagerAddress) {
revert BatchManagerNotNew();
}
}
function _requireCallerIsTroveManager() internal view {
if (msg.sender != address(troveManager)) {
revert CallerNotTroveManager();
}
}
function _requireCallerIsPriceFeed() internal view {
if (msg.sender != address(priceFeed)) {
revert CallerNotPriceFeed();
}
}
function _requireOraclesLive() internal returns (uint256) {
(uint256 price, bool newOracleFailureDetected) = priceFeed.fetchPrice();
if (newOracleFailureDetected) {
revert NewOracleFailureDetected();
}
return price;
}
function _getNewTCRFromTroveChange(TroveChange memory _troveChange, uint256 _price)
internal
view
returns (uint256 newTCR)
{
uint256 totalColl = getEntireSystemColl();
totalColl += _troveChange.collIncrease;
totalColl -= _troveChange.collDecrease;
uint256 totalDebt = getEntireSystemDebt();
totalDebt += _troveChange.debtIncrease;
totalDebt += _troveChange.upfrontFee;
totalDebt -= _troveChange.debtDecrease;
newTCR = LiquityMath._computeCR(totalColl, totalDebt, _price);
}
}
文件 5 的 46:Constants.sol
pragma solidity 0.8.24;
address constant ZERO_ADDRESS = address(0);
uint256 constant MAX_UINT256 = type(uint256).max;
uint256 constant DECIMAL_PRECISION = 1e18;
uint256 constant _100pct = DECIMAL_PRECISION;
uint256 constant _1pct = DECIMAL_PRECISION / 100;
uint256 constant ETH_GAS_COMPENSATION = 0.0375 ether;
uint256 constant MIN_LIQUIDATION_PENALTY_SP = 5e16;
uint256 constant MAX_LIQUIDATION_PENALTY_REDISTRIBUTION = 20e16;
uint256 constant COLL_GAS_COMPENSATION_DIVISOR = 200;
uint256 constant COLL_GAS_COMPENSATION_CAP = 2 ether;
uint256 constant MIN_DEBT = 2000e18;
uint256 constant MIN_ANNUAL_INTEREST_RATE = _1pct / 2;
uint256 constant MAX_ANNUAL_INTEREST_RATE = 250 * _1pct;
uint128 constant MAX_ANNUAL_BATCH_MANAGEMENT_FEE = uint128(_100pct / 10);
uint128 constant MIN_INTEREST_RATE_CHANGE_PERIOD = 1 hours;
uint256 constant REDEMPTION_FEE_FLOOR = _1pct / 2;
uint256 constant MAX_BATCH_SHARES_RATIO = 1e9;
uint256 constant REDEMPTION_MINUTE_DECAY_FACTOR = 998076443575628800;
uint256 constant REDEMPTION_BETA = 1;
uint256 constant INITIAL_BASE_RATE = _100pct;
uint256 constant URGENT_REDEMPTION_BONUS = 2e16;
uint256 constant ONE_MINUTE = 1 minutes;
uint256 constant ONE_YEAR = 365 days;
uint256 constant UPFRONT_INTEREST_PERIOD = 7 days;
uint256 constant INTEREST_RATE_ADJ_COOLDOWN = 7 days;
uint256 constant SP_YIELD_SPLIT = 75 * _1pct;
contract Constants {
uint256 public constant _ETH_GAS_COMPENSATION = ETH_GAS_COMPENSATION;
uint256 public constant _MIN_DEBT = MIN_DEBT;
}
文件 6 的 46:FixedAssets.sol
pragma solidity 0.8.24;
import "lib/Solady/src/utils/SSTORE2.sol";
contract FixedAssetReader {
struct Asset {
uint128 start;
uint128 end;
}
address public immutable pointer;
mapping(bytes4 => Asset) public assets;
function readAsset(bytes4 _sig) public view returns (string memory) {
return string(SSTORE2.read(pointer, uint256(assets[_sig].start), uint256(assets[_sig].end)));
}
constructor(address _pointer, bytes4[] memory _sigs, Asset[] memory _assets) {
pointer = _pointer;
require(_sigs.length == _assets.length, "FixedAssetReader: Invalid input");
for (uint256 i = 0; i < _sigs.length; i++) {
assets[_sigs[i]] = _assets[i];
}
}
}
文件 7 的 46:IActivePool.sol
pragma solidity ^0.8.0;
import "./IInterestRouter.sol";
import "./IBoldRewardsReceiver.sol";
import "../Types/TroveChange.sol";
interface IActivePool {
function defaultPoolAddress() external view returns (address);
function borrowerOperationsAddress() external view returns (address);
function troveManagerAddress() external view returns (address);
function interestRouter() external view returns (IInterestRouter);
function stabilityPool() external view returns (IBoldRewardsReceiver);
function getCollBalance() external view returns (uint256);
function getBoldDebt() external view returns (uint256);
function lastAggUpdateTime() external view returns (uint256);
function aggRecordedDebt() external view returns (uint256);
function aggWeightedDebtSum() external view returns (uint256);
function aggBatchManagementFees() external view returns (uint256);
function aggWeightedBatchManagementFeeSum() external view returns (uint256);
function calcPendingAggInterest() external view returns (uint256);
function calcPendingSPYield() external view returns (uint256);
function calcPendingAggBatchManagementFee() external view returns (uint256);
function getNewApproxAvgInterestRateFromTroveChange(TroveChange calldata _troveChange)
external
view
returns (uint256);
function mintAggInterest() external;
function mintAggInterestAndAccountForTroveChange(TroveChange calldata _troveChange, address _batchManager)
external;
function mintBatchManagementFeeAndAccountForChange(TroveChange calldata _troveChange, address _batchAddress)
external;
function setShutdownFlag() external;
function hasBeenShutDown() external view returns (bool);
function shutdownTime() external view returns (uint256);
function sendColl(address _account, uint256 _amount) external;
function sendCollToDefaultPool(uint256 _amount) external;
function receiveColl(uint256 _amount) external;
function accountForReceivedColl(uint256 _amount) external;
}
文件 8 的 46:IAddRemoveManagers.sol
pragma solidity ^0.8.0;
interface IAddRemoveManagers {
function setAddManager(uint256 _troveId, address _manager) external;
function setRemoveManager(uint256 _troveId, address _manager) external;
function setRemoveManagerWithReceiver(uint256 _troveId, address _manager, address _receiver) external;
function addManagerOf(uint256 _troveId) external view returns (address);
function removeManagerReceiverOf(uint256 _troveId) external view returns (address, address);
}
文件 9 的 46:IAddressesRegistry.sol
pragma solidity ^0.8.0;
import "./IActivePool.sol";
import "./IBoldToken.sol";
import "./IBorrowerOperations.sol";
import "./ICollSurplusPool.sol";
import "./IDefaultPool.sol";
import "./IHintHelpers.sol";
import "./IMultiTroveGetter.sol";
import "./ISortedTroves.sol";
import "./IStabilityPool.sol";
import "./ITroveManager.sol";
import "./ITroveNFT.sol";
import {IMetadataNFT} from "../NFTMetadata/MetadataNFT.sol";
import "./ICollateralRegistry.sol";
import "./IInterestRouter.sol";
import "./IPriceFeed.sol";
interface IAddressesRegistry {
struct AddressVars {
IERC20Metadata collToken;
IBorrowerOperations borrowerOperations;
ITroveManager troveManager;
ITroveNFT troveNFT;
IMetadataNFT metadataNFT;
IStabilityPool stabilityPool;
IPriceFeed priceFeed;
IActivePool activePool;
IDefaultPool defaultPool;
address gasPoolAddress;
ICollSurplusPool collSurplusPool;
ISortedTroves sortedTroves;
IInterestRouter interestRouter;
IHintHelpers hintHelpers;
IMultiTroveGetter multiTroveGetter;
ICollateralRegistry collateralRegistry;
IBoldToken boldToken;
IWETH WETH;
}
function CCR() external returns (uint256);
function SCR() external returns (uint256);
function MCR() external returns (uint256);
function LIQUIDATION_PENALTY_SP() external returns (uint256);
function LIQUIDATION_PENALTY_REDISTRIBUTION() external returns (uint256);
function collToken() external view returns (IERC20Metadata);
function borrowerOperations() external view returns (IBorrowerOperations);
function troveManager() external view returns (ITroveManager);
function troveNFT() external view returns (ITroveNFT);
function metadataNFT() external view returns (IMetadataNFT);
function stabilityPool() external view returns (IStabilityPool);
function priceFeed() external view returns (IPriceFeed);
function activePool() external view returns (IActivePool);
function defaultPool() external view returns (IDefaultPool);
function gasPoolAddress() external view returns (address);
function collSurplusPool() external view returns (ICollSurplusPool);
function sortedTroves() external view returns (ISortedTroves);
function interestRouter() external view returns (IInterestRouter);
function hintHelpers() external view returns (IHintHelpers);
function multiTroveGetter() external view returns (IMultiTroveGetter);
function collateralRegistry() external view returns (ICollateralRegistry);
function boldToken() external view returns (IBoldToken);
function WETH() external returns (IWETH);
function setAddresses(AddressVars memory _vars) external;
}
文件 10 的 46:IBoldRewardsReceiver.sol
pragma solidity ^0.8.0;
interface IBoldRewardsReceiver {
function triggerBoldRewards(uint256 _boldYield) external;
}
文件 11 的 46:IBoldToken.sol
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "openzeppelin-contracts/contracts/interfaces/IERC5267.sol";
interface IBoldToken is IERC20Metadata, IERC20Permit, IERC5267 {
function setBranchAddresses(
address _troveManagerAddress,
address _stabilityPoolAddress,
address _borrowerOperationsAddress,
address _activePoolAddress
) external;
function setCollateralRegistry(address _collateralRegistryAddress) external;
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;
}
文件 12 的 46:IBorrowerOperations.sol
pragma solidity ^0.8.0;
import "./ILiquityBase.sol";
import "./IAddRemoveManagers.sol";
import "./IBoldToken.sol";
import "./IPriceFeed.sol";
import "./ISortedTroves.sol";
import "./ITroveManager.sol";
import "./IWETH.sol";
interface IBorrowerOperations is ILiquityBase, IAddRemoveManagers {
function CCR() external view returns (uint256);
function MCR() external view returns (uint256);
function SCR() external view returns (uint256);
function openTrove(
address _owner,
uint256 _ownerIndex,
uint256 _ETHAmount,
uint256 _boldAmount,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _annualInterestRate,
uint256 _maxUpfrontFee,
address _addManager,
address _removeManager,
address _receiver
) external returns (uint256);
struct OpenTroveAndJoinInterestBatchManagerParams {
address owner;
uint256 ownerIndex;
uint256 collAmount;
uint256 boldAmount;
uint256 upperHint;
uint256 lowerHint;
address interestBatchManager;
uint256 maxUpfrontFee;
address addManager;
address removeManager;
address receiver;
}
function openTroveAndJoinInterestBatchManager(OpenTroveAndJoinInterestBatchManagerParams calldata _params)
external
returns (uint256);
function addColl(uint256 _troveId, uint256 _ETHAmount) external;
function withdrawColl(uint256 _troveId, uint256 _amount) external;
function withdrawBold(uint256 _troveId, uint256 _amount, uint256 _maxUpfrontFee) external;
function repayBold(uint256 _troveId, uint256 _amount) external;
function closeTrove(uint256 _troveId) external;
function adjustTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool isDebtIncrease,
uint256 _maxUpfrontFee
) external;
function adjustZombieTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
uint256 _boldChange,
bool _isDebtIncrease,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external;
function adjustTroveInterestRate(
uint256 _troveId,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external;
function applyPendingDebt(uint256 _troveId, uint256 _lowerHint, uint256 _upperHint) external;
function onLiquidateTrove(uint256 _troveId) external;
function claimCollateral() external;
function hasBeenShutDown() external view returns (bool);
function shutdown() external;
function shutdownFromOracleFailure() external;
function checkBatchManagerExists(address _batchMananger) external view returns (bool);
struct InterestIndividualDelegate {
address account;
uint128 minInterestRate;
uint128 maxInterestRate;
uint256 minInterestRateChangePeriod;
}
function getInterestIndividualDelegateOf(uint256 _troveId)
external
view
returns (InterestIndividualDelegate memory);
function setInterestIndividualDelegate(
uint256 _troveId,
address _delegate,
uint128 _minInterestRate,
uint128 _maxInterestRate,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee,
uint256 _minInterestRateChangePeriod
) external;
function removeInterestIndividualDelegate(uint256 _troveId) external;
struct InterestBatchManager {
uint128 minInterestRate;
uint128 maxInterestRate;
uint256 minInterestRateChangePeriod;
}
function registerBatchManager(
uint128 minInterestRate,
uint128 maxInterestRate,
uint128 currentInterestRate,
uint128 fee,
uint128 minInterestRateChangePeriod
) external;
function lowerBatchManagementFee(uint256 _newAnnualFee) external;
function setBatchManagerAnnualInterestRate(
uint128 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external;
function interestBatchManagerOf(uint256 _troveId) external view returns (address);
function getInterestBatchManager(address _account) external view returns (InterestBatchManager memory);
function setInterestBatchManager(
uint256 _troveId,
address _newBatchManager,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external;
function removeFromBatch(
uint256 _troveId,
uint256 _newAnnualInterestRate,
uint256 _upperHint,
uint256 _lowerHint,
uint256 _maxUpfrontFee
) external;
function switchBatchManager(
uint256 _troveId,
uint256 _removeUpperHint,
uint256 _removeLowerHint,
address _newBatchManager,
uint256 _addUpperHint,
uint256 _addLowerHint,
uint256 _maxUpfrontFee
) external;
}
文件 13 的 46:ICollSurplusPool.sol
pragma solidity ^0.8.0;
interface ICollSurplusPool {
function getCollBalance() external view returns (uint256);
function getCollateral(address _account) external view returns (uint256);
function accountSurplus(address _account, uint256 _amount) external;
function claimColl(address _account) external;
}
文件 14 的 46:ICollateralRegistry.sol
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IBoldToken.sol";
import "./ITroveManager.sol";
interface ICollateralRegistry {
function baseRate() external view returns (uint256);
function lastFeeOperationTime() external view returns (uint256);
function redeemCollateral(uint256 _boldamount, uint256 _maxIterations, uint256 _maxFeePercentage) external;
function totalCollaterals() external view returns (uint256);
function getToken(uint256 _index) external view returns (IERC20Metadata);
function getTroveManager(uint256 _index) external view returns (ITroveManager);
function boldToken() external view returns (IBoldToken);
function getRedemptionRate() external view returns (uint256);
function getRedemptionRateWithDecay() external view returns (uint256);
function getRedemptionRateForRedeemedAmount(uint256 _redeemAmount) external view returns (uint256);
function getRedemptionFeeWithDecay(uint256 _ETHDrawn) external view returns (uint256);
function getEffectiveRedemptionFeeInBold(uint256 _redeemAmount) external view returns (uint256);
}
文件 15 的 46:IDefaultPool.sol
pragma solidity ^0.8.0;
interface IDefaultPool {
function troveManagerAddress() external view returns (address);
function activePoolAddress() external view returns (address);
function getCollBalance() external view returns (uint256);
function getBoldDebt() external view returns (uint256);
function sendCollToActivePool(uint256 _amount) external;
function receiveColl(uint256 _amount) external;
function increaseBoldDebt(uint256 _amount) external;
function decreaseBoldDebt(uint256 _amount) external;
}
文件 16 的 46:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 17 的 46:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 18 的 46:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 19 的 46:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 20 的 46:IERC5267.sol
pragma solidity ^0.8.0;
interface IERC5267 {
event EIP712DomainChanged();
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
文件 21 的 46:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 22 的 46:IERC721Metadata.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 23 的 46:IHintHelpers.sol
pragma solidity ^0.8.0;
interface IHintHelpers {
function getApproxHint(uint256 _collIndex, uint256 _interestRate, uint256 _numTrials, uint256 _inputRandomSeed)
external
view
returns (uint256 hintId, uint256 diff, uint256 latestRandomSeed);
function predictOpenTroveUpfrontFee(uint256 _collIndex, uint256 _borrowedAmount, uint256 _interestRate)
external
view
returns (uint256);
function predictAdjustInterestRateUpfrontFee(uint256 _collIndex, uint256 _troveId, uint256 _newInterestRate)
external
view
returns (uint256);
function forcePredictAdjustInterestRateUpfrontFee(uint256 _collIndex, uint256 _troveId, uint256 _newInterestRate)
external
view
returns (uint256);
function predictAdjustTroveUpfrontFee(uint256 _collIndex, uint256 _troveId, uint256 _debtIncrease)
external
view
returns (uint256);
function predictAdjustBatchInterestRateUpfrontFee(
uint256 _collIndex,
address _batchAddress,
uint256 _newInterestRate
) external view returns (uint256);
function predictJoinBatchInterestRateUpfrontFee(uint256 _collIndex, uint256 _troveId, address _batchAddress)
external
view
returns (uint256);
}
文件 24 的 46:IInterestRouter.sol
pragma solidity ^0.8.0;
interface IInterestRouter {
}
文件 25 的 46:ILiquityBase.sol
pragma solidity ^0.8.0;
import "./IActivePool.sol";
import "./IDefaultPool.sol";
import "./IPriceFeed.sol";
interface ILiquityBase {
function activePool() external view returns (IActivePool);
function getEntireSystemDebt() external view returns (uint256);
function getEntireSystemColl() external view returns (uint256);
}
文件 26 的 46:IMultiTroveGetter.sol
pragma solidity ^0.8.0;
interface IMultiTroveGetter {
struct CombinedTroveData {
uint256 id;
uint256 debt;
uint256 coll;
uint256 stake;
uint256 annualInterestRate;
uint256 lastDebtUpdateTime;
uint256 lastInterestRateAdjTime;
address interestBatchManager;
uint256 batchDebtShares;
uint256 batchCollShares;
uint256 snapshotETH;
uint256 snapshotBoldDebt;
}
struct DebtPerInterestRate {
address interestBatchManager;
uint256 interestRate;
uint256 debt;
}
function getMultipleSortedTroves(uint256 _collIndex, int256 _startIdx, uint256 _count)
external
view
returns (CombinedTroveData[] memory _troves);
function getDebtPerInterestRateAscending(uint256 _collIndex, uint256 _startId, uint256 _maxIterations)
external
view
returns (DebtPerInterestRate[] memory, uint256 currId);
}
文件 27 的 46:IPriceFeed.sol
pragma solidity ^0.8.0;
interface IPriceFeed {
function fetchPrice() external returns (uint256, bool);
function fetchRedemptionPrice() external returns (uint256, bool);
function lastGoodPrice() external view returns (uint256);
function setAddresses(address _borrowerOperationsAddress) external;
}
文件 28 的 46:ISortedTroves.sol
pragma solidity ^0.8.0;
import "./ITroveManager.sol";
import {BatchId, BATCH_ID_ZERO} from "../Types/BatchId.sol";
interface ISortedTroves {
function insert(uint256 _id, uint256 _annualInterestRate, uint256 _prevId, uint256 _nextId) external;
function insertIntoBatch(
uint256 _troveId,
BatchId _batchId,
uint256 _annualInterestRate,
uint256 _prevId,
uint256 _nextId
) external;
function remove(uint256 _id) external;
function removeFromBatch(uint256 _id) external;
function reInsert(uint256 _id, uint256 _newAnnualInterestRate, uint256 _prevId, uint256 _nextId) external;
function reInsertBatch(BatchId _id, uint256 _newAnnualInterestRate, uint256 _prevId, uint256 _nextId) external;
function contains(uint256 _id) external view returns (bool);
function isBatchedNode(uint256 _id) external view returns (bool);
function isEmptyBatch(BatchId _id) external view returns (bool);
function isEmpty() external view returns (bool);
function getSize() external view returns (uint256);
function getFirst() external view returns (uint256);
function getLast() external view returns (uint256);
function getNext(uint256 _id) external view returns (uint256);
function getPrev(uint256 _id) external view returns (uint256);
function validInsertPosition(uint256 _annualInterestRate, uint256 _prevId, uint256 _nextId)
external
view
returns (bool);
function findInsertPosition(uint256 _annualInterestRate, uint256 _prevId, uint256 _nextId)
external
view
returns (uint256, uint256);
function borrowerOperationsAddress() external view returns (address);
function troveManager() external view returns (ITroveManager);
function size() external view returns (uint256);
function nodes(uint256 _id) external view returns (uint256 nextId, uint256 prevId, BatchId batchId, bool exists);
function batches(BatchId _id) external view returns (uint256 head, uint256 tail);
}
文件 29 的 46:IStabilityPool.sol
pragma solidity ^0.8.0;
import "./IActivePool.sol";
import "./ILiquityBase.sol";
import "./IBoldToken.sol";
import "./ITroveManager.sol";
import "./IBoldRewardsReceiver.sol";
interface IStabilityPool is ILiquityBase, IBoldRewardsReceiver {
function boldToken() external view returns (IBoldToken);
function troveManager() external view returns (ITroveManager);
function provideToSP(uint256 _amount, bool _doClaim) external;
function withdrawFromSP(uint256 _amount, bool doClaim) external;
function claimAllCollGains() external;
function offset(uint256 _debt, uint256 _coll) external;
function deposits(address _depositor) external view returns (uint256 initialValue);
function stashedColl(address _depositor) external view returns (uint256);
function getCollBalance() external view returns (uint256);
function getTotalBoldDeposits() external view returns (uint256);
function getYieldGainsOwed() external view returns (uint256);
function getYieldGainsPending() external view returns (uint256);
function getDepositorCollGain(address _depositor) external view returns (uint256);
function getDepositorYieldGain(address _depositor) external view returns (uint256);
function getDepositorYieldGainWithPending(address _depositor) external view returns (uint256);
function getCompoundedBoldDeposit(address _depositor) external view returns (uint256);
function epochToScaleToS(uint128 _epoch, uint128 _scale) external view returns (uint256);
function epochToScaleToB(uint128 _epoch, uint128 _scale) external view returns (uint256);
function P() external view returns (uint256);
function currentScale() external view returns (uint128);
function currentEpoch() external view returns (uint128);
}
文件 30 的 46:ITroveManager.sol
pragma solidity ^0.8.0;
import "./ILiquityBase.sol";
import "./ITroveNFT.sol";
import "./IBorrowerOperations.sol";
import "./IStabilityPool.sol";
import "./IBoldToken.sol";
import "./ISortedTroves.sol";
import "../Types/LatestTroveData.sol";
import "../Types/LatestBatchData.sol";
interface ITroveManager is ILiquityBase {
enum Status {
nonExistent,
active,
closedByOwner,
closedByLiquidation,
zombie
}
function shutdownTime() external view returns (uint256);
function troveNFT() external view returns (ITroveNFT);
function stabilityPool() external view returns (IStabilityPool);
function sortedTroves() external view returns (ISortedTroves);
function borrowerOperations() external view returns (IBorrowerOperations);
function Troves(uint256 _id)
external
view
returns (
uint256 debt,
uint256 coll,
uint256 stake,
Status status,
uint64 arrayIndex,
uint64 lastDebtUpdateTime,
uint64 lastInterestRateAdjTime,
uint256 annualInterestRate,
address interestBatchManager,
uint256 batchDebtShares
);
function rewardSnapshots(uint256 _id) external view returns (uint256 coll, uint256 boldDebt);
function getTroveIdsCount() external view returns (uint256);
function getTroveFromTroveIdsArray(uint256 _index) external view returns (uint256);
function getCurrentICR(uint256 _troveId, uint256 _price) external view returns (uint256);
function lastZombieTroveId() external view returns (uint256);
function batchLiquidateTroves(uint256[] calldata _troveArray) external;
function redeemCollateral(
address _sender,
uint256 _boldAmount,
uint256 _price,
uint256 _redemptionRate,
uint256 _maxIterations
) external returns (uint256 _redemeedAmount);
function shutdown() external;
function urgentRedemption(uint256 _boldAmount, uint256[] calldata _troveIds, uint256 _minCollateral) external;
function getUnbackedPortionPriceAndRedeemability() external returns (uint256, uint256, bool);
function getLatestTroveData(uint256 _troveId) external view returns (LatestTroveData memory);
function getTroveAnnualInterestRate(uint256 _troveId) external view returns (uint256);
function getTroveStatus(uint256 _troveId) external view returns (Status);
function getLatestBatchData(address _batchAddress) external view returns (LatestBatchData memory);
function onOpenTrove(address _owner, uint256 _troveId, TroveChange memory _troveChange, uint256 _annualInterestRate)
external;
function onOpenTroveAndJoinBatch(
address _owner,
uint256 _troveId,
TroveChange memory _troveChange,
address _batchAddress,
uint256 _batchColl,
uint256 _batchDebt
) external;
function setTroveStatusToActive(uint256 _troveId) external;
function onAdjustTroveInterestRate(
uint256 _troveId,
uint256 _newColl,
uint256 _newDebt,
uint256 _newAnnualInterestRate,
TroveChange calldata _troveChange
) external;
function onAdjustTrove(uint256 _troveId, uint256 _newColl, uint256 _newDebt, TroveChange calldata _troveChange)
external;
function onAdjustTroveInsideBatch(
uint256 _troveId,
uint256 _newTroveColl,
uint256 _newTroveDebt,
TroveChange memory _troveChange,
address _batchAddress,
uint256 _newBatchColl,
uint256 _newBatchDebt
) external;
function onApplyTroveInterest(
uint256 _troveId,
uint256 _newTroveColl,
uint256 _newTroveDebt,
address _batchAddress,
uint256 _newBatchColl,
uint256 _newBatchDebt,
TroveChange calldata _troveChange
) external;
function onCloseTrove(
uint256 _troveId,
TroveChange memory _troveChange,
address _batchAddress,
uint256 _newBatchColl,
uint256 _newBatchDebt
) external;
function onRegisterBatchManager(address _batchAddress, uint256 _annualInterestRate, uint256 _annualFee) external;
function onLowerBatchManagerAnnualFee(
address _batchAddress,
uint256 _newColl,
uint256 _newDebt,
uint256 _newAnnualManagementFee
) external;
function onSetBatchManagerAnnualInterestRate(
address _batchAddress,
uint256 _newColl,
uint256 _newDebt,
uint256 _newAnnualInterestRate,
uint256 _upfrontFee
) external;
struct OnSetInterestBatchManagerParams {
uint256 troveId;
uint256 troveColl;
uint256 troveDebt;
TroveChange troveChange;
address newBatchAddress;
uint256 newBatchColl;
uint256 newBatchDebt;
}
function onSetInterestBatchManager(OnSetInterestBatchManagerParams calldata _params) external;
function onRemoveFromBatch(
uint256 _troveId,
uint256 _newTroveColl,
uint256 _newTroveDebt,
TroveChange memory _troveChange,
address _batchAddress,
uint256 _newBatchColl,
uint256 _newBatchDebt,
uint256 _newAnnualInterestRate
) external;
}
文件 31 的 46:ITroveNFT.sol
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "./ITroveManager.sol";
interface ITroveNFT is IERC721Metadata {
function mint(address _owner, uint256 _troveId) external;
function burn(uint256 _troveId) external;
}
文件 32 的 46:IWETH.sol
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IWETH is IERC20Metadata {
function deposit() external payable;
function withdraw(uint256 wad) external;
}
文件 33 的 46:JSON.sol
pragma solidity ^0.8.12;
library json {
string constant DOUBLE_QUOTES = '\\"';
function formattedMetadata(
string memory name,
string memory description,
string memory svgImg,
string memory attributes
) internal pure returns (string memory) {
return string.concat(
"data:application/json;base64,",
encode(
bytes(
string.concat(
"{",
_prop("name", name),
_prop("description", description),
_xmlImage(svgImg),
',"attributes":',
attributes,
"}"
)
)
)
);
}
function _xmlImage(string memory _svgImg) internal pure returns (string memory) {
return _prop("image", string.concat("data:image/svg+xml;base64,", encode(bytes(_svgImg))), true);
}
function _prop(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat('"', _key, '": ', '"', _val, '", ');
}
function _prop(string memory _key, string memory _val, bool last) internal pure returns (string memory) {
if (last) {
return string.concat('"', _key, '": ', '"', _val, '"');
} else {
return string.concat('"', _key, '": ', '"', _val, '", ');
}
}
function _object(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat('"', _key, '": ', "{", _val, "}");
}
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
string memory table = _TABLE;
string memory result = new string(4 * ((data.length + 2) / 3));
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {} {
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 { mstore8(sub(resultPtr, 1), 0x3d) }
}
return result;
}
}
文件 34 的 46:LatestBatchData.sol
pragma solidity 0.8.24;
struct LatestBatchData {
uint256 entireDebtWithoutRedistribution;
uint256 entireCollWithoutRedistribution;
uint256 accruedInterest;
uint256 recordedDebt;
uint256 annualInterestRate;
uint256 weightedRecordedDebt;
uint256 annualManagementFee;
uint256 accruedManagementFee;
uint256 weightedRecordedBatchManagementFee;
uint256 lastDebtUpdateTime;
uint256 lastInterestRateAdjTime;
}
文件 35 的 46:LatestTroveData.sol
pragma solidity 0.8.24;
struct LatestTroveData {
uint256 entireDebt;
uint256 entireColl;
uint256 redistBoldDebtGain;
uint256 redistCollGain;
uint256 accruedInterest;
uint256 recordedDebt;
uint256 annualInterestRate;
uint256 weightedRecordedDebt;
uint256 accruedBatchManagementFee;
uint256 lastInterestRateAdjTime;
}
文件 36 的 46:LibString.sol
pragma solidity ^0.8.4;
library LibString {
error HexLengthInsufficient();
error TooBigForSmallString();
error StringNot7BitASCII();
uint256 internal constant NOT_FOUND = type(uint256).max;
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
let w := not(0)
for { let temp := value } 1 {} {
str := add(str, w)
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 0x20)
mstore(str, length)
}
}
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) return toString(uint256(value));
unchecked {
str = toString(~uint256(value) + 1);
}
assembly {
let length := mload(str)
mstore(str, 0x2d)
str := sub(str, 1)
mstore(str, add(length, 1))
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
assembly {
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1)
let temp := value
for {} 1 {} {
str := add(str, w)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a)
revert(0x1c, 0x04)
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength)
}
}
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30)
let strLength := add(mload(str), 2)
mstore(add(str, o), 0x3078)
str := sub(add(str, o), 2)
mstore(str, sub(strLength, o))
}
}
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30)
let strLength := mload(str)
str := add(str, o)
mstore(str, sub(strLength, o))
}
}
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
assembly {
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1)
for { let temp := value } 1 {} {
str := add(str, w)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength)
}
}
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
assembly {
let mask := shl(6, div(not(0), 255))
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask))
let t := shl(240, 136)
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
assembly {
str := mload(0x40)
mstore(0x40, add(str, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
assembly {
let length := mload(raw)
str := add(mload(0x40), 2)
mstore(str, add(length, length))
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0)
mstore(0x40, add(o, 0x20))
}
}
function runeCount(string memory s) internal pure returns (uint256 result) {
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
function is7BitASCII(string memory s) internal pure returns (bool result) {
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
assembly {
if mload(s) {
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d)
revert(0x1c, 0x04)
}
}
}
}
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k)
mstore(last, 0)
mstore(0x40, add(last, 0x20))
mstore(result, k)
}
}
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
assembly {
for {} 1 {} {
result := not(0)
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w)
if iszero(gt(subject, end)) { break }
}
break
}
}
}
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
assembly {
let searchLength := mload(search)
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
let withinRange := iszero(gt(searchLength, subjectLength))
result := and(
withinRange,
eq(
keccak256(
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0)
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength)
mstore(0x40, add(result, add(resultLength, 0x40)))
}
}
}
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w)
if iszero(o) { break }
}
mstore(add(add(result, 0x20), resultLength), 0)
mstore(0x40, add(result, add(resultLength, 0x40)))
}
}
}
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
result := mload(0x40)
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
mstore(0x40, add(resultEnd, 0x20))
}
}
}
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w)
if iszero(o) { break }
}
mstore(add(add(element, 0x20), elementLength), 0)
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w)
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w)
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
mstore(last, 0)
mstore(result, totalLength)
mstore(0x40, add(last, 0x20))
}
}
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length)
let last := add(add(result, 0x20), length)
mstore(last, 0)
mstore(0x40, add(last, 0x20))
}
}
}
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {}
mstore(result, n)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
assembly {
for {} byte(result, s) { result := add(result, 1) } {}
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
function toSmallString(string memory s) internal pure returns (bytes32 result) {
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3)
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
function escapeHTML(string memory s) internal pure returns (string memory result) {
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0)
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20)))
mstore(0x40, add(last, 0x20))
}
}
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c)
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
mstore8(0x1d, mload(shr(4, c)))
mstore8(0x1e, mload(and(c, 15)))
mstore(result, mload(0x19))
result := add(result, 6)
continue
}
mstore8(result, 0x5c)
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0)
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20)))
mstore(0x40, add(last, 0x20))
}
}
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
function eq(string memory a, string memory b) internal pure returns (bool result) {
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
assembly {
let m := not(shl(7, div(not(iszero(b)), 255)))
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
function packOne(string memory a) internal pure returns (bytes32 result) {
assembly {
result :=
mul(
mload(add(a, 0x1f)),
lt(sub(mload(a), 1), 0x1f)
)
}
}
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
assembly {
result := mload(0x40)
mstore(0x40, add(result, 0x40))
mstore(result, 0)
mstore(add(result, 0x1f), packed)
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
assembly {
let aLength := mload(a)
result :=
mul(
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
assembly {
resultA := mload(0x40)
resultB := add(resultA, 0x40)
mstore(0x40, add(resultB, 0x40))
mstore(resultA, 0)
mstore(resultB, 0)
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
function directReturn(string memory a) internal pure {
assembly {
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20)
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}
文件 37 的 46:LiquityBase.sol
pragma solidity 0.8.24;
import "./Constants.sol";
import "./LiquityMath.sol";
import "../Interfaces/IAddressesRegistry.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/IPriceFeed.sol";
import "../Interfaces/ILiquityBase.sol";
contract LiquityBase is ILiquityBase {
IActivePool public activePool;
IDefaultPool internal defaultPool;
IPriceFeed internal priceFeed;
event ActivePoolAddressChanged(address _newActivePoolAddress);
event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
constructor(IAddressesRegistry _addressesRegistry) {
activePool = _addressesRegistry.activePool();
defaultPool = _addressesRegistry.defaultPool();
priceFeed = _addressesRegistry.priceFeed();
emit ActivePoolAddressChanged(address(activePool));
emit DefaultPoolAddressChanged(address(defaultPool));
emit PriceFeedAddressChanged(address(priceFeed));
}
function getEntireSystemColl() public view returns (uint256 entireSystemColl) {
uint256 activeColl = activePool.getCollBalance();
uint256 liquidatedColl = defaultPool.getCollBalance();
return activeColl + liquidatedColl;
}
function getEntireSystemDebt() public view returns (uint256 entireSystemDebt) {
uint256 activeDebt = activePool.getBoldDebt();
uint256 closedDebt = defaultPool.getBoldDebt();
return activeDebt + closedDebt;
}
function _getTCR(uint256 _price) internal view returns (uint256 TCR) {
uint256 entireSystemColl = getEntireSystemColl();
uint256 entireSystemDebt = getEntireSystemDebt();
TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt, _price);
return TCR;
}
function _checkBelowCriticalThreshold(uint256 _price, uint256 _CCR) internal view returns (bool) {
uint256 TCR = _getTCR(_price);
return TCR < _CCR;
}
function _calcInterest(uint256 _weightedDebt, uint256 _period) internal pure returns (uint256) {
return _weightedDebt * _period / ONE_YEAR / DECIMAL_PRECISION;
}
}
文件 38 的 46:LiquityMath.sol
pragma solidity 0.8.24;
import {DECIMAL_PRECISION} from "./Constants.sol";
library LiquityMath {
function _min(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a < _b) ? _a : _b;
}
function _max(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a >= _b) ? _a : _b;
}
function _sub_min_0(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a > _b) ? _a - _b : 0;
}
function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
uint256 prod_xy = x * y;
decProd = (prod_xy + DECIMAL_PRECISION / 2) / DECIMAL_PRECISION;
}
function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint256) {
if (_minutes > 525600000) _minutes = 525600000;
if (_minutes == 0) return DECIMAL_PRECISION;
uint256 y = DECIMAL_PRECISION;
uint256 x = _base;
uint256 n = _minutes;
while (n > 1) {
if (n % 2 == 0) {
x = decMul(x, x);
n = n / 2;
} else {
y = decMul(x, y);
x = decMul(x, x);
n = (n - 1) / 2;
}
}
return decMul(x, y);
}
function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a >= _b) ? _a - _b : _b - _a;
}
function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint256) {
if (_debt > 0) {
uint256 newCollRatio = _coll * _price / _debt;
return newCollRatio;
}
else {
return 2 ** 256 - 1;
}
}
}
文件 39 的 46:MetadataNFT.sol
pragma solidity 0.8.24;
import "lib/Solady/src/utils/SSTORE2.sol";
import "./utils/JSON.sol";
import "./utils/baseSVG.sol";
import "./utils/bauhaus.sol";
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ITroveManager} from "src/Interfaces/ITroveManager.sol";
interface IMetadataNFT {
struct TroveData {
uint256 _tokenId;
address _owner;
address _collToken;
address _boldToken;
uint256 _collAmount;
uint256 _debtAmount;
uint256 _interestRate;
ITroveManager.Status _status;
}
function uri(TroveData memory _troveData) external view returns (string memory);
}
contract MetadataNFT is IMetadataNFT {
FixedAssetReader public immutable assetReader;
string public constant name = "Liquity V2 Trove";
string public constant description = "Liquity V2 Trove position";
constructor(FixedAssetReader _assetReader) {
assetReader = _assetReader;
}
function uri(TroveData memory _troveData) public view returns (string memory) {
string memory attr = attributes(_troveData);
return json.formattedMetadata(name, description, renderSVGImage(_troveData), attr);
}
function renderSVGImage(TroveData memory _troveData) internal view returns (string memory) {
return svg._svg(
baseSVG._svgProps(),
string.concat(
baseSVG._baseElements(assetReader),
bauhaus._bauhaus(IERC20Metadata(_troveData._collToken).symbol(), _troveData._tokenId),
dynamicTextComponents(_troveData)
)
);
}
function attributes(TroveData memory _troveData) public pure returns (string memory) {
return string.concat(
'[{"trait_type": "Collateral Token", "value": "',
LibString.toHexString(_troveData._collToken),
'"}, {"trait_type": "Collateral Amount", "value": "',
LibString.toString(_troveData._collAmount),
'"}, {"trait_type": "Debt Token", "value": "',
LibString.toHexString(_troveData._boldToken),
'"}, {"trait_type": "Debt Amount", "value": "',
LibString.toString(_troveData._debtAmount),
'"}, {"trait_type": "Interest Rate", "value": "',
LibString.toString(_troveData._interestRate),
'"}, {"trait_type": "Status", "value": "',
_status2Str(_troveData._status),
'"} ]'
);
}
function dynamicTextComponents(TroveData memory _troveData) public view returns (string memory) {
string memory id = LibString.toHexString(_troveData._tokenId);
id = string.concat(LibString.slice(id, 0, 6), "...", LibString.slice(id, 38, 42));
return string.concat(
baseSVG._formattedIdEl(id),
baseSVG._formattedAddressEl(_troveData._owner),
baseSVG._collLogo(IERC20Metadata(_troveData._collToken).symbol(), assetReader),
baseSVG._statusEl(_status2Str(_troveData._status)),
baseSVG._dynamicTextEls(_troveData._debtAmount, _troveData._collAmount, _troveData._interestRate)
);
}
function _status2Str(ITroveManager.Status status) internal pure returns (string memory) {
if (status == ITroveManager.Status.active) return "Active";
if (status == ITroveManager.Status.closedByOwner) return "Closed";
if (status == ITroveManager.Status.closedByLiquidation) return "Liquidated";
if (status == ITroveManager.Status.zombie) return "Below Min Debt";
return "";
}
}
文件 40 的 46:SSTORE2.sol
pragma solidity ^0.8.4;
library SSTORE2 {
uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;
bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
error DeploymentFailed();
function write(bytes memory data) internal returns (address pointer) {
assembly {
let n := mload(data)
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
pointer := create(0, add(data, 0x15), add(n, 0xb))
if iszero(pointer) {
mstore(0x00, 0x30116425)
revert(0x1c, 0x04)
}
mstore(data, n)
}
}
function writeCounterfactual(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
assembly {
let n := mload(data)
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
if iszero(pointer) {
mstore(0x00, 0x30116425)
revert(0x1c, 0x04)
}
mstore(data, n)
}
}
function writeDeterministic(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
assembly {
let n := mload(data)
mstore(0x00, _CREATE3_PROXY_INITCODE)
let proxy := create2(0, 0x10, 0x10, salt)
if iszero(proxy) {
mstore(0x00, 0x30116425)
revert(0x1c, 0x04)
}
mstore(0x14, proxy)
mstore(0x00, 0xd694)
mstore8(0x34, 0x01)
pointer := keccak256(0x1e, 0x17)
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
if iszero(
mul(
extcodesize(pointer),
call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
)
) {
mstore(0x00, 0x30116425)
revert(0x1c, 0x04)
}
mstore(data, n)
}
}
function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
assembly {
let n := mload(data)
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
hash := keccak256(add(data, 0x15), add(n, 0xb))
mstore(data, n)
}
}
function predictCounterfactualAddress(bytes memory data, bytes32 salt)
internal
view
returns (address pointer)
{
pointer = predictCounterfactualAddress(data, salt, address(this));
}
function predictCounterfactualAddress(bytes memory data, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(data);
assembly {
mstore8(0x00, 0xff)
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
mstore(0x35, 0)
}
}
function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
pointer = predictDeterministicAddress(salt, address(this));
}
function predictDeterministicAddress(bytes32 salt, address deployer)
internal
pure
returns (address pointer)
{
assembly {
let m := mload(0x40)
mstore(0x00, deployer)
mstore8(0x0b, 0xff)
mstore(0x20, salt)
mstore(0x40, CREATE3_PROXY_INITCODE_HASH)
mstore(0x14, keccak256(0x0b, 0x55))
mstore(0x40, m)
mstore(0x00, 0xd694)
mstore8(0x34, 0x01)
pointer := keccak256(0x1e, 0x17)
}
}
function read(address pointer) internal view returns (bytes memory data) {
assembly {
data := mload(0x40)
let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
mstore(data, n)
mstore(0x40, add(n, add(data, 0x40)))
}
}
function read(address pointer, uint256 start) internal view returns (bytes memory data) {
assembly {
data := mload(0x40)
let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
extcodecopy(pointer, add(data, 0x1f), start, add(n, 0x21))
mstore(data, mul(sub(n, start), lt(start, n)))
mstore(0x40, add(data, add(0x40, mload(data))))
}
}
function read(address pointer, uint256 start, uint256 end)
internal
view
returns (bytes memory data)
{
assembly {
data := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
if iszero(and(0xff, mload(add(data, d)))) {
let n := sub(extcodesize(pointer), 0x01)
returndatacopy(returndatasize(), returndatasize(), shr(64, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(data, d)
mstore(add(add(data, 0x20), d), 0)
mstore(0x40, add(add(data, 0x40), d))
}
}
}
文件 41 的 46:SVG.sol
pragma solidity ^0.8.18;
import {utils, LibString} from "./Utils.sol";
library svg {
string internal constant _SVG = 'xmlns="http://www.w3.org/2000/svg"';
string internal constant _HTML = 'xmlns="http://www.w3.org/1999/xhtml"';
string internal constant _XMLNS = "http://www.w3.org/2000/xmlns/ ";
string internal constant _XLINK = "http://www.w3.org/1999/xlink ";
function g(string memory _props, string memory _children) internal pure returns (string memory) {
return el("g", _props, _children);
}
function _svg(string memory _props, string memory _children) internal pure returns (string memory) {
return el("svg", string.concat(_SVG, " ", _props), _children);
}
function style(string memory _title, string memory _props) internal pure returns (string memory) {
return el("style", string.concat(".", _title, " ", _props));
}
function path(string memory _d) internal pure returns (string memory) {
return el("path", prop("d", _d, true));
}
function path(string memory _d, string memory _props) internal pure returns (string memory) {
return el("path", string.concat(prop("d", _d), _props));
}
function path(string memory _d, string memory _props, string memory _children)
internal
pure
returns (string memory)
{
return el("path", string.concat(prop("d", _d), _props), _children);
}
function text(string memory _props, string memory _children) internal pure returns (string memory) {
return el("text", _props, _children);
}
function line(string memory _props) internal pure returns (string memory) {
return el("line", _props);
}
function line(string memory _props, string memory _children) internal pure returns (string memory) {
return el("line", _props, _children);
}
function circle(string memory _props) internal pure returns (string memory) {
return el("circle", _props);
}
function circle(string memory _props, string memory _children) internal pure returns (string memory) {
return el("circle", _props, _children);
}
function circle(string memory cx, string memory cy, string memory r) internal pure returns (string memory) {
return el("circle", string.concat(prop("cx", cx), prop("cy", cy), prop("r", r, true)));
}
function circle(string memory cx, string memory cy, string memory r, string memory _children)
internal
pure
returns (string memory)
{
return el("circle", string.concat(prop("cx", cx), prop("cy", cy), prop("r", r, true)), _children);
}
function circle(string memory cx, string memory cy, string memory r, string memory _props, string memory _children)
internal
pure
returns (string memory)
{
return el("circle", string.concat(prop("cx", cx), prop("cy", cy), prop("r", r), _props), _children);
}
function ellipse(string memory _props) internal pure returns (string memory) {
return el("ellipse", _props);
}
function ellipse(string memory _props, string memory _children) internal pure returns (string memory) {
return el("ellipse", _props, _children);
}
function polygon(string memory _props) internal pure returns (string memory) {
return el("polygon", _props);
}
function polygon(string memory _props, string memory _children) internal pure returns (string memory) {
return el("polygon", _props, _children);
}
function polyline(string memory _props) internal pure returns (string memory) {
return el("polyline", _props);
}
function polyline(string memory _props, string memory _children) internal pure returns (string memory) {
return el("polyline", _props, _children);
}
function rect(string memory _props) internal pure returns (string memory) {
return el("rect", _props);
}
function rect(string memory _props, string memory _children) internal pure returns (string memory) {
return el("rect", _props, _children);
}
function filter(string memory _props, string memory _children) internal pure returns (string memory) {
return el("filter", _props, _children);
}
function cdata(string memory _content) internal pure returns (string memory) {
return string.concat("<![CDATA[", _content, "]]>");
}
function radialGradient(string memory _props, string memory _children) internal pure returns (string memory) {
return el("radialGradient", _props, _children);
}
function linearGradient(string memory _props, string memory _children) internal pure returns (string memory) {
return el("linearGradient", _props, _children);
}
function gradientStop(uint256 offset, string memory stopColor, string memory _props)
internal
pure
returns (string memory)
{
return el(
"stop",
string.concat(
prop("stop-color", stopColor),
" ",
prop("offset", string.concat(LibString.toString(offset), "%")),
" ",
_props
),
utils.NULL
);
}
function animateTransform(string memory _props) internal pure returns (string memory) {
return el("animateTransform", _props);
}
function animate(string memory _props) internal pure returns (string memory) {
return el("animate", _props);
}
function el(string memory _tag, string memory _props, string memory _children)
internal
pure
returns (string memory)
{
return string.concat("<", _tag, " ", _props, ">", _children, "</", _tag, ">");
}
function el(string memory _tag, string memory _props) internal pure returns (string memory) {
return string.concat("<", _tag, " ", _props, "/>");
}
function prop(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat(_key, "=", '"', _val, '" ');
}
function prop(string memory _key, string memory _val, bool last) internal pure returns (string memory) {
if (last) {
return string.concat(_key, "=", '"', _val, '"');
} else {
return string.concat(_key, "=", '"', _val, '" ');
}
}
}
文件 42 的 46:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 43 的 46:TroveChange.sol
pragma solidity 0.8.24;
struct TroveChange {
uint256 appliedRedistBoldDebtGain;
uint256 appliedRedistCollGain;
uint256 collIncrease;
uint256 collDecrease;
uint256 debtIncrease;
uint256 debtDecrease;
uint256 newWeightedRecordedDebt;
uint256 oldWeightedRecordedDebt;
uint256 upfrontFee;
uint256 batchAccruedManagementFee;
uint256 newWeightedRecordedBatchManagementFee;
uint256 oldWeightedRecordedBatchManagementFee;
}
文件 44 的 46:Utils.sol
pragma solidity 0.8.24;
import "lib/Solady/src/utils/LibString.sol";
library numUtils {
function toLocale(string memory _wholeNumber) internal pure returns (string memory) {
bytes memory b = bytes(_wholeNumber);
uint256 len = b.length;
if (len < 4) return _wholeNumber;
uint256 numCommas = (len - 1) / 3;
bytes memory result = new bytes(len + numCommas);
uint256 j = result.length - 1;
uint256 k = len;
for (uint256 i = 0; i < len; i++) {
result[j] = b[k - 1];
j = j > 1 ? j - 1 : 0;
k--;
if (k > 0 && (len - k) % 3 == 0) {
result[j] = ",";
j = j > 1 ? j - 1 : 0;
}
}
return string(result);
}
function toLocaleString(uint256 _value, uint8 _divisor, uint8 _precision) internal pure returns (string memory) {
uint256 whole;
uint256 fraction;
if (_divisor > 0) {
whole = _value / 10 ** _divisor;
if (_divisor <= _precision) {
fraction = (_value % 10 ** _divisor);
fraction = fraction * 10 ** (_precision - _divisor);
fraction = (whole == 0 && _value != 1) ? fraction * 10 : fraction;
} else {
fraction = (_value % 10 ** _divisor) / 10 ** (_divisor - _precision - 1);
}
} else {
whole = _value;
}
string memory wholeStr = toLocale(LibString.toString(whole));
if (fraction == 0) {
if (whole > 0 && _precision > 0) wholeStr = string.concat(wholeStr, ".");
for (uint8 i = 0; i < _precision; i++) {
wholeStr = string.concat(wholeStr, "0");
}
return wholeStr;
}
string memory fractionStr = LibString.slice(LibString.toString(fraction), 0, _precision);
if (_precision > bytes(fractionStr).length) {
uint256 len = _precision - bytes(fractionStr).length;
string memory zeroStr = "";
for (uint8 i = 0; i < len; i++) {
zeroStr = string.concat(zeroStr, "0");
}
fractionStr = string.concat(zeroStr, fractionStr);
}
return string.concat(wholeStr, _precision > 0 ? "." : "", fractionStr);
}
}
library utils {
string internal constant NULL = "";
function setCssVar(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat("--", _key, ":", _val, ";");
}
function getCssVar(string memory _key) internal pure returns (string memory) {
return string.concat("var(--", _key, ")");
}
function getDefURL(string memory _id) internal pure returns (string memory) {
return string.concat("url(#", _id, ")");
}
function white_a(uint256 _a) internal pure returns (string memory) {
return rgba(255, 255, 255, _a);
}
function black_a(uint256 _a) internal pure returns (string memory) {
return rgba(0, 0, 0, _a);
}
function rgba(uint256 _r, uint256 _g, uint256 _b, uint256 _a) internal pure returns (string memory) {
string memory formattedA = _a < 100 ? string.concat("0.", LibString.toString(_a)) : "1";
return string.concat(
"rgba(",
LibString.toString(_r),
",",
LibString.toString(_g),
",",
LibString.toString(_b),
",",
formattedA,
")"
);
}
function cssBraces(string memory _attribute, string memory _value) internal pure returns (string memory) {
return string.concat(" {", _attribute, ": ", _value, "}");
}
function cssBraces(string[] memory _attributes, string[] memory _values) internal pure returns (string memory) {
require(_attributes.length == _values.length, "Utils: Unbalanced Arrays");
uint256 len = _attributes.length;
string memory results = " {";
for (uint256 i = 0; i < len; i++) {
results = string.concat(results, _attributes[i], ": ", _values[i], "; ");
}
return string.concat(results, "}");
}
function points(uint256[2][] memory pointsArray) internal pure returns (string memory) {
require(pointsArray.length >= 3, "Utils: Array too short");
uint256 len = pointsArray.length - 1;
string memory results = 'points="';
for (uint256 i = 0; i < len; i++) {
results = string.concat(
results, LibString.toString(pointsArray[i][0]), ",", LibString.toString(pointsArray[i][1]), " "
);
}
return string.concat(
results, LibString.toString(pointsArray[len][0]), ",", LibString.toString(pointsArray[len][1]), '"'
);
}
function points(uint256[2][] memory pointsArray, uint256 decimalPrecision) internal pure returns (string memory) {
require(pointsArray.length >= 3, "Utils: Array too short");
uint256 len = pointsArray.length - 1;
string memory results = 'points="';
for (uint256 i = 0; i < len; i++) {
results = string.concat(
results,
toString(pointsArray[i][0], decimalPrecision),
",",
toString(pointsArray[i][1], decimalPrecision),
" "
);
}
return string.concat(
results,
toString(pointsArray[len][0], decimalPrecision),
",",
toString(pointsArray[len][1], decimalPrecision),
'"'
);
}
function stringsEqual(string memory _a, string memory _b) internal pure returns (bool) {
return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b));
}
function utfStringLength(string memory _str) internal pure returns (uint256 length) {
uint256 i = 0;
bytes memory string_rep = bytes(_str);
while (i < string_rep.length) {
if (string_rep[i] >> 7 == 0) {
i += 1;
} else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) {
i += 2;
} else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) {
i += 3;
} else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) {
i += 4;
}
else {
i += 1;
}
length++;
}
}
function toString(uint256 value, uint256 precision) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
require(precision <= digits && precision > 0, "Utils: precision invalid");
precision == digits ? digits += 2 : digits++;
uint256 decimalPlacement = digits - precision - 1;
bytes memory buffer = new bytes(digits);
buffer[decimalPlacement] = 0x2E;
if (decimalPlacement == 1) {
buffer[0] = 0x30;
}
while (value != 0) {
digits -= 1;
if (digits != decimalPlacement) {
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
}
return string(buffer);
}
}
文件 45 的 46:baseSVG.sol
pragma solidity 0.8.24;
import {svg} from "./SVG.sol";
import {utils, LibString, numUtils} from "./Utils.sol";
import "./FixedAssets.sol";
library baseSVG {
string constant GEIST = 'style="font-family: Geist" ';
string constant DARK_BLUE = "#121B44";
string constant STOIC_WHITE = "#DEE4FB";
function _svgProps() internal pure returns (string memory) {
return string.concat(
svg.prop("width", "300"),
svg.prop("height", "484"),
svg.prop("viewBox", "0 0 300 484"),
svg.prop("style", "background:none")
);
}
function _baseElements(FixedAssetReader _assetReader) internal view returns (string memory) {
return string.concat(
svg.rect(
string.concat(
svg.prop("fill", DARK_BLUE),
svg.prop("rx", "8"),
svg.prop("width", "300"),
svg.prop("height", "484")
)
),
_styles(_assetReader),
_leverageLogo(),
_boldLogo(_assetReader),
_staticTextEls()
);
}
function _styles(FixedAssetReader _assetReader) private view returns (string memory) {
return svg.el(
"style",
utils.NULL,
string.concat(
'@font-face { font-family: "Geist"; src: url("data:font/woff2;utf-8;base64,',
_assetReader.readAsset(bytes4(keccak256("geist"))),
'"); }'
)
);
}
function _leverageLogo() internal pure returns (string memory) {
return string.concat(
svg.path(
"M20.2 31.2C19.1 32.4 17.6 33 16 33L16 21C17.6 21 19.1 21.6 20.2 22.7C21.4 23.9 22 25.4 22 27C22 28.6 21.4 30.1 20.2 31.2Z",
svg.prop("fill", STOIC_WHITE)
),
svg.path(
"M22 27C22 25.4 22.6 23.9 23.8 22.7C25 21.6 26.4 21 28 21V33C26.4 33 25 32.4 24 31.2C22.6 30.1 22 28.6 22 27Z",
svg.prop("fill", STOIC_WHITE)
)
);
}
function _boldLogo(FixedAssetReader _assetReader) internal view returns (string memory) {
return svg.el(
"image",
string.concat(
svg.prop("x", "264"),
svg.prop("y", "373.5"),
svg.prop("width", "20"),
svg.prop("height", "20"),
svg.prop(
"href",
string.concat("data:image/svg+xml;base64,", _assetReader.readAsset(bytes4(keccak256("BOLD"))))
)
)
);
}
function _staticTextEls() internal pure returns (string memory) {
return string.concat(
svg.text(
string.concat(
GEIST,
svg.prop("x", "16"),
svg.prop("y", "358"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
"Collateral"
),
svg.text(
string.concat(
GEIST,
svg.prop("x", "16"),
svg.prop("y", "389"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
"Debt"
),
svg.text(
string.concat(
GEIST,
svg.prop("x", "16"),
svg.prop("y", "420"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
"Interest Rate"
),
svg.text(
string.concat(
GEIST,
svg.prop("x", "265"),
svg.prop("y", "422"),
svg.prop("font-size", "20"),
svg.prop("fill", "white")
),
"%"
),
svg.text(
string.concat(
GEIST,
svg.prop("x", "16"),
svg.prop("y", "462"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
"Owner"
)
);
}
function _formattedDynamicEl(string memory _value, uint256 _x, uint256 _y) internal pure returns (string memory) {
return svg.text(
string.concat(
GEIST,
svg.prop("text-anchor", "end"),
svg.prop("x", LibString.toString(_x)),
svg.prop("y", LibString.toString(_y)),
svg.prop("font-size", "20"),
svg.prop("fill", "white")
),
_value
);
}
function _formattedIdEl(string memory _id) internal pure returns (string memory) {
return svg.text(
string.concat(
GEIST,
svg.prop("text-anchor", "end"),
svg.prop("x", "284"),
svg.prop("y", "33"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
_id
);
}
function _formattedAddressEl(address _address) internal pure returns (string memory) {
return svg.text(
string.concat(
GEIST,
svg.prop("text-anchor", "end"),
svg.prop("x", "284"),
svg.prop("y", "462"),
svg.prop("font-size", "14"),
svg.prop("fill", "white")
),
string.concat(
LibString.slice(LibString.toHexStringChecksummed(_address), 0, 6),
"...",
LibString.slice(LibString.toHexStringChecksummed(_address), 38, 42)
)
);
}
function _collLogo(string memory _collName, FixedAssetReader _assetReader) internal view returns (string memory) {
return svg.el(
"image",
string.concat(
svg.prop("x", "264"),
svg.prop("y", "342.5"),
svg.prop("width", "20"),
svg.prop("height", "20"),
svg.prop(
"href",
string.concat(
"data:image/svg+xml;base64,", _assetReader.readAsset(bytes4(keccak256(bytes(_collName))))
)
)
)
);
}
function _statusEl(string memory _status) internal pure returns (string memory) {
return svg.text(
string.concat(
GEIST, svg.prop("x", "40"), svg.prop("y", "33"), svg.prop("font-size", "14"), svg.prop("fill", "white")
),
_status
);
}
function _dynamicTextEls(uint256 _debt, uint256 _coll, uint256 _annualInterestRate)
internal
pure
returns (string memory)
{
return string.concat(
_formattedDynamicEl(numUtils.toLocaleString(_coll, 18, 4), 256, 360),
_formattedDynamicEl(numUtils.toLocaleString(_debt, 18, 2), 256, 391),
_formattedDynamicEl(numUtils.toLocaleString(_annualInterestRate, 16, 2), 256, 422)
);
}
}
文件 46 的 46:bauhaus.sol
pragma solidity 0.8.24;
import "./SVG.sol";
library bauhaus {
string constant GOLDEN = "#F5D93A";
string constant CORAL = "#FB7C59";
string constant GREEN = "#63D77D";
string constant CYAN = "#95CBF3";
string constant BLUE = "#405AE5";
string constant DARK_BLUE = "#121B44";
string constant BROWN = "#D99664";
enum colorCode {
GOLDEN,
CORAL,
GREEN,
CYAN,
BLUE,
DARK_BLUE,
BROWN
}
function _bauhaus(string memory _collName, uint256 _troveId) internal pure returns (string memory) {
bytes32 collSig = keccak256(bytes(_collName));
uint256 variant = _troveId % 4;
if (collSig == keccak256("WETH")) {
return _img1(variant);
} else if (collSig == keccak256("wstETH")) {
return _img2(variant);
} else {
return _img3(variant);
}
}
function _colorCode2Hex(colorCode _color) private pure returns (string memory) {
if (_color == colorCode.GOLDEN) {
return GOLDEN;
} else if (_color == colorCode.CORAL) {
return CORAL;
} else if (_color == colorCode.GREEN) {
return GREEN;
} else if (_color == colorCode.CYAN) {
return CYAN;
} else if (_color == colorCode.BLUE) {
return BLUE;
} else if (_color == colorCode.DARK_BLUE) {
return DARK_BLUE;
} else {
return BROWN;
}
}
struct COLORS {
colorCode rect1;
colorCode rect2;
colorCode rect3;
colorCode rect4;
colorCode rect5;
colorCode poly;
colorCode circle1;
colorCode circle2;
colorCode circle3;
}
function _colors1(uint256 _variant) internal pure returns (COLORS memory) {
if (_variant == 0) {
return COLORS(
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.CORAL,
colorCode.CYAN,
colorCode.GREEN,
colorCode.DARK_BLUE,
colorCode.GOLDEN
);
} else if (_variant == 1) {
return COLORS(
colorCode.GREEN,
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.GOLDEN,
colorCode.CORAL,
colorCode.BLUE,
colorCode.DARK_BLUE,
colorCode.BLUE
);
} else if (_variant == 2) {
return COLORS(
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.CYAN,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.GREEN,
colorCode.CORAL,
colorCode.DARK_BLUE,
colorCode.BROWN
);
} else {
return COLORS(
colorCode.CYAN,
colorCode.BLUE,
colorCode.BLUE,
colorCode.BROWN,
colorCode.BLUE,
colorCode.GREEN,
colorCode.GOLDEN,
colorCode.DARK_BLUE,
colorCode.BLUE
);
}
}
function _img1(uint256 _variant) internal pure returns (string memory) {
COLORS memory colors = _colors1(_variant);
return string.concat(_rects1(colors), _polygons1(colors), _circles1(colors));
}
function _rects1(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "55"),
svg.prop("width", "268"),
svg.prop("height", "268"),
svg.prop("fill", DARK_BLUE)
)
),
svg.rect(
string.concat(
svg.prop("x", "128"),
svg.prop("y", "55"),
svg.prop("width", "156"),
svg.prop("height", "268"),
svg.prop("fill", _colorCode2Hex(_colors.rect1))
)
),
svg.rect(
string.concat(
svg.prop("x", "228"),
svg.prop("y", "55"),
svg.prop("width", "56"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect2))
)
),
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "111"),
svg.prop("width", "134"),
svg.prop("height", "156"),
svg.prop("fill", _colorCode2Hex(_colors.rect3))
)
),
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "267"),
svg.prop("width", "112"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect4))
)
),
svg.rect(
string.concat(
svg.prop("x", "228"),
svg.prop("y", "267"),
svg.prop("width", "56"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect5))
)
)
);
}
function _polygons1(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.polygon(
string.concat(svg.prop("points", "16,55 72,55 16,111"), svg.prop("fill", _colorCode2Hex(_colors.poly)))
),
svg.polygon(
string.concat(svg.prop("points", "72,55 128,55 72,111"), svg.prop("fill", _colorCode2Hex(_colors.poly)))
)
);
}
function _circles1(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.circle(
string.concat(
svg.prop("cx", "150"),
svg.prop("cy", "189"),
svg.prop("r", "78"),
svg.prop("fill", _colorCode2Hex(_colors.circle1))
)
),
svg.circle(
string.concat(
svg.prop("cx", "228"),
svg.prop("cy", "295"),
svg.prop("r", "28"),
svg.prop("fill", _colorCode2Hex(_colors.circle2))
)
),
svg.path(
"M228 267C220.574 267 213.452 269.95 208.201 275.201C202.95 280.452 200 287.574 200 295C200 302.426 202.95 309.548 208.201 314.799C213.452 320.05 220.574 323 228 323L228 267Z",
svg.prop("fill", _colorCode2Hex(_colors.circle3))
)
);
}
function _colors2(uint256 _variant) internal pure returns (COLORS memory) {
if (_variant == 0) {
return COLORS(
colorCode.BROWN,
colorCode.GOLDEN,
colorCode.BLUE,
colorCode.GREEN,
colorCode.CORAL,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.CYAN,
colorCode.GREEN
);
} else if (_variant == 1) {
return COLORS(
colorCode.GREEN,
colorCode.BROWN,
colorCode.GOLDEN,
colorCode.BLUE,
colorCode.CYAN,
colorCode.GOLDEN,
colorCode.GREEN,
colorCode.CORAL,
colorCode.BLUE
);
} else if (_variant == 2) {
return COLORS(
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.GREEN,
colorCode.BLUE,
colorCode.CORAL,
colorCode.GOLDEN,
colorCode.CYAN,
colorCode.BROWN,
colorCode.BROWN
);
} else {
return COLORS(
colorCode.GOLDEN,
colorCode.GREEN,
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.CYAN,
colorCode.CORAL
);
}
}
function _img2(uint256 _variant) internal pure returns (string memory) {
COLORS memory colors = _colors2(_variant);
return string.concat(_rects2(colors), _circles2(colors));
}
function _rects2(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "55"),
svg.prop("width", "268"),
svg.prop("height", "268"),
svg.prop("fill", DARK_BLUE)
)
),
svg.rect(
string.concat(
svg.prop("x", "128"),
svg.prop("y", "55"),
svg.prop("width", "156"),
svg.prop("height", "156"),
svg.prop("fill", _colorCode2Hex(_colors.rect1))
)
),
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "111"),
svg.prop("width", "134"),
svg.prop("height", "100"),
svg.prop("fill", _colorCode2Hex(_colors.rect2))
)
),
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "211"),
svg.prop("width", "212"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect3))
)
),
svg.rect(
string.concat(
svg.prop("x", "72"),
svg.prop("y", "267"),
svg.prop("width", "78"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect4))
)
),
svg.rect(
string.concat(
svg.prop("x", "150"),
svg.prop("y", "267"),
svg.prop("width", "134"),
svg.prop("height", "56"),
svg.prop("fill", _colorCode2Hex(_colors.rect5))
)
)
);
}
function _circles2(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.circle(
string.concat(
svg.prop("cx", "44"),
svg.prop("cy", "295"),
svg.prop("r", "28"),
svg.prop("fill", _colorCode2Hex(_colors.circle1))
)
),
svg.path(
"M16 55C16 62.4 17.4 69.6 20.3 76.4C23.1 83.2 27.2 89.4 32.4 94.6C37.6 99.8 43.8 103.9 50.6 106.7C57.4 109.6 64.6 111 72 111C79.4 111 86.6 109.6 93.4 106.7C100.2 103.9 106.4 99.8 111.6 94.6C116.8 89.4 120.9 83.2 123.7 76.4C126.6 69.6 128 62.4 128 55L16 55Z",
svg.prop("fill", _colorCode2Hex(_colors.circle2))
),
svg.path(
"M284 211C284 190.3 275.8 170.5 261.2 155.8C246.5 141.2 226.7 133 206 133C185.3 133 165.5 141.2 150.9 155.86C136.2 170.5 128 190.3 128 211L284 211Z",
svg.prop("fill", _colorCode2Hex(_colors.circle3))
)
);
}
function _colors3(uint256 _variant) internal pure returns (COLORS memory) {
if (_variant == 0) {
return COLORS(
colorCode.BLUE,
colorCode.CORAL,
colorCode.BLUE,
colorCode.GREEN,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.CYAN,
colorCode.GOLDEN
);
} else if (_variant == 1) {
return COLORS(
colorCode.CORAL,
colorCode.GREEN,
colorCode.BROWN,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.BLUE,
colorCode.BLUE,
colorCode.CYAN
);
} else if (_variant == 2) {
return COLORS(
colorCode.CORAL,
colorCode.CYAN,
colorCode.CORAL,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.GREEN,
colorCode.BLUE,
colorCode.GREEN
);
} else {
return COLORS(
colorCode.GOLDEN,
colorCode.CORAL,
colorCode.GREEN,
colorCode.BLUE,
colorCode.GOLDEN,
colorCode.GOLDEN,
colorCode.BROWN,
colorCode.BLUE,
colorCode.GREEN
);
}
}
function _img3(uint256 _variant) internal pure returns (string memory) {
COLORS memory colors = _colors3(_variant);
return string.concat(_rects3(colors), _circles3(colors));
}
function _rects3(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "55"),
svg.prop("width", "268"),
svg.prop("height", "268"),
svg.prop("fill", DARK_BLUE)
)
),
svg.rect(
string.concat(
svg.prop("x", "16"),
svg.prop("y", "205"),
svg.prop("width", "75"),
svg.prop("height", "118"),
svg.prop("fill", _colorCode2Hex(_colors.rect1))
)
),
svg.rect(
string.concat(
svg.prop("x", "91"),
svg.prop("y", "205"),
svg.prop("width", "136"),
svg.prop("height", "59"),
svg.prop("fill", _colorCode2Hex(_colors.rect2))
)
),
svg.rect(
string.concat(
svg.prop("x", "166"),
svg.prop("y", "180"),
svg.prop("width", "118"),
svg.prop("height", "25"),
svg.prop("fill", _colorCode2Hex(_colors.rect3))
)
),
svg.rect(
string.concat(
svg.prop("x", "166"),
svg.prop("y", "55"),
svg.prop("width", "118"),
svg.prop("height", "126"),
svg.prop("fill", _colorCode2Hex(_colors.rect4))
)
)
);
}
function _circles3(COLORS memory _colors) internal pure returns (string memory) {
return string.concat(
svg.circle(
string.concat(
svg.prop("cx", "91"),
svg.prop("cy", "130"),
svg.prop("r", "75"),
svg.prop("fill", _colorCode2Hex(_colors.circle1))
)
),
svg.path(
"M284 264 166 264 166 263C166 232 193 206 225 205C258 206 284 232 284 264C284 264 284 264 284 264Z",
svg.prop("fill", _colorCode2Hex(_colors.circle2))
),
svg.path(
"M284 323 166 323 166 323C166 290 193 265 225 264C258 265 284 290 284 323C284 323 284 323 284 323Z",
svg.prop("fill", _colorCode2Hex(_colors.circle3))
)
);
}
}
{
"compilationTarget": {
"src/BorrowerOperations.sol": "BorrowerOperations"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@chimera/=lib/V2-gov/lib/chimera/src/",
":@openzeppelin/contracts/=lib/V2-gov/lib/openzeppelin-contracts/contracts/",
":Solady/=lib/Solady/src/",
":V2-gov/=lib/V2-gov/",
":chimera/=lib/V2-gov/lib/chimera/src/",
":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/",
":v4-core/=lib/V2-gov/lib/v4-core/"
]
}
[{"inputs":[{"internalType":"contract IAddressesRegistry","name":"_addressesRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AnnualManagementFeeTooHigh","type":"error"},{"inputs":[],"name":"BatchInterestRateChangePeriodNotPassed","type":"error"},{"inputs":[],"name":"BatchManagerExists","type":"error"},{"inputs":[],"name":"BatchManagerNotNew","type":"error"},{"inputs":[],"name":"CallerNotPriceFeed","type":"error"},{"inputs":[],"name":"CallerNotTroveManager","type":"error"},{"inputs":[],"name":"CollWithdrawalTooHigh","type":"error"},{"inputs":[],"name":"DebtBelowMin","type":"error"},{"inputs":[],"name":"DelegateInterestRateChangePeriodNotPassed","type":"error"},{"inputs":[],"name":"EmptyManager","type":"error"},{"inputs":[],"name":"ICRBelowMCR","type":"error"},{"inputs":[],"name":"InterestNotInRange","type":"error"},{"inputs":[],"name":"InterestRateNotNew","type":"error"},{"inputs":[],"name":"InterestRateTooHigh","type":"error"},{"inputs":[],"name":"InterestRateTooLow","type":"error"},{"inputs":[],"name":"InvalidInterestBatchManager","type":"error"},{"inputs":[],"name":"IsShutDown","type":"error"},{"inputs":[],"name":"MinGeMax","type":"error"},{"inputs":[],"name":"MinInterestRateChangePeriodTooLow","type":"error"},{"inputs":[],"name":"NewFeeNotLower","type":"error"},{"inputs":[],"name":"NewOracleFailureDetected","type":"error"},{"inputs":[],"name":"NotBorrower","type":"error"},{"inputs":[],"name":"NotEnoughBoldBalance","type":"error"},{"inputs":[],"name":"NotOwnerNorAddManager","type":"error"},{"inputs":[],"name":"NotOwnerNorInterestManager","type":"error"},{"inputs":[],"name":"NotOwnerNorRemoveManager","type":"error"},{"inputs":[],"name":"RepaymentNotMatchingCollWithdrawal","type":"error"},{"inputs":[],"name":"TCRBelowCCR","type":"error"},{"inputs":[],"name":"TCRNotBelowSCR","type":"error"},{"inputs":[],"name":"TroveExists","type":"error"},{"inputs":[],"name":"TroveInBatch","type":"error"},{"inputs":[],"name":"TroveNotActive","type":"error"},{"inputs":[],"name":"TroveNotInBatch","type":"error"},{"inputs":[],"name":"TroveNotOpen","type":"error"},{"inputs":[],"name":"TroveNotZombie","type":"error"},{"inputs":[],"name":"TroveWithZeroDebt","type":"error"},{"inputs":[],"name":"UpfrontFeeTooHigh","type":"error"},{"inputs":[],"name":"ZeroAdjustment","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newActivePoolAddress","type":"address"}],"name":"ActivePoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_troveId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_newAddManager","type":"address"}],"name":"AddManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_boldTokenAddress","type":"address"}],"name":"BoldTokenAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_collSurplusPoolAddress","type":"address"}],"name":"CollSurplusPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newDefaultPoolAddress","type":"address"}],"name":"DefaultPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_gasPoolAddress","type":"address"}],"name":"GasPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newPriceFeedAddress","type":"address"}],"name":"PriceFeedAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_troveId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_newRemoveManager","type":"address"},{"indexed":false,"internalType":"address","name":"_newReceiver","type":"address"}],"name":"RemoveManagerAndReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tcr","type":"uint256"}],"name":"ShutDown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sortedTrovesAddress","type":"address"}],"name":"SortedTrovesAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newTroveManagerAddress","type":"address"}],"name":"TroveManagerAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newTroveNFTAddress","type":"address"}],"name":"TroveNFTAddressChanged","type":"event"},{"inputs":[],"name":"CCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activePool","outputs":[{"internalType":"contract IActivePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_collAmount","type":"uint256"}],"name":"addColl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"addManagerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_collChange","type":"uint256"},{"internalType":"bool","name":"_isCollIncrease","type":"bool"},{"internalType":"uint256","name":"_boldChange","type":"uint256"},{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"adjustTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_newAnnualInterestRate","type":"uint256"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"adjustTroveInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_collChange","type":"uint256"},{"internalType":"bool","name":"_isCollIncrease","type":"bool"},{"internalType":"uint256","name":"_boldChange","type":"uint256"},{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"adjustZombieTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_upperHint","type":"uint256"}],"name":"applyPendingDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_batchManager","type":"address"}],"name":"checkBatchManagerExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"}],"name":"closeTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getEntireSystemColl","outputs":[{"internalType":"uint256","name":"entireSystemColl","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemDebt","outputs":[{"internalType":"uint256","name":"entireSystemDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getInterestBatchManager","outputs":[{"components":[{"internalType":"uint128","name":"minInterestRate","type":"uint128"},{"internalType":"uint128","name":"maxInterestRate","type":"uint128"},{"internalType":"uint256","name":"minInterestRateChangePeriod","type":"uint256"}],"internalType":"struct IBorrowerOperations.InterestBatchManager","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"}],"name":"getInterestIndividualDelegateOf","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint128","name":"minInterestRate","type":"uint128"},{"internalType":"uint128","name":"maxInterestRate","type":"uint128"},{"internalType":"uint256","name":"minInterestRateChangePeriod","type":"uint256"}],"internalType":"struct IBorrowerOperations.InterestIndividualDelegate","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasBeenShutDown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"interestBatchManagerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newAnnualManagementFee","type":"uint256"}],"name":"lowerBatchManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"}],"name":"onLiquidateTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_ownerIndex","type":"uint256"},{"internalType":"uint256","name":"_collAmount","type":"uint256"},{"internalType":"uint256","name":"_boldAmount","type":"uint256"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_annualInterestRate","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"},{"internalType":"address","name":"_addManager","type":"address"},{"internalType":"address","name":"_removeManager","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"openTrove","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"ownerIndex","type":"uint256"},{"internalType":"uint256","name":"collAmount","type":"uint256"},{"internalType":"uint256","name":"boldAmount","type":"uint256"},{"internalType":"uint256","name":"upperHint","type":"uint256"},{"internalType":"uint256","name":"lowerHint","type":"uint256"},{"internalType":"address","name":"interestBatchManager","type":"address"},{"internalType":"uint256","name":"maxUpfrontFee","type":"uint256"},{"internalType":"address","name":"addManager","type":"address"},{"internalType":"address","name":"removeManager","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IBorrowerOperations.OpenTroveAndJoinInterestBatchManagerParams","name":"_params","type":"tuple"}],"name":"openTroveAndJoinInterestBatchManager","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_minInterestRate","type":"uint128"},{"internalType":"uint128","name":"_maxInterestRate","type":"uint128"},{"internalType":"uint128","name":"_currentInterestRate","type":"uint128"},{"internalType":"uint128","name":"_annualManagementFee","type":"uint128"},{"internalType":"uint128","name":"_minInterestRateChangePeriod","type":"uint128"}],"name":"registerBatchManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_newAnnualInterestRate","type":"uint256"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"removeFromBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"}],"name":"removeInterestIndividualDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"removeManagerReceiverOf","outputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_boldAmount","type":"uint256"}],"name":"repayBold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"address","name":"_manager","type":"address"}],"name":"setAddManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_newAnnualInterestRate","type":"uint128"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"setBatchManagerAnnualInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"address","name":"_newBatchManager","type":"address"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"setInterestBatchManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"uint128","name":"_minInterestRate","type":"uint128"},{"internalType":"uint128","name":"_maxInterestRate","type":"uint128"},{"internalType":"uint256","name":"_newAnnualInterestRate","type":"uint256"},{"internalType":"uint256","name":"_upperHint","type":"uint256"},{"internalType":"uint256","name":"_lowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"},{"internalType":"uint256","name":"_minInterestRateChangePeriod","type":"uint256"}],"name":"setInterestIndividualDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"address","name":"_manager","type":"address"}],"name":"setRemoveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"setRemoveManagerWithReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shutdownFromOracleFailure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_removeUpperHint","type":"uint256"},{"internalType":"uint256","name":"_removeLowerHint","type":"uint256"},{"internalType":"address","name":"_newBatchManager","type":"address"},{"internalType":"uint256","name":"_addUpperHint","type":"uint256"},{"internalType":"uint256","name":"_addLowerHint","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"switchBatchManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_boldAmount","type":"uint256"},{"internalType":"uint256","name":"_maxUpfrontFee","type":"uint256"}],"name":"withdrawBold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_troveId","type":"uint256"},{"internalType":"uint256","name":"_collWithdrawal","type":"uint256"}],"name":"withdrawColl","outputs":[],"stateMutability":"nonpayable","type":"function"}]