Skip to content

Commit b59c104

Browse files
authored
Added clampArr functions and tests (#76)
1 parent f962ee0 commit b59c104

6 files changed

Lines changed: 320 additions & 23 deletions

File tree

src/helpers/HelperClamp.sol

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ abstract contract HelperClamp is HelperAssert {
1515
error InvalidRangeInt128(int128 low, int128 high);
1616
error UnsupportedClampLtValue(uint256 value);
1717
error UnsupportedClampGtValue(uint256 value);
18+
error ClampEmptyArray();
1819

1920
event Clamped(string);
2021

@@ -388,25 +389,111 @@ abstract contract HelperClamp is HelperAssert {
388389
return clamp(a, b, type(int128).max, enableLogs);
389390
}
390391

392+
/**
393+
* @dev Clamps an index to be within the bounds of the given unsigned integer array and returns the element at that index.
394+
* Reverts with EmptyArray error if the array is empty.
395+
* @param arr The unsigned integer array to index into
396+
* @param _index The index to clamp
397+
* @return The element at the clamped index
398+
*/
399+
function clampArr(uint256[] memory arr, uint256 _index) public returns (uint256) {
400+
if (arr.length == 0) {
401+
revert ClampEmptyArray();
402+
}
403+
uint256 index = clamp(_index, 0, arr.length - 1);
404+
return arr[index];
405+
}
406+
407+
/**
408+
* @dev Clamps an index to be within the bounds of the given signed integer array and returns the element at that index.
409+
* Reverts with EmptyArray error if the array is empty.
410+
* @param arr The signed integer array to index into
411+
* @param _index The index to clamp
412+
* @return The element at the clamped index
413+
*/
414+
function clampArr(int256[] memory arr, uint256 _index) public returns (int256) {
415+
if (arr.length == 0) {
416+
revert ClampEmptyArray();
417+
}
418+
uint256 index = clamp(_index, 0, arr.length - 1);
419+
return arr[index];
420+
}
421+
422+
/**
423+
* @dev Clamps an index to be within the bounds of the given address array and returns the element at that index.
424+
* Reverts with EmptyArray error if the array is empty.
425+
* @param arr The address array to index into
426+
* @param _index The index to clamp
427+
* @return The element at the clamped index
428+
*/
429+
function clampArr(address[] memory arr, uint256 _index) public returns (address) {
430+
if (arr.length == 0) {
431+
revert ClampEmptyArray();
432+
}
433+
uint256 index = clamp(_index, 0, arr.length - 1);
434+
return arr[index];
435+
}
436+
437+
/**
438+
* @dev Clamps an index to be within the bounds of the given boolean array and returns the element at that index.
439+
* Reverts with EmptyArray error if the array is empty.
440+
* @param arr The boolean array to index into
441+
* @param _index The index to clamp
442+
* @return The element at the clamped index
443+
*/
444+
function clampArr(bool[] memory arr, uint256 _index) public returns (bool) {
445+
if (arr.length == 0) {
446+
revert ClampEmptyArray();
447+
}
448+
uint256 index = clamp(_index, 0, arr.length - 1);
449+
return arr[index];
450+
}
451+
452+
/**
453+
* @dev Clamps an index to be within the bounds of the given string array and returns the element at that index.
454+
* Reverts with EmptyArray error if the array is empty.
455+
* @param arr The string array to index into
456+
* @param _index The index to clamp
457+
* @return The element at the clamped index
458+
*/
459+
function clampArr(string[] memory arr, uint256 _index) public returns (string memory) {
460+
if (arr.length == 0) {
461+
revert ClampEmptyArray();
462+
}
463+
uint256 index = clamp(_index, 0, arr.length - 1);
464+
return arr[index];
465+
}
466+
467+
/**
468+
* @dev Clamps an index to be within the bounds of the given bytes array and returns the element at that index.
469+
* Reverts with EmptyArray error if the array is empty.
470+
* @param arr The bytes array to index into
471+
* @param _index The index to clamp
472+
* @return The element at the clamped index
473+
*/
474+
function clampArr(bytes[] memory arr, uint256 _index) public returns (bytes memory) {
475+
if (arr.length == 0) {
476+
revert ClampEmptyArray();
477+
}
478+
uint256 index = clamp(_index, 0, arr.length - 1);
479+
return arr[index];
480+
}
481+
391482
/*
392483
**************************************************************************
393484
* Private Helper Functions
394485
**************************************************************************
395486
*/
396487

397488
function logClamp(uint256 value, uint256 ans) private {
398-
emit Clamped(
399-
string(
489+
emit Clamped(string(
400490
abi.encodePacked("Clamping value ", FuzzLibString.toString(value), " to ", FuzzLibString.toString(ans))
401-
)
402-
);
491+
));
403492
}
404493

405494
function logClamp(int256 value, int256 ans) private {
406-
emit Clamped(
407-
string(
495+
emit Clamped(string(
408496
abi.encodePacked("Clamping value ", FuzzLibString.toString(value), " to ", FuzzLibString.toString(ans))
409-
)
410-
);
497+
));
411498
}
412499
}

test/HelperAssert.t.sol

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,9 @@ contract TestHelperAssert is Test, HelperAssert, ErrAllowTestHelper {
240240

241241
function test_eq_int256_mixed_signs() public {
242242
string memory reason = "mixed signs test";
243-
string memory failReason =
244-
createAssertFailMessage(FuzzLibString.toString(int256(-5)), FuzzLibString.toString(int256(5)), "!=", reason);
243+
string memory failReason = createAssertFailMessage(
244+
FuzzLibString.toString(int256(-5)), FuzzLibString.toString(int256(5)), "!=", reason
245+
);
245246
vm.expectEmit(true, false, false, true);
246247
emit AssertEqFail(failReason);
247248
vm.expectRevert(PlatformTest.TestAssertFail.selector);
@@ -366,8 +367,9 @@ contract TestHelperAssert is Test, HelperAssert, ErrAllowTestHelper {
366367

367368
function test_gte_uint256_less() public {
368369
string memory reason = "gte less test";
369-
string memory failReason =
370-
createAssertFailMessage(FuzzLibString.toString(uint256(3)), FuzzLibString.toString(uint256(7)), "<", reason);
370+
string memory failReason = createAssertFailMessage(
371+
FuzzLibString.toString(uint256(3)), FuzzLibString.toString(uint256(7)), "<", reason
372+
);
371373
vm.expectEmit(true, false, false, true);
372374
emit AssertGteFail(failReason);
373375
vm.expectRevert(PlatformTest.TestAssertFail.selector);
@@ -724,8 +726,9 @@ contract TestHelperAssert is Test, HelperAssert, ErrAllowTestHelper {
724726

725727
function test_lt_int256_greater() public {
726728
string memory reason = "lt int256 greater test";
727-
string memory failReason =
728-
createAssertFailMessage(FuzzLibString.toString(int256(10)), FuzzLibString.toString(int256(5)), ">=", reason);
729+
string memory failReason = createAssertFailMessage(
730+
FuzzLibString.toString(int256(10)), FuzzLibString.toString(int256(5)), ">=", reason
731+
);
729732
vm.expectEmit(true, false, false, true);
730733
emit AssertLtFail(failReason);
731734
vm.expectRevert(PlatformTest.TestAssertFail.selector);

test/HelperClamp.t.sol

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,4 +863,208 @@ contract TestHelperClamp is Test, HelperClamp {
863863
);
864864
this.clampGte(int256(50), int256(type(int128).max) + 1);
865865
}
866+
867+
function test_clampArr_within_bounds() public {
868+
uint256[] memory arr = new uint256[](5);
869+
arr[0] = 10;
870+
arr[1] = 20;
871+
arr[2] = 30;
872+
arr[3] = 40;
873+
arr[4] = 50;
874+
assertEq(arr[0], this.clampArr(arr, 0));
875+
assertEq(arr[2], this.clampArr(arr, 2));
876+
assertEq(arr[4], this.clampArr(arr, 4));
877+
}
878+
879+
function test_clampArr_out_of_bounds() public {
880+
uint256[] memory arr = new uint256[](3);
881+
arr[0] = 100;
882+
arr[1] = 200;
883+
arr[2] = 300;
884+
885+
assertEq(this.clampArr(arr, 5), arr[2]);
886+
assertEq(this.clampArr(arr, 10), arr[1]);
887+
assertEq(this.clampArr(arr, 15), arr[0]);
888+
}
889+
890+
function test_clampArr_empty_array() public {
891+
uint256[] memory arr = new uint256[](0);
892+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
893+
this.clampArr(arr, 0);
894+
}
895+
896+
/**
897+
* Tests for clampArr(int256[], uint256)
898+
*/
899+
function test_clampArr_int256_within_bounds() public {
900+
int256[] memory arr = new int256[](4);
901+
arr[0] = -100;
902+
arr[1] = -50;
903+
arr[2] = 0;
904+
arr[3] = 50;
905+
assertEq(arr[0], this.clampArr(arr, 0));
906+
assertEq(arr[1], this.clampArr(arr, 1));
907+
assertEq(arr[3], this.clampArr(arr, 3));
908+
}
909+
910+
function test_clampArr_int256_out_of_bounds() public {
911+
int256[] memory arr = new int256[](7);
912+
arr[0] = -1000;
913+
arr[1] = -500;
914+
arr[2] = -100;
915+
arr[3] = 0;
916+
arr[4] = 100;
917+
arr[5] = 500;
918+
arr[6] = 1000;
919+
920+
assertEq(this.clampArr(arr, 10), arr[3]);
921+
assertEq(this.clampArr(arr, 20), arr[6]);
922+
assertEq(this.clampArr(arr, 25), arr[4]);
923+
}
924+
925+
function test_clampArr_int256_empty_array() public {
926+
int256[] memory arr = new int256[](0);
927+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
928+
this.clampArr(arr, 0);
929+
}
930+
931+
/**
932+
* Tests for clampArr(address[], uint256)
933+
*/
934+
function test_clampArr_address_within_bounds() public {
935+
address[] memory arr = new address[](6);
936+
arr[0] = address(0x1111);
937+
arr[1] = address(0x2222);
938+
arr[2] = address(0x3333);
939+
arr[3] = address(0x4444);
940+
arr[4] = address(0x5555);
941+
arr[5] = address(0x6666);
942+
assertEq(arr[0], this.clampArr(arr, 0));
943+
assertEq(arr[3], this.clampArr(arr, 3));
944+
assertEq(arr[5], this.clampArr(arr, 5));
945+
}
946+
947+
function test_clampArr_address_out_of_bounds() public {
948+
address[] memory arr = new address[](2);
949+
arr[0] = address(0xAAAA);
950+
arr[1] = address(0xBBBB);
951+
952+
assertEq(this.clampArr(arr, 4), arr[0]);
953+
assertEq(this.clampArr(arr, 7), arr[1]);
954+
assertEq(this.clampArr(arr, 12), arr[0]);
955+
}
956+
957+
function test_clampArr_address_empty_array() public {
958+
address[] memory arr = new address[](0);
959+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
960+
this.clampArr(arr, 0);
961+
}
962+
963+
/**
964+
* Tests for clampArr(bool[], uint256)
965+
*/
966+
function test_clampArr_bool_within_bounds() public {
967+
bool[] memory arr = new bool[](3);
968+
arr[0] = true;
969+
arr[1] = false;
970+
arr[2] = true;
971+
assertEq(arr[0], this.clampArr(arr, 0));
972+
assertEq(arr[1], this.clampArr(arr, 1));
973+
assertEq(arr[2], this.clampArr(arr, 2));
974+
}
975+
976+
function test_clampArr_bool_out_of_bounds() public {
977+
bool[] memory arr = new bool[](5);
978+
arr[0] = false;
979+
arr[1] = true;
980+
arr[2] = false;
981+
arr[3] = true;
982+
arr[4] = false;
983+
984+
assertEq(this.clampArr(arr, 8), arr[3]);
985+
assertEq(this.clampArr(arr, 11), arr[1]);
986+
assertEq(this.clampArr(arr, 17), arr[2]);
987+
}
988+
989+
function test_clampArr_bool_empty_array() public {
990+
bool[] memory arr = new bool[](0);
991+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
992+
this.clampArr(arr, 0);
993+
}
994+
995+
/**
996+
* Tests for clampArr(string[], uint256)
997+
*/
998+
function test_clampArr_string_within_bounds() public {
999+
string[] memory arr = new string[](8);
1000+
arr[0] = "first";
1001+
arr[1] = "second";
1002+
arr[2] = "third";
1003+
arr[3] = "fourth";
1004+
arr[4] = "fifth";
1005+
arr[5] = "sixth";
1006+
arr[6] = "seventh";
1007+
arr[7] = "eighth";
1008+
assertEq(arr[0], this.clampArr(arr, 0));
1009+
assertEq(arr[4], this.clampArr(arr, 4));
1010+
assertEq(arr[7], this.clampArr(arr, 7));
1011+
}
1012+
1013+
function test_clampArr_string_out_of_bounds() public {
1014+
string[] memory arr = new string[](4);
1015+
arr[0] = "alpha";
1016+
arr[1] = "beta";
1017+
arr[2] = "gamma";
1018+
arr[3] = "delta";
1019+
1020+
assertEq(this.clampArr(arr, 6), arr[2]);
1021+
assertEq(this.clampArr(arr, 13), arr[1]);
1022+
assertEq(this.clampArr(arr, 20), arr[0]);
1023+
}
1024+
1025+
function test_clampArr_string_empty_array() public {
1026+
string[] memory arr = new string[](0);
1027+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
1028+
this.clampArr(arr, 0);
1029+
}
1030+
1031+
/**
1032+
* Tests for clampArr(bytes[], uint256)
1033+
*/
1034+
function test_clampArr_bytes_within_bounds() public {
1035+
bytes[] memory arr = new bytes[](10);
1036+
arr[0] = hex"00";
1037+
arr[1] = hex"11";
1038+
arr[2] = hex"22";
1039+
arr[3] = hex"33";
1040+
arr[4] = hex"44";
1041+
arr[5] = hex"55";
1042+
arr[6] = hex"66";
1043+
arr[7] = hex"77";
1044+
arr[8] = hex"88";
1045+
arr[9] = hex"99";
1046+
assertEq(arr[0], this.clampArr(arr, 0));
1047+
assertEq(arr[5], this.clampArr(arr, 5));
1048+
assertEq(arr[9], this.clampArr(arr, 9));
1049+
}
1050+
1051+
function test_clampArr_bytes_out_of_bounds() public {
1052+
bytes[] memory arr = new bytes[](6);
1053+
arr[0] = hex"AAAA";
1054+
arr[1] = hex"BBBB";
1055+
arr[2] = hex"CCCC";
1056+
arr[3] = hex"DDDD";
1057+
arr[4] = hex"EEEE";
1058+
arr[5] = hex"FFFF";
1059+
1060+
assertEq(this.clampArr(arr, 9), arr[3]);
1061+
assertEq(this.clampArr(arr, 19), arr[1]);
1062+
assertEq(this.clampArr(arr, 30), arr[0]);
1063+
}
1064+
1065+
function test_clampArr_bytes_empty_array() public {
1066+
bytes[] memory arr = new bytes[](0);
1067+
vm.expectRevert(abi.encodeWithSelector(HelperClamp.ClampEmptyArray.selector));
1068+
this.clampArr(arr, 0);
1069+
}
8661070
}

test/e2e/echidna/EchidnaEntry.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import "./EchidnaTest.sol";
99
* @author Perimeter <info@perimetersec.io>
1010
*/
1111
contract EchidnaEntry is EchidnaTest {
12-
// This contract serves as the entry point for Echidna
13-
// All functionality is inherited from the parent contracts:
14-
// - EchidnaHandler: handler_* functions
15-
// - EchidnaTest: fuzz_* wrapper functions and _testSelf helper
16-
}
12+
// This contract serves as the entry point for Echidna
13+
// All functionality is inherited from the parent contracts:
14+
// - EchidnaHandler: handler_* functions
15+
// - EchidnaTest: fuzz_* wrapper functions and _testSelf helper
16+
17+
}

test/e2e/echidna/EchidnaTest.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ contract EchidnaTest is EchidnaHandler {
145145
function fuzz_function_call_multiple_returns(uint256 value, string memory testString, bool flag, address actor)
146146
public
147147
{
148-
bytes memory callData =
149-
abi.encodeWithSelector(this.handler_function_call_multiple_returns.selector, value, testString, flag, actor);
148+
bytes memory callData = abi.encodeWithSelector(
149+
this.handler_function_call_multiple_returns.selector, value, testString, flag, actor
150+
);
150151
(bool success, bytes4 errorSelector) = _testSelf(callData);
151152
if (!success) {
152153
fl.t(false, "CALL-03: Unexpected function call multiple returns failure");

0 commit comments

Comments
 (0)