diff --git a/l1-contracts/deploy-scripts/DeployL2Contracts.sol b/l1-contracts/deploy-scripts/DeployL2Contracts.sol index 4578f1717..9ce7583fd 100644 --- a/l1-contracts/deploy-scripts/DeployL2Contracts.sol +++ b/l1-contracts/deploy-scripts/DeployL2Contracts.sol @@ -33,6 +33,7 @@ contract DeployL2Script is Script { address consensusRegistryProxy; address multicall3; address forceDeployUpgraderAddress; + address timestampAsserter; } struct ContractsBytecodes { @@ -45,6 +46,7 @@ contract DeployL2Script is Script { bytes consensusRegistryProxyBytecode; bytes multicall3Bytecode; bytes forceDeployUpgrader; + bytes timestampAsserterBytecode; } function run() public { @@ -67,6 +69,7 @@ contract DeployL2Script is Script { deployConsensusRegistry(); deployConsensusRegistryProxy(); deployMulticall3(); + deployTimestampAsserter(); saveOutput(); } @@ -157,6 +160,10 @@ contract DeployL2Script is Script { contracts.forceDeployUpgrader = Utils.readFoundryBytecode( "/../l2-contracts/zkout/ForceDeployUpgrader.sol/ForceDeployUpgrader.json" ); + + contracts.timestampAsserterBytecode = Utils.readFoundryBytecode( + "/../l2-contracts/zkout/TimestampAsserter.sol/TimestampAsserter.json" + ); } function initializeConfig() internal { @@ -178,6 +185,7 @@ contract DeployL2Script is Script { vm.serializeAddress("root", "consensus_registry_implementation", config.consensusRegistryImplementation); vm.serializeAddress("root", "consensus_registry_proxy", config.consensusRegistryProxy); vm.serializeAddress("root", "multicall3", config.multicall3); + vm.serializeAddress("root", "timestamp_asserter", config.timestampAsserter); string memory toml = vm.serializeAddress("root", "l2_default_upgrader", config.forceDeployUpgraderAddress); string memory root = vm.projectRoot(); string memory path = string.concat(root, "/script-out/output-deploy-l2-contracts.toml"); @@ -295,6 +303,19 @@ contract DeployL2Script is Script { }); } + function deployTimestampAsserter() internal { + config.timestampAsserter = Utils.deployThroughL1({ + bytecode: contracts.timestampAsserterBytecode, + constructorargs: hex"", + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainId, + bridgehubAddress: config.bridgehubAddress, + l1SharedBridgeProxy: config.l1SharedBridgeProxy + }); + } + // Deploy a transparent upgradable proxy for the already deployed consensus registry // implementation and save its address into the config. function deployConsensusRegistryProxy() internal { diff --git a/l2-contracts/contracts/dev-contracts/ITimestampAsserter.sol b/l2-contracts/contracts/dev-contracts/ITimestampAsserter.sol new file mode 100644 index 000000000..47d9ec103 --- /dev/null +++ b/l2-contracts/contracts/dev-contracts/ITimestampAsserter.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +interface ITimestampAsserter { + function assertTimestampInRange(uint256 start, uint256 end) external view; +} diff --git a/l2-contracts/contracts/dev-contracts/TimestampAsserter.sol b/l2-contracts/contracts/dev-contracts/TimestampAsserter.sol new file mode 100644 index 000000000..445313fe0 --- /dev/null +++ b/l2-contracts/contracts/dev-contracts/TimestampAsserter.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {ITimestampAsserter} from "./ITimestampAsserter.sol"; + +error TimestampOutOfRange(uint256 currentTimestamp, uint256 start, uint256 end); + +/// @title TimestampAsserter +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @dev A contract that verifies if the current block timestamp falls within a specified range. +/// This is useful for custom account abstraction where time-bound checks are needed but accessing block.timestamp +/// directly is not possible. +contract TimestampAsserter is ITimestampAsserter { + function assertTimestampInRange(uint256 _start, uint256 _end) external view { + if (block.timestamp < _start || block.timestamp > _end) { + revert TimestampOutOfRange(block.timestamp, _start, _end); + } + } +}