Skip to content

Commit 3cd11be

Browse files
committed
M Feat: Refactor to use two tests with varying maxSlippage.
1 parent 2b2d077 commit 3cd11be

File tree

1 file changed

+43
-21
lines changed

1 file changed

+43
-21
lines changed

test/mainnet-fork/ERC4626DonationAttack.t.sol

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ contract ERC4626DonationAttackTestBase is ForkTestBase {
2929
address allocator = makeAddr("allocator");
3030
address skim_recipient = makeAddr("skim_recipient");
3131

32+
address attacker = makeAddr("attacker");
33+
3234
function setUp() override public {
3335
super.setUp();
3436

@@ -65,12 +67,12 @@ contract ERC4626DonationAttackTestBase is ForkTestBase {
6567
assertEq(morphoVault.curator(), curator);
6668
assertEq(morphoVault.guardian(), guardian);
6769
assertEq(morphoVault.feeRecipient(), fee_recipient);
70+
6871
assertTrue(morphoVault.isAllocator(allocator));
6972

7073
vm.startPrank(Ethereum.SPARK_PROXY);
7174
rateLimits.setRateLimitData(depositKey, 5_000_000e18, uint256(1_000_000e18) / 4 hours);
7275
rateLimits.setRateLimitData(withdrawKey, 5_000_000e18, uint256(1_000_000e18) / 4 hours);
73-
mainnetController.setMaxSlippage(address(morphoVault), 1e18 - 1e4); // Rounding slippage
7476
vm.stopPrank();
7577
}
7678

@@ -82,18 +84,51 @@ contract ERC4626DonationAttackTestBase is ForkTestBase {
8284

8385
contract ERC4626DonationAttack is ERC4626DonationAttackTestBase {
8486

85-
function test_donationAttackERC4626_usds() external {
86-
address mallory = makeAddr("mallory");
87+
function test_depositERC4626_donationAttackFailure() external {
88+
vm.prank(Ethereum.SPARK_PROXY);
89+
mainnetController.setMaxSlippage(address(morphoVault), 1e18 - 1e4); // Rounding slippage
90+
91+
_doAttack();
92+
vm.prank(relayer);
93+
vm.expectRevert("MainnetController/slippage-too-high");
94+
mainnetController.depositERC4626(address(morphoVault), 2_000_000e18);
95+
}
96+
97+
function test_depositERC4626_donationAttackSuccess() external {
98+
// Set max slippage to (close to) 100%
99+
vm.prank(Ethereum.SPARK_PROXY);
100+
mainnetController.setMaxSlippage(address(morphoVault), 1);
101+
102+
_doAttack();
103+
vm.prank(relayer);
104+
uint256 shares = mainnetController.depositERC4626(address(morphoVault), 2_000_000e18);
105+
106+
// shares == assets * (totalSupply + 1) / (totalAssets + 1)
107+
// == 2_000_000e18 * (1 + 1) / (1_000_000e18 + 1 + 1)
108+
// == 3.9..
109+
// Rounding down, the proxy receives 3 shares.
110+
assertEq(shares, 3);
111+
assertEq(morphoVault.totalAssets(), 3_000_000e18 + 1);
112+
assertEq(morphoVault.totalSupply(), 4);
113+
114+
uint256 assetsOfProxy = morphoVault.convertToAssets(morphoVault.balanceOf(almProxy));
115+
uint256 assetsOfAttacker = morphoVault.convertToAssets(morphoVault.balanceOf(attacker));
116+
117+
assertEq(assetsOfProxy, 1_500_000e18 + 1);
118+
assertLt(assetsOfProxy, 2_000_000e18); // The proxy owns less than it deposited
119+
assertEq(assetsOfAttacker, 500_000e18);
120+
}
87121

122+
function _doAttack() internal {
88123
Market memory market = morpho.market(marketId);
89124
assertEq(market.totalSupplyAssets, 36_095_481.319542091092211965e18); // ~36M USDS
90125
assertEq(market.totalSupplyShares, 36_095_481.319542091092211965000000e24);
91126

92-
deal(address(usds), mallory, 1_000_000e18 + 1);
127+
deal(address(usds), attacker, 1_000_000e18 + 1);
93128

94-
vm.startPrank(mallory);
129+
vm.startPrank(attacker);
95130
usds.approve(address(morphoVault), 1);
96-
morphoVault.deposit(1, mallory);
131+
morphoVault.deposit(1, attacker);
97132
usds.approve(address(morpho), 1_000_000e18);
98133
// Donation attack
99134
(uint256 assets, uint256 shares) = morpho.supply(
@@ -105,8 +140,8 @@ contract ERC4626DonationAttack is ERC4626DonationAttackTestBase {
105140
assertEq(shares, uint256(1_000_000e18) * market.totalSupplyShares / market.totalSupplyAssets);
106141
assertEq(shares, 1e30);
107142

108-
assertEq(morphoVault.balanceOf(mallory), 1);
109-
assertEq(morphoVault.totalSupply(), 1);
143+
assertEq(morphoVault.balanceOf(attacker), 1);
144+
assertEq(morphoVault.totalSupply(), 1);
110145

111146
assertEq(morphoVault.totalAssets(), 1_000_000e18 + 1);
112147
// Instead of performing shares * totalAssets / totalShares, aka
@@ -116,19 +151,6 @@ contract ERC4626DonationAttack is ERC4626DonationAttackTestBase {
116151
assertEq(morphoVault.convertToAssets(1), 500_000e18 + 1);
117152

118153
deal(address(usds), address(almProxy), 2_000_000e18);
119-
120-
vm.prank(relayer);
121-
try mainnetController.depositERC4626(address(morphoVault), 2_000_000e18) {
122-
// The deposit went through. The only time this is permissible is if the attack had no
123-
// effect.
124-
uint256 assetsOfProxy = morphoVault.convertToAssets(morphoVault.balanceOf(address(almProxy)));
125-
assertEq(assetsOfProxy, 2_000_000e18);
126-
assertEq(morphoVault.balanceOf(address(almProxy)), 2_000_000e24);
127-
} catch Error(string memory reason) {
128-
// The deposit was correctly reverted.
129-
assertEq(reason, "MainnetController/slippage-too-high");
130-
}
131154
}
132-
133155
}
134156

0 commit comments

Comments
 (0)