Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: implement AxelarExecutable and AxelarExecutableWithToken in ITS #274

Merged
merged 27 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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');
Expand All @@ -40,7 +39,7 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
interchainTokenService = IInterchainTokenService(interchainTokenService_);

chainNameHash = interchainTokenService.chainNameHash();
gateway = interchainTokenService.gateway();
gateway = IAxelarGatewayWithToken(address(interchainTokenService.gateway()));
}

/**
Expand All @@ -51,6 +50,8 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
return CONTRACT_ID;
}

function _setup(bytes calldata params) internal override {}

/**
* @notice Calculates the salt for an interchain token.
* @param chainNameHash_ The hash of the chain name.
Expand Down
18 changes: 15 additions & 3 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

/**
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1247,4 +1255,8 @@ contract InterchainTokenService is
emit ExpressExecutionFulfilled(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);
}
}

function gatewayWithToken() internal view returns (IAxelarGatewayWithToken) {
return IAxelarGatewayWithToken(address(gateway));
}
}
3 changes: 2 additions & 1 deletion contracts/utils/GatewayCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -117,6 +118,6 @@ contract GatewayCaller is IGatewayCaller {
}
}

gateway.callContractWithToken(destinationChain, destinationAddress, payload, symbol, amount);
IAxelarGatewayWithToken(address(gateway)).callContractWithToken(destinationChain, destinationAddress, payload, symbol, amount);
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,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.3.1",
"@axelar-network/axelar-gmp-sdk-solidity": "6.0.0"
},
"devDependencies": {
"@axelar-network/axelar-chains-config": "^1.2.0",
Expand Down
6 changes: 6 additions & 0 deletions test/InterchainTokenService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 7 additions & 20 deletions test/InterchainTokenServiceUpgradeFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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';

Expand Down Expand Up @@ -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;
Expand All @@ -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)
ahramy marked this conversation as resolved.
Show resolved Hide resolved
.then((d) => d.deployed());

service = await deployInterchainTokenService(
Expand Down Expand Up @@ -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);
Expand Down
Loading