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

implement AxelarGMPExecutableWithToken in ITS #274

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
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 { IAxelarGMPGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGMPGatewayWithToken.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;
IAxelarGMPGatewayWithToken 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 = IAxelarGMPGatewayWithToken(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
35 changes: 26 additions & 9 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ 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 { IAxelarGMPGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGMPGateway.sol';
import { IAxelarGMPGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGMPGatewayWithToken.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,7 +44,14 @@ contract InterchainTokenService is
using AddressBytes for bytes;
using AddressBytes for address;

IAxelarGateway public immutable gateway;
/**
* @dev Axelar GMP Gateway for cross-chain messaging.
* Two available approaches:
ahramy marked this conversation as resolved.
Show resolved Hide resolved
* 1. Pure GMP: Uses IAxelarGMPGateway to handle messaging without token transfers, compatible across Amplifier chains.
ahramy marked this conversation as resolved.
Show resolved Hide resolved
* 2. GMP with Token: Legacy functionality. Explicitly cast to IAxelarGMPGatewayWithToken when needed.
* This approach only functions on chains that rely on Axelar consensus Connection.
ahramy marked this conversation as resolved.
Show resolved Hide resolved
*/
IAxelarGMPGateway public immutable gateway;
IAxelarGasService public immutable gasService;
address public immutable interchainTokenFactory;
bytes32 public immutable chainNameHash;
Expand Down Expand Up @@ -136,7 +143,7 @@ contract InterchainTokenService is
gatewayCaller_ == address(0)
) revert ZeroAddress();

gateway = IAxelarGateway(gateway_);
gateway = IAxelarGMPGateway(gateway_);
gasService = IAxelarGasService(gasService_);
tokenManagerDeployer = tokenManagerDeployer_;
interchainTokenDeployer = interchainTokenDeployer_;
Expand Down Expand Up @@ -377,7 +384,7 @@ contract InterchainTokenService is
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) public payable whenNotPaused {
) public payable override whenNotPaused {
uint256 messageType = abi.decode(payload, (uint256));
if (messageType != MESSAGE_TYPE_INTERCHAIN_TRANSFER) {
revert InvalidExpressMessageType(messageType);
Expand Down Expand Up @@ -707,8 +714,10 @@ contract InterchainTokenService is
function _checkPayloadAgainstGatewayData(bytes memory payload, string calldata tokenSymbol, uint256 amount) internal view {
(, bytes32 tokenId, , , uint256 amountInPayload) = abi.decode(payload, (uint256, bytes32, uint256, uint256, uint256));

if (validTokenAddress(tokenId) != gateway.tokenAddresses(tokenSymbol) || amount != amountInPayload)
revert InvalidGatewayTokenTransfer(tokenId, payload, tokenSymbol, amount);
if (
validTokenAddress(tokenId) != IAxelarGMPGatewayWithToken(address(gateway)).tokenAddresses(tokenSymbol) ||
ahramy marked this conversation as resolved.
Show resolved Hide resolved
amount != amountInPayload
) revert InvalidGatewayTokenTransfer(tokenId, payload, tokenSymbol, amount);
}

/**
Expand Down Expand Up @@ -930,8 +939,16 @@ contract InterchainTokenService is
) internal {
bytes32 payloadHash = keccak256(payload);

if (!gateway.validateContractCallAndMint(commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount))
revert NotApprovedByGateway();
if (
!IAxelarGMPGatewayWithToken(address(gateway)).validateContractCallAndMint(
commandId,
sourceChain,
sourceAddress,
payloadHash,
tokenSymbol,
amount
)
) revert NotApprovedByGateway();

uint256 messageType;
string memory originalSourceChain;
Expand Down
9 changes: 5 additions & 4 deletions contracts/utils/GatewayCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
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 { IAxelarGMPGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGMPGateway.sol';
import { IAxelarGMPGatewayWithToken } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGMPGatewayWithToken.sol';
import { IGatewayCaller } from '../interfaces/IGatewayCaller.sol';

/**
* @title GatewayCaller contract
* @dev This contract is used to handle cross-chain ITS calls via the Axelar gateway.
*/
contract GatewayCaller is IGatewayCaller {
IAxelarGateway public immutable gateway;
IAxelarGMPGateway public immutable gateway;
IAxelarGasService public immutable gasService;

/**
Expand All @@ -20,7 +21,7 @@ contract GatewayCaller is IGatewayCaller {
* @param gasService_ The address of the AxelarGasService contract
*/
constructor(address gateway_, address gasService_) {
gateway = IAxelarGateway(gateway_);
gateway = IAxelarGMPGateway(gateway_);
gasService = IAxelarGasService(gasService_);
}

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

gateway.callContractWithToken(destinationChain, destinationAddress, payload, symbol, amount);
IAxelarGMPGatewayWithToken(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": "5.11.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 @@ -495,6 +495,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
23 changes: 5 additions & 18 deletions test/InterchainTokenServiceUpgradeFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Create3Deployer = getContractJSON('Create3Deployer');
const MINT_BURN = 4;

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 @@ -31,7 +31,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 @@ -59,8 +59,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 @@ -86,14 +85,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(
Expand Down Expand Up @@ -228,12 +220,7 @@ describe('Interchain Token Service Upgrade Flow', () => {
.to.emit(axelarServiceGovernance, 'MultisigApproved')
.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))
await expect(axelarServiceGovernance.connect(operator).executeMultisigProposal(target, calldata, nativeValue))
.to.emit(axelarServiceGovernance, 'MultisigExecuted')
.withArgs(proposalHash, target, calldata, nativeValue)
.and.to.emit(service, 'Upgraded')
Expand Down
Loading