Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contracts: Convert Troves to ERC721 NFTs #81

Merged
merged 5 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 152 additions & 67 deletions contracts/src/BorrowerOperations.sol

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions contracts/src/CollSurplusPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
// deposited ether tracker
uint256 internal ETHBalance;
// Collateral surplus claimable by trove owners
mapping (address => uint) internal balances;
mapping (uint256 => uint) internal balances;

// --- Events ---

event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _newActivePoolAddress);

event CollBalanceUpdated(address indexed _account, uint _newBalance);
event CollBalanceUpdated(uint256 indexed _troveId, uint _newBalance);
event EtherSent(address _to, uint _amount);

constructor(address _ETHAddress) {
Expand Down Expand Up @@ -70,29 +70,29 @@ contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
return ETHBalance;
}

function getCollateral(address _account) external view override returns (uint) {
return balances[_account];
function getCollateral(uint256 _troveId) external view override returns (uint) {
return balances[_troveId];
}

// --- Pool functionality ---

function accountSurplus(address _account, uint _amount) external override {
function accountSurplus(uint256 _troveId, uint _amount) external override {
_requireCallerIsTroveManager();

uint newAmount = balances[_account] + _amount;
balances[_account] = newAmount;
uint newAmount = balances[_troveId] + _amount;
balances[_troveId] = newAmount;
ETHBalance = ETHBalance + _amount;

emit CollBalanceUpdated(_account, newAmount);
emit CollBalanceUpdated(_troveId, newAmount);
}

function claimColl(address _account) external override {
function claimColl(address _account, uint256 _troveId) external override {
_requireCallerIsBorrowerOperations();
uint claimableColl = balances[_account];
uint claimableColl = balances[_troveId];
require(claimableColl > 0, "CollSurplusPool: No collateral available to claim");

balances[_account] = 0;
emit CollBalanceUpdated(_account, 0);
balances[_troveId] = 0;
emit CollBalanceUpdated(_troveId, 0);

ETHBalance = ETHBalance - claimableColl;
emit EtherSent(_account, claimableColl);
Expand Down
21 changes: 0 additions & 21 deletions contracts/src/Dependencies/LiquityMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,6 @@ pragma solidity 0.8.18;
library LiquityMath {
uint internal constant DECIMAL_PRECISION = 1e18;

/* Precision for Nominal ICR (independent of price). Rationale for the value:
*
* - Making it “too high” could lead to overflows.
* - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.
*
* This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH,
* and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.
*
*/
uint internal constant NICR_PRECISION = 1e20;

function _min(uint _a, uint _b) internal pure returns (uint) {
return (_a < _b) ? _a : _b;
}
Expand Down Expand Up @@ -85,16 +74,6 @@ library LiquityMath {
return (_a >= _b) ? _a - _b : _b - _a;
}

function _computeNominalCR(uint _coll, uint _debt) internal pure returns (uint) {
if (_debt > 0) {
return _coll * NICR_PRECISION / _debt;
}
// Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
else { // if (_debt == 0)
return 2**256 - 1;
}
}

function _computeCR(uint _coll, uint _debt, uint _price) internal pure returns (uint) {
if (_debt > 0) {
uint newCollRatio = _coll * _price / _debt;
Expand Down
112 changes: 15 additions & 97 deletions contracts/src/HintHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";


contract HintHelpers is LiquityBase, Ownable, CheckContract {
string constant public NAME = "HintHelpers";

Expand Down Expand Up @@ -42,103 +43,28 @@ contract HintHelpers is LiquityBase, Ownable, CheckContract {

// --- Functions ---

/* getRedemptionHints() - Helper function for finding the right hints to pass to redeemCollateral().
*
* It simulates a redemption of `_boldamount` to figure out where the redemption sequence will start and what state the final Trove
* of the sequence will end up in.
*
* Returns three hints:
* - `firstRedemptionHint` is the address of the first Trove with ICR >= MCR (i.e. the first Trove that will be redeemed).
* - `partialRedemptionHintNICR` is the final nominal ICR of the last Trove of the sequence after being hit by partial redemption,
* or zero in case of no partial redemption.
* - `truncatedBoldamount` is the maximum amount that can be redeemed out of the the provided `_boldamount`. This can be lower than
* `_boldamount` when redeeming the full amount would leave the last Trove of the redemption sequence with less net debt than the
* minimum allowed value (i.e. MIN_NET_DEBT).
*
* The number of Troves to consider for redemption can be capped by passing a non-zero value as `_maxIterations`, while passing zero
* will leave it uncapped.
*/

function getRedemptionHints(
uint _boldamount,
uint _price,
uint _maxIterations
)
external
view
returns (
address firstRedemptionHint,
uint partialRedemptionHintNICR,
uint truncatedBoldamount
)
{
ISortedTroves sortedTrovesCached = sortedTroves;

uint remainingBold = _boldamount;
address currentTroveuser = sortedTrovesCached.getLast();

while (currentTroveuser != address(0) && troveManager.getCurrentICR(currentTroveuser, _price) < MCR) {
currentTroveuser = sortedTrovesCached.getPrev(currentTroveuser);
}

firstRedemptionHint = currentTroveuser;

if (_maxIterations == 0) {
_maxIterations = type(uint256).max;
}

while (currentTroveuser != address(0) && remainingBold > 0 && _maxIterations-- > 0) {
uint netBoldDebt = _getNetDebt(troveManager.getTroveDebt(currentTroveuser))
+ troveManager.getPendingBoldDebtReward(currentTroveuser);

if (netBoldDebt > remainingBold) {
if (netBoldDebt > MIN_NET_DEBT) {
uint maxRedeemableBold = LiquityMath._min(remainingBold, netBoldDebt - MIN_NET_DEBT);

uint ETH = troveManager.getTroveColl(currentTroveuser)
+ troveManager.getPendingETHReward(currentTroveuser);

uint newColl = ETH - maxRedeemableBold * DECIMAL_PRECISION / _price;
uint newDebt = netBoldDebt - maxRedeemableBold;

uint compositeDebt = _getCompositeDebt(newDebt);
partialRedemptionHintNICR = LiquityMath._computeNominalCR(newColl, compositeDebt);

remainingBold = remainingBold - maxRedeemableBold;
}
break;
} else {
remainingBold = remainingBold - netBoldDebt;
}

currentTroveuser = sortedTrovesCached.getPrev(currentTroveuser);
}

truncatedBoldamount = _boldamount - remainingBold;
}

/* getApproxHint() - return address of a Trove that is, on average, (length / numTrials) positions away in the
/* getApproxHint() - return id of a Trove that is, on average, (length / numTrials) positions away in the
sortedTroves list from the correct insert position of the Trove to be inserted.

Note: The output address is worst-case O(n) positions away from the correct insert position, however, the function
Note: The output id is worst-case O(n) positions away from the correct insert position, however, the function
is probabilistic. Input can be tuned to guarantee results to a high degree of confidence, e.g:

Submitting numTrials = k * sqrt(length), with k = 15 makes it very, very likely that the ouput address will
Submitting numTrials = k * sqrt(length), with k = 15 makes it very, very likely that the ouput id will
be <= sqrt(length) positions away from the correct insert position.
*/
function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed)
function getApproxHint(uint _interestRate, uint _numTrials, uint _inputRandomSeed)
external
view
returns (address hintAddress, uint diff, uint latestRandomSeed)
returns (uint256 hintId, uint diff, uint latestRandomSeed)
{
uint arrayLength = troveManager.getTroveOwnersCount();
uint arrayLength = troveManager.getTroveIdsCount();

if (arrayLength == 0) {
return (address(0), 0, _inputRandomSeed);
return (0, 0, _inputRandomSeed);
}

hintAddress = sortedTroves.getLast();
diff = LiquityMath._getAbsoluteDifference(_CR, troveManager.getNominalICR(hintAddress));
hintId = sortedTroves.getLast();
diff = LiquityMath._getAbsoluteDifference(_interestRate, troveManager.getTroveAnnualInterestRate(hintId));
latestRandomSeed = _inputRandomSeed;

uint i = 1;
Expand All @@ -147,25 +73,17 @@ contract HintHelpers is LiquityBase, Ownable, CheckContract {
latestRandomSeed = uint(keccak256(abi.encodePacked(latestRandomSeed)));

uint arrayIndex = latestRandomSeed % arrayLength;
address currentAddress = troveManager.getTroveFromTroveOwnersArray(arrayIndex);
uint currentNICR = troveManager.getNominalICR(currentAddress);
uint256 currentId = troveManager.getTroveFromTroveIdsArray(arrayIndex);
uint currentInterestRate = troveManager.getTroveAnnualInterestRate(currentId);

// check if abs(current - CR) > abs(closest - CR), and update closest if current is closer
uint currentDiff = LiquityMath._getAbsoluteDifference(currentNICR, _CR);
// check if abs(current - IR) > abs(closest - IR), and update closest if current is closer
uint currentDiff = LiquityMath._getAbsoluteDifference(currentInterestRate, _interestRate);

if (currentDiff < diff) {
diff = currentDiff;
hintAddress = currentAddress;
hintId = currentId;
}
i++;
}
}

function computeNominalCR(uint _coll, uint _debt) external pure returns (uint) {
return LiquityMath._computeNominalCR(_coll, _debt);
}

function computeCR(uint _coll, uint _debt, uint _price) external pure returns (uint) {
return LiquityMath._computeCR(_coll, _debt, _price);
}
}
25 changes: 15 additions & 10 deletions contracts/src/Interfaces/IBorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,30 @@ interface IBorrowerOperations is ILiquityBase {
address _boldTokenAddress
) external;

function openTrove(uint _maxFee, uint256 _ETHAmount, uint _boldAmount, address _upperHint, address _lowerHint, uint256 _annualInterestRate) external;
function openTrove(address _owner, uint256 _ownerIndex, uint _maxFee, uint256 _ETHAmount, uint _boldAmount, uint256 _upperHint, uint256 _lowerHint, uint256 _annualInterestRate) external returns (uint256);

function addColl(uint256 _ETHAmount) external;
function addColl(uint256 _troveId, uint256 _ETHAmount) external;

function moveETHGainToTrove(address _user, uint256 _ETHAmount) external;
function moveETHGainToTrove(address _sender, uint256 _troveId, uint256 _ETHAmount) external;

function withdrawColl(uint _amount) external;
function withdrawColl(uint256 _troveId, uint _amount) external;

function withdrawBold(uint _maxFee, uint _amount) external;
function withdrawBold(uint256 _troveId, uint _maxFee, uint _amount) external;

function repayBold(uint _amount) external;
function repayBold(uint256 _troveId, uint _amount) external;

function closeTrove() external;
function closeTrove(uint256 _troveId) external;

function adjustTrove(uint _maxFee, uint _collChange, bool _isCollIncrease, uint _debtChange, bool isDebtIncrease) external;
function adjustTrove(uint256 _troveId, uint _maxFee, uint _collChange, bool _isCollIncrease, uint _debtChange, bool isDebtIncrease) external;

function claimCollateral() external;
function claimCollateral(uint256 _troveId) external;

function setAddManager(uint256 _troveId, address _manager) external;
function setRemoveManager(uint256 _troveId, address _manager) external;

// TODO: addRepayWhitelistedAddress?(see github issue #64)

function getCompositeDebt(uint _debt) external pure returns (uint);

function adjustTroveInterestRate(uint _newAnnualInterestRate, address _upperHint, address _lowerHint) external;
function adjustTroveInterestRate(uint256 _troveId, uint _newAnnualInterestRate, uint256 _upperHint, uint256 _lowerHint) external;
}
6 changes: 3 additions & 3 deletions contracts/src/Interfaces/ICollSurplusPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ interface ICollSurplusPool {

function getETHBalance() external view returns (uint);

function getCollateral(address _account) external view returns (uint);
function getCollateral(uint256 _troveId) external view returns (uint);

function accountSurplus(address _account, uint _amount) external;
function accountSurplus(uint256 _troveId, uint _amount) external;

function claimColl(address _account) external;
function claimColl(address _account, uint256 _troveId) external;
}
25 changes: 15 additions & 10 deletions contracts/src/Interfaces/ISortedTroves.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ pragma solidity 0.8.18;

import "./ITroveManager.sol";

// TODO
//type Id is uint256;
//type Value is uint256;


// Common interface for the SortedTroves Doubly Linked List.
interface ISortedTroves {
function borrowerOperationsAddress() external view returns (address);
function troveManager() external view returns (ITroveManager);

function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress) external;

function insert(address _id, uint256 _ICR, address _prevId, address _nextId) external;
function insert(uint256 _id, uint256 _value, uint256 _prevId, uint256 _nextId) external;

function remove(address _id) external;
function remove(uint256 _id) external;

function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) external;
function reInsert(uint256 _id, uint256 _newValue, uint256 _prevId, uint256 _nextId) external;

function contains(address _id) external view returns (bool);
function contains(uint256 _id) external view returns (bool);

function isFull() external view returns (bool);

Expand All @@ -27,15 +32,15 @@ interface ISortedTroves {

function getMaxSize() external view returns (uint256);

function getFirst() external view returns (address);
function getFirst() external view returns (uint256);

function getLast() external view returns (address);
function getLast() external view returns (uint256);

function getNext(address _id) external view returns (address);
function getNext(uint256 _id) external view returns (uint256);

function getPrev(address _id) external view returns (address);
function getPrev(uint256 _id) external view returns (uint256);

function validInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (bool);
function validInsertPosition(uint256 _value, uint256 _prevId, uint256 _nextId) external view returns (bool);

function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address);
function findInsertPosition(uint256 _value, uint256 _prevId, uint256 _nextId) external view returns (uint256, uint256);
}
4 changes: 2 additions & 2 deletions contracts/src/Interfaces/IStabilityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ interface IStabilityPool is ILiquityBase {
*/
function provideToSP(uint _amount) external;


/* withdrawFromSP():
* - Calculates depositor's ETH gain
* - Calculates the compounded deposit
Expand All @@ -69,7 +69,7 @@ interface IStabilityPool is ILiquityBase {
* - Leaves their compounded deposit in the Stability Pool
* - Takes new snapshots of accumulators P and S
*/
function withdrawETHGainToTrove() external;
function withdrawETHGainToTrove(uint256 _troveId) external;

/*
* Initial checks:
Expand Down
Loading
Loading