Skip to content

Commit 82bae10

Browse files
committed
Fix invariant tests based on new code
1 parent d68bf88 commit 82bae10

File tree

9 files changed

+185
-420
lines changed

9 files changed

+185
-420
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"editor.defaultFormatter": "esbenp.prettier-vscode"
55
},
66
"editor.defaultFormatter": "esbenp.prettier-vscode",
7-
"solidity.compileUsingRemoteVersion": "v0.8.18+commit.87f61d96"
7+
"solidity.compileUsingRemoteVersion": "v0.8.23+commit.f704f362",
8+
"prettier.configPath": "./.prettierrc"
89
}

foundry.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
src = 'contracts'
33
out = 'out'
44
libs = ['node_modules', 'lib']
5-
test = 'test'
5+
test = 'test/invariant'
66
cache_path = 'cache_forge'
77
optimizer = true
88
optimizer_runs = 200

scripts/invariant-tool.ts

Lines changed: 13 additions & 80 deletions
Large diffs are not rendered by default.

test/invariant/BaseInvariants.sol

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ struct PoolDeployParameters {
2020
string creditType;
2121
}
2222

23-
contract BaseInvariants is
24-
BaseTest {
23+
contract BaseInvariants is BaseTest {
2524
uint256 minReinvestFees;
2625

2726
bytes4[] selectors;
@@ -180,8 +179,12 @@ BaseTest {
180179
console.log("closeEpoch starts...");
181180
if (hasProfit) {
182181
console.log("processYieldForLenders starts...");
183-
seniorTranche.processYieldForLenders();
184-
juniorTranche.processYieldForLenders();
182+
if (poolSafe.unprocessedTrancheProfit(address(seniorTranche)) > 0) {
183+
seniorTranche.processYieldForLenders();
184+
}
185+
if (poolSafe.unprocessedTrancheProfit(address(juniorTranche)) > 0) {
186+
juniorTranche.processYieldForLenders();
187+
}
185188
console.log("processYieldForLenders done.");
186189
}
187190
uint256 seniorAssetsBefore = mockToken.balanceOf(address(seniorTranche));

test/invariant/BaseTest.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {PoolConfig, AdminRnR, LPConfig, FirstLossCoverConfig, PoolSettings} from
1010
import {PoolFeeManager} from "contracts/liquidity/PoolFeeManager.sol";
1111
import {PoolSafe} from "contracts/liquidity/PoolSafe.sol";
1212
import {FirstLossCover} from "contracts/liquidity/FirstLossCover.sol";
13-
import {FixedSeniorYieldTranchePolicy} from "contracts/liquidity/FixedSeniorYieldTranchesPolicy.sol";
13+
import {FixedSeniorYieldTranchePolicyForTest} from "./FixedSeniorYieldTranchesPolicyForTest.sol";
1414
import {RiskAdjustedTranchesPolicy} from "contracts/liquidity/RiskAdjustedTranchesPolicy.sol";
1515
import {Pool} from "contracts/liquidity/Pool.sol";
1616
import {EpochManager} from "contracts/liquidity/EpochManager.sol";
@@ -87,7 +87,7 @@ contract BaseTest is Test, Utils {
8787
_deployProtocolContracts();
8888
_deployFactory();
8989
_deployReceivableWithFactory();
90-
vm.warp(1704067800); // 2024-1-1 00:10:00 UTC
90+
vm.warp(1717200600); // 2024-6-1 00:10:00 UTC
9191
}
9292

9393
function _createAccounts() internal {
@@ -140,7 +140,7 @@ contract BaseTest is Test, Utils {
140140
address(new RiskAdjustedTranchesPolicy())
141141
);
142142
poolFactory.setFixedSeniorYieldTranchesPolicyImplAddress(
143-
address(new FixedSeniorYieldTranchePolicy())
143+
address(new FixedSeniorYieldTranchePolicyForTest())
144144
);
145145

146146
poolFactory.setCreditDueManagerImplAddress(address(new CreditDueManager()));
@@ -225,7 +225,6 @@ contract BaseTest is Test, Utils {
225225
uint256 amount = (lpConfig.liquidityCap * adminRnR.liquidityRateInBpsByPoolOwner) / 10000;
226226
mockToken.mint(poolOwnerTreasury, amount);
227227
juniorTranche.makeInitialDeposit(amount);
228-
juniorInitialShares += juniorTranche.balanceOf(poolOwnerTreasury);
229228
amount = flcConfig.minLiquidity / 2;
230229
mockToken.mint(poolOwnerTreasury, amount);
231230
adminFLC.depositCover(amount);
@@ -241,7 +240,6 @@ contract BaseTest is Test, Utils {
241240
amount = (lpConfig.liquidityCap * adminRnR.liquidityRateInBpsByEA) / 10000;
242241
mockToken.mint(evaluationAgent, amount);
243242
juniorTranche.makeInitialDeposit(amount);
244-
juniorInitialShares += juniorTranche.balanceOf(evaluationAgent);
245243
amount = flcConfig.minLiquidity / 2;
246244
mockToken.mint(evaluationAgent, amount);
247245
adminFLC.depositCover(amount);
@@ -272,9 +270,11 @@ contract BaseTest is Test, Utils {
272270
mockToken.approve(address(poolSafe), type(uint256).max);
273271
mockToken.mint(initLender, _toToken(100_000));
274272
seniorTranche.deposit(_toToken(100_000));
275-
seniorInitialShares += seniorTranche.balanceOf(initLender);
276273
vm.stopPrank();
277274

275+
seniorInitialShares = seniorTranche.totalSupply();
276+
juniorInitialShares = juniorTranche.totalSupply();
277+
278278
// console.log(
279279
// "_enablePool - block.timestamp: %s, block.number: %s",
280280
// block.timestamp,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
pragma solidity 0.8.23;
3+
4+
import {Errors} from "contracts/common/Errors.sol";
5+
import {LPConfig, PoolConfig} from "contracts/common/PoolConfig.sol";
6+
import {BaseTranchesPolicy} from "contracts/liquidity/BaseTranchesPolicy.sol";
7+
import {SENIOR_TRANCHE, DAYS_IN_A_YEAR, HUNDRED_PERCENT_IN_BPS} from "contracts/common/SharedDefs.sol";
8+
import {ICalendar} from "contracts/common/interfaces/ICalendar.sol";
9+
10+
/**
11+
* @notice Tranche policy where the yield for the senior tranche is fixed as long as
12+
* the risk loss does not make it impossible.
13+
*/
14+
contract FixedSeniorYieldTranchePolicyForTest is BaseTranchesPolicy {
15+
/**
16+
* @notice Tracks the amount of assets and unpaid yield for the senior tranche.
17+
* @param totalAssets The total assets in the senior tranche.
18+
* @param unpaidYield The amount of unpaid yield to the senior tranche.
19+
* @param lastUpdatedDate The last time the tracker was updated.
20+
*/
21+
struct SeniorYieldTracker {
22+
uint96 totalAssets;
23+
uint96 unpaidYield;
24+
uint64 lastUpdatedDate;
25+
}
26+
27+
SeniorYieldTracker public seniorYieldTracker;
28+
ICalendar public calender;
29+
30+
/**
31+
* @notice The senior yield tracker has been refreshed.
32+
* @param totalAssets The total assets in the senior tranche after the refresh.
33+
* @param unpaidYield The amount of unpaid yield to the senior tranche after the refresh.
34+
* @param lastUpdatedDate The last time the tracker was updated after the refresh.
35+
*/
36+
event YieldTrackerRefreshed(uint256 totalAssets, uint256 unpaidYield, uint256 lastUpdatedDate);
37+
38+
/// @inheritdoc BaseTranchesPolicy
39+
function refreshYieldTracker(uint96[2] memory assets) external override {
40+
if (msg.sender != address(poolConfig) && msg.sender != pool) {
41+
revert Errors.AuthorizedContractCallerRequired();
42+
}
43+
44+
(SeniorYieldTracker memory tracker, bool updated) = _getLatestYieldTracker();
45+
if (tracker.totalAssets != assets[SENIOR_TRANCHE]) {
46+
tracker.totalAssets = assets[SENIOR_TRANCHE];
47+
updated = true;
48+
}
49+
if (updated) {
50+
seniorYieldTracker = tracker;
51+
emit YieldTrackerRefreshed(
52+
tracker.totalAssets,
53+
tracker.unpaidYield,
54+
tracker.lastUpdatedDate
55+
);
56+
}
57+
}
58+
59+
function _calcProfitForSeniorTranche(
60+
uint256 profit,
61+
uint96[2] memory assets
62+
) internal virtual override returns (uint256 seniorProfit, uint256 remainingProfit) {
63+
(SeniorYieldTracker memory tracker, ) = _getLatestYieldTracker();
64+
65+
seniorProfit = tracker.unpaidYield > profit ? profit : tracker.unpaidYield;
66+
remainingProfit = profit - seniorProfit;
67+
68+
tracker.unpaidYield -= uint96(seniorProfit);
69+
tracker.totalAssets = uint96(assets[SENIOR_TRANCHE] + seniorProfit);
70+
seniorYieldTracker = tracker;
71+
72+
emit YieldTrackerRefreshed(
73+
tracker.totalAssets,
74+
tracker.unpaidYield,
75+
tracker.lastUpdatedDate
76+
);
77+
}
78+
79+
function _updatePoolConfigData(PoolConfig poolConfig_) internal virtual override {
80+
super._updatePoolConfigData(poolConfig_);
81+
82+
address addr = poolConfig_.calendar();
83+
assert(addr != address(0));
84+
calender = ICalendar(addr);
85+
}
86+
87+
/**
88+
* @notice Calculates the amount of yield that the senior tranche should have earned until the current
89+
* block timestamp.
90+
* @return The (potentially) updated SeniorYieldTracker.
91+
* @return updated Whether the SeniorYieldTracker has been updated.
92+
*/
93+
function _getLatestYieldTracker()
94+
internal
95+
view
96+
returns (SeniorYieldTracker memory, bool updated)
97+
{
98+
SeniorYieldTracker memory tracker = seniorYieldTracker;
99+
uint256 startOfNextDay = calender.getStartOfNextDay(block.timestamp);
100+
if (tracker.lastUpdatedDate < startOfNextDay) {
101+
uint256 daysDiff = calender.getDaysDiff(tracker.lastUpdatedDate, startOfNextDay);
102+
LPConfig memory lpConfig = poolConfig.getLPConfig();
103+
tracker.unpaidYield += uint96(
104+
(tracker.totalAssets * lpConfig.fixedSeniorYieldInBps * daysDiff) /
105+
(DAYS_IN_A_YEAR * HUNDRED_PERCENT_IN_BPS)
106+
);
107+
tracker.lastUpdatedDate = uint64(startOfNextDay);
108+
updated = true;
109+
}
110+
111+
return (tracker, updated);
112+
}
113+
}

0 commit comments

Comments
 (0)