diff --git a/script/WormholeL2FlexibleVotingDeploy.s.sol b/script/WormholeL2FlexibleVotingDeploy.s.sol index 0088d855..93b08316 100644 --- a/script/WormholeL2FlexibleVotingDeploy.s.sol +++ b/script/WormholeL2FlexibleVotingDeploy.s.sol @@ -73,7 +73,11 @@ contract WormholeL2FlexibleVotingDeploy is Script, ScriptConstants { // Create L1 metadata bridge that sends proposal metadata to L2 vm.broadcast(); WormholeL1GovernorMetadataBridge l1MetadataBridge = new WormholeL1GovernorMetadataBridge( - governorAddress, L1_CHAIN.wormholeRelayer, L1_CHAIN.wormholeChainId, L2_CHAIN.wormholeChainId + governorAddress, + L1_CHAIN.wormholeRelayer, + L1_CHAIN.wormholeChainId, + L2_CHAIN.wormholeChainId, + vm.envOr("CONTRACT_OWNER", msg.sender) ); vm.createSelectFork(L2_CHAIN.rpcUrl); diff --git a/src/WormholeBase.sol b/src/WormholeBase.sol index 0b1837b3..de7f9610 100644 --- a/src/WormholeBase.sol +++ b/src/WormholeBase.sol @@ -1,14 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; +import {Ownable} from "openzeppelin/access/Ownable.sol"; import {IWormholeRelayer} from "wormhole/interfaces/relayer/IWormholeRelayer.sol"; -contract WormholeBase { +contract WormholeBase is Ownable { /// @notice The wormhole relayer used to trustlessly send messages. IWormholeRelayer public immutable WORMHOLE_RELAYER; /// @param _relayer The address of the Wormhole relayer contract. - constructor(address _relayer) { + /// @param _owner The address of the owner. + constructor(address _relayer, address _owner) { WORMHOLE_RELAYER = IWormholeRelayer(_relayer); + transferOwnership(_owner); } } diff --git a/src/WormholeL1ERC20Bridge.sol b/src/WormholeL1ERC20Bridge.sol index 43eab511..a9f7c6bc 100644 --- a/src/WormholeL1ERC20Bridge.sol +++ b/src/WormholeL1ERC20Bridge.sol @@ -50,9 +50,8 @@ contract WormholeL1ERC20Bridge is WormholeL1VotePool, WormholeSender, WormholeRe address _owner ) WormholeL1VotePool(_governor) - WormholeBase(_relayer) + WormholeBase(_relayer, _owner) WormholeSender(_sourceChain, _targetChain) - WormholeReceiver(_owner) { L1_TOKEN = ERC20Votes(l1TokenAddress); } @@ -84,7 +83,7 @@ contract WormholeL1ERC20Bridge is WormholeL1VotePool, WormholeSender, WormholeRe L2_TOKEN_ADDRESS, mintCalldata, 0, // no receiver value needed since we're just passing a message - GAS_LIMIT, + gasLimit, REFUND_CHAIN, msg.sender ); diff --git a/src/WormholeL1GovernorMetadataBridge.sol b/src/WormholeL1GovernorMetadataBridge.sol index 0f32450e..7f3ded05 100644 --- a/src/WormholeL1GovernorMetadataBridge.sol +++ b/src/WormholeL1GovernorMetadataBridge.sol @@ -37,10 +37,13 @@ contract WormholeL1GovernorMetadataBridge is WormholeSender { /// @param _relayer The address of the L1 Wormhole relayer contract. /// @param _sourceChain The chain id sending the wormhole messages. /// @param _targetChain The chain id receiving the wormhole messages. - constructor(address _governor, address _relayer, uint16 _sourceChain, uint16 _targetChain) - WormholeBase(_relayer) - WormholeSender(_sourceChain, _targetChain) - { + constructor( + address _governor, + address _relayer, + uint16 _sourceChain, + uint16 _targetChain, + address _owner + ) WormholeBase(_relayer, _owner) WormholeSender(_sourceChain, _targetChain) { GOVERNOR = IGovernor(_governor); } @@ -69,7 +72,7 @@ contract WormholeL1GovernorMetadataBridge is WormholeSender { L2_GOVERNOR_ADDRESS, proposalCalldata, 0, // no receiver value needed since we're just passing a message - GAS_LIMIT, + gasLimit, REFUND_CHAIN, msg.sender ); diff --git a/src/WormholeL2ERC20.sol b/src/WormholeL2ERC20.sol index 2ea218b9..a874cd43 100644 --- a/src/WormholeL2ERC20.sol +++ b/src/WormholeL2ERC20.sol @@ -47,11 +47,10 @@ contract WormholeL2ERC20 is ERC20Votes, WormholeReceiver, WormholeSender { uint16 _targetChain, address _owner ) - WormholeBase(_relayer) + WormholeBase(_relayer, _owner) ERC20(_name, _symbol) ERC20Permit(_name) WormholeSender(_sourceChain, _targetChain) - WormholeReceiver(_owner) { L1_BLOCK = IL1Block(_l1Block); } @@ -107,7 +106,7 @@ contract WormholeL2ERC20 is ERC20Votes, WormholeReceiver, WormholeSender { L1_BRIDGE_ADDRESS, withdrawCalldata, 0, // no receiver value needed since we're just passing a message - GAS_LIMIT, + gasLimit, REFUND_CHAIN, msg.sender ); diff --git a/src/WormholeL2GovernorMetadata.sol b/src/WormholeL2GovernorMetadata.sol index 93e1b60b..397d32a2 100644 --- a/src/WormholeL2GovernorMetadata.sol +++ b/src/WormholeL2GovernorMetadata.sol @@ -10,8 +10,8 @@ contract WormholeL2GovernorMetadata is L2GovernorMetadata, WormholeReceiver { /// @param _relayer The address of the WormholeL2GovernorMetadata contract. /// @param _owner The address that will become the contract owner. constructor(address _relayer, address _owner, address _l1BlockAddress, uint32 _castWindow) - WormholeBase(_relayer) - WormholeReceiver(_owner) + WormholeBase(_relayer, _owner) + WormholeReceiver() L2GovernorMetadata(_l1BlockAddress, _castWindow) {} diff --git a/src/WormholeL2VoteAggregator.sol b/src/WormholeL2VoteAggregator.sol index cc489e0f..ffa161e1 100644 --- a/src/WormholeL2VoteAggregator.sol +++ b/src/WormholeL2VoteAggregator.sol @@ -37,7 +37,7 @@ contract WormholeL2VoteAggregator is WormholeSender, L2VoteAggregator, WormholeL L1_BRIDGE_ADDRESS, proposalCalldata, 0, // no receiver value needed since we're just passing a message - GAS_LIMIT, + gasLimit, REFUND_CHAIN, msg.sender ); diff --git a/src/WormholeReceiver.sol b/src/WormholeReceiver.sol index b7de8f6a..4eaf0aea 100644 --- a/src/WormholeReceiver.sol +++ b/src/WormholeReceiver.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {Ownable} from "openzeppelin/access/Ownable.sol"; - import {WormholeBase} from "src/WormholeBase.sol"; -abstract contract WormholeReceiver is Ownable, WormholeBase { +abstract contract WormholeReceiver is WormholeBase { /// @dev Function called with an address that isn't a relayer. error OnlyRelayerAllowed(); @@ -26,10 +24,6 @@ abstract contract WormholeReceiver is Ownable, WormholeBase { address indexed owner, uint16 indexed sourceChain, bytes32 indexed sourceAddress ); - constructor(address owner) Ownable() { - transferOwnership(owner); - } - /// @notice The function the wormhole relayer calls when the DeliveryProvider competes a delivery. /// @dev Implementation should emit `WormholeMessageReceived`. /// @param payload The payload that was sent to in the delivery request. diff --git a/src/WormholeSender.sol b/src/WormholeSender.sol index 802be0e9..0be89b27 100644 --- a/src/WormholeSender.sol +++ b/src/WormholeSender.sol @@ -11,7 +11,13 @@ abstract contract WormholeSender is WormholeBase { uint16 public immutable REFUND_CHAIN; /// @notice The gas limit for cross chain transactions. - uint256 constant GAS_LIMIT = 500_000; + uint256 public gasLimit = 200_000; + + /// @notice Emitted when the gas limit has been updated + /// @param oldValue The old gas limit value. + /// @param newValue The new gas limit value. + /// @param caller The address changing the gas limit. + event GasLimitUpdate(uint256 oldValue, uint256 newValue, address caller); /// @param _refundChain The chain id of the chain sending the messages. /// @param _targetChain The chain id of the chain receiving the messages. @@ -22,6 +28,13 @@ abstract contract WormholeSender is WormholeBase { /// @param targetChain The chain id of the chain receiving the messages. function quoteDeliveryCost(uint16 targetChain) public view virtual returns (uint256 cost) { - (cost,) = WORMHOLE_RELAYER.quoteEVMDeliveryPrice(targetChain, 0, GAS_LIMIT); + (cost,) = WORMHOLE_RELAYER.quoteEVMDeliveryPrice(targetChain, 0, gasLimit); + } + + /// @param _gasLimit The new gas limit value which is used to estimate the delivery cost for + /// sending messages cross chain. + function updateGasLimit(uint256 _gasLimit) public onlyOwner { + emit GasLimitUpdate(gasLimit, _gasLimit, msg.sender); + gasLimit = _gasLimit; } } diff --git a/test/L2CountingFractional.t.sol b/test/L2CountingFractional.t.sol index 8c5e4422..0b5e4972 100644 --- a/test/L2CountingFractional.t.sol +++ b/test/L2CountingFractional.t.sol @@ -233,6 +233,7 @@ contract _CountVoteFractional is L2CountingFractionalTest { uint40 abstain ) public { uint128 totalWeight = 0; + vm.assume(uint128(against) + inFavor + abstain != 0); bytes memory voteData = abi.encodePacked(uint128(against), uint128(inFavor), uint128(abstain)); vm.expectRevert("L2CountingFractional: vote would exceed weight"); diff --git a/test/WormholeBase.t.sol b/test/WormholeBase.t.sol index a646d1fe..b5e90eaf 100644 --- a/test/WormholeBase.t.sol +++ b/test/WormholeBase.t.sol @@ -7,11 +7,13 @@ import {WormholeBase} from "src/WormholeBase.sol"; import {TestConstants} from "test/Constants.sol"; contract Constructor is Test, TestConstants { - function testForkFuzz_CorrectlySetsAllArgs(address _wormholeRelayer) public { - WormholeBase base = new WormholeBase(_wormholeRelayer); + function testForkFuzz_CorrectlySetsAllArgs(address _wormholeRelayer, address _owner) public { + vm.assume(_owner != address(0)); + WormholeBase base = new WormholeBase(_wormholeRelayer, _owner); assertEq( address(base.WORMHOLE_RELAYER()), _wormholeRelayer, "Wormhole relayer is not set correctly" ); + assertEq(address(base.owner()), _owner, "Contract owner is incorrect"); } } diff --git a/test/WormholeL1GovernorMetadataBridge.t.sol b/test/WormholeL1GovernorMetadataBridge.t.sol index 0f0ab721..900a7d8e 100644 --- a/test/WormholeL1GovernorMetadataBridge.t.sol +++ b/test/WormholeL1GovernorMetadataBridge.t.sol @@ -52,7 +52,8 @@ contract L1GovernorMetadataBridgeTest is TestConstants, WormholeRelayerBasicTest address(governorMock), L1_CHAIN.wormholeRelayer, L1_CHAIN.wormholeChainId, - L2_CHAIN.wormholeChainId + L2_CHAIN.wormholeChainId, + msg.sender ); } @@ -71,7 +72,11 @@ contract L1GovernorMetadataBridgeTest is TestConstants, WormholeRelayerBasicTest contract Constructor is Test, TestConstants { function testFork_CorrectlySetAllArgs(address governorMock) public { WormholeL1GovernorMetadataBridge l1GovernorMetadataBridge = new WormholeL1GovernorMetadataBridge( - governorMock, L1_CHAIN.wormholeRelayer, L1_CHAIN.wormholeChainId, L2_CHAIN.wormholeChainId + governorMock, + L1_CHAIN.wormholeRelayer, + L1_CHAIN.wormholeChainId, + L2_CHAIN.wormholeChainId, + msg.sender ); assertEq( address(l1GovernorMetadataBridge.GOVERNOR()), governorMock, "Governor is not set correctly" diff --git a/test/WormholeReceiver.t.sol b/test/WormholeReceiver.t.sol index 774a18ee..7080627e 100644 --- a/test/WormholeReceiver.t.sol +++ b/test/WormholeReceiver.t.sol @@ -8,7 +8,7 @@ import {WormholeReceiver} from "src/WormholeReceiver.sol"; import {TestConstants} from "test/Constants.sol"; contract WormholeReceiverTestHarness is WormholeReceiver { - constructor(address _relayer, address _owner) WormholeBase(_relayer) WormholeReceiver(_owner) {} + constructor(address _relayer, address _owner) WormholeBase(_relayer, _owner) {} function receiveWormholeMessages( bytes calldata payload, bytes[] memory additionalVaas, diff --git a/test/WormholeSender.t.sol b/test/WormholeSender.t.sol index a26cfb38..747379d3 100644 --- a/test/WormholeSender.t.sol +++ b/test/WormholeSender.t.sol @@ -11,7 +11,7 @@ import {TestConstants} from "test/Constants.sol"; contract WormholeSenderHarness is WormholeSender { constructor(address _relayer, uint16 _sourceChain, uint16 _targetChain) - WormholeBase(_relayer) + WormholeBase(_relayer, msg.sender) WormholeSender(_sourceChain, _targetChain) {} @@ -23,6 +23,8 @@ contract WormholeSenderHarness is WormholeSender { contract WormholeSenderTest is TestConstants, WormholeRelayerBasicTest { WormholeSender wormholeSender; + event GasLimitUpdate(uint256 oldValue, uint256 newValue, address caller); + constructor() { setForkChains(TESTNET, L1_CHAIN.wormholeChainId, L2_CHAIN.wormholeChainId); } @@ -61,3 +63,19 @@ contract QuoteDeliveryCost is WormholeSenderTest { assertGt(cost, 0, "No cost was quoted"); } } + +contract UpdateGasLimit is WormholeSenderTest { + function testFuzz_CorrectlyUpdateGasLimit(uint256 gasLimit) public { + vm.expectEmit(); + emit GasLimitUpdate(wormholeSender.gasLimit(), gasLimit, address(this)); + + wormholeSender.updateGasLimit(gasLimit); + assertEq(wormholeSender.gasLimit(), gasLimit, "The gas limit is incorrect"); + } + + function testFuzz_RevertIf_CalledByNonOwner(address owner, uint256 gasLimit) public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(owner); + wormholeSender.updateGasLimit(gasLimit); + } +} diff --git a/test/harness/WormholeL1VotePoolHarness.sol b/test/harness/WormholeL1VotePoolHarness.sol index d02433aa..b1c520ef 100644 --- a/test/harness/WormholeL1VotePoolHarness.sol +++ b/test/harness/WormholeL1VotePoolHarness.sol @@ -10,9 +10,8 @@ import {WormholeReceiver} from "src/WormholeReceiver.sol"; contract WormholeL1VotePoolHarness is WormholeL1VotePool, WormholeReceiver, CommonBase { constructor(address _relayer, address _l1Governor) - WormholeBase(_relayer) + WormholeBase(_relayer, msg.sender) WormholeL1VotePool(_l1Governor) - WormholeReceiver(msg.sender) {} function receiveWormholeMessages(