From 78704d36d88a0f73b3a88f8fd748431ccce76b3d Mon Sep 17 00:00:00 2001 From: Andrej Date: Tue, 19 Nov 2024 16:15:38 +0100 Subject: [PATCH 1/3] CCIP-4288 Refactor MockCCIPRouter to support EVMExtraArgsV2 (#15301) * feat: refactor MockCCIPRouter to support EVMExtraArgsV2 * chore: commit changeset * CCIP-4288 add changeset --- contracts/.changeset/wet-eyes-accept.md | 7 ++++++ contracts/gas-snapshots/ccip.gas-snapshot | 11 ++++++---- .../src/v0.8/ccip/test/mocks/MockRouter.sol | 17 ++++++++++---- .../ccip/test/mocks/test/MockRouterTest.t.sol | 22 ++++++++++++++++++- 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 contracts/.changeset/wet-eyes-accept.md diff --git a/contracts/.changeset/wet-eyes-accept.md b/contracts/.changeset/wet-eyes-accept.md new file mode 100644 index 0000000000..ea78336622 --- /dev/null +++ b/contracts/.changeset/wet-eyes-accept.md @@ -0,0 +1,7 @@ +--- +'@chainlink/contracts': patch +--- + +Refactor MockCCIPRouter to support EVMExtraArgsV2 + +PR issue : CCIP-4288 diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 1d3bb7b294..b3d324b0e5 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -482,11 +482,14 @@ MerkleMultiProofTest:test_EmptyLeaf_Revert() (gas: 3585) MerkleMultiProofTest:test_MerkleRoot256() (gas: 394891) MerkleMultiProofTest:test_MerkleRootSingleLeaf_Success() (gas: 3661) MerkleMultiProofTest:test_SpecSync_gas() (gas: 34129) +MockRouterTest:test_ccipSendWithEVMExtraArgsV1_Success() (gas: 110073) +MockRouterTest:test_ccipSendWithEVMExtraArgsV2_Success() (gas: 132614) MockRouterTest:test_ccipSendWithInsufficientNativeTokens_Revert() (gas: 34037) -MockRouterTest:test_ccipSendWithInvalidMsgValue_Revert() (gas: 60842) -MockRouterTest:test_ccipSendWithLinkFeeTokenAndValidMsgValue_Success() (gas: 126576) -MockRouterTest:test_ccipSendWithLinkFeeTokenbutInsufficientAllowance_Revert() (gas: 63455) -MockRouterTest:test_ccipSendWithSufficientNativeFeeTokens_Success() (gas: 44012) +MockRouterTest:test_ccipSendWithInvalidEVMExtraArgs_Revert() (gas: 106684) +MockRouterTest:test_ccipSendWithInvalidMsgValue_Revert() (gas: 60886) +MockRouterTest:test_ccipSendWithLinkFeeTokenAndValidMsgValue_Success() (gas: 126640) +MockRouterTest:test_ccipSendWithLinkFeeTokenButInsufficientAllowance_Revert() (gas: 63478) +MockRouterTest:test_ccipSendWithSufficientNativeFeeTokens_Success() (gas: 44048) MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigsBothLanes_Success() (gas: 133528) MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigs_Success() (gas: 315630) MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_OnlyCallableByOwner_Revert() (gas: 17864) diff --git a/contracts/src/v0.8/ccip/test/mocks/MockRouter.sol b/contracts/src/v0.8/ccip/test/mocks/MockRouter.sol index 88a1740886..5e4c39dc19 100644 --- a/contracts/src/v0.8/ccip/test/mocks/MockRouter.sol +++ b/contracts/src/v0.8/ccip/test/mocks/MockRouter.sol @@ -119,12 +119,21 @@ contract MockCCIPRouter is IRouter, IRouterClient { return mockMsgId; } - function _fromBytes(bytes calldata extraArgs) internal pure returns (Client.EVMExtraArgsV1 memory) { + function _fromBytes( + bytes calldata extraArgs + ) internal pure returns (Client.EVMExtraArgsV2 memory) { if (extraArgs.length == 0) { - return Client.EVMExtraArgsV1({gasLimit: DEFAULT_GAS_LIMIT}); + return Client.EVMExtraArgsV2({gasLimit: DEFAULT_GAS_LIMIT, allowOutOfOrderExecution: false}); } - if (bytes4(extraArgs) != Client.EVM_EXTRA_ARGS_V1_TAG) revert InvalidExtraArgsTag(); - return abi.decode(extraArgs[4:], (Client.EVMExtraArgsV1)); + + bytes4 extraArgsTag = bytes4(extraArgs); + if (extraArgsTag == Client.EVM_EXTRA_ARGS_V2_TAG) { + return abi.decode(extraArgs[4:], (Client.EVMExtraArgsV2)); + } else if (extraArgsTag == Client.EVM_EXTRA_ARGS_V1_TAG) { + return Client.EVMExtraArgsV2({gasLimit: abi.decode(extraArgs[4:], (uint256)), allowOutOfOrderExecution: false}); + } + + revert InvalidExtraArgsTag(); } /// @notice Always returns true to make sure this check can be performed on any chain. diff --git a/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol b/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol index 6cbe7bf58f..9acfd9b1ce 100644 --- a/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol +++ b/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol @@ -49,7 +49,7 @@ contract MockRouterTest is TokenSetup { mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); } - function test_ccipSendWithLinkFeeTokenbutInsufficientAllowance_Revert() public { + function test_ccipSendWithLinkFeeTokenButInsufficientAllowance_Revert() public { message.feeToken = s_sourceFeeToken; vm.expectRevert(bytes("ERC20: insufficient allowance")); @@ -65,4 +65,24 @@ contract MockRouterTest is TokenSetup { mockRouter.ccipSend(mockChainSelector, message); } + + function test_ccipSendWithEVMExtraArgsV1_Success() public { + Client.EVMExtraArgsV1 memory extraArgs = Client.EVMExtraArgsV1({gasLimit: 500_000}); + message.extraArgs = Client._argsToBytes(extraArgs); + mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); + } + + function test_ccipSendWithEVMExtraArgsV2_Success() public { + Client.EVMExtraArgsV2 memory extraArgs = Client.EVMExtraArgsV2({gasLimit: 500_000, allowOutOfOrderExecution: true}); + message.extraArgs = Client._argsToBytes(extraArgs); + mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); + } + + function test_ccipSendWithInvalidEVMExtraArgs_Revert() public { + uint256 gasLimit = 500_000; + bytes4 invalidExtraArgsTag = bytes4(keccak256("CCIP EVMExtraArgsInvalid")); + message.extraArgs = abi.encodeWithSelector(invalidExtraArgsTag, gasLimit); + vm.expectRevert(MockCCIPRouter.InvalidExtraArgsTag.selector); + mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); + } } From 4b0c72e70bc63f2dec42650a6ce71a5e3eef747d Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 5 Nov 2024 15:25:52 +0100 Subject: [PATCH 2/3] CCIP-4105: adds OZ AccessControl support to the registry module (#15067) * adds OZ AccessControl support to the registry module * [Bot] Update changeset file with jira issues * fix snap * update version --------- Co-Authored-By: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> --- contracts/.changeset/metal-ducks-hunt.md | 10 ++++ contracts/gas-snapshots/ccip.gas-snapshot | 12 +++-- .../RegistryModuleOwnerCustom.t.sol | 52 +++++++++++++++++++ .../RegistryModuleOwnerCustom.sol | 19 ++++++- .../registry_module_owner_custom.go | 18 ++++++- 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 contracts/.changeset/metal-ducks-hunt.md diff --git a/contracts/.changeset/metal-ducks-hunt.md b/contracts/.changeset/metal-ducks-hunt.md new file mode 100644 index 0000000000..caba481925 --- /dev/null +++ b/contracts/.changeset/metal-ducks-hunt.md @@ -0,0 +1,10 @@ +--- +'@chainlink/contracts': patch +--- + +#feature adds OZ AccessControl support to the registry module + + +PR issue: CCIP-4105 + +Solidity Review issue: CCIP-3966 \ No newline at end of file diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index b3d324b0e5..47daada4da 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -920,11 +920,13 @@ RateLimiter_consume:test_TokenRateLimitReached_Revert() (gas: 24886) RateLimiter_currentTokenBucketState:test_CurrentTokenBucketState_Success() (gas: 38944) RateLimiter_currentTokenBucketState:test_Refill_Success() (gas: 46849) RateLimiter_setTokenBucketConfig:test_SetRateLimiterConfig_Success() (gas: 38506) -RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36033) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 19739) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 130086) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 19559) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 129905) +RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36107) +RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin:test_registerAccessControlDefaultAdmin_Revert() (gas: 20206) +RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin:test_registerAccessControlDefaultAdmin_Success() (gas: 130628) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 19773) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 130108) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 19593) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 129927) Router_applyRampUpdates:test_OffRampMismatch_Revert() (gas: 89366) Router_applyRampUpdates:test_OffRampUpdatesWithRouting() (gas: 10662612) Router_applyRampUpdates:test_OnRampDisable() (gas: 56007) diff --git a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol index dfb599bd30..cf40fb62d2 100644 --- a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol +++ b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol @@ -8,6 +8,7 @@ import {RegistryModuleOwnerCustom} from "../../tokenAdminRegistry/RegistryModule import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; import {BurnMintERC677Helper} from "../helpers/BurnMintERC677Helper.sol"; +import {AccessControl} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/access/AccessControl.sol"; import {Test} from "forge-std/Test.sol"; contract RegistryModuleOwnerCustomSetup is Test { @@ -102,3 +103,54 @@ contract RegistryModuleOwnerCustom_registerAdminViaOwner is RegistryModuleOwnerC s_registryModuleOwnerCustom.registerAdminViaOwner(s_token); } } + +contract AccessController is AccessControl { + constructor( + address admin + ) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + } +} + +contract RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin is RegistryModuleOwnerCustomSetup { + function setUp() public override { + super.setUp(); + + s_token = address(new AccessController(OWNER)); + } + + function test_registerAccessControlDefaultAdmin_Success() public { + assertEq(s_tokenAdminRegistry.getTokenConfig(s_token).administrator, address(0)); + + bytes32 defaultAdminRole = AccessController(s_token).DEFAULT_ADMIN_ROLE(); + + vm.expectCall(address(s_token), abi.encodeWithSelector(AccessControl.hasRole.selector, defaultAdminRole, OWNER), 1); + vm.expectCall( + address(s_tokenAdminRegistry), + abi.encodeWithSelector(TokenAdminRegistry.proposeAdministrator.selector, s_token, OWNER), + 1 + ); + + vm.expectEmit(); + emit RegistryModuleOwnerCustom.AdministratorRegistered(s_token, OWNER); + + s_registryModuleOwnerCustom.registerAccessControlDefaultAdmin(s_token); + + assertEq(s_tokenAdminRegistry.getTokenConfig(s_token).pendingAdministrator, OWNER); + } + + function test_registerAccessControlDefaultAdmin_Revert() public { + bytes32 defaultAdminRole = AccessController(s_token).DEFAULT_ADMIN_ROLE(); + + address wrongSender = makeAddr("Not_expected_owner"); + vm.startPrank(wrongSender); + + vm.expectRevert( + abi.encodeWithSelector( + RegistryModuleOwnerCustom.RequiredRoleNotFound.selector, wrongSender, defaultAdminRole, s_token + ) + ); + + s_registryModuleOwnerCustom.registerAccessControlDefaultAdmin(s_token); + } +} diff --git a/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol b/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol index a794d68c9e..549963ff4a 100644 --- a/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol +++ b/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol @@ -6,13 +6,16 @@ import {IGetCCIPAdmin} from "../interfaces/IGetCCIPAdmin.sol"; import {IOwner} from "../interfaces/IOwner.sol"; import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol"; +import {AccessControl} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/access/AccessControl.sol"; + contract RegistryModuleOwnerCustom is ITypeAndVersion { error CanOnlySelfRegister(address admin, address token); + error RequiredRoleNotFound(address msgSender, bytes32 role, address token); error AddressZero(); event AdministratorRegistered(address indexed token, address indexed administrator); - string public constant override typeAndVersion = "RegistryModuleOwnerCustom 1.5.0"; + string public constant override typeAndVersion = "RegistryModuleOwnerCustom 1.6.0"; // The TokenAdminRegistry contract ITokenAdminRegistry internal immutable i_tokenAdminRegistry; @@ -38,6 +41,20 @@ contract RegistryModuleOwnerCustom is ITypeAndVersion { _registerAdmin(token, IOwner(token).owner()); } + /// @notice Registers the admin of the token using OZ's AccessControl DEFAULT_ADMIN_ROLE. + /// @param token The token to register the admin for. + /// @dev The caller must have the DEFAULT_ADMIN_ROLE as defined by the contract itself. + function registerAccessControlDefaultAdmin( + address token + ) external { + bytes32 defaultAdminRole = AccessControl(token).DEFAULT_ADMIN_ROLE(); + if (!AccessControl(token).hasRole(defaultAdminRole, msg.sender)) { + revert RequiredRoleNotFound(msg.sender, defaultAdminRole, token); + } + + _registerAdmin(token, msg.sender); + } + /// @notice Registers the admin of the token to msg.sender given that the /// admin is equal to msg.sender. /// @param token The token to register the admin for. diff --git a/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go b/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go index 121135075d..315d75121b 100644 --- a/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go +++ b/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go @@ -31,8 +31,8 @@ var ( ) var RegistryModuleOwnerCustomMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AddressZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"CanOnlySelfRegister\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"administrator\",\"type\":\"address\"}],\"name\":\"AdministratorRegistered\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaGetCCIPAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b5060405161047e38038061047e83398101604081905261002f91610067565b6001600160a01b03811661005657604051639fabe1c160e01b815260040160405180910390fd5b6001600160a01b0316608052610097565b60006020828403121561007957600080fd5b81516001600160a01b038116811461009057600080fd5b9392505050565b6080516103cc6100b2600039600061024a01526103cc6000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063181f5a771461004657806396ea2f7a14610098578063ff12c354146100ad575b600080fd5b6100826040518060400160405280601f81526020017f52656769737472794d6f64756c654f776e6572437573746f6d20312e352e300081525081565b60405161008f91906102ef565b60405180910390f35b6100ab6100a636600461037e565b6100c0565b005b6100ab6100bb36600461037e565b61013b565b610138818273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561010f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013391906103a2565b61018a565b50565b610138818273ffffffffffffffffffffffffffffffffffffffff16638fd6a6ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561010f573d6000803e3d6000fd5b73ffffffffffffffffffffffffffffffffffffffff811633146101fd576040517fc454d18200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80831660048301528316602482015260440160405180910390fd5b6040517fe677ae3700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015282811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063e677ae3790604401600060405180830381600087803b15801561028e57600080fd5b505af11580156102a2573d6000803e3d6000fd5b505060405173ffffffffffffffffffffffffffffffffffffffff8085169350851691507f09590fb70af4b833346363965e043a9339e8c7d378b8a2b903c75c277faec4f990600090a35050565b60006020808352835180602085015260005b8181101561031d57858101830151858201604001528201610301565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461013857600080fd5b60006020828403121561039057600080fd5b813561039b8161035c565b9392505050565b6000602082840312156103b457600080fd5b815161039b8161035c56fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AddressZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"CanOnlySelfRegister\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"RequiredRoleNotFound\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"administrator\",\"type\":\"address\"}],\"name\":\"AdministratorRegistered\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAccessControlDefaultAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaGetCCIPAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60a060405234801561001057600080fd5b5060405161064a38038061064a83398101604081905261002f91610067565b6001600160a01b03811661005657604051639fabe1c160e01b815260040160405180910390fd5b6001600160a01b0316608052610097565b60006020828403121561007957600080fd5b81516001600160a01b038116811461009057600080fd5b9392505050565b6080516105986100b260003960006103db01526105986000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063181f5a771461005157806369c0081e146100a357806396ea2f7a146100b8578063ff12c354146100cb575b600080fd5b61008d6040518060400160405280601f81526020017f52656769737472794d6f64756c654f776e6572437573746f6d20312e362e300081525081565b60405161009a9190610480565b60405180910390f35b6100b66100b136600461050f565b6100de565b005b6100b66100c636600461050f565b610255565b6100b66100d936600461050f565b6102d0565b60008173ffffffffffffffffffffffffffffffffffffffff1663a217fddf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014f9190610533565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810182905233602482015290915073ffffffffffffffffffffffffffffffffffffffff8316906391d1485490604401602060405180830381865afa1580156101c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e7919061054c565b610247576040517f86e0b3440000000000000000000000000000000000000000000000000000000081523360048201526024810182905273ffffffffffffffffffffffffffffffffffffffff831660448201526064015b60405180910390fd5b610251823361031f565b5050565b6102cd818273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c8919061056e565b61031f565b50565b6102cd818273ffffffffffffffffffffffffffffffffffffffff16638fd6a6ac6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102a4573d6000803e3d6000fd5b73ffffffffffffffffffffffffffffffffffffffff8116331461038e576040517fc454d18200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80831660048301528316602482015260440161023e565b6040517fe677ae3700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015282811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063e677ae3790604401600060405180830381600087803b15801561041f57600080fd5b505af1158015610433573d6000803e3d6000fd5b505060405173ffffffffffffffffffffffffffffffffffffffff8085169350851691507f09590fb70af4b833346363965e043a9339e8c7d378b8a2b903c75c277faec4f990600090a35050565b60006020808352835180602085015260005b818110156104ae57858101830151858201604001528201610492565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146102cd57600080fd5b60006020828403121561052157600080fd5b813561052c816104ed565b9392505050565b60006020828403121561054557600080fd5b5051919050565b60006020828403121561055e57600080fd5b8151801515811461052c57600080fd5b60006020828403121561058057600080fd5b815161052c816104ed56fea164736f6c6343000818000a", } var RegistryModuleOwnerCustomABI = RegistryModuleOwnerCustomMetaData.ABI @@ -193,6 +193,18 @@ func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomCallerSession) TypeAn return _RegistryModuleOwnerCustom.Contract.TypeAndVersion(&_RegistryModuleOwnerCustom.CallOpts) } +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactor) RegisterAccessControlDefaultAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.contract.Transact(opts, "registerAccessControlDefaultAdmin", token) +} + +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomSession) RegisterAccessControlDefaultAdmin(token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.Contract.RegisterAccessControlDefaultAdmin(&_RegistryModuleOwnerCustom.TransactOpts, token) +} + +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactorSession) RegisterAccessControlDefaultAdmin(token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.Contract.RegisterAccessControlDefaultAdmin(&_RegistryModuleOwnerCustom.TransactOpts, token) +} + func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactor) RegisterAdminViaGetCCIPAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { return _RegistryModuleOwnerCustom.contract.Transact(opts, "registerAdminViaGetCCIPAdmin", token) } @@ -374,6 +386,8 @@ func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustom) Address() common.Ad type RegistryModuleOwnerCustomInterface interface { TypeAndVersion(opts *bind.CallOpts) (string, error) + RegisterAccessControlDefaultAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) + RegisterAdminViaGetCCIPAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) RegisterAdminViaOwner(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) From a0dfd3a84a946f8812d33a89ac3ee0bde15b4cc0 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 30 Oct 2024 00:00:48 +0100 Subject: [PATCH 3/3] add ownable2step (#14989) --- contracts/foundry.toml | 2 +- contracts/gas-snapshots/shared.gas-snapshot | 58 +++++++------ .../v0.8/shared/access/AuthorizedCallers.sol | 2 +- .../src/v0.8/shared/access/Ownable2Step.sol | 84 ++++++++++++++++++ .../shared/access/Ownable2StepMsgSender.sol | 9 ++ contracts/src/v0.8/shared/test/BaseTest.t.sol | 2 +- .../test/access/AuthorizedCallers.t.sol | 2 +- .../shared/test/access/Ownable2Step.t.sol | 87 +++++++++++++++++++ .../shared/test/call/CallWithExactGas.t.sol | 2 +- .../test/call/CallWithExactGasHelper.sol | 2 +- .../enumerable/EnumerableMapAddresses.t.sol | 2 +- .../test/token/ERC677/BurnMintERC677.t.sol | 2 +- 12 files changed, 221 insertions(+), 33 deletions(-) create mode 100644 contracts/src/v0.8/shared/access/Ownable2Step.sol create mode 100644 contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol create mode 100644 contracts/src/v0.8/shared/test/access/Ownable2Step.t.sol diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 781e875eb6..e1399964dd 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -95,7 +95,7 @@ test = 'src/v0.8/transmission/test' optimizer_runs = 1_000_000 src = 'src/v0.8/shared' test = 'src/v0.8/shared/test' -solc_version = '0.8.19' +solc_version = '0.8.24' # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/contracts/gas-snapshots/shared.gas-snapshot b/contracts/gas-snapshots/shared.gas-snapshot index dda850089c..e2163b9981 100644 --- a/contracts/gas-snapshots/shared.gas-snapshot +++ b/contracts/gas-snapshots/shared.gas-snapshot @@ -1,15 +1,15 @@ -AuthorizedCallers_applyAuthorizedCallerUpdates:test_AddAndRemove_Success() (gas: 125205) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyAdd_Success() (gas: 133100) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyCallableByOwner_Revert() (gas: 12350) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyRemove_Success() (gas: 45064) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_RemoveThenAdd_Success() (gas: 57241) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_SkipRemove_Success() (gas: 32121) -AuthorizedCallers_applyAuthorizedCallerUpdates:test_ZeroAddressNotAllowed_Revert() (gas: 64473) -AuthorizedCallers_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 64473) -AuthorizedCallers_constructor:test_constructor_Success() (gas: 720513) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_AddAndRemove_Success() (gas: 125022) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyAdd_Success() (gas: 132980) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyCallableByOwner_Revert() (gas: 12356) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyRemove_Success() (gas: 45007) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_RemoveThenAdd_Success() (gas: 57121) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_SkipRemove_Success() (gas: 32064) +AuthorizedCallers_applyAuthorizedCallerUpdates:test_ZeroAddressNotAllowed_Revert() (gas: 64440) +AuthorizedCallers_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 64440) +AuthorizedCallers_constructor:test_constructor_Success() (gas: 704809) BurnMintERC677_approve:testApproveSuccess() (gas: 55512) BurnMintERC677_approve:testInvalidAddressReverts() (gas: 10663) -BurnMintERC677_burn:testBasicBurnSuccess() (gas: 173939) +BurnMintERC677_burn:testBasicBurnSuccess() (gas: 172100) BurnMintERC677_burn:testBurnFromZeroAddressReverts() (gas: 47201) BurnMintERC677_burn:testExceedsBalanceReverts() (gas: 21841) BurnMintERC677_burn:testSenderNotBurnerReverts() (gas: 13359) @@ -21,7 +21,7 @@ BurnMintERC677_burnFromAlias:testBurnFromSuccess() (gas: 57949) BurnMintERC677_burnFromAlias:testExceedsBalanceReverts() (gas: 35880) BurnMintERC677_burnFromAlias:testInsufficientAllowanceReverts() (gas: 21869) BurnMintERC677_burnFromAlias:testSenderNotBurnerReverts() (gas: 13379) -BurnMintERC677_constructor:testConstructorSuccess() (gas: 1672809) +BurnMintERC677_constructor:testConstructorSuccess() (gas: 1672812) BurnMintERC677_decreaseApproval:testDecreaseApprovalSuccess() (gas: 31069) BurnMintERC677_grantMintAndBurnRoles:testGrantMintAndBurnRolesSuccess() (gas: 121324) BurnMintERC677_grantRole:testGrantBurnAccessSuccess() (gas: 53460) @@ -34,14 +34,14 @@ BurnMintERC677_mint:testSenderNotMinterReverts() (gas: 11195) BurnMintERC677_supportsInterface:testConstructorSuccess() (gas: 12476) BurnMintERC677_transfer:testInvalidAddressReverts() (gas: 10639) BurnMintERC677_transfer:testTransferSuccess() (gas: 42299) -CallWithExactGas__callWithExactGas:test_CallWithExactGasReceiverErrorSuccess() (gas: 67209) +CallWithExactGas__callWithExactGas:test_CallWithExactGasReceiverErrorSuccess() (gas: 65949) CallWithExactGas__callWithExactGas:test_CallWithExactGasSafeReturnDataExactGas() (gas: 18324) CallWithExactGas__callWithExactGas:test_NoContractReverts() (gas: 11559) CallWithExactGas__callWithExactGas:test_NoGasForCallExactCheckReverts() (gas: 15788) CallWithExactGas__callWithExactGas:test_NotEnoughGasForCallReverts() (gas: 16241) CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 256, μ: 15766, ~: 15719) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractExactGasSuccess() (gas: 20116) -CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() (gas: 67721) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() (gas: 66461) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 256, μ: 16276, ~: 16229) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoContractSuccess() (gas: 12962) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoGasForCallExactCheckReturnFalseSuccess() (gas: 13005) @@ -75,20 +75,28 @@ EnumerableMapAddresses_tryGet:testBytes32TryGetSuccess() (gas: 94622) EnumerableMapAddresses_tryGet:testBytesTryGetSuccess() (gas: 96279) EnumerableMapAddresses_tryGet:testTryGetSuccess() (gas: 94893) OpStackBurnMintERC677_constructor:testConstructorSuccess() (gas: 1743649) -OpStackBurnMintERC677_interfaceCompatibility:testBurnCompatibility() (gas: 298649) +OpStackBurnMintERC677_interfaceCompatibility:testBurnCompatibility() (gas: 291393) OpStackBurnMintERC677_interfaceCompatibility:testMintCompatibility() (gas: 137957) OpStackBurnMintERC677_interfaceCompatibility:testStaticFunctionsCompatibility() (gas: 13781) OpStackBurnMintERC677_supportsInterface:testConstructorSuccess() (gas: 12752) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_EmptySubset_Reverts() (gas: 5460) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_EmptySuperset_Reverts() (gas: 4661) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_HasDuplicates_Reverts() (gas: 8265) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_NotASubset_Reverts() (gas: 12487) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SingleElementSubset() (gas: 4489) +Ownable2Step_acceptOwnership:test_acceptOwnership_MustBeProposedOwner_reverts() (gas: 10360) +Ownable2Step_acceptOwnership:test_acceptOwnership_success() (gas: 31088) +Ownable2Step_constructor:test_constructor_OwnerCannotBeZero_reverts() (gas: 35858) +Ownable2Step_constructor:test_constructor_success() (gas: 10428) +Ownable2Step_onlyOwner:test_onlyOwner_OnlyCallableByOwner_reverts() (gas: 10754) +Ownable2Step_onlyOwner:test_onlyOwner_success() (gas: 7506) +Ownable2Step_transferOwnership:test_transferOwnership_CannotTransferToSelf_reverts() (gas: 10501) +Ownable2Step_transferOwnership:test_transferOwnership_success() (gas: 30140) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_EmptySubset_Reverts() (gas: 5208) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_EmptySuperset_Reverts() (gas: 4535) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_HasDuplicates_Reverts() (gas: 7761) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_NotASubset_Reverts() (gas: 11668) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SingleElementSubset() (gas: 3922) SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SingleElementSubsetAndSuperset_Equal() (gas: 1464) SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SingleElementSubsetAndSuperset_NotEqual_Reverts() (gas: 6172) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SubsetEqualsSuperset_NoRevert() (gas: 8867) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SubsetLargerThanSuperset_Reverts() (gas: 16544) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SupersetHasDuplicates_Reverts() (gas: 9420) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_UnsortedSubset_Reverts() (gas: 7380) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_UnsortedSuperset_Reverts() (gas: 9600) -SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_ValidSubset_Success() (gas: 6490) \ No newline at end of file +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SubsetEqualsSuperset_NoRevert() (gas: 7859) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SubsetLargerThanSuperset_Reverts() (gas: 15410) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_SupersetHasDuplicates_Reverts() (gas: 8790) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_UnsortedSubset_Reverts() (gas: 7128) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_UnsortedSuperset_Reverts() (gas: 8970) +SortedSetValidationUtil_CheckIsValidUniqueSubsetTest:test__checkIsValidUniqueSubset_ValidSubset_Success() (gas: 5671) \ No newline at end of file diff --git a/contracts/src/v0.8/shared/access/AuthorizedCallers.sol b/contracts/src/v0.8/shared/access/AuthorizedCallers.sol index 93102d1a97..8c54d7bac8 100644 --- a/contracts/src/v0.8/shared/access/AuthorizedCallers.sol +++ b/contracts/src/v0.8/shared/access/AuthorizedCallers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity ^0.8.4; import {OwnerIsCreator} from "./OwnerIsCreator.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; diff --git a/contracts/src/v0.8/shared/access/Ownable2Step.sol b/contracts/src/v0.8/shared/access/Ownable2Step.sol new file mode 100644 index 0000000000..5eac576072 --- /dev/null +++ b/contracts/src/v0.8/shared/access/Ownable2Step.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {IOwnable} from "../interfaces/IOwnable.sol"; + +/// @notice A minimal contract that implements 2-step ownership transfer and nothing more. It's made to be minimal +/// to reduce the impact of the bytecode size on any contract that inherits from it. +contract Ownable2Step is IOwnable { + /// @notice The pending owner is the address to which ownership may be transferred. + address private s_pendingOwner; + /// @notice The owner is the current owner of the contract. + /// @dev The owner is the second storage variable so any implementing contract could pack other state with it + /// instead of the much less used s_pendingOwner. + address private s_owner; + + error OwnerCannotBeZero(); + error MustBeProposedOwner(); + error CannotTransferToSelf(); + error OnlyCallableByOwner(); + + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + + constructor(address newOwner, address pendingOwner) { + if (newOwner == address(0)) { + revert OwnerCannotBeZero(); + } + + s_owner = newOwner; + if (pendingOwner != address(0)) { + _transferOwnership(pendingOwner); + } + } + + /// @notice Get the current owner + function owner() public view override returns (address) { + return s_owner; + } + + /// @notice Allows an owner to begin transferring ownership to a new address. The new owner needs to call + /// `acceptOwnership` to accept the transfer before any permissions are changed. + /// @param to The address to which ownership will be transferred. + function transferOwnership(address to) public override onlyOwner { + _transferOwnership(to); + } + + /// @notice validate, transfer ownership, and emit relevant events + /// @param to The address to which ownership will be transferred. + function _transferOwnership(address to) private { + if (to == msg.sender) { + revert CannotTransferToSelf(); + } + + s_pendingOwner = to; + + emit OwnershipTransferRequested(s_owner, to); + } + + /// @notice Allows an ownership transfer to be completed by the recipient. + function acceptOwnership() external override { + if (msg.sender != s_pendingOwner) { + revert MustBeProposedOwner(); + } + + address oldOwner = s_owner; + s_owner = msg.sender; + s_pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /// @notice validate access + function _validateOwnership() internal view { + if (msg.sender != s_owner) { + revert OnlyCallableByOwner(); + } + } + + /// @notice Reverts if called by anyone other than the contract owner. + modifier onlyOwner() { + _validateOwnership(); + _; + } +} diff --git a/contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol b/contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol new file mode 100644 index 0000000000..b3de785a70 --- /dev/null +++ b/contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {Ownable2Step} from "./Ownable2Step.sol"; + +/// @notice Sets the msg.sender to be the owner of the contract and does not set a pending owner. +contract Ownable2StepMsgSender is Ownable2Step { + constructor() Ownable2Step(msg.sender, address(0)) {} +} diff --git a/contracts/src/v0.8/shared/test/BaseTest.t.sol b/contracts/src/v0.8/shared/test/BaseTest.t.sol index 4d8ef60eb2..b2e9afcd35 100644 --- a/contracts/src/v0.8/shared/test/BaseTest.t.sol +++ b/contracts/src/v0.8/shared/test/BaseTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import "forge-std/Test.sol"; diff --git a/contracts/src/v0.8/shared/test/access/AuthorizedCallers.t.sol b/contracts/src/v0.8/shared/test/access/AuthorizedCallers.t.sol index 34ae6848a4..54f74fbf16 100644 --- a/contracts/src/v0.8/shared/test/access/AuthorizedCallers.t.sol +++ b/contracts/src/v0.8/shared/test/access/AuthorizedCallers.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import {AuthorizedCallers} from "../../access/AuthorizedCallers.sol"; import {BaseTest} from "../BaseTest.t.sol"; diff --git a/contracts/src/v0.8/shared/test/access/Ownable2Step.t.sol b/contracts/src/v0.8/shared/test/access/Ownable2Step.t.sol new file mode 100644 index 0000000000..0421d647c5 --- /dev/null +++ b/contracts/src/v0.8/shared/test/access/Ownable2Step.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {BaseTest} from "../BaseTest.t.sol"; +import {Ownable2Step} from "../../access/Ownable2Step.sol"; + +contract Ownable2Step_setup is BaseTest { + Ownable2StepHelper internal s_ownable2Step; + + function setUp() public override { + super.setUp(); + s_ownable2Step = new Ownable2StepHelper(OWNER, address(0)); + } +} + +contract Ownable2Step_constructor is Ownable2Step_setup { + function test_constructor_success() public view { + assertEq(OWNER, s_ownable2Step.owner()); + } + + function test_constructor_OwnerCannotBeZero_reverts() public { + vm.expectRevert(Ownable2Step.OwnerCannotBeZero.selector); + new Ownable2Step(address(0), address(0)); + } +} + +contract Ownable2Step_transferOwnership is Ownable2Step_setup { + function test_transferOwnership_success() public { + vm.expectEmit(); + emit Ownable2Step.OwnershipTransferRequested(OWNER, STRANGER); + + s_ownable2Step.transferOwnership(STRANGER); + + assertTrue(STRANGER != s_ownable2Step.owner()); + + vm.startPrank(STRANGER); + s_ownable2Step.acceptOwnership(); + } + + function test_transferOwnership_CannotTransferToSelf_reverts() public { + vm.expectRevert(Ownable2Step.CannotTransferToSelf.selector); + s_ownable2Step.transferOwnership(OWNER); + } +} + +contract Ownable2Step_acceptOwnership is Ownable2Step_setup { + function test_acceptOwnership_success() public { + s_ownable2Step.transferOwnership(STRANGER); + + assertTrue(STRANGER != s_ownable2Step.owner()); + + vm.startPrank(STRANGER); + + vm.expectEmit(); + emit Ownable2Step.OwnershipTransferred(OWNER, STRANGER); + + s_ownable2Step.acceptOwnership(); + + assertEq(STRANGER, s_ownable2Step.owner()); + } + + function test_acceptOwnership_MustBeProposedOwner_reverts() public { + vm.expectRevert(Ownable2Step.MustBeProposedOwner.selector); + s_ownable2Step.acceptOwnership(); + } +} + +contract Ownable2StepHelper is Ownable2Step { + constructor(address newOwner, address pendingOwner) Ownable2Step(newOwner, pendingOwner) {} + + function validateOwnership() external view { + _validateOwnership(); + } +} + +contract Ownable2Step_onlyOwner is Ownable2Step_setup { + function test_onlyOwner_success() public view { + s_ownable2Step.validateOwnership(); + } + + function test_onlyOwner_OnlyCallableByOwner_reverts() public { + vm.stopPrank(); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_ownable2Step.validateOwnership(); + } +} diff --git a/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol b/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol index 3623cb8b12..b432de58a5 100644 --- a/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol +++ b/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import {CallWithExactGas} from "../../call/CallWithExactGas.sol"; import {CallWithExactGasHelper} from "./CallWithExactGasHelper.sol"; diff --git a/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol b/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol index 932315639b..0ba8602e08 100644 --- a/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol +++ b/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity ^0.8.4; import {CallWithExactGas} from "../../call/CallWithExactGas.sol"; diff --git a/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol b/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol index 097e79e372..689657105e 100644 --- a/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol +++ b/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "../BaseTest.t.sol"; import {EnumerableMapAddresses} from "../../enumerable/EnumerableMapAddresses.sol"; diff --git a/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol b/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol index 2815f99256..a778a1ada2 100644 --- a/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol +++ b/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity 0.8.24; import {IBurnMintERC20} from "../../../token/ERC20/IBurnMintERC20.sol"; import {IERC677} from "../../../token/ERC677/IERC677.sol";