Skip to content

Commit c167aca

Browse files
feat: Safety param checks on ERC4626 functions (DEV-1126) (#225)
* feat: safety param checks to erc4626 * fix: review * fix: cleanup review * fix : format --------- Co-authored-by: Lucas Manuel <lucasmanuel.tech@gmail.com>
1 parent 5c67f4e commit c167aca

File tree

12 files changed

+324
-111
lines changed

12 files changed

+324
-111
lines changed

script/staging/test/StagingDeployment.t.sol

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,15 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
212212
}
213213

214214
function test_depositAndWithdrawUsdsFromSUsds() public {
215+
vm.skip(true);
216+
215217
uint256 startingBalance = usds.balanceOf(address(almProxy));
216218

217219
vm.startPrank(relayerSafe);
218220
mainnetController.mintUSDS(10e18);
219-
mainnetController.depositERC4626(Ethereum.SUSDS, 10e18);
221+
mainnetController.depositERC4626(Ethereum.SUSDS, 10e18, 0);
220222
skip(1 days);
221-
mainnetController.withdrawERC4626(Ethereum.SUSDS, 10e18);
223+
mainnetController.withdrawERC4626(Ethereum.SUSDS, 10e18, 10e18);
222224
vm.stopPrank();
223225

224226
assertEq(usds.balanceOf(address(almProxy)), startingBalance + 10e18);
@@ -227,13 +229,21 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
227229
}
228230

229231
function test_depositAndRedeemUsdsFromSUsds() public {
232+
vm.skip(true);
233+
230234
uint256 startingBalance = usds.balanceOf(address(almProxy));
231235

232236
vm.startPrank(relayerSafe);
233237
mainnetController.mintUSDS(10e18);
234-
mainnetController.depositERC4626(Ethereum.SUSDS, 10e18);
238+
mainnetController.depositERC4626(Ethereum.SUSDS, 10e18, 0);
239+
235240
skip(1 days);
236-
mainnetController.redeemERC4626(Ethereum.SUSDS, IERC4626(Ethereum.SUSDS).balanceOf(address(almProxy)));
241+
242+
mainnetController.redeemERC4626(
243+
Ethereum.SUSDS,
244+
IERC4626(Ethereum.SUSDS).balanceOf(address(almProxy)),
245+
0
246+
);
237247
vm.stopPrank();
238248

239249
assertGe(usds.balanceOf(address(almProxy)), startingBalance + 10e18); // Interest earned
@@ -269,6 +279,8 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
269279
}
270280

271281
function test_mintDepositCooldownAssetsBurnUsde() public {
282+
vm.skip(true);
283+
272284
uint256 startingBalance = usdc.balanceOf(address(almProxy));
273285

274286
vm.startPrank(relayerSafe);
@@ -280,7 +292,7 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
280292
_simulateUsdeMint(10e6);
281293

282294
vm.startPrank(relayerSafe);
283-
mainnetController.depositERC4626(Ethereum.SUSDE, 10e18);
295+
mainnetController.depositERC4626(Ethereum.SUSDE, 10e18, 0);
284296
skip(1 days);
285297
mainnetController.cooldownAssetsSUSDe(10e18 - 1); // Rounding
286298
skip(7 days);
@@ -296,6 +308,8 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
296308
}
297309

298310
function test_mintDepositCooldownSharesBurnUsde() public {
311+
vm.skip(true);
312+
299313
vm.startPrank(relayerSafe);
300314
mainnetController.mintUSDS(10e18);
301315
mainnetController.swapUSDSToUSDC(10e6);
@@ -307,7 +321,7 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
307321
_simulateUsdeMint(10e6);
308322

309323
vm.startPrank(relayerSafe);
310-
mainnetController.depositERC4626(Ethereum.SUSDE, 10e18);
324+
mainnetController.depositERC4626(Ethereum.SUSDE, 10e18, 0);
311325
skip(1 days);
312326
uint256 usdeAmount = mainnetController.cooldownSharesSUSDe(IERC4626(Ethereum.SUSDE).balanceOf(address(almProxy)));
313327
skip(7 days);
@@ -328,6 +342,8 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
328342
}
329343

330344
function test_mintDepositWithdrawSyrupUsdc() public {
345+
vm.skip(true);
346+
331347
// --- Maple onboarding process ---
332348

333349
IPermissionManagerLike permissionManager
@@ -361,7 +377,7 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
361377
uint256 startingBalance = usdc.balanceOf(address(almProxy));
362378

363379
vm.startPrank(relayerSafe);
364-
uint256 shares = mainnetController.depositERC4626(Ethereum.SYRUP_USDC, 10e6);
380+
uint256 shares = mainnetController.depositERC4626(Ethereum.SYRUP_USDC, 10e6, 0);
365381

366382
skip(1 days);
367383

@@ -378,13 +394,15 @@ contract MainnetStagingDeploymentTests is StagingDeploymentTestBase {
378394
}
379395

380396
function test_depositSwapWithdrawCurve() public {
397+
vm.skip(true);
398+
381399
skip(1 days); // Recharge rate limits
382400

383401
uint256 startingBalance = usdc.balanceOf(address(almProxy));
384402

385403
vm.startPrank(relayerSafe);
386404
mainnetController.mintUSDS(10e18);
387-
uint256 shares = mainnetController.depositERC4626(Ethereum.SUSDS, 10e18);
405+
uint256 shares = mainnetController.depositERC4626(Ethereum.SUSDS, 10e18, 0);
388406
uint256 usdtAmount = mainnetController.swapCurve(Ethereum.CURVE_SUSDSUSDT, 0, 1, shares, 9.99e6);
389407

390408
uint256[] memory amounts = new uint256[](2);
@@ -570,6 +588,8 @@ contract BaseStagingDeploymentTests is StagingDeploymentTestBase {
570588
}
571589

572590
function test_depositWithdrawFundsFromBaseMorphoUsdc() public {
591+
vm.skip(true);
592+
573593
mainnet.selectFork();
574594

575595
vm.startPrank(relayerSafe);
@@ -581,9 +601,11 @@ contract BaseStagingDeploymentTests is StagingDeploymentTestBase {
581601
cctpBridgeBase.relayMessagesToDestination(true);
582602

583603
vm.startPrank(relayerSafeBase);
584-
baseController.depositERC4626(Base.MORPHO_VAULT_SUSDC, 10e6);
604+
baseController.depositERC4626(Base.MORPHO_VAULT_SUSDC, 10e6, 0);
605+
585606
skip(1 days);
586-
baseController.withdrawERC4626(Base.MORPHO_VAULT_SUSDC, 10e6);
607+
608+
baseController.withdrawERC4626(Base.MORPHO_VAULT_SUSDC, 10e6, 10e6);
587609

588610
assertEq(usdcBase.balanceOf(address(baseAlmProxy)), 10e6);
589611

@@ -601,6 +623,8 @@ contract BaseStagingDeploymentTests is StagingDeploymentTestBase {
601623
}
602624

603625
function test_depositRedeemFundsFromBaseMorphoUsdc() public {
626+
vm.skip(true);
627+
604628
mainnet.selectFork();
605629

606630
vm.startPrank(relayerSafe);
@@ -612,9 +636,15 @@ contract BaseStagingDeploymentTests is StagingDeploymentTestBase {
612636
cctpBridgeBase.relayMessagesToDestination(true);
613637

614638
vm.startPrank(relayerSafeBase);
615-
baseController.depositERC4626(Base.MORPHO_VAULT_SUSDC, 10e6);
639+
baseController.depositERC4626(Base.MORPHO_VAULT_SUSDC, 10e6, 0);
640+
616641
skip(1 days);
617-
baseController.redeemERC4626(Base.MORPHO_VAULT_SUSDC, IERC20(Base.MORPHO_VAULT_SUSDC).balanceOf(address(baseAlmProxy)));
642+
643+
baseController.redeemERC4626(
644+
Base.MORPHO_VAULT_SUSDC,
645+
IERC20(Base.MORPHO_VAULT_SUSDC).balanceOf(address(baseAlmProxy)),
646+
0
647+
);
618648

619649
assertGe(usdcBase.balanceOf(address(baseAlmProxy)), 10e6); // Interest earned
620650

src/ForeignController.sol

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ contract ForeignController is ReentrancyGuard, AccessControlEnumerable {
346346
/*** Relayer ERC4626 functions ***/
347347
/**********************************************************************************************/
348348

349-
function depositERC4626(address token, uint256 amount)
349+
function depositERC4626(address token, uint256 amount, uint256 minSharesOut)
350350
external
351351
nonReentrant
352352
onlyRole(RELAYER)
@@ -365,13 +365,15 @@ contract ForeignController is ReentrancyGuard, AccessControlEnumerable {
365365
(uint256)
366366
);
367367

368+
require(shares >= minSharesOut, "FC/min-shares-out-not-met");
369+
368370
require(
369371
_getExchangeRate(shares, amount) <= maxExchangeRates[token],
370372
"FC/exchange-rate-too-high"
371373
);
372374
}
373375

374-
function withdrawERC4626(address token, uint256 amount)
376+
function withdrawERC4626(address token, uint256 amount, uint256 maxSharesIn)
375377
external
376378
nonReentrant
377379
onlyRole(RELAYER)
@@ -388,14 +390,16 @@ contract ForeignController is ReentrancyGuard, AccessControlEnumerable {
388390
(uint256)
389391
);
390392

393+
require(shares <= maxSharesIn, "FC/shares-burned-too-high");
394+
391395
rateLimits.triggerRateLimitIncrease(
392396
RateLimitHelpers.makeAddressKey(LIMIT_4626_DEPOSIT, token),
393397
amount
394398
);
395399
}
396400

397401
// NOTE: !!! Rate limited at end of function !!!
398-
function redeemERC4626(address token, uint256 shares)
402+
function redeemERC4626(address token, uint256 shares, uint256 minAssetsOut)
399403
external nonReentrant onlyRole(RELAYER) returns (uint256 assets)
400404
{
401405
// Redeem shares for assets from the token, decode the resulting assets.
@@ -408,6 +412,8 @@ contract ForeignController is ReentrancyGuard, AccessControlEnumerable {
408412
(uint256)
409413
);
410414

415+
require(assets >= minAssetsOut, "FC/min-assets-out-not-met");
416+
411417
rateLimits.triggerRateLimitDecrease(
412418
RateLimitHelpers.makeAddressKey(LIMIT_4626_WITHDRAW, token),
413419
assets

src/MainnetController.sol

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
545545
/*** Relayer ERC4626 functions ***/
546546
/**********************************************************************************************/
547547

548-
function depositERC4626(address token, uint256 amount)
548+
function depositERC4626(address token, uint256 amount, uint256 minSharesOut)
549549
external nonReentrant returns (uint256 shares)
550550
{
551551
_checkRole(RELAYER);
@@ -554,13 +554,14 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
554554
proxy : address(proxy),
555555
token : token,
556556
amount : amount,
557+
minSharesOut : minSharesOut,
557558
maxExchangeRate : maxExchangeRates[token],
558559
rateLimits : address(rateLimits),
559560
rateLimitId : LIMIT_4626_DEPOSIT
560561
});
561562
}
562563

563-
function withdrawERC4626(address token, uint256 amount)
564+
function withdrawERC4626(address token, uint256 amount, uint256 maxSharesIn)
564565
external nonReentrant returns (uint256 shares)
565566
{
566567
_checkRole(RELAYER);
@@ -569,13 +570,14 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
569570
proxy : address(proxy),
570571
token : token,
571572
amount : amount,
573+
maxSharesIn : maxSharesIn,
572574
rateLimits : address(rateLimits),
573575
withdrawRateLimitId : LIMIT_4626_WITHDRAW,
574576
depositRateLimitId : LIMIT_4626_DEPOSIT
575577
});
576578
}
577579

578-
function redeemERC4626(address token, uint256 shares)
580+
function redeemERC4626(address token, uint256 shares, uint256 minAssetsOut)
579581
external nonReentrant returns (uint256 assets)
580582
{
581583
_checkRole(RELAYER);
@@ -584,6 +586,7 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
584586
proxy : address(proxy),
585587
token : token,
586588
shares : shares,
589+
minAssetsOut : minAssetsOut,
587590
rateLimits : address(rateLimits),
588591
withdrawRateLimitId : LIMIT_4626_WITHDRAW,
589592
depositRateLimitId : LIMIT_4626_DEPOSIT

src/libraries/ERC4626Lib.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ library ERC4626Lib {
1818
address proxy,
1919
address token,
2020
uint256 amount,
21+
uint256 minSharesOut,
2122
uint256 maxExchangeRate,
2223
address rateLimits,
2324
bytes32 rateLimitId
@@ -39,13 +40,16 @@ library ERC4626Lib {
3940
(uint256)
4041
);
4142

43+
require(shares >= minSharesOut, "MC/min-shares-out-not-met");
44+
4245
require(getExchangeRate(shares, amount) <= maxExchangeRate, "MC/exchange-rate-too-high");
4346
}
4447

4548
function withdraw(
4649
address proxy,
4750
address token,
4851
uint256 amount,
52+
uint256 maxSharesIn,
4953
address rateLimits,
5054
bytes32 withdrawRateLimitId,
5155
bytes32 depositRateLimitId
@@ -65,6 +69,8 @@ library ERC4626Lib {
6569
(uint256)
6670
);
6771

72+
require(shares <= maxSharesIn, "MC/shares-burned-too-high");
73+
6874
IRateLimits(rateLimits).triggerRateLimitIncrease(
6975
RateLimitHelpers.makeAddressKey(depositRateLimitId, token),
7076
amount
@@ -75,6 +81,7 @@ library ERC4626Lib {
7581
address proxy,
7682
address token,
7783
uint256 shares,
84+
uint256 minAssetsOut,
7885
address rateLimits,
7986
bytes32 withdrawRateLimitId,
8087
bytes32 depositRateLimitId
@@ -89,6 +96,8 @@ library ERC4626Lib {
8996
(uint256)
9097
);
9198

99+
require(assets >= minAssetsOut, "MC/min-assets-out-not-met");
100+
92101
IRateLimits(rateLimits).triggerRateLimitDecrease(
93102
RateLimitHelpers.makeAddressKey(withdrawRateLimitId, token),
94103
assets

0 commit comments

Comments
 (0)