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

feat: rename deployTokenManager to linkToken and change the message type for deploying remote token managers #318

Merged
merged 15 commits into from
Jan 13, 2025
29 changes: 18 additions & 11 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ contract InterchainTokenService is

uint256 private constant MESSAGE_TYPE_INTERCHAIN_TRANSFER = 0;
uint256 private constant MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN = 1;
//uint256 private constant MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2;
// uint256 private constant MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2;
uint256 private constant MESSAGE_TYPE_SEND_TO_HUB = 3;
uint256 private constant MESSAGE_TYPE_RECEIVE_FROM_HUB = 4;
uint256 private constant MESSAGE_TYPE_LINK_TOKEN = 5;
Expand Down Expand Up @@ -312,23 +312,33 @@ contract InterchainTokenService is
if (!success) revert GatewayCallFailed(returnData);
}

/**
* @notice This replaces the old deployTokenManager function.
* It can either deploy token managers on this chain, if an empty string is provided as the destinationChain, or link an existing token registered to another chain.
* @param salt A unique identifier to allow for multiple tokens registered per deployer.
* @param destinationChain The chain to link the token to. Pass an empty string for this chain.
* @param destinationTokenAddress The token address to link, as bytes.
* @param tokenManagerType The type of the token manager to use to send and receive tokens.
* @param linkParams Additional parameteres to use to link the token. Fow not it is just the address of the operator.
* @param gasValue Pass a non-zero value only for remote linking, which should be the gas to use to pay for the contract call.
* @return tokenId The tokenId associated with the token manager.
*/
function linkToken(
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
bytes32 salt,
string calldata destinationChain,
bytes memory destinationTokenAddress,
bytes calldata destinationTokenAddress,
TokenManagerType tokenManagerType,
bytes memory linkParams,
bytes calldata linkParams,
uint256 gasValue
) public payable whenNotPaused returns (bytes32 tokenId) {
if (destinationTokenAddress.length == 0) revert EmptyDestinationAddress();

// TODO: Should we only allow mint/burn or lock/unlock for remote linking for simplicity? Makes it easier for external chains
// Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType);

address deployer = msg.sender;

if (msg.sender == interchainTokenFactory) {
if (deployer == interchainTokenFactory) {
deployer = TOKEN_FACTORY_DEPLOYER;
} else if (bytes(destinationChain).length == 0) {
// TODO: Only support linking new tokens via ITS factory, to include chain name in token id derivation
Expand Down Expand Up @@ -890,9 +900,6 @@ contract InterchainTokenService is

// Get message type of the inner ITS message
messageType = _getMessageType(payload);

// Prevent link token to be usable on ITS HUB.
if (messageType == MESSAGE_TYPE_LINK_TOKEN) revert NotSupported();
} else {
// Prevent receiving a direct message from the ITS Hub. This is not supported yet.
if (keccak256(abi.encodePacked(sourceChain)) == ITS_HUB_CHAIN_NAME_HASH) revert UntrustedChain();
Expand All @@ -913,9 +920,9 @@ contract InterchainTokenService is
function _linkToken(
bytes32 tokenId,
string calldata destinationChain,
bytes memory destinationTokenAddress,
bytes calldata destinationTokenAddress,
TokenManagerType tokenManagerType,
bytes memory params,
bytes calldata params,
uint256 gasValue
) internal {
// slither-disable-next-line unused-return
Expand Down Expand Up @@ -968,7 +975,7 @@ contract InterchainTokenService is
_callContract(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue);
}

/*
/**
* @notice Deploys a token manager.
* @param tokenId The ID of the token.
* @param tokenManagerType The type of the token manager to be deployed.
Expand Down
15 changes: 8 additions & 7 deletions contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,14 @@ interface IInterchainTokenService is
function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable;

/**
* @notice Links a source token to a destination token on a remote chain.
* @param salt The salt used for token manager deployment.
* @param destinationChain The name of the destination chain.
* @param destinationTokenAddress The address of the token on the destination chain.
* @param tokenManagerType The type of token manager. Cannot be NATIVE_INTERCHAIN_TOKEN.
* @param linkParams The link parameters.
* @param gasValue The gas value for deployment.
* @notice This replaces the old deployTokenManager function.
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
* It can either deploy token managers on this chain, if an empty string is provided as the destinationChain, or link an existing token registered to another chain.
* @param salt A unique identifier to allow for multiple tokens registered per deployer.
* @param destinationChain The chain to link the token to. Pass an empty string for this chain.
* @param destinationTokenAddress The token address to link, as bytes.
* @param tokenManagerType The type of the token manager to use to send and receive tokens.
* @param linkParams Additional parameteres to use to link the token. Fow not it is just the address of the operator.
* @param gasValue Pass a non-zero value only for remote linking, which should be the gas to use to pay for the contract call.
* @return tokenId The tokenId associated with the token manager.
*/
function linkToken(
Expand Down
13 changes: 12 additions & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ const compilerSettings = {
optimizer: optimizerSettings,
},
};
const itsCompilerService = {
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
version: '0.8.21',
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
settings: {
evmVersion: process.env.EVM_VERSION || 'london',
optimizer: {
...optimizerSettings,
runs: 100,
},
},
};

/**
* @type import('hardhat/config').HardhatUserConfig
Expand All @@ -59,7 +69,8 @@ module.exports = {
'contracts/proxies/InterchainProxy.sol': fixedContractCompilerSettings,
'contracts/proxies/TokenManagerProxy.sol': fixedContractCompilerSettings,
'contracts/interchain-token/InterchainToken.sol': fixedContractCompilerSettings,
'contracts/test/TestInterchainTokenService.sol': fixedContractCompilerSettings,
'contracts/test/TestInterchainTokenService.sol': itsCompilerService,
'contracts/InterchainTokenService.sol': itsCompilerService,
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
},
},
defaultNetwork: 'hardhat',
Expand Down
47 changes: 34 additions & 13 deletions test/InterchainTokenService.js
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ describe('Interchain Token Service', () => {
const salt = getRandomBytes32();
const tokenAddress = wallet.address;

await (await service.linkToken(salt, '', tokenAddress, MINT_BURN, '0x', 0)).wait();
await service.linkToken(salt, '', tokenAddress, MINT_BURN, '0x', 0).then((tx) => tx.wait);

const tokenId = await service.interchainTokenId(wallet.address, salt);
const remoteTokenAddress = '0x1234';
Expand Down Expand Up @@ -2108,36 +2108,57 @@ describe('Interchain Token Service', () => {
);
});

it('Should revert with NotSupported when the message type is RECEIVE_FROM_HUB and has MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER type.', async () => {
const salt = getRandomBytes32();
const tokenAddress = wallet.address;
it('Should receive a message wrapped with RECEIVE_FROM_HUB and has MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER type.', async () => {
const tokenName = 'Token Name';
const tokenSymbol = 'TS';
const tokenDecimals = 53;
const tokenId = getRandomBytes32();

await (await service.linkToken(salt, '', tokenAddress, MINT_BURN, wallet.address, 0)).wait();
const token = await deployContract(wallet, 'TestInterchainTokenStandard', [
tokenName,
tokenSymbol,
tokenDecimals,
service.address,
tokenId,
]);

const tokenManagerAddress = await service.tokenManagerAddress(tokenId);
const tokenManagerType = LOCK_UNLOCK;
const minter = wallet.address;

const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

const tokenId = await service.interchainTokenId(wallet.address, salt);
const remoteTokenAddress = '0x1234';
const minter = '0x5678';
const type = LOCK_UNLOCK;
const sourceChain = 'hub chain 1';
const itsMessage = defaultAbiCoder.encode(
['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'],
[MESSAGE_TYPE_LINK_TOKEN, tokenId, type, tokenAddress, remoteTokenAddress, minter],
[MESSAGE_TYPE_LINK_TOKEN, tokenId, type, remoteTokenAddress, token.address, minter],
);
const payload = defaultAbiCoder.encode(
['uint256', 'string', 'bytes'],
[MESSAGE_TYPE_RECEIVE_FROM_HUB, sourceChain, itsMessage],
);
const commandId = await approveContractCall(gateway, ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS, service.address, payload);
const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId);

await expect(service.setTrustedAddress(ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS))
.to.emit(service, 'TrustedAddressSet')
.withArgs(ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS);

await expect(service.setTrustedAddress(sourceChain, ITS_HUB_ROUTING_IDENTIFIER))
.to.emit(service, 'TrustedAddressSet')
.withArgs(sourceChain, ITS_HUB_ROUTING_IDENTIFIER);

await expectRevert(
(gasOptions) => service.execute(commandId, ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS, payload, gasOptions),
service,
'NotSupported',
);
await expect(
reportGas(service.execute(commandId, ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS, payload), 'Receive GMP DEPLOY_TOKEN_MANAGER'),
)
.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, expectedTokenManagerAddress, tokenManagerType, params);

const tokenManager = await getContractAt('TokenManager', tokenManagerAddress, wallet);
expect(await tokenManager.tokenAddress()).to.equal(token.address);
expect(await tokenManager.hasRole(wallet.address, OPERATOR_ROLE)).to.be.true;
});

it('Should revert with UntrustedChain when receiving a direct message from the ITS Hub. Not supported yet', async () => {
Expand Down