@@ -1414,4 +1414,269 @@ contract TestHelperAssert is Test, HelperAssert, ErrAllowTestHelper {
14141414 require (! success3, "should fail " );
14151415 assertFalse (_isErrorString (bytes4 (emptyErrorData)), "empty error should not be Error(string) type " );
14161416 }
1417+
1418+ /**
1419+ * @dev Alternative implementation of errAllow using mcopy opcode for testing equivalence.
1420+ */
1421+ function errAllow_mcopy (
1422+ bytes memory errorData ,
1423+ string [] memory allowedRequireErrorMessages ,
1424+ string memory errorContext
1425+ ) public {
1426+ // space for error message without selector (4 bytes)
1427+ bytes memory strippedData = new bytes (errorData.length - 4 );
1428+ assembly {
1429+ // calculate the memory position of strippedData
1430+ let strippedDataPtr := add (strippedData, 32 )
1431+ // calculate the memory position of errorData (4 bytes for selector)
1432+ let errorDataPtr := add (add (errorData, 32 ), 4 )
1433+ // calculate the data length
1434+ let dataLength := sub (mload (errorData), 4 )
1435+ // copy the data
1436+ // strippedDataPtr: memory position of where to copy the data
1437+ // errorDataPtr: memory position of what to copy
1438+ // dataLength: length of the data to copy
1439+ mcopy (strippedDataPtr, errorDataPtr, dataLength)
1440+
1441+ // now "strippedData" is the error message (string) without selector
1442+ }
1443+
1444+ // extract the string from the remaining data
1445+ string memory decodedString = abi.decode (strippedData, (string ));
1446+
1447+ // compare with allowedRequireErrorMessages
1448+ bool allowed = false ;
1449+ for (uint256 i = 0 ; i < allowedRequireErrorMessages.length ; i++ ) {
1450+ if (keccak256 (abi.encode (decodedString)) == keccak256 (abi.encode (allowedRequireErrorMessages[i]))) {
1451+ allowed = true ;
1452+ break ;
1453+ }
1454+ }
1455+
1456+ t (allowed, errorContext);
1457+ }
1458+
1459+ /**
1460+ * Tests for errAllow vs errAllow_mcopy equivalence
1461+ */
1462+ function test_errAllow_equivalence_short_message () public {
1463+ string [] memory allowedErrors = new string [](1 );
1464+ allowedErrors[0 ] = "short " ;
1465+
1466+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), "short " );
1467+
1468+ // Both should pass with allowed message
1469+ errAllow (errorData, allowedErrors, "errAllow test " );
1470+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1471+ }
1472+
1473+ function test_errAllow_equivalence_short_message_fail () public {
1474+ string [] memory allowedErrors = new string [](1 );
1475+ allowedErrors[0 ] = "expected " ;
1476+
1477+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), "different " );
1478+
1479+ // Both should fail with disallowed message
1480+ vm.expectEmit (false , false , false , true );
1481+ emit AssertFail ("errAllow test " );
1482+ vm.expectRevert (PlatformTest.TestAssertFail.selector );
1483+ errAllow (errorData, allowedErrors, "errAllow test " );
1484+
1485+ vm.expectEmit (false , false , false , true );
1486+ emit AssertFail ("errAllow_mcopy test " );
1487+ vm.expectRevert (PlatformTest.TestAssertFail.selector );
1488+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1489+ }
1490+
1491+ function test_errAllow_equivalence_32_byte_message () public {
1492+ // Exactly 32 bytes (one word)
1493+ string memory message = "12345678901234567890123456789012 " ;
1494+ string [] memory allowedErrors = new string [](1 );
1495+ allowedErrors[0 ] = message;
1496+
1497+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1498+
1499+ // Both should pass
1500+ errAllow (errorData, allowedErrors, "errAllow test " );
1501+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1502+ }
1503+
1504+ function test_errAllow_equivalence_31_byte_message () public {
1505+ // Just under 32 bytes
1506+ string memory message = "1234567890123456789012345678901 " ;
1507+ string [] memory allowedErrors = new string [](1 );
1508+ allowedErrors[0 ] = message;
1509+
1510+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1511+
1512+ // Both should pass
1513+ errAllow (errorData, allowedErrors, "errAllow test " );
1514+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1515+ }
1516+
1517+ function test_errAllow_equivalence_33_byte_message () public {
1518+ // Just over 32 bytes
1519+ string memory message = "123456789012345678901234567890123 " ;
1520+ string [] memory allowedErrors = new string [](1 );
1521+ allowedErrors[0 ] = message;
1522+
1523+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1524+
1525+ // Both should pass
1526+ errAllow (errorData, allowedErrors, "errAllow test " );
1527+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1528+ }
1529+
1530+ function test_errAllow_equivalence_64_byte_message () public {
1531+ // Exactly two words
1532+ string memory message = "1234567890123456789012345678901212345678901234567890123456789012 " ;
1533+ string [] memory allowedErrors = new string [](1 );
1534+ allowedErrors[0 ] = message;
1535+
1536+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1537+
1538+ // Both should pass
1539+ errAllow (errorData, allowedErrors, "errAllow test " );
1540+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1541+ }
1542+
1543+ function test_errAllow_equivalence_long_message () public {
1544+ // Very long message (over 100 bytes)
1545+ string memory message =
1546+ "This is a very long error message that exceeds multiple memory words to test proper copying behavior " ;
1547+ string [] memory allowedErrors = new string [](1 );
1548+ allowedErrors[0 ] = message;
1549+
1550+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1551+
1552+ // Both should pass
1553+ errAllow (errorData, allowedErrors, "errAllow test " );
1554+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1555+ }
1556+
1557+ function test_errAllow_equivalence_special_characters () public {
1558+ string memory message = "Error: Value out of range! @#$%^&*() " ;
1559+ string [] memory allowedErrors = new string [](1 );
1560+ allowedErrors[0 ] = message;
1561+
1562+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1563+
1564+ // Both should pass
1565+ errAllow (errorData, allowedErrors, "errAllow test " );
1566+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1567+ }
1568+
1569+ function test_errAllow_equivalence_unicode_characters () public {
1570+ string memory message = unicode "Error: Invalid value \u2713 \u2717 " ;
1571+ string [] memory allowedErrors = new string [](1 );
1572+ allowedErrors[0 ] = message;
1573+
1574+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1575+
1576+ // Both should pass
1577+ errAllow (errorData, allowedErrors, "errAllow test " );
1578+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1579+ }
1580+
1581+ function test_errAllow_equivalence_empty_message () public {
1582+ string memory message = "" ;
1583+ string [] memory allowedErrors = new string [](1 );
1584+ allowedErrors[0 ] = message;
1585+
1586+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1587+
1588+ // Both should pass
1589+ errAllow (errorData, allowedErrors, "errAllow test " );
1590+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1591+ }
1592+
1593+ function test_errAllow_equivalence_single_character () public {
1594+ string memory message = "x " ;
1595+ string [] memory allowedErrors = new string [](1 );
1596+ allowedErrors[0 ] = message;
1597+
1598+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1599+
1600+ // Both should pass
1601+ errAllow (errorData, allowedErrors, "errAllow test " );
1602+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1603+ }
1604+
1605+ function test_errAllow_equivalence_multiple_allowed_messages () public {
1606+ string [] memory allowedErrors = new string [](3 );
1607+ allowedErrors[0 ] = "first error " ;
1608+ allowedErrors[1 ] = "second error " ;
1609+ allowedErrors[2 ] = "third error " ;
1610+
1611+ // Test first message
1612+ bytes memory errorData1 = abi.encodeWithSelector (bytes4 (0x08c379a0 ), "first error " );
1613+ errAllow (errorData1, allowedErrors, "errAllow test 1 " );
1614+ errAllow_mcopy (errorData1, allowedErrors, "errAllow_mcopy test 1 " );
1615+
1616+ // Test middle message
1617+ bytes memory errorData2 = abi.encodeWithSelector (bytes4 (0x08c379a0 ), "second error " );
1618+ errAllow (errorData2, allowedErrors, "errAllow test 2 " );
1619+ errAllow_mcopy (errorData2, allowedErrors, "errAllow_mcopy test 2 " );
1620+
1621+ // Test last message
1622+ bytes memory errorData3 = abi.encodeWithSelector (bytes4 (0x08c379a0 ), "third error " );
1623+ errAllow (errorData3, allowedErrors, "errAllow test 3 " );
1624+ errAllow_mcopy (errorData3, allowedErrors, "errAllow_mcopy test 3 " );
1625+ }
1626+
1627+ function test_errAllow_equivalence_numbers_in_message () public {
1628+ string memory message = "Error code: 12345678901234567890 " ;
1629+ string [] memory allowedErrors = new string [](1 );
1630+ allowedErrors[0 ] = message;
1631+
1632+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), message);
1633+
1634+ // Both should pass
1635+ errAllow (errorData, allowedErrors, "errAllow test " );
1636+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1637+ }
1638+
1639+ function test_errAllow_equivalence_real_dummy_error () public {
1640+ // Use actual dummy contract error
1641+ string [] memory allowedErrors = new string [](1 );
1642+ allowedErrors[0 ] = "require failure message 1 " ;
1643+
1644+ (bool success , bytes memory errorData ) =
1645+ address (dummy).call (abi.encodeWithSignature ("requireFailWithMessage() " ));
1646+ require (! success, "should fail " );
1647+
1648+ // Both should pass with real error data
1649+ errAllow (errorData, allowedErrors, "errAllow test " );
1650+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1651+ }
1652+
1653+ function testFuzz_errAllow_equivalence (string [] memory allowedErrors , string memory actualMsg ) public {
1654+ bytes memory errorData = abi.encodeWithSelector (bytes4 (0x08c379a0 ), actualMsg);
1655+
1656+ // Check if actualMsg matches any in allowedErrors
1657+ bool shouldMatch = false ;
1658+ for (uint256 i = 0 ; i < allowedErrors.length ; i++ ) {
1659+ if (keccak256 (abi.encode (allowedErrors[i])) == keccak256 (abi.encode (actualMsg))) {
1660+ shouldMatch = true ;
1661+ break ;
1662+ }
1663+ }
1664+
1665+ if (shouldMatch) {
1666+ // Both should pass when actualMsg is in allowedErrors
1667+ errAllow (errorData, allowedErrors, "errAllow test " );
1668+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1669+ } else {
1670+ // Both should fail when actualMsg is not in allowedErrors
1671+ vm.expectEmit (false , false , false , true );
1672+ emit AssertFail ("errAllow test " );
1673+ vm.expectRevert (PlatformTest.TestAssertFail.selector );
1674+ errAllow (errorData, allowedErrors, "errAllow test " );
1675+
1676+ vm.expectEmit (false , false , false , true );
1677+ emit AssertFail ("errAllow_mcopy test " );
1678+ vm.expectRevert (PlatformTest.TestAssertFail.selector );
1679+ errAllow_mcopy (errorData, allowedErrors, "errAllow_mcopy test " );
1680+ }
1681+ }
14171682}
0 commit comments