From e77c6221903ee2ea6abbebab4fb594748eb66c09 Mon Sep 17 00:00:00 2001 From: ahramy Date: Fri, 25 Oct 2024 14:18:19 -0700 Subject: [PATCH] refactor!: implement AxelarExecutable and AxelarExecutableWithToken in ITS (#274) --- contracts/InterchainTokenFactory.sol | 9 ++++--- contracts/InterchainTokenService.sol | 18 +++++++++++--- contracts/utils/GatewayCaller.sol | 3 ++- package-lock.json | 30 ++++++++++++++--------- package.json | 4 +-- test/InterchainTokenService.js | 6 +++++ test/InterchainTokenServiceUpgradeFlow.js | 27 ++++++-------------- 7 files changed, 56 insertions(+), 41 deletions(-) diff --git a/contracts/InterchainTokenFactory.sol b/contracts/InterchainTokenFactory.sol index 14e8c0b0..fa07b360 100644 --- a/contracts/InterchainTokenFactory.sol +++ b/contracts/InterchainTokenFactory.sol @@ -5,8 +5,7 @@ pragma solidity ^0.8.0; import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol'; import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol'; import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol'; -import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol'; - +import { IAxelarGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGatewayWithToken.sol'; import { IInterchainTokenService } from './interfaces/IInterchainTokenService.sol'; import { IInterchainTokenFactory } from './interfaces/IInterchainTokenFactory.sol'; import { ITokenManagerType } from './interfaces/ITokenManagerType.sol'; @@ -22,7 +21,7 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M IInterchainTokenService public immutable interchainTokenService; bytes32 public immutable chainNameHash; - IAxelarGateway public immutable gateway; + IAxelarGatewayWithToken public immutable gateway; bytes32 private constant CONTRACT_ID = keccak256('interchain-token-factory'); bytes32 internal constant PREFIX_CANONICAL_TOKEN_SALT = keccak256('canonical-token-salt'); @@ -40,9 +39,11 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M interchainTokenService = IInterchainTokenService(interchainTokenService_); chainNameHash = interchainTokenService.chainNameHash(); - gateway = interchainTokenService.gateway(); + gateway = IAxelarGatewayWithToken(address(interchainTokenService.gateway())); } + function _setup(bytes calldata data) internal override {} + /** * @notice Getter for the contract id. * @return bytes32 The contract id of this contract. diff --git a/contracts/InterchainTokenService.sol b/contracts/InterchainTokenService.sol index fb0f6a6d..ec4d5d02 100644 --- a/contracts/InterchainTokenService.sol +++ b/contracts/InterchainTokenService.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.0; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol'; import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol'; +import { IAxelarGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGatewayWithToken.sol'; import { ExpressExecutorTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutorTracker.sol'; import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol'; import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol'; @@ -21,7 +22,6 @@ import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainToken import { ITokenManager } from './interfaces/ITokenManager.sol'; import { IGatewayCaller } from './interfaces/IGatewayCaller.sol'; import { Create3AddressFixed } from './utils/Create3AddressFixed.sol'; - import { Operator } from './utils/Operator.sol'; /** @@ -44,6 +44,14 @@ contract InterchainTokenService is using AddressBytes for bytes; using AddressBytes for address; + /** + * @dev There are two types of Axelar Gateways for cross-chain messaging: + * 1. Cross-chain messaging (GMP): The Axelar Gateway allows sending cross-chain messages. + * This is compatible across both Amplifier and consensus chains. IAxelarGateway interface exposes this functionality. + * 2. Cross-chain messaging with Gateway Token: The AxelarGateway on legacy consensus EVM connections supports this (via callContractWithToken) + * but not Amplifier chains. The gateway is cast to IAxelarGatewayWithToken when gateway tokens need to be handled. + * ITS deployments on Amplifier chains will revert when this functionality is used. + */ IAxelarGateway public immutable gateway; IAxelarGasService public immutable gasService; address public immutable interchainTokenFactory; @@ -717,7 +725,7 @@ contract InterchainTokenService is // This is intentional, as using `uint256` instead of `bytes` improves gas efficiency without any functional difference. (, bytes32 tokenId, , , uint256 amountInPayload) = abi.decode(payload, (uint256, bytes32, uint256, uint256, uint256)); - if (validTokenAddress(tokenId) != gateway.tokenAddresses(tokenSymbol) || amount != amountInPayload) + if (validTokenAddress(tokenId) != gatewayWithToken().tokenAddresses(tokenSymbol) || amount != amountInPayload) revert InvalidGatewayTokenTransfer(tokenId, payload, tokenSymbol, amount); } @@ -940,7 +948,7 @@ contract InterchainTokenService is ) internal { bytes32 payloadHash = keccak256(payload); - if (!gateway.validateContractCallAndMint(commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount)) + if (!gatewayWithToken().validateContractCallAndMint(commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount)) revert NotApprovedByGateway(); uint256 messageType; @@ -1247,4 +1255,8 @@ contract InterchainTokenService is emit ExpressExecutionFulfilled(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor); } } + + function gatewayWithToken() internal view returns (IAxelarGatewayWithToken) { + return IAxelarGatewayWithToken(address(gateway)); + } } diff --git a/contracts/utils/GatewayCaller.sol b/contracts/utils/GatewayCaller.sol index f739facd..df81612c 100644 --- a/contracts/utils/GatewayCaller.sol +++ b/contracts/utils/GatewayCaller.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol'; import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol'; +import { IAxelarGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGatewayWithToken.sol'; import { IGatewayCaller } from '../interfaces/IGatewayCaller.sol'; /** @@ -117,6 +118,6 @@ contract GatewayCaller is IGatewayCaller { } } - gateway.callContractWithToken(destinationChain, destinationAddress, payload, symbol, amount); + IAxelarGatewayWithToken(address(gateway)).callContractWithToken(destinationChain, destinationAddress, payload, symbol, amount); } } diff --git a/package-lock.json b/package-lock.json index 291e19a4..3b713616 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "@axelar-network/axelar-cgp-solidity": "6.2.1", - "@axelar-network/axelar-gmp-sdk-solidity": "5.6.4" + "@axelar-network/axelar-cgp-solidity": "6.4.0", + "@axelar-network/axelar-gmp-sdk-solidity": "6.0.1" }, "devDependencies": { "@axelar-network/axelar-chains-config": "^1.2.0", @@ -51,14 +51,22 @@ "dev": true }, "node_modules/@axelar-network/axelar-cgp-solidity": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-solidity/-/axelar-cgp-solidity-6.2.1.tgz", - "integrity": "sha512-0RaxLYmsp3elXBytn7+eZUil1KFS6jjHR/ECrN/3IC7TeTDPUYunyy6JDxCdNYNtiD6EKgUAfQ7G4DrBb6hRxg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-solidity/-/axelar-cgp-solidity-6.4.0.tgz", + "integrity": "sha512-Xnw5xi234B1cmTCzgudV8zq+DDjJ1d1U362CM0vKH1FWmZprKIdqgmOYkiRyu+QiVhnznKiBURiSEHVrNjtYpw==", "dependencies": { - "@axelar-network/axelar-gmp-sdk-solidity": "5.6.4" + "@axelar-network/axelar-gmp-sdk-solidity": "5.10.0" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/@axelar-network/axelar-cgp-solidity/node_modules/@axelar-network/axelar-gmp-sdk-solidity": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.10.0.tgz", + "integrity": "sha512-s8SImALvYB+5AeiT3tbfWNBI2Mhqw1x91i/zM3DNpVUCnAR2HKtsB9T84KnUn/OJjOVgb4h0lv7q9smeYniRPw==", + "engines": { + "node": ">=18" } }, "node_modules/@axelar-network/axelar-chains-config": { @@ -72,11 +80,11 @@ } }, "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.6.4.tgz", - "integrity": "sha512-PQjV+HeJynmSRMhyM3SexwnbFNruSaiRUeNCWjV8/7CkdPsDqypoqIXVRVU8Zk92DUUHeqZZzL/3qP2LYuvlnA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-6.0.1.tgz", + "integrity": "sha512-hxLRvxrx7LjG8QqL5Myh4Fq1hGN4y64ZK6a0YtoIK44YXLloeWSzgDVn5C8E8FoQHOTYAE6RpSVbilDgss480Q==", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@axelarjs/evm": { diff --git a/package.json b/package.json index accb819a..bddef914 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "node": ">=18" }, "dependencies": { - "@axelar-network/axelar-cgp-solidity": "6.2.1", - "@axelar-network/axelar-gmp-sdk-solidity": "5.6.4" + "@axelar-network/axelar-cgp-solidity": "6.4.0", + "@axelar-network/axelar-gmp-sdk-solidity": "6.0.1" }, "devDependencies": { "@axelar-network/axelar-chains-config": "^1.2.0", diff --git a/test/InterchainTokenService.js b/test/InterchainTokenService.js index 17468286..28cdb142 100644 --- a/test/InterchainTokenService.js +++ b/test/InterchainTokenService.js @@ -500,6 +500,12 @@ describe('Interchain Token Service', () => { ); }); + it('Should return the correct contract id', async () => { + const expectedContractid = keccak256(toUtf8Bytes('interchain-token-service')); + const contractId = await service.contractId(); + expect(contractId).to.eq(expectedContractid); + }); + it('Should return the token manager implementation', async () => { const tokenManagerImplementation = await service.tokenManagerImplementation(getRandomInt(1000)); expect(tokenManagerImplementation).to.eq(tokenManager.address); diff --git a/test/InterchainTokenServiceUpgradeFlow.js b/test/InterchainTokenServiceUpgradeFlow.js index c98a62a5..de145646 100644 --- a/test/InterchainTokenServiceUpgradeFlow.js +++ b/test/InterchainTokenServiceUpgradeFlow.js @@ -18,7 +18,7 @@ const Create3Deployer = getContractJSON('Create3Deployer'); const { MINT_BURN } = require('./constants'); describe('Interchain Token Service Upgrade Flow', () => { - let wallet, otherWallet, signer; + let wallet, otherWallet, operator; let service, gateway, gasService; let tokenManagerDeployer, interchainTokenDeployer, tokenManager, tokenHandler, gatewayCaller; let interchainTokenFactoryAddress; @@ -30,7 +30,7 @@ describe('Interchain Token Service Upgrade Flow', () => { let buffer; const governanceChain = 'Governance Chain'; - const threshold = 2; + const minimumTimeDelay = isHardhat ? 10 * 60 * 60 : 15; const deploymentKey = 'InterchainTokenService'; const chainName = 'Test'; @@ -58,8 +58,7 @@ describe('Interchain Token Service Upgrade Flow', () => { } before(async () => { - [wallet, otherWallet, signer] = await ethers.getSigners(); - const signers = [wallet, otherWallet, signer]; + [wallet, otherWallet, operator] = await ethers.getSigners(); governanceAddress = otherWallet.address; buffer = isHardhat ? 10 * 60 * 60 : 10; @@ -85,14 +84,7 @@ describe('Interchain Token Service Upgrade Flow', () => { ); axelarServiceGovernance = await axelarServiceGovernanceFactory - .deploy( - gateway.address, - governanceChain, - governanceAddress, - buffer, - signers.map((signer) => signer.address), - threshold, - ) + .deploy(gateway.address, governanceChain, governanceAddress, minimumTimeDelay, operator.address) .then((d) => d.deployed()); service = await deployInterchainTokenService( @@ -224,16 +216,11 @@ describe('Interchain Token Service Upgrade Flow', () => { ); await expect(axelarServiceGovernance.execute(commandIdGateway, governanceChain, governanceAddress, payload)) - .to.emit(axelarServiceGovernance, 'MultisigApproved') + .to.emit(axelarServiceGovernance, 'OperatorProposalApproved') .withArgs(proposalHash, target, calldata, nativeValue); - await axelarServiceGovernance - .connect(wallet) - .executeMultisigProposal(target, calldata, nativeValue) - .then((tx) => tx.wait); - - await expect(axelarServiceGovernance.connect(otherWallet).executeMultisigProposal(target, calldata, nativeValue)) - .to.emit(axelarServiceGovernance, 'MultisigExecuted') + await expect(axelarServiceGovernance.connect(operator).executeOperatorProposal(target, calldata, nativeValue)) + .to.emit(axelarServiceGovernance, 'OperatorProposalExecuted') .withArgs(proposalHash, target, calldata, nativeValue) .and.to.emit(service, 'Upgraded') .withArgs(newServiceImplementation.address);