diff --git a/packages/contracts-rfq/contracts/AdminV2.sol b/packages/contracts-rfq/contracts/AdminV2.sol index 0ae47d9fc0..0ec8a6eed0 100644 --- a/packages/contracts-rfq/contracts/AdminV2.sol +++ b/packages/contracts-rfq/contracts/AdminV2.sol @@ -71,6 +71,12 @@ contract AdminV2 is AccessControlEnumerable, IAdminV2, IAdminV2Errors { /// @notice The delay period after which a transaction can be permissionlessly cancelled. uint256 public cancelDelay; + /// @notice The block number at which the contract was deployed. + /// @dev This used to be immutable in V1, but was adjusted to be mutable in V2 for chains like Arbitrum that + /// implement the `block.number` as the underlying L1 block number rather than the chain's native block number. + /// This is exposed for conveniece for off-chain indexers that need to know the deployment block. + uint256 public deployBlock; + /// @notice The timeout period that is used to temporarily disactivate a disputed prover. uint256 public disputePenaltyTime; @@ -86,6 +92,7 @@ contract AdminV2 is AccessControlEnumerable, IAdminV2, IAdminV2Errors { constructor(address defaultAdmin) { _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin); _setCancelDelay(DEFAULT_CANCEL_DELAY); + _setDeployBlock(block.number); _setDisputePenaltyTime(DEFAULT_DISPUTE_PENALTY_TIME); } @@ -122,6 +129,11 @@ contract AdminV2 is AccessControlEnumerable, IAdminV2, IAdminV2Errors { _setCancelDelay(newCancelDelay); } + /// @inheritdoc IAdminV2 + function setDeployBlock(uint256 blockNumber) external onlyRole(DEFAULT_ADMIN_ROLE) { + _setDeployBlock(blockNumber); + } + /// @inheritdoc IAdminV2 function setDisputePenaltyTime(uint256 newDisputePenaltyTime) external onlyRole(GOVERNOR_ROLE) { _setDisputePenaltyTime(newDisputePenaltyTime); @@ -220,6 +232,13 @@ contract AdminV2 is AccessControlEnumerable, IAdminV2, IAdminV2Errors { emit CancelDelayUpdated(oldCancelDelay, newCancelDelay); } + /// @notice Internal logic to set the deploy block. Security checks are performed outside of this function. + /// @dev This function is marked as private to prevent child contracts from calling it directly. + function _setDeployBlock(uint256 blockNumber) private { + deployBlock = blockNumber; + emit DeployBlockSet(blockNumber); + } + /// @notice Internal logic to set the dispute penalty time. Security checks are performed outside of this function. /// @dev This function is marked as private to prevent child contracts from calling it directly. function _setDisputePenaltyTime(uint256 newDisputePenaltyTime) private { diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index a03e4ddb2e..706859c113 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -52,14 +52,10 @@ contract FastBridgeV2 is AdminV2, MulticallTarget, IFastBridgeV2, IFastBridgeV2E /// @notice This variable is deprecated and should not be used. /// @dev Replaced by senderNonces. uint256 public immutable nonce = 0; - /// @notice The block number at which this contract was deployed. - uint256 public immutable deployBlock; /// @notice Initializes the FastBridgeV2 contract with the provided default admin, /// sets the default cancel delay, and records the deploy block number. - constructor(address defaultAdmin) AdminV2(defaultAdmin) { - deployBlock = block.number; - } + constructor(address defaultAdmin) AdminV2(defaultAdmin) {} // ══════════════════════════════════════ EXTERNAL MUTABLE (USER FACING) ═══════════════════════════════════════════ diff --git a/packages/contracts-rfq/contracts/interfaces/IAdminV2.sol b/packages/contracts-rfq/contracts/interfaces/IAdminV2.sol index 44c4009041..728193110a 100644 --- a/packages/contracts-rfq/contracts/interfaces/IAdminV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IAdminV2.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.4; interface IAdminV2 { event CancelDelayUpdated(uint256 oldCancelDelay, uint256 newCancelDelay); + event DeployBlockSet(uint256 blockNumber); event DisputePenaltyTimeUpdated(uint256 oldDisputePenaltyTime, uint256 newDisputePenaltyTime); event FeeRateUpdated(uint256 oldFeeRate, uint256 newFeeRate); event FeesSwept(address token, address recipient, uint256 amount); @@ -21,6 +22,11 @@ interface IAdminV2 { /// deadline during which a transaction can be permissionlessly cancelled if it hasn't been proven by any Relayer. function setCancelDelay(uint256 newCancelDelay) external; + /// @notice Allows the default admin to set the deploy block. + /// @dev This is only relevant for chains like Arbitrum that implement the `block.number` as the underlying L1 + /// block number rather than the chain's native block number. + function setDeployBlock(uint256 blockNumber) external; + /// @notice Allows the governor to set the dispute penalty time. The dispute penalty time is the time period used to /// temporarily deactivate a prover if its proof is disputed: prover will be inactive for the dispute penalty time /// after the dispute is submitted. diff --git a/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol index 814e6d049c..51610b1029 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol @@ -26,6 +26,7 @@ contract FastBridgeV2ManagementTest is FastBridgeV2Test { event ProverRemoved(address prover); event CancelDelayUpdated(uint256 oldCancelDelay, uint256 newCancelDelay); + event DeployBlockSet(uint256 blockNumber); event DisputePenaltyTimeUpdated(uint256 oldDisputePenaltyTime, uint256 newDisputePenaltyTime); event FeeRateUpdated(uint256 oldFeeRate, uint256 newFeeRate); event FeesSwept(address token, address recipient, uint256 amount); @@ -65,6 +66,11 @@ contract FastBridgeV2ManagementTest is FastBridgeV2Test { fastBridge.setCancelDelay(newCancelDelay); } + function setDeployBlock(address caller, uint256 blockNumber) public { + vm.prank(caller); + fastBridge.setDeployBlock(blockNumber); + } + function setDisputePenaltyTime(address caller, uint256 newDisputePenaltyTime) public { vm.prank(caller); fastBridge.setDisputePenaltyTime(newDisputePenaltyTime); @@ -94,6 +100,7 @@ contract FastBridgeV2ManagementTest is FastBridgeV2Test { function test_defaultValues() public view { assertEq(fastBridge.cancelDelay(), DEFAULT_CANCEL_DELAY); + assertEq(fastBridge.deployBlock(), block.number); assertEq(fastBridge.disputePenaltyTime(), DEFAULT_DISPUTE_PENALTY_TIME); assertEq(fastBridge.protocolFeeRate(), 0); } @@ -273,6 +280,21 @@ contract FastBridgeV2ManagementTest is FastBridgeV2Test { setCancelDelay(caller, 4 days); } + // ═════════════════════════════════════════════ SET DEPLOY BLOCK ══════════════════════════════════════════════════ + + function test_setDeployBlock() public { + vm.expectEmit(address(fastBridge)); + emit DeployBlockSet(123_456); + setDeployBlock(admin, 123_456); + assertEq(fastBridge.deployBlock(), 123_456); + } + + function test_setDeployBlock_revertNotAdmin(address caller) public { + vm.assume(caller != admin); + expectUnauthorized(caller, fastBridge.DEFAULT_ADMIN_ROLE()); + setDeployBlock(caller, 123_456); + } + // ═════════════════════════════════════════ SET DISPUTE PENALTY TIME ══════════════════════════════════════════════ function test_setDisputePenaltyTime() public {