From bd7bab2328d51a92292c851a1d6a47a6b6d19de1 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Fri, 10 Jul 2026 16:57:45 +0000 Subject: [PATCH 1/4] feat(L2): add BaseTime predeploy contract --- interfaces/L2/IBaseTime.sol | 24 +++++++ snapshots/abi/BaseTime.json | 64 +++++++++++++++++++ snapshots/semver-lock.json | 6 +- snapshots/storageLayout/BaseTime.json | 9 +++ src/L2/BaseTime.sol | 37 +++++++++++ src/libraries/Predeploys.sol | 4 ++ test/L2/BaseTime.t.sol | 90 +++++++++++++++++++++++++++ 7 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 interfaces/L2/IBaseTime.sol create mode 100644 snapshots/abi/BaseTime.json create mode 100644 snapshots/storageLayout/BaseTime.json create mode 100644 src/L2/BaseTime.sol create mode 100644 test/L2/BaseTime.t.sol diff --git a/interfaces/L2/IBaseTime.sol b/interfaces/L2/IBaseTime.sol new file mode 100644 index 000000000..6a8d18a74 --- /dev/null +++ b/interfaces/L2/IBaseTime.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Interfaces +import { ISemver } from "interfaces/universal/ISemver.sol"; + +/// @title IBaseTime +/// @notice Interface for the BaseTime predeploy. +interface IBaseTime is ISemver { + /// @notice Thrown when a caller other than the protocol depositor attempts to update BaseTime. + error BaseTime_NotDepositor(); + + /// @notice Thrown when the millisecond component is not aligned to a 200 millisecond interval. + error BaseTime_InvalidTimestampMillisPart(); + + /// @notice Returns the millisecond component of the current L2 block timestamp. + function timestampMillisPart() external view returns (uint16); + + /// @notice Returns the current L2 block timestamp in milliseconds. + function timestampMs() external view returns (uint64 timestampMs_); + + /// @notice Updates the millisecond component of the current L2 block timestamp. + function setTimestampMillisPart(uint16 _timestampMillisPart) external; +} diff --git a/snapshots/abi/BaseTime.json b/snapshots/abi/BaseTime.json new file mode 100644 index 000000000..e37d75c6f --- /dev/null +++ b/snapshots/abi/BaseTime.json @@ -0,0 +1,64 @@ +[ + { + "inputs": [ + { + "internalType": "uint16", + "name": "_timestampMillisPart", + "type": "uint16" + } + ], + "name": "setTimestampMillisPart", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "timestampMillisPart", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "timestampMs", + "outputs": [ + { + "internalType": "uint64", + "name": "timestampMs_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BaseTime_InvalidTimestampMillisPart", + "type": "error" + }, + { + "inputs": [], + "name": "BaseTime_NotDepositor", + "type": "error" + } +] \ No newline at end of file diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 8f8e8fda5..25845f808 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -63,6 +63,10 @@ "initCodeHash": "0x812f8ec3945f0b2e2a9615cd7c5cdd60dbe8927f41e606cb2bd488c5e6dec02a", "sourceCodeHash": "0xcb329746df0baddd3dc03c6c88da5d6bdc0f0a96d30e6dc78d0891bb1e935032" }, + "src/L2/BaseTime.sol:BaseTime": { + "initCodeHash": "0x4c46d5d34b55f2df2c9a8478444d3fb699ea9c2f73459a568b6d501d13021a2e", + "sourceCodeHash": "0xb48649c7e7ffaab6ab0b228e672ccf83159e8a71211e9d0ac19cacc8d68810b2" + }, "src/L2/FeeDisburser.sol:FeeDisburser": { "initCodeHash": "0x1278027e3756e2989e80c0a7b513e221a5fe0d3dbd9ded108375a29b2c1f3d57", "sourceCodeHash": "0xac49a0ecf22b8a7bb3ebef830a2d27b19050f9b08941186e8563d5113cf0ce9c" @@ -135,4 +139,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} \ No newline at end of file +} diff --git a/snapshots/storageLayout/BaseTime.json b/snapshots/storageLayout/BaseTime.json new file mode 100644 index 000000000..7e863ba7b --- /dev/null +++ b/snapshots/storageLayout/BaseTime.json @@ -0,0 +1,9 @@ +[ + { + "bytes": "2", + "label": "timestampMillisPart", + "offset": 0, + "slot": "0", + "type": "uint16" + } +] \ No newline at end of file diff --git a/src/L2/BaseTime.sol b/src/L2/BaseTime.sol new file mode 100644 index 000000000..7e46760ae --- /dev/null +++ b/src/L2/BaseTime.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Libraries +import { Constants } from "src/libraries/Constants.sol"; + +// Interfaces +import { IBaseTime } from "interfaces/L2/IBaseTime.sol"; + +/// @custom:proxied true +/// @custom:predeploy 0x4200000000000000000000000000000000000030 +/// @title BaseTime +/// @notice Exposes the millisecond component of the current L2 block timestamp. +contract BaseTime is IBaseTime { + /// @notice Semantic version. + /// @custom:semver 1.0.0 + string public constant version = "1.0.0"; + + /// @inheritdoc IBaseTime + uint16 public timestampMillisPart; + + /// @inheritdoc IBaseTime + function timestampMs() external view returns (uint64 timestampMs_) { + timestampMs_ = uint64(block.timestamp * 1000 + timestampMillisPart); + } + + /// @inheritdoc IBaseTime + function setTimestampMillisPart(uint16 _timestampMillisPart) external { + if (msg.sender != Constants.DEPOSITOR_ACCOUNT) revert BaseTime_NotDepositor(); + if ( + _timestampMillisPart != 0 && _timestampMillisPart != 200 && _timestampMillisPart != 400 + && _timestampMillisPart != 600 && _timestampMillisPart != 800 + ) revert BaseTime_InvalidTimestampMillisPart(); + + timestampMillisPart = _timestampMillisPart; + } +} diff --git a/src/libraries/Predeploys.sol b/src/libraries/Predeploys.sol index 79a55612a..3558baa3f 100644 --- a/src/libraries/Predeploys.sol +++ b/src/libraries/Predeploys.sol @@ -57,6 +57,9 @@ library Predeploys { /// @notice Address of the OperatorFeeVault predeploy. address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b; + /// @notice Address of the BaseTime predeploy. + address internal constant BASE_TIME = 0x4200000000000000000000000000000000000030; + /// @notice Address of the SchemaRegistry predeploy. address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020; @@ -87,6 +90,7 @@ library Predeploys { if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == OPERATOR_FEE_VAULT) return "OperatorFeeVault"; + if (_addr == BASE_TIME) return "BaseTime"; if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry"; if (_addr == EAS) return "EAS"; if (_addr == LEGACY_ERC20_ETH) return "LegacyERC20ETH"; diff --git a/test/L2/BaseTime.t.sol b/test/L2/BaseTime.t.sol new file mode 100644 index 000000000..9ee09eed1 --- /dev/null +++ b/test/L2/BaseTime.t.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing +import { Test } from "forge-std/Test.sol"; + +// Libraries +import { Constants } from "src/libraries/Constants.sol"; + +// Target contract +import { BaseTime } from "src/L2/BaseTime.sol"; +import { IBaseTime } from "interfaces/L2/IBaseTime.sol"; + +/// @title BaseTime_TestInit +/// @notice Reusable test initialization for BaseTime tests. +abstract contract BaseTime_TestInit is Test { + BaseTime internal baseTime; + + /// @notice Sets up the test suite. + function setUp() public { + baseTime = new BaseTime(); + } + + /// @notice Sets the millisecond component as the protocol depositor. + function setTimestampMillisPart(uint16 _timestampMillisPart) internal { + vm.prank(Constants.DEPOSITOR_ACCOUNT); + baseTime.setTimestampMillisPart(_timestampMillisPart); + } +} + +/// @title BaseTime_InitialValue_Test +/// @notice Tests BaseTime's initial value. +contract BaseTime_InitialValue_Test is BaseTime_TestInit { + /// @notice Tests that the millisecond component initially equals zero. + function test_initialValue_succeeds() external view { + assertEq(baseTime.timestampMillisPart(), 0); + } +} + +/// @title BaseTime_SetTimestampMillisPart_Test +/// @notice Tests updating BaseTime's millisecond component. +contract BaseTime_SetTimestampMillisPart_Test is BaseTime_TestInit { + /// @notice Tests every valid millisecond component. + function test_setTimestampMillisPart_succeeds() external { + uint16[5] memory validValues = [uint16(0), 200, 400, 600, 800]; + + for (uint256 i; i < validValues.length; i++) { + setTimestampMillisPart(validValues[i]); + assertEq(baseTime.timestampMillisPart(), validValues[i]); + } + } + + /// @notice Tests that an invalid millisecond component is rejected. + function testFuzz_setTimestampMillisPart_invalidValue_reverts(uint16 _timestampMillisPart) external { + vm.assume( + _timestampMillisPart != 0 && _timestampMillisPart != 200 && _timestampMillisPart != 400 + && _timestampMillisPart != 600 && _timestampMillisPart != 800 + ); + vm.expectRevert(IBaseTime.BaseTime_InvalidTimestampMillisPart.selector); + vm.prank(Constants.DEPOSITOR_ACCOUNT); + baseTime.setTimestampMillisPart(_timestampMillisPart); + } + + /// @notice Tests that callers other than the protocol depositor are rejected. + function testFuzz_setTimestampMillisPart_notDepositor_reverts(address _caller) external { + vm.assume(_caller != Constants.DEPOSITOR_ACCOUNT); + vm.expectRevert(IBaseTime.BaseTime_NotDepositor.selector); + vm.prank(_caller); + baseTime.setTimestampMillisPart(200); + } + + /// @notice Tests that the millisecond component occupies slot zero as a uint16. + function test_setTimestampMillisPart_usesSlotZero_succeeds() external { + setTimestampMillisPart(600); + + assertEq(vm.load(address(baseTime), bytes32(0)), bytes32(uint256(600))); + } +} + +/// @title BaseTime_TimestampMs_Test +/// @notice Tests BaseTime's full millisecond timestamp getter. +contract BaseTime_TimestampMs_Test is BaseTime_TestInit { + /// @notice Tests that timestampMs combines block.timestamp with the millisecond component. + function test_timestampMs_succeeds() external { + vm.warp(1725); + setTimestampMillisPart(600); + + assertEq(baseTime.timestampMs(), 1_725_600); + } +} From 68d9abe13463050a8078eec7abea5290e49046be Mon Sep 17 00:00:00 2001 From: Francis Li Date: Fri, 10 Jul 2026 18:02:56 +0000 Subject: [PATCH 2/4] feat(L2): install BaseTime in fresh genesis --- scripts/L2Genesis.s.sol | 6 ++++++ snapshots/semver-lock.json | 4 ++-- src/libraries/Predeploys.sol | 10 +++++----- test/scripts/L2Genesis.t.sol | 8 ++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/scripts/L2Genesis.s.sol b/scripts/L2Genesis.s.sol index bf1412b4e..46bb72bf7 100644 --- a/scripts/L2Genesis.s.sol +++ b/scripts/L2Genesis.s.sol @@ -183,6 +183,7 @@ contract L2Genesis is Script { // 1C,1D,1E,1F: not used. setSchemaRegistry(); // 20 setEAS(); // 21 + setBaseTime(); // 30 } function setProxyAdmin(Input memory _input) internal { @@ -313,6 +314,11 @@ contract L2Genesis is Script { }); } + /// @notice This predeploy is following the safety invariant #1. + function setBaseTime() internal { + _setImplementationCode(Predeploys.BASE_TIME); + } + /// @notice This predeploy is following the safety invariant #1. function setSchemaRegistry() internal { _setImplementationCode(Predeploys.SCHEMA_REGISTRY); diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 25845f808..2686e7961 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -65,7 +65,7 @@ }, "src/L2/BaseTime.sol:BaseTime": { "initCodeHash": "0x4c46d5d34b55f2df2c9a8478444d3fb699ea9c2f73459a568b6d501d13021a2e", - "sourceCodeHash": "0xb48649c7e7ffaab6ab0b228e672ccf83159e8a71211e9d0ac19cacc8d68810b2" + "sourceCodeHash": "0x6b17d0e39c059297dbc3643685a5145d348a6c8d4947d1b14f0858921eb40b6b" }, "src/L2/FeeDisburser.sol:FeeDisburser": { "initCodeHash": "0x1278027e3756e2989e80c0a7b513e221a5fe0d3dbd9ded108375a29b2c1f3d57", @@ -139,4 +139,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file diff --git a/src/libraries/Predeploys.sol b/src/libraries/Predeploys.sol index 3558baa3f..694b083e7 100644 --- a/src/libraries/Predeploys.sol +++ b/src/libraries/Predeploys.sol @@ -57,15 +57,15 @@ library Predeploys { /// @notice Address of the OperatorFeeVault predeploy. address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b; - /// @notice Address of the BaseTime predeploy. - address internal constant BASE_TIME = 0x4200000000000000000000000000000000000030; - /// @notice Address of the SchemaRegistry predeploy. address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020; /// @notice Address of the EAS predeploy. address internal constant EAS = 0x4200000000000000000000000000000000000021; + /// @notice Address of the BaseTime predeploy. + address internal constant BASE_TIME = 0x4200000000000000000000000000000000000030; + /// @custom:legacy /// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the /// state trie as of the Bedrock upgrade. Contract has been locked and write functions @@ -90,10 +90,10 @@ library Predeploys { if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == OPERATOR_FEE_VAULT) return "OperatorFeeVault"; - if (_addr == BASE_TIME) return "BaseTime"; if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry"; if (_addr == EAS) return "EAS"; if (_addr == LEGACY_ERC20_ETH) return "LegacyERC20ETH"; + if (_addr == BASE_TIME) return "BaseTime"; revert("Predeploys: unnamed predeploy"); } @@ -108,7 +108,7 @@ library Predeploys { || _addr == L2_STANDARD_BRIDGE || _addr == SEQUENCER_FEE_WALLET || _addr == OPTIMISM_MINTABLE_ERC20_FACTORY || _addr == L2_ERC721_BRIDGE || _addr == L1_BLOCK_ATTRIBUTES || _addr == L2_TO_L1_MESSAGE_PASSER || _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT - || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS; + || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS || _addr == BASE_TIME; } function isPredeployNamespace(address _addr) internal pure returns (bool) { diff --git a/test/scripts/L2Genesis.t.sol b/test/scripts/L2Genesis.t.sol index c0bd80933..13f232731 100644 --- a/test/scripts/L2Genesis.t.sol +++ b/test/scripts/L2Genesis.t.sol @@ -6,6 +6,7 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { L2Genesis } from "scripts/L2Genesis.s.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { LATEST_FORK } from "scripts/libraries/Config.sol"; +import { IBaseTime } from "interfaces/L2/IBaseTime.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; import { IOptimismMintableERC721Factory } from "interfaces/L2/IOptimismMintableERC721Factory.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; @@ -59,6 +60,12 @@ abstract contract L2Genesis_TestInit is Test { assertGt(Predeploys.WETH.code.length, 0); } + function _assertBaseTime() internal view { + assertEq(Predeploys.BASE_TIME, 0x4200000000000000000000000000000000000030); + assertTrue(Predeploys.isSupportedPredeploy(Predeploys.BASE_TIME)); + assertEq(IBaseTime(Predeploys.BASE_TIME).timestampMillisPart(), 0); + } + function _assertFeeVaultsWithoutRevenueShare() internal view { _assertFeeVault( Predeploys.BASE_FEE_VAULT, @@ -160,6 +167,7 @@ contract L2Genesis_Run_Test is L2Genesis_TestInit { _assertProxyAdmin(); _assertPredeploys(); + _assertBaseTime(); _assertFeeVaultsWithoutRevenueShare(); _assertFactories(); _assertForks(); From 68be091a9e7d95cedf27e33166f61e9be60b92ed Mon Sep 17 00:00:00 2001 From: Francis Li Date: Fri, 10 Jul 2026 18:21:14 +0000 Subject: [PATCH 3/4] style: format predeploy registry --- src/libraries/Predeploys.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Predeploys.sol b/src/libraries/Predeploys.sol index 694b083e7..86425f3b2 100644 --- a/src/libraries/Predeploys.sol +++ b/src/libraries/Predeploys.sol @@ -108,7 +108,8 @@ library Predeploys { || _addr == L2_STANDARD_BRIDGE || _addr == SEQUENCER_FEE_WALLET || _addr == OPTIMISM_MINTABLE_ERC20_FACTORY || _addr == L2_ERC721_BRIDGE || _addr == L1_BLOCK_ATTRIBUTES || _addr == L2_TO_L1_MESSAGE_PASSER || _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT - || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS || _addr == BASE_TIME; + || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS + || _addr == BASE_TIME; } function isPredeployNamespace(address _addr) internal pure returns (bool) { From 4e408f10d9ea30d4258cb5510cab95bf5001c7f6 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Fri, 10 Jul 2026 21:45:15 +0000 Subject: [PATCH 4/4] fix(L2): address BaseTime review feedback --- scripts/Artifacts.s.sol | 1 + snapshots/semver-lock.json | 4 ++-- src/L2/BaseTime.sol | 7 +++---- test/L2/BaseTime.t.sol | 9 +++------ 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/scripts/Artifacts.s.sol b/scripts/Artifacts.s.sol index a84afb3b5..04f6aa189 100644 --- a/scripts/Artifacts.s.sol +++ b/scripts/Artifacts.s.sol @@ -48,6 +48,7 @@ contract Artifacts { _predeploys[keccak256("OperatorFeeVault")] = payable(Predeploys.OPERATOR_FEE_VAULT); _predeploys[keccak256("SchemaRegistry")] = payable(Predeploys.SCHEMA_REGISTRY); _predeploys[keccak256("EAS")] = payable(Predeploys.EAS); + _predeploys[keccak256("BaseTime")] = payable(Predeploys.BASE_TIME); } /// @notice Returns the address of a deployment. Also handles the predeploys. diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 2686e7961..ed3e6bd84 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -64,8 +64,8 @@ "sourceCodeHash": "0xcb329746df0baddd3dc03c6c88da5d6bdc0f0a96d30e6dc78d0891bb1e935032" }, "src/L2/BaseTime.sol:BaseTime": { - "initCodeHash": "0x4c46d5d34b55f2df2c9a8478444d3fb699ea9c2f73459a568b6d501d13021a2e", - "sourceCodeHash": "0x6b17d0e39c059297dbc3643685a5145d348a6c8d4947d1b14f0858921eb40b6b" + "initCodeHash": "0xfcd48af0cfd907d0ce91f61ea437c81c9a98269ca2071d7d27291f20c483ccc0", + "sourceCodeHash": "0x98e246b6ff058ecb8aaeacaa33ce70674f4d3d4a8dcabe91df6f772c6d237667" }, "src/L2/FeeDisburser.sol:FeeDisburser": { "initCodeHash": "0x1278027e3756e2989e80c0a7b513e221a5fe0d3dbd9ded108375a29b2c1f3d57", diff --git a/src/L2/BaseTime.sol b/src/L2/BaseTime.sol index 7e46760ae..74f2eefa3 100644 --- a/src/L2/BaseTime.sol +++ b/src/L2/BaseTime.sol @@ -27,10 +27,9 @@ contract BaseTime is IBaseTime { /// @inheritdoc IBaseTime function setTimestampMillisPart(uint16 _timestampMillisPart) external { if (msg.sender != Constants.DEPOSITOR_ACCOUNT) revert BaseTime_NotDepositor(); - if ( - _timestampMillisPart != 0 && _timestampMillisPart != 200 && _timestampMillisPart != 400 - && _timestampMillisPart != 600 && _timestampMillisPart != 800 - ) revert BaseTime_InvalidTimestampMillisPart(); + if (_timestampMillisPart > 800 || _timestampMillisPart % 200 != 0) { + revert BaseTime_InvalidTimestampMillisPart(); + } timestampMillisPart = _timestampMillisPart; } diff --git a/test/L2/BaseTime.t.sol b/test/L2/BaseTime.t.sol index 9ee09eed1..b5106232b 100644 --- a/test/L2/BaseTime.t.sol +++ b/test/L2/BaseTime.t.sol @@ -28,9 +28,9 @@ abstract contract BaseTime_TestInit is Test { } } -/// @title BaseTime_InitialValue_Test +/// @title BaseTime_TimestampMillisPart_Test /// @notice Tests BaseTime's initial value. -contract BaseTime_InitialValue_Test is BaseTime_TestInit { +contract BaseTime_TimestampMillisPart_Test is BaseTime_TestInit { /// @notice Tests that the millisecond component initially equals zero. function test_initialValue_succeeds() external view { assertEq(baseTime.timestampMillisPart(), 0); @@ -52,10 +52,7 @@ contract BaseTime_SetTimestampMillisPart_Test is BaseTime_TestInit { /// @notice Tests that an invalid millisecond component is rejected. function testFuzz_setTimestampMillisPart_invalidValue_reverts(uint16 _timestampMillisPart) external { - vm.assume( - _timestampMillisPart != 0 && _timestampMillisPart != 200 && _timestampMillisPart != 400 - && _timestampMillisPart != 600 && _timestampMillisPart != 800 - ); + vm.assume(_timestampMillisPart > 800 || _timestampMillisPart % 200 != 0); vm.expectRevert(IBaseTime.BaseTime_InvalidTimestampMillisPart.selector); vm.prank(Constants.DEPOSITOR_ACCOUNT); baseTime.setTimestampMillisPart(_timestampMillisPart);