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

App: trove transaction flows (open / update / close) #422

Merged
merged 58 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
14dbd7b
DeployLiquity2 demo mode: add stETH + more troves
bpierre Aug 27, 2024
c84e538
Contracts deploy script: fix debug mode output
bpierre Aug 27, 2024
9280f1f
Merge branch 'deployment-demo-troves' into subgraph
bpierre Aug 27, 2024
1a9d086
Merge branch 'contracts-deploy-script-verbose-fix' into subgraph
bpierre Aug 27, 2024
10b04c1
Subgraph
bpierre Aug 27, 2024
6c0d4b1
Remove comment
bpierre Aug 27, 2024
c1c4803
Merge branch 'main' into subgraph
bpierre Aug 30, 2024
3d01fca
Docker compose: move to volumes (+ add a cleanup script)
bpierre Sep 2, 2024
c829da4
DeployLiquity2: fix incorrect trove index
bpierre Sep 2, 2024
0715364
Subgraph: various updates
bpierre Sep 2, 2024
c4aad58
Update README + add a codegen script
bpierre Sep 2, 2024
5522079
Instantiate TroveManager from BoldToken
bpierre Sep 4, 2024
308e622
DeployLiquity2: set interest rate from the demo troves
bpierre Sep 4, 2024
043d610
Subgraph: preserve the magnitude when flooring the rates
bpierre Sep 4, 2024
b6770bc
Update default BoldToken address
bpierre Sep 4, 2024
0079e1e
Merge branch 'main' into subgraph
bpierre Sep 4, 2024
35c5b06
Merge branch 'main' into subgraph
bpierre Sep 4, 2024
4352c72
Subgraph: update scripts
bpierre Sep 10, 2024
6d00b9c
Collateral: add totalDeposited, totalDebt, minCollRatio
bpierre Sep 12, 2024
50b21f4
Syntax
bpierre Sep 12, 2024
ea5c78c
dprint: add graphql
bpierre Sep 12, 2024
c6224ee
App: fetch loans from the subgraph
bpierre Sep 12, 2024
c00e9ed
Merge branch 'main' into subgraph
bpierre Sep 12, 2024
c9bdfbd
Merge branch 'subgraph' into app-subgraph
bpierre Sep 12, 2024
a2f0c05
App: add LQTY and LUSD addresses to the env vars
bpierre Sep 13, 2024
cc37ac4
Add LUSD to the balances in demo mode
bpierre Sep 13, 2024
1aa1e2d
fmtnum(): accept undefined values
bpierre Sep 13, 2024
5187d8c
App: add useBalance() (watch any known token balance)
bpierre Sep 13, 2024
1e443ca
openLoanPosition flow: add missing params
bpierre Sep 13, 2024
7ef2a60
Replace useFindAvailableTroveIndex() by a subgraph query + various ch…
bpierre Sep 13, 2024
52262fe
Subgraph: add BorrowerInfo
bpierre Sep 13, 2024
9522921
Fix demo mode
bpierre Sep 13, 2024
f000010
Merge branch 'main' into app-open-trove-tx
bpierre Sep 15, 2024
3f9dc30
Subgraph: add Collateral.collIndex and BorrowerInfo.trovesByCollateral
bpierre Sep 16, 2024
8b165a2
App: update .graphclient
bpierre Sep 16, 2024
2b77da2
App: add WETH contract ABI
bpierre Sep 16, 2024
65ca577
App: update ABIs
bpierre Sep 16, 2024
fcbb295
App: only show collaterals defined in the environment
bpierre Sep 16, 2024
902592d
TransactionFlow: various improvements, add generic transaction page
bpierre Sep 17, 2024
bef31c6
Comment
bpierre Sep 17, 2024
bdc5935
Subgraph: use collateral-aware IDs
bpierre Sep 20, 2024
469b4ec
StrongCard: add loading state
bpierre Sep 20, 2024
6991661
Dashboard: new loading states
bpierre Sep 20, 2024
f39aadc
Dashboard: "new position" card tweaks
bpierre Sep 20, 2024
de62a2b
Tabs: add event context on select
bpierre Sep 23, 2024
d4bc099
New valibot utilities: vCollIndex(), vPrefixedTroveId()
bpierre Sep 23, 2024
8413d45
New types: CollIndex, PrefixedTroveId
bpierre Sep 23, 2024
372af19
Comment
bpierre Sep 23, 2024
6abd68b
Add Spinner component
bpierre Sep 23, 2024
c3544de
Not Found screen: layout fix
bpierre Sep 23, 2024
f74f9bc
NewPositionCard: remove fixed areas mode
bpierre Sep 23, 2024
c9daa1b
Update txflows
bpierre Sep 23, 2024
2a2490e
LoanScreen and BorrowScreen: various tweaks & fixes
bpierre Sep 23, 2024
d1b7e71
Subgraph queries: exclude closed troves on the dashboard
bpierre Sep 24, 2024
9e5de66
LoanScreen: move the card states bar behind a demo flag
bpierre Sep 24, 2024
ca75b46
TxFlow implementations: closeLoanPosition, updateLoanInterestRate
bpierre Sep 24, 2024
0a27c5b
Merge branch 'main' into app-open-trove-tx
bpierre Sep 24, 2024
0430cbf
App + Subgraph: add Sepolia
bpierre Sep 24, 2024
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
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