diff --git a/contracts/crosschain/axelar/AxelarGatewayBase.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol index 95073554..7710097b 100644 --- a/contracts/crosschain/axelar/AxelarGatewayBase.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.27; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; /** @@ -12,7 +11,7 @@ import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/ * to Axelar chain identifiers) and remote gateways (i.e. gateways on other chains) to * facilitate cross-chain communication. */ -abstract contract AxelarGatewayBase is Ownable { +abstract contract AxelarGatewayBase { /// @dev A remote gateway has been registered for a chain. event RegisteredRemoteGateway(string caip2, string gatewayAddress); @@ -31,6 +30,12 @@ abstract contract AxelarGatewayBase is Ownable { mapping(string caip2 => string remoteGateway) private _remoteGateways; mapping(string caip2OrAxelar => string axelarOrCaip2) private _chainEquivalence; + /// @dev Only allows an authorized registrant to call the function. See {_checkRegistrant}. + modifier onlyRegistrant() { + _checkRegistrant(); + _; + } + /// @dev Sets the local gateway address (i.e. Axelar's official gateway for the current chain). constructor(IAxelarGateway _gateway) { localGateway = _gateway; @@ -49,7 +54,10 @@ abstract contract AxelarGatewayBase is Ownable { } /// @dev Registers a chain equivalence between a CAIP-2 chain identifier and an Axelar network identifier. - function registerChainEquivalence(string calldata caip2, string calldata axelarSupported) public virtual onlyOwner { + function registerChainEquivalence( + string calldata caip2, + string calldata axelarSupported + ) public virtual onlyRegistrant { require(bytes(_chainEquivalence[caip2]).length == 0, ChainEquivalenceAlreadyRegistered(caip2)); _chainEquivalence[caip2] = axelarSupported; _chainEquivalence[axelarSupported] = caip2; @@ -57,9 +65,12 @@ abstract contract AxelarGatewayBase is Ownable { } /// @dev Registers the address string of the remote gateway for a given CAIP-2 chain identifier. - function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public virtual onlyOwner { + function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public virtual onlyRegistrant { require(bytes(_remoteGateways[caip2]).length == 0, RemoteGatewayAlreadyRegistered(caip2)); _remoteGateways[caip2] = remoteGateway; emit RegisteredRemoteGateway(caip2, remoteGateway); } + + /// @dev Modifier to check if the caller is allowed to register remote gateways and chain equivalences. + function _checkRegistrant() internal virtual; } diff --git a/contracts/crosschain/axelar/AxelarGatewayDuplex.sol b/contracts/crosschain/axelar/AxelarGatewayDuplex.sol index 73b22b07..623499fa 100644 --- a/contracts/crosschain/axelar/AxelarGatewayDuplex.sol +++ b/contracts/crosschain/axelar/AxelarGatewayDuplex.sol @@ -12,10 +12,7 @@ import {AxelarGatewaySource} from "./AxelarGatewaySource.sol"; * adapters for the Axelar Network. Allowing to either send or receive messages across chains. */ // slither-disable-next-line locked-ether -contract AxelarGatewayDuplex is AxelarGatewaySource, AxelarGatewayDestination { +abstract contract AxelarGatewayDuplex is AxelarGatewaySource, AxelarGatewayDestination { /// @dev Initializes the contract with the Axelar gateway and the initial owner. - constructor( - IAxelarGateway gateway, - address initialOwner - ) Ownable(initialOwner) AxelarGatewayBase(gateway) AxelarExecutable(address(gateway)) {} + constructor(IAxelarGateway gateway) AxelarGatewayBase(gateway) AxelarExecutable(address(gateway)) {} } diff --git a/contracts/mocks/crosschain/axelar/AxelarGatewayDestinationOwnableMock.sol b/contracts/mocks/crosschain/axelar/AxelarGatewayDestinationOwnableMock.sol new file mode 100644 index 00000000..1118911b --- /dev/null +++ b/contracts/mocks/crosschain/axelar/AxelarGatewayDestinationOwnableMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {AxelarGatewayDestination} from "../../../crosschain/axelar/AxelarGatewayDestination.sol"; + +abstract contract AxelarGatewayDestinationOwnableMock is AxelarGatewayDestination, Ownable { + function _checkRegistrant() internal override onlyOwner {} +} diff --git a/contracts/mocks/crosschain/axelar/AxelarGatewaySourceOwnableMock.sol b/contracts/mocks/crosschain/axelar/AxelarGatewaySourceOwnableMock.sol new file mode 100644 index 00000000..ddc64d4c --- /dev/null +++ b/contracts/mocks/crosschain/axelar/AxelarGatewaySourceOwnableMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {AxelarGatewaySource} from "../../../crosschain/axelar/AxelarGatewaySource.sol"; + +abstract contract AxelarGatewaySourceOwnableMock is AxelarGatewaySource, Ownable { + function _checkRegistrant() internal override onlyOwner {} +} diff --git a/test/crosschain/axelar/AxelarGateway.test.js b/test/crosschain/axelar/AxelarGateway.test.js index b2802f07..73112164 100644 --- a/test/crosschain/axelar/AxelarGateway.test.js +++ b/test/crosschain/axelar/AxelarGateway.test.js @@ -13,8 +13,8 @@ async function fixture() { const asCAIP10 = account => `eip155:${chainId}:${getAddress(account)}`; const axelar = await ethers.deployContract('$AxelarGatewayMock'); - const srcGateway = await ethers.deployContract('$AxelarGatewaySource', [owner, axelar]); - const dstGateway = await ethers.deployContract('$AxelarGatewayDestination', [owner, axelar, axelar]); + const srcGateway = await ethers.deployContract('$AxelarGatewaySourceOwnableMock', [axelar, owner]); + const dstGateway = await ethers.deployContract('$AxelarGatewayDestinationOwnableMock', [axelar, axelar, owner]); const receiver = await ethers.deployContract('$ERC7786ReceiverMock', [dstGateway]); const invalidReceiver = await ethers.deployContract('$ERC7786ReceiverInvalidMock');