Skip to content

Commit

Permalink
chore: the author's barely-disguised OCD
Browse files Browse the repository at this point in the history
  • Loading branch information
danielattilasimon committed Sep 19, 2024
1 parent a6397d7 commit 3237e25
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 59 deletions.
4 changes: 2 additions & 2 deletions contracts/src/test/Invariants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ contract InvariantsTest is Logging, BaseInvariantTest, BaseMultiCollateralTest {
TestDeployer.LiquityContractsDev memory c = branches[i];

assertEq(c.troveManager.getTroveIdsCount(), handler.numTroves(i), "Wrong number of Troves");
assertEq(c.troveManager.lastZombieTroveId(), handler.designatedVictimId(i), "Wrong designated victim");
assertEq(c.sortedTroves.getSize(), handler.numTroves(i) - handler.numZombies(i), "Wrong SortedTroves size");
assertApproxEqAbsDecimal(
c.activePool.calcPendingAggInterest(), handler.getPendingInterest(i), 1e-10 ether, 18, "Wrong interest"
Expand Down Expand Up @@ -186,8 +187,7 @@ contract InvariantsTest is Logging, BaseInvariantTest, BaseMultiCollateralTest {
ITroveManager.Status status = c.troveManager.getTroveStatus(troveId);

assertTrue(
status == ITroveManager.Status.active || status == ITroveManager.Status.zombie,
"Unexpected status"
status == ITroveManager.Status.active || status == ITroveManager.Status.zombie, "Unexpected status"
);

if (status == ITroveManager.Status.active) {
Expand Down
117 changes: 60 additions & 57 deletions contracts/src/test/TestContracts/InvariantsTestHandler.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,6 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
string errorString;
}

struct RedemptionContext {
uint256 totalProportions;
uint256[] proportions;
uint256 remainingAmount;
uint256 troveId;
uint256 lastZombieTroveId;
uint256 j;
uint256 i;
uint256 debtRedeemed;
uint256 collRedeemedPlusFee;
uint256 fee;
uint256 collRedeemed;
}

struct LiquidationTotals {
uint256 collGasComp;
uint256 spCollGain;
Expand Down Expand Up @@ -314,6 +300,7 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
uint256 totalCollRedeemed;
Redeemed[] redeemed;
EnumerableAddressSet batchManagers; // batch managers touched by redemption
uint256 newDesignatedVictimId;
}

struct UrgentRedemptionTransientState {
Expand Down Expand Up @@ -355,6 +342,7 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
mapping(uint256 branchIdx => uint256) LIQ_PENALTY_REDIST;

// Public ghost variables (per branch, exposed to InvariantsTest)
mapping(uint256 branchIdx => uint256) public designatedVictimId; // ID of zombie Trove that'll be redeemed first
mapping(uint256 branchIdx => uint256) public collSurplus;
mapping(uint256 branchIdx => uint256) public spBoldDeposits;
mapping(uint256 branchIdx => uint256) public spBoldYield;
Expand Down Expand Up @@ -814,6 +802,7 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
v.trove.debt = v.trove.debt.add(v.debtDelta) + v.upfrontFee;
_troves[i][v.troveId] = v.trove;
_zombieTroveIds[i].remove(v.troveId);
if (designatedVictimId[i] == v.troveId) designatedVictimId[i] = 0;

// Effects (batch)
if (v.batchManager != address(0)) _touchBatch(i, v.batchManager);
Expand Down Expand Up @@ -1028,6 +1017,7 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
delete _timeSinceLastTroveInterestRateAdjustment[i][v.troveId];
_troveIds[i].remove(v.troveId);
_zombieTroveIds[i].remove(v.troveId);
if (designatedVictimId[i] == v.troveId) designatedVictimId[i] = 0;

// Effects (batch)
if (v.batchManager != address(0)) {
Expand Down Expand Up @@ -1126,6 +1116,7 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
delete _timeSinceLastTroveInterestRateAdjustment[i][troveId];
_troveIds[i].remove(troveId);
_zombieTroveIds[i].remove(troveId);
if (designatedVictimId[i] == troveId) designatedVictimId[i] = 0;
if (batchManager != address(0)) _batches[i][batchManager].troves.remove(troveId);
}

Expand Down Expand Up @@ -1295,6 +1286,8 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
}
}

designatedVictimId[j] = r[j].newDesignatedVictimId;

// Effects (batches)
for (uint256 i = 0; i < r[j].batchManagers.size(); ++i) {
_touchBatch(j, r[j].batchManagers.get(i));
Expand Down Expand Up @@ -1524,7 +1517,11 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
// Effects (Trove)
v.trove.applyPending();
_troves[i][v.troveId] = v.trove;
if (v.t.entireDebt >= MIN_DEBT) _zombieTroveIds[i].remove(v.troveId);

if (v.t.entireDebt >= MIN_DEBT) {
_zombieTroveIds[i].remove(v.troveId);
if (designatedVictimId[i] == v.troveId) designatedVictimId[i] = 0;
}

// Effects (batch)
if (v.batchManager != address(0)) _touchBatch(i, v.batchManager);
Expand Down Expand Up @@ -2647,67 +2644,73 @@ contract InvariantsTestHandler is BaseHandler, BaseMultiCollateralTest {
delete _liquidation;
}

function _planOneRedemption(uint256 i, uint256 troveId, uint256 remainingAmount, uint256 feePct)
internal
returns (uint256 debtRedeemed)
{
LatestTroveData memory trove = branches[i].troveManager.getLatestTroveData(troveId);
if (_ICR(i, trove) < _100pct) return 0;

debtRedeemed = Math.min(remainingAmount, trove.entireDebt);
uint256 collRedeemedPlusFee = debtRedeemed * DECIMAL_PRECISION / _price[i];
uint256 fee = collRedeemedPlusFee * feePct / _100pct;
uint256 collRedeemed = collRedeemedPlusFee - fee;

mapping(uint256 branchIdx => RedemptionTransientState) storage r = _redemption;
r[i].redeemed.push(Redeemed({troveId: troveId, coll: collRedeemed, debt: debtRedeemed}));
r[i].totalCollRedeemed += collRedeemed;

address batchManager = _batchManagerOf[i][troveId];
if (batchManager != address(0)) r[i].batchManagers.add(batchManager);

uint256 newDebt = trove.entireDebt - debtRedeemed;
if (0 < newDebt && newDebt < MIN_DEBT) r[i].newDesignatedVictimId = troveId;
}

function _planRedemption(uint256 amount, uint256 maxIterationsPerCollateral, uint256 feePct)
internal
returns (uint256 totalDebtRedeemed, mapping(uint256 branchIdx => RedemptionTransientState) storage r)
{
RedemptionContext memory vars;
vars.totalProportions = 0;
vars.proportions = new uint256[](branches.length);
uint256 totalProportions = 0;
uint256[] memory proportions = new uint256[](branches.length);
r = _redemption;

// Try in proportion to unbacked
for (vars.j = 0; vars.j < branches.length; ++vars.j) {
if (isShutdown[vars.j] || _TCR(vars.j) < SCR[vars.j]) continue;
vars.totalProportions += vars.proportions[vars.j] = _getUnbacked(vars.j);
for (uint256 i = 0; i < branches.length; ++i) {
if (isShutdown[i] || _TCR(i) < SCR[i]) continue;
totalProportions += proportions[i] = _getUnbacked(i);
}

// Fallback: in proportion to branch debt
if (vars.totalProportions == 0) {
for (vars.j = 0; vars.j < branches.length; ++vars.j) {
if (isShutdown[vars.j] || _TCR(vars.j) < SCR[vars.j]) continue;
vars.totalProportions += vars.proportions[vars.j] = _getTotalDebt(vars.j);
if (totalProportions == 0) {
for (uint256 i = 0; i < branches.length; ++i) {
if (isShutdown[i] || _TCR(i) < SCR[i]) continue;
totalProportions += proportions[i] = _getTotalDebt(i);
}
}

if (vars.totalProportions == 0) return (0, r);

for (vars.j = 0; vars.j < branches.length; ++vars.j) {
r[vars.j].attemptedAmount = amount * vars.proportions[vars.j] / vars.totalProportions;
if (r[vars.j].attemptedAmount == 0) continue;
if (totalProportions == 0) return (0, r);

TestDeployer.LiquityContractsDev memory c = branches[vars.j];
vars.remainingAmount = r[vars.j].attemptedAmount;
vars.troveId = 0; // "root node" ID
vars.lastZombieTroveId = c.troveManager.lastZombieTroveId();

for (vars.i = 0; vars.i < maxIterationsPerCollateral || maxIterationsPerCollateral == 0; ++vars.i) {
if (vars.remainingAmount == 0) break;
for (uint256 i = 0; i < branches.length; ++i) {
r[i].attemptedAmount = amount * proportions[i] / totalProportions;
if (r[i].attemptedAmount == 0) continue;

vars.troveId = vars.lastZombieTroveId != 0 ? vars.lastZombieTroveId : c.sortedTroves.getPrev(vars.troveId);
if (vars.troveId == 0) break;
uint256 remainingAmount = r[i].attemptedAmount;
uint256 lastTrove = branches[i].sortedTroves.getPrev(0);

LatestTroveData memory trove = c.troveManager.getLatestTroveData(vars.troveId);
if (_ICR(vars.j, trove) >= _100pct) {
vars.debtRedeemed = Math.min(vars.remainingAmount, trove.entireDebt);
vars.collRedeemedPlusFee = vars.debtRedeemed * DECIMAL_PRECISION / _price[vars.j];
vars.fee = vars.collRedeemedPlusFee * feePct / _100pct;
vars.collRedeemed = vars.collRedeemedPlusFee - vars.fee;
(uint256 troveId, uint256 nextTroveId) = designatedVictimId[i] != 0
? (designatedVictimId[i], lastTrove)
: (lastTrove, branches[i].sortedTroves.getPrev(lastTrove));

r[vars.j].redeemed.push(Redeemed({troveId: vars.troveId, coll: vars.collRedeemed, debt: vars.debtRedeemed}));
for (uint256 j = 0; j < maxIterationsPerCollateral || maxIterationsPerCollateral == 0; ++j) {
if (remainingAmount == 0 || troveId == 0) break;

address batchManager = _batchManagerOf[vars.j][vars.troveId];
if (batchManager != address(0)) r[vars.j].batchManagers.add(batchManager);
uint256 debtRedeemed = _planOneRedemption(i, troveId, remainingAmount, feePct);
totalDebtRedeemed += debtRedeemed;
remainingAmount -= debtRedeemed;

r[vars.j].totalCollRedeemed += vars.collRedeemed;
totalDebtRedeemed += vars.debtRedeemed;
vars.remainingAmount -= vars.debtRedeemed;
}

if (vars.lastZombieTroveId != 0) {
vars.lastZombieTroveId = 0;
vars.troveId = 0;
}
troveId = nextTroveId;
nextTroveId = branches[i].sortedTroves.getPrev(nextTroveId);
}
}
}
Expand Down

0 comments on commit 3237e25

Please sign in to comment.