Skip to content

Commit

Permalink
App: trove transaction flows (open / update / close) + Sepolia deploy…
Browse files Browse the repository at this point in the history
…ment (#422)

- Generic transaction page
- Borrowing tx flow
- Loan update tx flow
- Loan rate tx flow
- Closing flow tx flow

Also add changes related to the deployment to Sepolia.
  • Loading branch information
bpierre authored Sep 24, 2024
1 parent 530e28b commit 235c05c
Show file tree
Hide file tree
Showing 99 changed files with 11,958 additions and 6,511 deletions.
121 changes: 28 additions & 93 deletions README.md

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ $ forge build
$ forge test
```

Run tests with `forge test -vvv` to see the console logs, which will show trove URI data.

### Format

```shell
Expand Down
50 changes: 24 additions & 26 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
error BatchInterestRateChangePeriodNotPassed();
error TroveNotOpen();
error TroveNotActive();
error TroveNotZombie();
error TroveNotUnredeemable();
error TroveOpen();
error UpfrontFeeTooHigh();
error BelowCriticalThreshold();
Expand Down Expand Up @@ -470,7 +470,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
_adjustTrove(troveManagerCached, _troveId, troveChange, _maxUpfrontFee);
}

function adjustZombieTrove(
function adjustUnredeemableTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
Expand All @@ -481,7 +481,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
uint256 _maxUpfrontFee
) external override {
ITroveManager troveManagerCached = troveManager;
_requireTroveIsZombie(troveManagerCached, _troveId);
_requireTroveIsUnredeemable(troveManagerCached, _troveId);

TroveChange memory troveChange;
_initTroveChange(troveChange, _collChange, _isCollIncrease, _boldChange, _isDebtIncrease);
Expand Down Expand Up @@ -520,7 +520,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
_requireIsNotInBatch(_troveId);
address owner = troveNFT.ownerOf(_troveId);
_requireSenderIsOwnerOrInterestManager(_troveId, owner);
_requireInterestRateInDelegateRange(_troveId, _newAnnualInterestRate, owner);
_requireInterestRateInDelegateRange(_troveId, _newAnnualInterestRate);
_requireTroveIsActive(troveManagerCached, _troveId);

LatestTroveData memory trove = troveManagerCached.getLatestTroveData(_troveId);
Expand Down Expand Up @@ -650,8 +650,8 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
}
}

// Make sure the Trove doesn't end up zombie
// Now the max repayment is capped to stay above MIN_DEBT, so this only applies to adjustZombieTrove
// Make sure the Trove doesn't end up unredeemable
// Now the max repayment is capped to stay above MIN_DEBT, so this only applies to adjustUnredeemableTrove
_requireAtLeastMinDebt(vars.newDebt);

vars.newICR = LiquityMath._computeCR(vars.newColl, vars.newDebt, vars.price);
Expand Down Expand Up @@ -787,8 +787,8 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
);
activePool.mintAggInterestAndAccountForTroveChange(change, batchManager);

// If the trove was zombie, and now it’s not anymore, put it back in the list
if (_checkTroveIsZombie(troveManagerCached, _troveId) && trove.entireDebt >= MIN_DEBT) {
// If the trove was unredeemable, and now it’s not anymore, put it back in the list
if (_checkTroveIsUnredeemable(troveManagerCached, _troveId) && trove.entireDebt >= MIN_DEBT) {
troveManagerCached.setTroveStatusToActive(_troveId);
_reInsertIntoSortedTroves(
_troveId, trove.annualInterestRate, _upperHint, _lowerHint, batchManager, batch.annualInterestRate
Expand Down Expand Up @@ -1287,17 +1287,6 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
}
}

function _requireInterestRateInDelegateRange(uint256 _troveId, uint256 _annualInterestRate, address _owner) internal view {
InterestIndividualDelegate memory individualDelegate = interestIndividualDelegateOf[_troveId];
// We have previously checked that sender is either owner or delegate
// If it’s owner, this restriction doesn’t apply
if (individualDelegate.account == msg.sender) {
_requireInterestRateInRange(
_annualInterestRate, individualDelegate.minInterestRate, individualDelegate.maxInterestRate
);
}
}

function _requireIsNotInBatch(uint256 _troveId) internal view {
if (interestBatchManagerOf[_troveId] != address(0)) {
revert TroveInBatch();
Expand All @@ -1315,7 +1304,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio

function _requireTroveIsOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status != ITroveManager.Status.active && status != ITroveManager.Status.zombie) {
if (status != ITroveManager.Status.active && status != ITroveManager.Status.unredeemable) {
revert TroveNotOpen();
}
}
Expand All @@ -1327,20 +1316,20 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
}
}

function _requireTroveIsZombie(ITroveManager _troveManager, uint256 _troveId) internal view {
if (!_checkTroveIsZombie(_troveManager, _troveId)) {
revert TroveNotZombie();
function _requireTroveIsUnredeemable(ITroveManager _troveManager, uint256 _troveId) internal view {
if (!_checkTroveIsUnredeemable(_troveManager, _troveId)) {
revert TroveNotUnredeemable();
}
}

function _checkTroveIsZombie(ITroveManager _troveManager, uint256 _troveId) internal view returns (bool) {
function _checkTroveIsUnredeemable(ITroveManager _troveManager, uint256 _troveId) internal view returns (bool) {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
return status == ITroveManager.Status.zombie;
return status == ITroveManager.Status.unredeemable;
}

function _requireTroveIsNotOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status == ITroveManager.Status.active || status == ITroveManager.Status.zombie) {
if (status == ITroveManager.Status.active || status == ITroveManager.Status.unredeemable) {
revert TroveOpen();
}
}
Expand Down Expand Up @@ -1453,6 +1442,15 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
if (_minInterestRate >= _maxInterestRate) revert MinGeMax();
}

function _requireInterestRateInDelegateRange(uint256 _troveId, uint256 _annualInterestRate) internal view {
InterestIndividualDelegate memory individualDelegate = interestIndividualDelegateOf[_troveId];
if (individualDelegate.account != address(0)) {
_requireInterestRateInRange(
_annualInterestRate, individualDelegate.minInterestRate, individualDelegate.maxInterestRate
);
}
}

function _requireInterestRateInBatchManagerRange(address _interestBatchManagerAddress, uint256 _annualInterestRate)
internal
view
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/Interfaces/IBorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface IBorrowerOperations is ILiquityBase, IAddRemoveManagers {
uint256 _maxUpfrontFee
) external;

function adjustZombieTrove(
function adjustUnredeemableTrove(
uint256 _troveId,
uint256 _collChange,
bool _isCollIncrease,
Expand Down
6 changes: 2 additions & 4 deletions contracts/src/Interfaces/ITroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface ITroveManager is ILiquityBase {
active,
closedByOwner,
closedByLiquidation,
zombie
unredeemable
}

function shutdownTime() external view returns (uint256);
Expand Down Expand Up @@ -53,8 +53,6 @@ interface ITroveManager is ILiquityBase {

function getCurrentICR(uint256 _troveId, uint256 _price) external view returns (uint256);

function lastZombieTroveId() external view returns (uint256);

function batchLiquidateTroves(uint256[] calldata _troveArray) external;

function redeemCollateral(
Expand Down Expand Up @@ -90,7 +88,7 @@ interface ITroveManager is ILiquityBase {
uint256 _batchDebt
) external;

// Called from `adjustZombieTrove()`
// Called from `adjustUnredeemableTrove()`
function setTroveStatusToActive(uint256 _troveId) external;

function onAdjustTroveInterestRate(
Expand Down
27 changes: 2 additions & 25 deletions contracts/src/NFTMetadata/MetadataNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import "./utils/baseSVG.sol";
import "./utils/bauhaus.sol";

import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.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;
Expand All @@ -38,8 +35,7 @@ contract MetadataNFT is IMetadataNFT {
}

function uri(TroveData memory _troveData) public view returns (string memory) {
string memory attr = attributes(_troveData);
return json.formattedMetadata(name, description, renderSVGImage(_troveData), attr);
return json.formattedMetadata(name, description, renderSVGImage(_troveData));
}

function renderSVGImage(TroveData memory _troveData) internal view returns (string memory) {
Expand All @@ -49,25 +45,6 @@ contract MetadataNFT is IMetadataNFT {
);
}

function attributes(TroveData memory _troveData) public view returns (string memory) {
//include: collateral token address, collateral amount, debt token address, debt amount, interest rate, status
return string.concat(
'[{"trait_type": "Collateral Token", "value": "',
Strings.toHexString(_troveData._collToken),
'"}, {"trait_type": "Collateral Amount", "value": "',
Strings.toString(_troveData._collAmount),
'"}, {"trait_type": "Debt Token", "value": "',
Strings.toHexString(_troveData._boldToken),
'"}, {"trait_type": "Debt Amount", "value": "',
Strings.toString(_troveData._debtAmount),
'"}, {"trait_type": "Interest Rate", "value": "',
Strings.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));
Expand All @@ -85,7 +62,7 @@ contract MetadataNFT is IMetadataNFT {
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 "Zombie";
if (status == ITroveManager.Status.unredeemable) return "Unredeemable";
return "";
}
}
21 changes: 6 additions & 15 deletions contracts/src/NFTMetadata/utils/JSON.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,16 @@ 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) {
function formattedMetadata(string memory name, string memory description, string memory svgImg)
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,
"}"
)
string.concat("{", _prop("name", name), _prop("description", description), _xmlImage(svgImg), "}")
)
)
);
Expand Down
Loading

0 comments on commit 235c05c

Please sign in to comment.