Skip to content

Commit 5138921

Browse files
committed
Refactor repay logic and add comprehensive tests
Refactored the repay function in NostalgicPool to standardize argument order and removed unused console logs. Improved pool validation in NostalgicController's liquidateBorrower. Added extensive unit tests for NostalgicController and NostalgicPool, including edge cases, revert scenarios, and multi-pool interactions to increase coverage and robustness.
1 parent 05c3a79 commit 5138921

4 files changed

Lines changed: 293 additions & 22 deletions

File tree

src/NostalgicController.sol

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ contract NostalgicController is AccessControl, INostalgicController {
7878

7979
totalUsdCollateral += getUsdValue(investedPools[i], userCollateral);
8080
}
81-
console.log("totalUsdBorrow", totalUsdBorrow);
82-
console.log("totalUsdCollateral", totalUsdCollateral);
8381
}
8482

8583
function canBorrow(
@@ -103,7 +101,11 @@ contract NostalgicController is AccessControl, INostalgicController {
103101
uint256 repaidAmount,
104102
INostalgicPool collateralPool
105103
) external {
106-
require(existingPools[INostalgicPool(msg.sender)], PoolNotFound());
104+
require(
105+
existingPools[INostalgicPool(msg.sender)] &&
106+
existingPools[collateralPool],
107+
PoolNotFound()
108+
);
107109
uint256 repaidAmountUsd = INostalgicPool(msg.sender).getTokenValueInUSD(
108110
repaidAmount
109111
);
@@ -211,7 +213,6 @@ contract NostalgicController is AccessControl, INostalgicController {
211213

212214
function _removePool(address user, INostalgicPool pool) internal {
213215
uint length = userEnteredMarkets[user].length;
214-
215216
for (uint i = 0; i < length; i++) {
216217
if (userEnteredMarkets[user][i] == pool) {
217218
// Shift elements left

src/NostalgicPool.sol

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
1515
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
1616
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
1717
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
18-
import {console} from "forge-std/console.sol";
1918
// /$$ /$$ /$$ /$$ /$$ /$$$$$$$ /$$
2019
// | $$$ | $$ | $$ | $$ |__/ | $$__ $$ | $$
2120
// | $$$$| $$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ | $$ /$$$$$$ /$$ /$$$$$$$| $$ \ $$ /$$$$$$ /$$$$$$ | $$
@@ -184,10 +183,10 @@ contract NostalgicPool is
184183
}
185184
// @audit check spamming vector, maybe it will decrease fees from borrowing?
186185
function repay(
187-
address user,
188-
uint256 amount
186+
uint256 amount,
187+
address user
189188
) public accrueInterest nonReentrant whenNotPaused returns (uint256) {
190-
return _repay(user, amount);
189+
return _repay(amount, user);
191190
}
192191
// @e here we need to account for bad debt
193192
function liquidate(
@@ -203,7 +202,7 @@ contract NostalgicPool is
203202
{
204203
require(!controller.isHealthy(borrower), HealthyPosition());
205204
require(borrower != msg.sender, CannotLiquidateSelf());
206-
uint256 repaidAmount = _repay(borrower, repayAmount);
205+
uint256 repaidAmount = _repay(repayAmount, borrower);
207206
controller.liquidateBorrower(
208207
borrower,
209208
msg.sender,
@@ -285,13 +284,12 @@ contract NostalgicPool is
285284
}
286285

287286
function _repay(
288-
address user,
289-
uint256 amount
287+
uint256 amount,
288+
address user
290289
) internal interactWithPool(user) returns (uint256) {
291290
require(amount > 0, ZeroAmount());
292291

293292
uint256 debt = scaledBorrowPosition(user);
294-
console.log("DEBT", debt);
295293

296294
if (amount == type(uint256).max) {
297295
amount = debt;

test/NostalgicController.t.sol

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {Test, console} from "forge-std/Test.sol";
5+
import {NostalgicPool} from "../src/NostalgicPool.sol";
6+
import {IAggregatorV3} from "../src/interfaces/IAggregatorV3.sol";
7+
import {MockERC20} from "./mocks/MockERC20.t.sol";
8+
import {MockPriceFeed} from "./mocks/MockPriceFeed.t.sol";
9+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
10+
import {NostalgicController} from "../src/NostalgicController.sol";
11+
import {NostalgicJumpRateModel} from "../src/NostalgicJumpRateModel.sol";
12+
import {INostalgicRateModel} from "../src/interfaces/INostalgicRateModel.sol";
13+
import {INostalgicControllerErrors} from "../src/interfaces/INostalgicController.sol";
14+
import {INostalgicPool} from "../src/interfaces/INostalgicPool.sol";
15+
contract NostalgicPoolTest is Test {
16+
NostalgicController controller;
17+
18+
NostalgicPool poolUsdc;
19+
20+
IERC20 usdc;
21+
IAggregatorV3 priceFeedUsdc;
22+
INostalgicRateModel rateModelUsdc;
23+
address alice = makeAddr("alice");
24+
25+
function setUp() public {
26+
rateModelUsdc = new NostalgicJumpRateModel(
27+
23782343987,
28+
518455098934,
29+
0.8e18,
30+
0
31+
);
32+
33+
controller = new NostalgicController(
34+
address(this),
35+
makeAddr("factory")
36+
);
37+
usdc = new MockERC20();
38+
39+
poolUsdc = new NostalgicPool();
40+
priceFeedUsdc = new MockPriceFeed();
41+
MockPriceFeed(address(priceFeedUsdc)).setMockAnswer(100000000);
42+
43+
usdc = new MockERC20();
44+
poolUsdc = new NostalgicPool();
45+
46+
poolUsdc.initialize(
47+
usdc,
48+
"nstlUSDC",
49+
"nstlUSDC",
50+
controller,
51+
priceFeedUsdc,
52+
rateModelUsdc,
53+
address(this),
54+
address(this),
55+
0.15e18
56+
);
57+
deal(address(usdc), address(this), 10_000e18);
58+
deal(address(usdc), alice, 10_000e18);
59+
vm.startPrank(alice);
60+
usdc.approve(address(poolUsdc), type(uint256).max);
61+
vm.stopPrank();
62+
}
63+
64+
function testInvalidInit() public {
65+
vm.expectRevert(INostalgicControllerErrors.ZeroAddress.selector);
66+
controller = new NostalgicController(address(0), makeAddr("factory"));
67+
}
68+
69+
function testUpdateFactory() public {
70+
vm.expectRevert(INostalgicControllerErrors.ZeroAddress.selector);
71+
controller.updateFactory(address(0));
72+
73+
controller.updateFactory(makeAddr("factory2"));
74+
}
75+
76+
function testInvalidPoolCaller() public {
77+
vm.expectRevert(INostalgicControllerErrors.PoolNotFound.selector);
78+
controller.canBorrow(INostalgicPool(address(this)), address(1), 1);
79+
vm.expectRevert(INostalgicControllerErrors.PoolNotFound.selector);
80+
controller.liquidateBorrower(
81+
address(1),
82+
address(1),
83+
1,
84+
INostalgicPool(address(2))
85+
);
86+
vm.expectRevert(INostalgicControllerErrors.PoolNotFound.selector);
87+
controller.interactWithPool(address(1));
88+
}
89+
90+
function testAddRemovePool() public {
91+
NostalgicPool mockPool = new NostalgicPool();
92+
93+
controller.addPool(mockPool);
94+
controller.removePool(mockPool);
95+
}
96+
function testAssetAddRemoveUpdate() public {
97+
vm.expectRevert(
98+
INostalgicControllerErrors.InvalidCollateralFactor.selector
99+
);
100+
controller.addAsset(address(1), 10e18);
101+
controller.addAsset(address(1), 0.5e18);
102+
vm.expectRevert(INostalgicControllerErrors.AssetAlreadyAdded.selector);
103+
controller.addAsset(address(1), 0.5e18);
104+
controller.update(address(1), 0.6e18);
105+
vm.expectRevert(
106+
INostalgicControllerErrors.InvalidCollateralFactor.selector
107+
);
108+
controller.update(address(1), 10e18);
109+
controller.removeAsset(address(1));
110+
vm.expectRevert(INostalgicControllerErrors.AssetNotAdded.selector);
111+
controller.removeAsset(address(1));
112+
vm.expectRevert(INostalgicControllerErrors.AssetNotAdded.selector);
113+
controller.update(address(1), 0.1e18);
114+
}
115+
116+
function testForCoverage() public {
117+
controller.getUsdValue(poolUsdc, 1);
118+
controller.userPosition(address(2));
119+
}
120+
function testInteractRemovePool() public {
121+
NostalgicPool mockPool = new NostalgicPool();
122+
123+
controller.addPool(mockPool);
124+
vm.startPrank(address(mockPool));
125+
controller.interactWithPool(address(1));
126+
}
127+
128+
function testRemoveNonEmptyPool() public {
129+
controller.addPool(poolUsdc);
130+
usdc.approve(address(poolUsdc), type(uint256).max);
131+
poolUsdc.deposit(10e18, alice);
132+
vm.expectRevert(INostalgicControllerErrors.PoolNotEmpty.selector);
133+
controller.removePool(poolUsdc);
134+
}
135+
136+
function testInvalidCaller() public {
137+
vm.startPrank(address(2));
138+
vm.expectRevert(INostalgicControllerErrors.InvalidCaller.selector);
139+
controller.addPool(poolUsdc);
140+
}
141+
}

0 commit comments

Comments
 (0)