Skip to content

Commit 28bb091

Browse files
committed
Add fuzz tests for admin and distributor management
1 parent 806f086 commit 28bb091

File tree

6 files changed

+682
-0
lines changed

6 files changed

+682
-0
lines changed

test/02_InitRepo.t.sol

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,137 @@ contract InitRepo_Test is Base_Test {
912912
assertFalse(escrow.getAccountExists(124, 456));
913913
}
914914

915+
function test_initRepo_fuzz_signatures(uint256 wrongNonce, uint256 wrongDeadline) public {
916+
vm.assume(wrongNonce != escrow.ownerNonce());
917+
vm.assume(wrongDeadline != block.timestamp + 1 hours);
918+
vm.assume(wrongDeadline > block.timestamp); // Must be future timestamp
919+
920+
address[] memory admins = new address[](1);
921+
admins[0] = repoAdmin;
922+
uint256 repoId = 999;
923+
uint256 accountId = 999;
924+
925+
// Test with wrong nonce
926+
bytes32 digestWrongNonce = keccak256(
927+
abi.encodePacked(
928+
"\x19\x01",
929+
escrow.DOMAIN_SEPARATOR(),
930+
keccak256(abi.encode(
931+
escrow.SET_ADMIN_TYPEHASH(),
932+
repoId,
933+
accountId,
934+
keccak256(abi.encode(admins)),
935+
wrongNonce,
936+
block.timestamp + 1 hours
937+
))
938+
)
939+
);
940+
941+
(uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(ownerPrivateKey, digestWrongNonce);
942+
expectRevert(Errors.INVALID_SIGNATURE);
943+
escrow.initRepo(repoId, accountId, admins, block.timestamp + 1 hours, v1, r1, s1);
944+
945+
// Test with wrong deadline in signature
946+
bytes32 digestWrongDeadline = keccak256(
947+
abi.encodePacked(
948+
"\x19\x01",
949+
escrow.DOMAIN_SEPARATOR(),
950+
keccak256(abi.encode(
951+
escrow.SET_ADMIN_TYPEHASH(),
952+
repoId,
953+
accountId,
954+
keccak256(abi.encode(admins)),
955+
escrow.ownerNonce(),
956+
wrongDeadline
957+
))
958+
)
959+
);
960+
961+
(uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(ownerPrivateKey, digestWrongDeadline);
962+
expectRevert(Errors.INVALID_SIGNATURE);
963+
escrow.initRepo(repoId, accountId, admins, block.timestamp + 1 hours, v2, r2, s2);
964+
}
965+
966+
function test_initRepo_fuzz_batchLimits(uint8 numAdmins) public {
967+
vm.assume(numAdmins > 0);
968+
uint256 batchLimit = escrow.batchLimit();
969+
970+
address[] memory admins = new address[](numAdmins);
971+
for (uint i = 0; i < numAdmins; i++) {
972+
admins[i] = makeAddr(string(abi.encodePacked("batchAdmin", i)));
973+
}
974+
975+
uint256 repoId = 888;
976+
uint256 accountId = 888;
977+
uint256 deadline = block.timestamp + 1 hours;
978+
979+
bytes32 digest = keccak256(
980+
abi.encodePacked(
981+
"\x19\x01",
982+
escrow.DOMAIN_SEPARATOR(),
983+
keccak256(abi.encode(
984+
escrow.SET_ADMIN_TYPEHASH(),
985+
repoId,
986+
accountId,
987+
keccak256(abi.encode(admins)),
988+
escrow.ownerNonce(),
989+
deadline
990+
))
991+
)
992+
);
993+
994+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
995+
996+
if (numAdmins <= batchLimit) {
997+
// Should succeed if within batch limit
998+
escrow.initRepo(repoId, accountId, admins, deadline, v, r, s);
999+
1000+
// Verify all admins were added
1001+
for (uint i = 0; i < numAdmins; i++) {
1002+
assertTrue(escrow.getIsAuthorizedAdmin(repoId, accountId, admins[i]));
1003+
}
1004+
1005+
address[] memory allAdmins = escrow.getAllAdmins(repoId, accountId);
1006+
assertEq(allAdmins.length, numAdmins);
1007+
} else {
1008+
// Should fail if exceeds batch limit
1009+
expectRevert(Errors.BATCH_LIMIT_EXCEEDED);
1010+
escrow.initRepo(repoId, accountId, admins, deadline, v, r, s);
1011+
}
1012+
}
1013+
1014+
function test_initRepo_fuzz_timeDeadlines(uint32 timeOffset) public {
1015+
vm.assume(timeOffset > 0 && timeOffset <= 365 days);
1016+
1017+
address[] memory admins = new address[](1);
1018+
admins[0] = repoAdmin;
1019+
uint256 repoId = 777;
1020+
uint256 accountId = 777;
1021+
uint256 deadline = block.timestamp + timeOffset;
1022+
1023+
bytes32 digest = keccak256(
1024+
abi.encodePacked(
1025+
"\x19\x01",
1026+
escrow.DOMAIN_SEPARATOR(),
1027+
keccak256(abi.encode(
1028+
escrow.SET_ADMIN_TYPEHASH(),
1029+
repoId,
1030+
accountId,
1031+
keccak256(abi.encode(admins)),
1032+
escrow.ownerNonce(),
1033+
deadline
1034+
))
1035+
)
1036+
);
1037+
1038+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
1039+
1040+
// Should work with any reasonable future deadline
1041+
escrow.initRepo(repoId, accountId, admins, deadline, v, r, s);
1042+
assertTrue(escrow.getIsAuthorizedAdmin(repoId, accountId, repoAdmin));
1043+
assertTrue(escrow.getAccountExists(repoId, accountId));
1044+
}
1045+
9151046
/* -------------------------------------------------------------------------- */
9161047
/* EVENTS */
9171048
/* -------------------------------------------------------------------------- */

test/03_FundRepo.t.sol

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,66 @@ contract FundRepo_Test is Base_Test {
148148
assertEq(escrow.getAccountBalance(REPO_ID, ACCOUNT_ID, address(wETH)), amount);
149149
}
150150

151+
function test_fundRepo_fuzz_repoAndAccountIds(uint256 repoId, uint256 accountId, uint256 amount) public {
152+
vm.assume(amount > 0 && amount <= 1000e18);
153+
vm.assume(repoId <= type(uint128).max && accountId <= type(uint128).max);
154+
155+
vm.prank(alice);
156+
escrow.fundRepo(repoId, accountId, wETH, amount, "");
157+
158+
assertEq(escrow.getAccountBalance(repoId, accountId, address(wETH)), amount);
159+
}
160+
161+
function test_fundRepo_fuzz_multipleFundings(uint8 numFundings, uint256 baseAmount) public {
162+
vm.assume(numFundings > 0 && numFundings <= 20);
163+
vm.assume(baseAmount > 0 && baseAmount <= 50e18);
164+
165+
uint256 totalAmount = 0;
166+
167+
for (uint i = 0; i < numFundings; i++) {
168+
uint256 amount = baseAmount + (i * 1e18); // Vary amounts
169+
vm.prank(alice);
170+
escrow.fundRepo(REPO_ID, ACCOUNT_ID, wETH, amount, abi.encodePacked("funding ", i));
171+
totalAmount += amount;
172+
}
173+
174+
assertEq(escrow.getAccountBalance(REPO_ID, ACCOUNT_ID, address(wETH)), totalAmount);
175+
}
176+
177+
function test_fundRepo_fuzz_dataField(bytes calldata data) public {
178+
vm.assume(data.length <= 1000); // Reasonable data size limit
179+
uint256 amount = 100e18;
180+
181+
vm.expectEmit(true, true, true, true);
182+
emit Funded(REPO_ID, address(wETH), alice, amount, data);
183+
184+
vm.prank(alice);
185+
escrow.fundRepo(REPO_ID, ACCOUNT_ID, wETH, amount, data);
186+
187+
assertEq(escrow.getAccountBalance(REPO_ID, ACCOUNT_ID, address(wETH)), amount);
188+
}
189+
190+
function test_fundRepo_fuzz_multipleUsers(uint8 numUsers, uint256 amountPerUser) public {
191+
vm.assume(numUsers > 0 && numUsers <= 10);
192+
vm.assume(amountPerUser > 0 && amountPerUser <= 100e18);
193+
194+
uint256 totalAmount = 0;
195+
196+
for (uint i = 0; i < numUsers; i++) {
197+
address user = makeAddr(string(abi.encodePacked("user", i)));
198+
wETH.mint(user, amountPerUser);
199+
200+
vm.startPrank(user);
201+
wETH.approve(address(escrow), amountPerUser);
202+
escrow.fundRepo(REPO_ID, ACCOUNT_ID, wETH, amountPerUser, "");
203+
vm.stopPrank();
204+
205+
totalAmount += amountPerUser;
206+
}
207+
208+
assertEq(escrow.getAccountBalance(REPO_ID, ACCOUNT_ID, address(wETH)), totalAmount);
209+
}
210+
151211
// Event for testing
152212
event Funded(uint256 indexed repoId, address indexed token, address indexed sender, uint256 amount, bytes data);
153213
}

test/07_ReclaimFund.t.sol

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,68 @@ contract ReclaimFund_Test is Base_Test {
269269
escrow.reclaimFund(REPO_ID, ACCOUNT_ID, address(wETH), reclaimAmount);
270270
}
271271

272+
function test_reclaimFund_fuzz_repoAndAccountIds(uint256 repoId, uint256 accountId, uint256 amount) public {
273+
vm.assume(amount > 0 && amount <= 1000e18);
274+
vm.assume(repoId != REPO_ID || accountId != ACCOUNT_ID); // Avoid conflict with existing repo
275+
vm.assume(repoId <= type(uint128).max && accountId <= type(uint128).max);
276+
277+
address admin = makeAddr("fuzzAdmin");
278+
279+
// Initialize new repo
280+
uint256 deadline = block.timestamp + 1 hours;
281+
bytes32 digest = keccak256(
282+
abi.encodePacked(
283+
"\x19\x01",
284+
escrow.DOMAIN_SEPARATOR(),
285+
keccak256(abi.encode(
286+
escrow.SET_ADMIN_TYPEHASH(),
287+
repoId,
288+
accountId,
289+
keccak256(abi.encode(_toArray(admin))),
290+
escrow.ownerNonce(),
291+
deadline
292+
))
293+
)
294+
);
295+
296+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
297+
escrow.initRepo(repoId, accountId, _toArray(admin), deadline, v, r, s);
298+
299+
// Fund the repo
300+
wETH.mint(address(this), amount);
301+
wETH.approve(address(escrow), amount);
302+
escrow.fundRepo(repoId, accountId, wETH, amount, "");
303+
304+
uint256 initialAdminBalance = wETH.balanceOf(admin);
305+
306+
// Reclaim funds
307+
vm.prank(admin);
308+
escrow.reclaimFund(repoId, accountId, address(wETH), amount);
309+
310+
assertEq(wETH.balanceOf(admin), initialAdminBalance + amount);
311+
assertEq(escrow.getAccountBalance(repoId, accountId, address(wETH)), 0);
312+
}
313+
314+
function test_reclaimFund_fuzz_multipleReclaims(uint8 numReclaims, uint256 baseAmount) public {
315+
vm.assume(numReclaims > 0 && numReclaims <= 10);
316+
vm.assume(baseAmount > 0 && baseAmount <= 100e18);
317+
318+
uint256 totalFundAmount = baseAmount * numReclaims;
319+
_fundRepo(totalFundAmount);
320+
321+
uint256 totalReclaimed = 0;
322+
uint256 initialAdminBalance = wETH.balanceOf(repoAdmin);
323+
324+
for (uint i = 0; i < numReclaims; i++) {
325+
vm.prank(repoAdmin);
326+
escrow.reclaimFund(REPO_ID, ACCOUNT_ID, address(wETH), baseAmount);
327+
totalReclaimed += baseAmount;
328+
}
329+
330+
assertEq(wETH.balanceOf(repoAdmin), initialAdminBalance + totalReclaimed);
331+
assertEq(escrow.getAccountBalance(REPO_ID, ACCOUNT_ID, address(wETH)), totalFundAmount - totalReclaimed);
332+
}
333+
272334
/* -------------------------------------------------------------------------- */
273335
/* EVENTS */
274336
/* -------------------------------------------------------------------------- */

0 commit comments

Comments
 (0)