Skip to content

Commit c224748

Browse files
committed
test: add SECVinvariant depositValidator mock function
1 parent 765d974 commit c224748

4 files changed

Lines changed: 105 additions & 116 deletions

File tree

script/data/lido-data-sample-json

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/safe-modules/SimpleETHContributionVault.sol

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ contract SimpleETHContributionVault {
3232
/// @param signatures array of validator signatures
3333
/// @param deposit_data_roots array of deposit data roots
3434
event DepositValidator(
35-
bytes[] pubkeys,
36-
bytes[] withdrawal_credentials,
37-
bytes[] signatures,
38-
bytes32[] deposit_data_roots
35+
bytes[] pubkeys, bytes[] withdrawal_credentials, bytes[] signatures, bytes32[] deposit_data_roots
3936
);
4037

4138
/// @notice Emitted on user rage quit
@@ -49,7 +46,7 @@ contract SimpleETHContributionVault {
4946

5047
/// @notice ETH2 deposit contract
5148
IETH2DepositContract public immutable depositContract;
52-
49+
5350
/// @notice Address of gnosis safe
5451
address public immutable safe;
5552

@@ -59,7 +56,6 @@ contract SimpleETHContributionVault {
5956
/// @notice tracks if validator have been activated
6057
bool public activated;
6158

62-
6359
modifier onlySafe() {
6460
if (msg.sender != safe) revert Unauthorized(msg.sender);
6561
_;

src/test/safe/SimpleETHContributionVault.t.sol

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import "forge-std/Test.sol";
55
import {MockERC20} from "src/test/utils/mocks/MockERC20.sol";
66
import {SimpleETHContributionVault} from "src/safe-modules/SimpleETHContributionVault.sol";
77

8-
98
contract SimpleETHContributionVaultTest is Test {
10-
119
error CannotRageQuit();
1210
error Unauthorized(address user);
1311

@@ -74,12 +72,11 @@ contract SimpleETHContributionVaultTest is Test {
7472

7573
function test_rageQuit() external {
7674
vm.deal(user1, ETH_STAKE);
77-
75+
7876
vm.prank(user1);
7977
(bool _success,) = payable(contributionVault).call{value: ETH_STAKE}("");
8078
assertTrue(_success, "call failed");
8179

82-
8380
vm.expectEmit(false, false, false, true);
8481
emit RageQuit(user1, ETH_STAKE);
8582

@@ -188,15 +185,21 @@ function getETHValidatorData()
188185
)
189186
{
190187
pubkeys = new bytes[](1);
191-
pubkeys[0] =
192-
bytes(abi.encodePacked(hex"83fa9495bb0944a74fc6a66e699039b66134b22a52a710f8d0f7cde318a2db3da40081a5867667389d206e21b5e37e52"));
188+
pubkeys[0] = bytes(
189+
abi.encodePacked(
190+
hex"83fa9495bb0944a74fc6a66e699039b66134b22a52a710f8d0f7cde318a2db3da40081a5867667389d206e21b5e37e52"
191+
)
192+
);
193193

194194
withdrawal_credentials = new bytes[](1);
195-
withdrawal_credentials[0] = bytes(abi.encodePacked(hex"010000000000000000000000e839a3e9efb32c6a56ab7128e51056585275506c"));
195+
withdrawal_credentials[0] =
196+
bytes(abi.encodePacked(hex"010000000000000000000000e839a3e9efb32c6a56ab7128e51056585275506c"));
196197

197198
signatures = new bytes[](1);
198199
signatures[0] = bytes(
199-
abi.encodePacked(hex"95f00435e80e59a8fed41581e2050a3fe56272d6be845686ef014a57909c6621d7847fa550b77cb8e541b955f3c2ea031983d9e4336f215e75c8ba75d94e05f1e23460de6611a980ef629d3e32ca09cffaf2a63372496079b1ee22310d336ded")
200+
abi.encodePacked(
201+
hex"95f00435e80e59a8fed41581e2050a3fe56272d6be845686ef014a57909c6621d7847fa550b77cb8e541b955f3c2ea031983d9e4336f215e75c8ba75d94e05f1e23460de6611a980ef629d3e32ca09cffaf2a63372496079b1ee22310d336ded"
202+
)
200203
);
201204

202205
deposit_data_roots = new bytes32[](1);
Lines changed: 92 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,125 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.19;
3+
34
import {Test} from "forge-std/Test.sol";
45
import {StdInvariant} from "forge-std/StdInvariant.sol";
56
import {SimpleETHContributionVault} from "src/safe-modules/SimpleETHContributionVault.sol";
67
import {CommonBase} from "forge-std/Base.sol";
78
import {StdCheats} from "forge-std/StdCheats.sol";
89
import {StdUtils} from "forge-std/StdUtils.sol";
10+
import {getETHValidatorData} from "../SimpleETHContributionVault.t.sol";
911

1012
contract MockDepositContract {
11-
event Deposit (
12-
bytes[] pubkeys,
13-
bytes[] withdrawal_credentials,
14-
bytes[] signatures,
15-
bytes32[] deposit_data_roots
16-
);
17-
function depositValidator(
18-
bytes[] calldata,
19-
bytes[] calldata,
20-
bytes[] calldata,
21-
bytes32[] calldata
22-
) external payable {}
13+
uint256 public ghost_depositSum;
14+
15+
event Deposit(bytes[] pubkeys, bytes[] withdrawal_credentials, bytes[] signatures, bytes32[] deposit_data_roots);
16+
17+
function deposit(bytes[] calldata, bytes[] calldata, bytes[] calldata, bytes32[] calldata) external payable {
18+
ghost_depositSum += msg.value;
19+
}
2320
}
2421

25-
contract SECVHandler is CommonBase, StdCheats, StdUtils {
26-
SimpleETHContributionVault public contributionVault;
22+
contract SECVMock is SimpleETHContributionVault {
23+
constructor(address _safe, address eth2DepositContract) SimpleETHContributionVault(_safe, eth2DepositContract) {}
24+
25+
/// @notice Mock depositValidator function
26+
function depositValidatorMock(
27+
bytes[] calldata pubkeys,
28+
bytes[] calldata withdrawal_credentials,
29+
bytes[] calldata signatures,
30+
bytes32[] calldata deposit_data_roots
31+
) external payable {
32+
for (uint256 i = 0; i < 1;) {
33+
depositContract.deposit{value: msg.value}(
34+
pubkeys[i], withdrawal_credentials[i], signatures[i], deposit_data_roots[i]
35+
);
36+
unchecked {
37+
i++;
38+
}
39+
}
40+
}
41+
}
2742

28-
uint256 public constant ETH_SUPPLY = 100_000 ether;
43+
contract SECVBoundedHandler is CommonBase, StdCheats, StdUtils {
44+
SECVMock public contributionVault;
2945

30-
uint256 public ghost_depositSum;
31-
uint256 public ghost_rageQuitSum;
46+
uint256 public constant ETH_SUPPLY = 1_000_000 ether;
3247

33-
receive() external payable {}
48+
uint256 public ghost_depositSum;
49+
uint256 public ghost_rageQuitSum;
3450

35-
constructor(SimpleETHContributionVault vault) {
36-
contributionVault = vault;
37-
deal(address(this), ETH_SUPPLY);
38-
}
51+
receive() external payable {}
3952

40-
function deposit(uint256 amount) external payable {
41-
amount = bound(amount, 0, address(this).balance);
42-
(bool _success,) = payable(contributionVault).call{value: amount}("");
43-
assert(_success);
53+
constructor(SECVMock vault) {
54+
contributionVault = vault;
55+
deal(address(this), ETH_SUPPLY);
56+
}
4457

45-
ghost_depositSum += amount;
58+
function deposit(uint256 amount) external payable {
59+
amount = bound(amount, 0, address(this).balance);
60+
(bool _success,) = payable(contributionVault).call{value: amount}("");
61+
assert(_success);
4662

47-
}
63+
ghost_depositSum += amount;
64+
}
4865

49-
function rageQuit(uint256 amount) external payable {
50-
amount = bound(amount, 0 , contributionVault.userBalances(address(this)));
51-
contributionVault.rageQuit(address(this), amount);
66+
function rageQuit(uint256 amount) external payable {
67+
amount = bound(amount, 0, contributionVault.userBalances(address(this)));
68+
contributionVault.rageQuit(address(this), amount);
5269

53-
ghost_rageQuitSum += amount;
54-
}
70+
ghost_rageQuitSum += amount;
71+
}
72+
73+
function depositValidator(uint256 amount) external payable {
74+
amount = bound(amount, 0, address(contributionVault).balance);
75+
(
76+
bytes[] memory pubkeys,
77+
bytes[] memory withdrawal_credentials,
78+
bytes[] memory signatures,
79+
bytes32[] memory deposit_data_roots
80+
) = getETHValidatorData();
81+
82+
contributionVault.depositValidatorMock{value: amount}(
83+
pubkeys, withdrawal_credentials, signatures, deposit_data_roots
84+
);
85+
}
5586
}
5687

5788
contract SECVInvariant is Test {
89+
MockDepositContract public mockDepositContract;
90+
SECVMock public contributionVault;
91+
SECVBoundedHandler public handler;
5892

59-
MockDepositContract public mockDepositContract;
60-
SimpleETHContributionVault public contributionVault;
61-
SECVHandler public handler;
62-
63-
address public safe;
93+
address public safe;
6494

65-
function setUp() public {
66-
safe = makeAddr("safe");
95+
function setUp() public {
96+
safe = makeAddr("safe");
6797

68-
mockDepositContract = new MockDepositContract();
69-
contributionVault = new SimpleETHContributionVault(
98+
mockDepositContract = new MockDepositContract();
99+
contributionVault = new SECVMock(
70100
safe,
71101
address(mockDepositContract)
72102
);
73-
handler = new SECVHandler(contributionVault);
103+
handler = new SECVBoundedHandler(contributionVault);
74104

75-
targetContract(address(handler));
76-
}
77-
78-
function invariant_balanceEqual() public {
79-
assertEq(
80-
handler.ETH_SUPPLY(),
81-
address(handler).balance + contributionVault.userBalances(address(handler))
82-
);
83-
}
105+
targetContract(address(handler));
106+
}
84107

85-
function invariant_vaultIsSolvent() public {
86-
assertEq(
87-
address(contributionVault).balance,
88-
handler.ghost_depositSum() - handler.ghost_rageQuitSum()
89-
);
90-
}
108+
/// @notice This invariant checks that the sum of balances in the handler,
109+
/// vault and mock deposit contract is equal
110+
function invariant_balanceEqual() public {
111+
assertEq(
112+
handler.ETH_SUPPLY(),
113+
address(handler).balance + contributionVault.userBalances(address(handler)) + address(mockDepositContract).balance
114+
);
115+
}
116+
117+
/// @notice This invariant checks that the vault is
118+
/// always solvent when a user ragequits.
119+
function invariant_vaultIsSolvent() public {
120+
assertEq(
121+
address(contributionVault).balance,
122+
handler.ghost_depositSum() + mockDepositContract.ghost_depositSum() - handler.ghost_rageQuitSum()
123+
);
124+
}
91125
}

0 commit comments

Comments
 (0)