diff --git a/contracts/InterchainTokenFactory.sol b/contracts/InterchainTokenFactory.sol index 51c0e439..238fab0e 100644 --- a/contracts/InterchainTokenFactory.sol +++ b/contracts/InterchainTokenFactory.sol @@ -402,16 +402,24 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M * @return tokenId The tokenId corresponding to the registered canonical token. */ function registerCanonicalInterchainToken(address tokenAddress) external payable returns (bytes32 tokenId) { - bytes memory params = abi.encode('', tokenAddress); bytes32 deploySalt = canonicalInterchainTokenDeploySalt(tokenAddress); string memory currentChain = ''; + // No custom operator is set for canonical token registration + bytes memory linkParams = ''; uint256 gasValue = 0; // Ensure that the ERC20 token has metadata before registering it // slither-disable-next-line unused-return _getTokenMetadata(tokenAddress); - tokenId = interchainTokenService.deployTokenManager(deploySalt, currentChain, TokenManagerType.LOCK_UNLOCK, params, gasValue); + tokenId = interchainTokenService.linkToken( + deploySalt, + currentChain, + tokenAddress.toBytes(), + TokenManagerType.LOCK_UNLOCK, + linkParams, + gasValue + ); } /** diff --git a/contracts/InterchainTokenService.sol b/contracts/InterchainTokenService.sol index 84053af1..7fdd4b7f 100644 --- a/contracts/InterchainTokenService.sol +++ b/contracts/InterchainTokenService.sol @@ -19,6 +19,7 @@ import { IInterchainTokenDeployer } from './interfaces/IInterchainTokenDeployer. import { IInterchainTokenExecutable } from './interfaces/IInterchainTokenExecutable.sol'; import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainTokenExpressExecutable.sol'; import { ITokenManager } from './interfaces/ITokenManager.sol'; +import { IERC20Named } from './interfaces/IERC20Named.sol'; import { IGatewayCaller } from './interfaces/IGatewayCaller.sol'; import { Create3AddressFixed } from './utils/Create3AddressFixed.sol'; import { Operator } from './utils/Operator.sol'; @@ -79,9 +80,11 @@ 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; + uint256 private constant MESSAGE_TYPE_REGISTER_TOKEN_METADATA = 6; /** * @dev Tokens and token managers deployed via the Token Factory contract use a special deployer address. @@ -280,29 +283,51 @@ contract InterchainTokenService is \************/ /** - * @notice Used to deploy remote custom TokenManagers. - * @dev At least the `gasValue` amount of native token must be passed to the function call. `gasValue` exists because this function can be - * part of a multicall involving multiple functions that could make remote contract calls. - * This method is temporarily restricted in the following scenarios: - * - Deploying to a remote chain and the destination chain is connected via ITS Hub - * - Deploying to the current chain, if connected as an Amplifier chain, i.e existing ITS contracts on consensus chains aren't affected. - * Once ITS Hub adds support for deploy token manager msg, the restriction will be lifted. - * Note that the factory contract can still call `deployTokenManager` to facilitate canonical token registration. - * @param salt The salt to be used during deployment. - * @param destinationChain The name of the chain to deploy the TokenManager and standardized token to. - * @param tokenManagerType The type of token manager to be deployed. Cannot be NATIVE_INTERCHAIN_TOKEN. - * @param params The params that will be used to initialize the TokenManager. - * @param gasValue The amount of native tokens to be used to pay for gas for the remote deployment. - * @return tokenId The tokenId corresponding to the deployed TokenManager. + * @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens. + * The token metadata must be registered before linkToken can be called for the corresponding token. + * @param tokenAddress The address of the token. + * @param gasValue The cross-chain gas value for sending the registration message to ITS Hub. + */ + function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable { + if (tokenAddress == address(0)) revert EmptyTokenAddress(); + + uint8 decimals = IERC20Named(tokenAddress).decimals(); + + bytes memory payload = abi.encode(MESSAGE_TYPE_REGISTER_TOKEN_METADATA, tokenAddress.toBytes(), decimals); + + emit TokenMetadataRegistered(tokenAddress, decimals); + + _callContract( + ITS_HUB_CHAIN_NAME, + trustedAddress(ITS_HUB_CHAIN_NAME), + payload, + IGatewayCaller.MetadataVersion.CONTRACT_CALL, + gasValue + ); + } + + /** + * @notice If `destinationChain` is an empty string, this function will register the token address on the current chain. + * Otherwise, it will link the token address on the destination chain with the token corresponding to the tokenId on the current chain. + * A token manager is deployed on EVM chains that's responsible for managing the linked token. + * @dev This function replaces the prior `deployTokenManager` function. + * @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 deployTokenManager( + function linkToken( bytes32 salt, string calldata destinationChain, + bytes calldata destinationTokenAddress, TokenManagerType tokenManagerType, - bytes calldata params, + bytes calldata linkParams, uint256 gasValue - ) external payable whenNotPaused returns (bytes32 tokenId) { - if (bytes(params).length == 0) revert EmptyParams(); + ) public payable whenNotPaused returns (bytes32 tokenId) { + if (destinationTokenAddress.length == 0) revert EmptyTokenAddress(); // 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); @@ -311,9 +336,10 @@ contract InterchainTokenService is if (deployer == interchainTokenFactory) { deployer = TOKEN_FACTORY_DEPLOYER; - } else if (bytes(destinationChain).length == 0 && trustedAddressHash(chainName()) == ITS_HUB_ROUTING_IDENTIFIER_HASH) { - // Restricted on ITS contracts deployed to Amplifier chains until ITS Hub adds support - revert NotSupported(); + } else if (bytes(destinationChain).length == 0) { + // TODO: Only support linking new tokens via ITS factory, to include chain name in token id derivation + // Custom token usage needs to be moved to ITS factory tests + // revert NotSupported(); } tokenId = interchainTokenId(deployer, salt); @@ -321,11 +347,11 @@ contract InterchainTokenService is emit InterchainTokenIdClaimed(tokenId, deployer, salt); if (bytes(destinationChain).length == 0) { - _deployTokenManager(tokenId, tokenManagerType, params); + _deployTokenManager(tokenId, tokenManagerType, destinationTokenAddress.toAddress(), linkParams); } else { if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); - _deployRemoteTokenManager(tokenId, destinationChain, gasValue, tokenManagerType, params); + _linkToken(tokenId, destinationChain, destinationTokenAddress, tokenManagerType, linkParams, gasValue); } } @@ -370,7 +396,7 @@ contract InterchainTokenService is if (bytes(destinationChain).length == 0) { address tokenAddress = _deployInterchainToken(tokenId, minter, name, symbol, decimals); - _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minter, tokenAddress)); + _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minter); } else { if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); @@ -731,15 +757,13 @@ contract InterchainTokenService is /** * @notice Processes a deploy token manager payload. */ - function _processDeployTokenManagerPayload(bytes memory payload) internal { - (, bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) = abi.decode( - payload, - (uint256, bytes32, TokenManagerType, bytes) - ); + function _processLinkTokenPayload(bytes memory payload) internal { + (, bytes32 tokenId, TokenManagerType tokenManagerType, , bytes memory destinationTokenAddress, bytes memory linkParams) = abi + .decode(payload, (uint256, bytes32, TokenManagerType, bytes, bytes, bytes)); if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); - _deployTokenManager(tokenId, tokenManagerType, params); + _deployTokenManager(tokenId, tokenManagerType, destinationTokenAddress.toAddress(), linkParams); } /** @@ -755,18 +779,18 @@ contract InterchainTokenService is tokenAddress = _deployInterchainToken(tokenId, minterBytes, name, symbol, decimals); - _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minterBytes, tokenAddress)); + _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minterBytes); } /** - * @notice Calls a contract on a specific destination chain with the given payload + * @notice Route the ITS message to the destination chain with the given payload * @dev This method also determines whether the ITS call should be routed via the ITS Hub. * If the `trustedAddress(destinationChain) == 'hub'`, then the call is wrapped and routed to the ITS Hub destination. * @param destinationChain The target chain where the contract will be called. * @param payload The data payload for the transaction. * @param gasValue The amount of gas to be paid for the transaction. */ - function _callContract( + function _routeMessage( string memory destinationChain, bytes memory payload, IGatewayCaller.MetadataVersion metadataVersion, @@ -776,6 +800,27 @@ contract InterchainTokenService is (destinationChain, destinationAddress, payload) = _getCallParams(destinationChain, payload); + _callContract(destinationChain, destinationAddress, payload, metadataVersion, gasValue); + } + + /** + * @notice Calls a contract on a destination chain via the gateway caller. + * @param destinationChain The chain where the contract will be called. + * @param destinationAddress The address of the contract to call. + * @param payload The data payload for the transaction. + * @param metadataVersion The version of the metadata. + * @param gasValue The amount of gas to be paid for the transaction. + */ + function _callContract( + string memory destinationChain, + string memory destinationAddress, + bytes memory payload, + IGatewayCaller.MetadataVersion metadataVersion, + uint256 gasValue + ) internal { + // Check whether no trusted address was set for the destination chain + if (bytes(destinationAddress).length == 0) revert UntrustedChain(); + (bool success, bytes memory returnData) = gatewayCaller.delegatecall( abi.encodeWithSelector( IGatewayCaller.callContract.selector, @@ -804,18 +849,12 @@ contract InterchainTokenService is // Check whether the ITS call should be routed via ITS hub for this destination chain if (keccak256(abi.encodePacked(destinationAddress)) == ITS_HUB_ROUTING_IDENTIFIER_HASH) { - // Prevent deploy token manager to be usable on ITS hub - if (_getMessageType(payload) == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) revert NotSupported(); - // Wrap ITS message in an ITS Hub message payload = abi.encode(MESSAGE_TYPE_SEND_TO_HUB, destinationChain, payload); destinationChain = ITS_HUB_CHAIN_NAME; destinationAddress = trustedAddress(ITS_HUB_CHAIN_NAME); } - // Check whether no trusted address was set for the destination chain - if (bytes(destinationAddress).length == 0) revert UntrustedChain(); - return (destinationChain, destinationAddress, payload); } @@ -833,10 +872,10 @@ contract InterchainTokenService is if (messageType == MESSAGE_TYPE_INTERCHAIN_TRANSFER) { address expressExecutor = _getExpressExecutorAndEmitEvent(commandId, sourceChain, sourceAddress, payloadHash); _processInterchainTransferPayload(commandId, expressExecutor, originalSourceChain, payload); - } else if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) { - _processDeployTokenManagerPayload(payload); } else if (messageType == MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) { _processDeployInterchainTokenPayload(payload); + } else if (messageType == MESSAGE_TYPE_LINK_TOKEN) { + _processLinkTokenPayload(payload); } else { revert InvalidMessageType(messageType); } @@ -875,9 +914,6 @@ contract InterchainTokenService is // Get message type of the inner ITS message messageType = _getMessageType(payload); - - // Prevent deploy token manager to be usable on ITS hub - if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) 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(); @@ -890,25 +926,34 @@ contract InterchainTokenService is * @notice Deploys a token manager on a destination chain. * @param tokenId The ID of the token. * @param destinationChain The chain where the token manager will be deployed. - * @param gasValue The amount of gas to be paid for the transaction. + * @param destinationTokenAddress The address of the token on the destination chain. * @param tokenManagerType The type of token manager to be deployed. - * @param params Additional parameters for the token manager deployment. + * @param params Additional parameters for the token linking. + * @param gasValue The amount of gas to be paid for the transaction. */ - function _deployRemoteTokenManager( + function _linkToken( bytes32 tokenId, string calldata destinationChain, - uint256 gasValue, + bytes calldata destinationTokenAddress, TokenManagerType tokenManagerType, - bytes calldata params + bytes calldata params, + uint256 gasValue ) internal { // slither-disable-next-line unused-return - deployedTokenManager(tokenId); + bytes memory sourceTokenAddress = registeredTokenAddress(tokenId).toBytes(); - emit TokenManagerDeploymentStarted(tokenId, destinationChain, tokenManagerType, params); + emit LinkTokenStarted(tokenId, destinationChain, sourceTokenAddress, destinationTokenAddress, tokenManagerType, params); - bytes memory payload = abi.encode(MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params); + bytes memory payload = abi.encode( + MESSAGE_TYPE_LINK_TOKEN, + tokenId, + tokenManagerType, + sourceTokenAddress, + destinationTokenAddress, + params + ); - _callContract(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); + _routeMessage(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); } /** @@ -941,16 +986,20 @@ contract InterchainTokenService is bytes memory payload = abi.encode(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, minter); - _callContract(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); + _routeMessage(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. - * @param params Additional parameters for the token manager deployment. + * @param tokenAddress The address of the token to be managed. + * @param operator The operator of the token manager. */ - function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) internal { + function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, address tokenAddress, bytes memory operator) internal { + // TokenManagerProxy params + bytes memory params = abi.encode(operator, tokenAddress); + (bool success, bytes memory returnData) = tokenManagerDeployer.delegatecall( abi.encodeWithSelector(ITokenManagerDeployer.deployTokenManager.selector, tokenId, tokenManagerType, params) ); @@ -1080,7 +1129,7 @@ contract InterchainTokenService is data ); - _callContract(destinationChain, payload, metadataVersion, gasValue); + _routeMessage(destinationChain, payload, metadataVersion, gasValue); } /** diff --git a/contracts/interfaces/IInterchainTokenService.sol b/contracts/interfaces/IInterchainTokenService.sol index f6aef9cd..7cc70e2c 100644 --- a/contracts/interfaces/IInterchainTokenService.sol +++ b/contracts/interfaces/IInterchainTokenService.sol @@ -53,6 +53,7 @@ interface IInterchainTokenService is error EmptyTokenSymbol(); error EmptyParams(); error EmptyDestinationAddress(); + error EmptyTokenAddress(); error NotSupported(); event InterchainTransfer( @@ -72,9 +73,12 @@ interface IInterchainTokenService is uint256 amount, bytes32 dataHash ); - event TokenManagerDeploymentStarted( + event TokenMetadataRegistered(address indexed tokenAddress, uint8 decimals); + event LinkTokenStarted( bytes32 indexed tokenId, string destinationChain, + bytes sourceTokenAddress, + bytes destinationTokenAddress, TokenManagerType indexed tokenManagerType, bytes params ); @@ -170,19 +174,32 @@ interface IInterchainTokenService is function interchainTokenId(address operator_, bytes32 salt) external view returns (bytes32 tokenId); /** - * @notice Deploys a custom token manager contract on a remote chain. - * @param salt The salt used for token manager deployment. - * @param destinationChain The name of the destination chain. - * @param tokenManagerType The type of token manager. Cannot be NATIVE_INTERCHAIN_TOKEN. - * @param params The deployment parameters. - * @param gasValue The gas value for deployment. + * @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens. + * The token metadata must be registered before linkToken can be called for the corresponding token. + * @param tokenAddress The address of the token. + * @param gasValue The cross-chain gas value for sending the registration message to ITS Hub. + */ + function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable; + + /** + * @notice If `destinationChain` is an empty string, this function will register the token address on the current chain. + * Otherwise, it will link the token address on the destination chain with the token corresponding to the tokenId on the current chain. + * A token manager is deployed on EVM chains that's responsible for managing the linked token. + * @dev This function replaces the prior `deployTokenManager` function. + * @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 deployTokenManager( + function linkToken( bytes32 salt, string calldata destinationChain, + bytes memory destinationTokenAddress, TokenManagerType tokenManagerType, - bytes calldata params, + bytes memory linkParams, uint256 gasValue ) external payable returns (bytes32 tokenId); diff --git a/hardhat.config.js b/hardhat.config.js index 562c8142..992fe52b 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -45,6 +45,16 @@ const compilerSettings = { optimizer: optimizerSettings, }, }; +const itsCompilerSettings = { + version: '0.8.27', + settings: { + evmVersion: process.env.EVM_VERSION || 'london', + optimizer: { + ...optimizerSettings, + runs: 100, + }, + }, +}; /** * @type import('hardhat/config').HardhatUserConfig @@ -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': itsCompilerSettings, + 'contracts/InterchainTokenService.sol': itsCompilerSettings, }, }, defaultNetwork: 'hardhat', diff --git a/scripts/deploy.js b/scripts/deploy.js index a1db9206..aa29904c 100644 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -42,7 +42,6 @@ async function deployInterchainTokenService( operatorAddress = wallet.address, ) { const interchainTokenServiceAddress = await getCreate3Address(create3DeployerAddress, wallet, deploymentKey); - const implementation = await deployContract(wallet, 'InterchainTokenService', [ tokenManagerDeployerAddress, interchainTokenDeployerAddress, diff --git a/test/InterchainTokenService.js b/test/InterchainTokenService.js index 2cb75946..ab47c5e0 100644 --- a/test/InterchainTokenService.js +++ b/test/InterchainTokenService.js @@ -19,6 +19,7 @@ const { MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, MESSAGE_TYPE_RECEIVE_FROM_HUB, + MESSAGE_TYPE_LINK_TOKEN, INVALID_MESSAGE_TYPE, NATIVE_INTERCHAIN_TOKEN, MINT_BURN_FROM, @@ -30,6 +31,7 @@ const { ITS_HUB_CHAIN_NAME, ITS_HUB_ROUTING_IDENTIFIER, ITS_HUB_ADDRESS, + MESSAGE_TYPE_REGISTER_TOKEN_METADATA, } = require('./constants'); const reportGas = gasReporter('Interchain Token Service'); @@ -67,9 +69,8 @@ describe('Interchain Token Service', () => { service.address, tokenId, ]); - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - await service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0).then((tx) => tx.wait); + await service.linkToken(salt, '', token.address, LOCK_UNLOCK, wallet.address, 0).then((tx) => tx.wait); if (mintAmount > 0) { await token.mint(wallet.address, mintAmount).then((tx) => tx.wait); @@ -120,9 +121,7 @@ describe('Interchain Token Service', () => { ]); } - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - - await service.deployTokenManager(salt, '', LOCK_UNLOCK_FEE_ON_TRANSFER, params, 0).then((tx) => tx.wait); + await service.linkToken(salt, '', token.address, LOCK_UNLOCK_FEE_ON_TRANSFER, wallet.address, 0).then((tx) => tx.wait); if (mintAmount > 0) { await token.mint(wallet.address, mintAmount).then((tx) => tx.wait); @@ -155,8 +154,7 @@ describe('Interchain Token Service', () => { await token.transferMintership(tokenManager.address).then((tx) => tx.wait); - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - await service.deployTokenManager(salt, '', type, params, 0).then((tx) => tx.wait); + await service.linkToken(salt, '', token.address, type, wallet.address, 0).then((tx) => tx.wait); return [token, tokenManager, tokenId]; }; @@ -542,15 +540,7 @@ describe('Interchain Token Service', () => { const salt = getRandomBytes32(); await expectRevert( - (gasOptions) => - serviceTest.deployTokenManager( - salt, - chainName, - LOCK_UNLOCK, - defaultAbiCoder.encode(['bytes', 'address'], ['0x', testToken.address]), - 0, - gasOptions, - ), + (gasOptions) => serviceTest.linkToken(salt, chainName, testToken.address, LOCK_UNLOCK, '0x', 0, gasOptions), serviceTest, 'CannotDeployRemotelyToSelf', ); @@ -721,9 +711,7 @@ describe('Interchain Token Service', () => { before(async () => { salt = getRandomBytes32(); - await service - .deployTokenManager(salt, '', LOCK_UNLOCK, defaultAbiCoder.encode(['bytes', 'address'], ['0x', testToken.address]), 0) - .then((tx) => tx.wait); + await service.linkToken(salt, '', testToken.address, LOCK_UNLOCK, '0x', 0).then((tx) => tx.wait); }); it('Should initialize a remote interchain token deployment', async () => { @@ -854,6 +842,39 @@ describe('Interchain Token Service', () => { }); }); + describe('Register Token Metadata', () => { + const decimals = 18; + let token; + + before(async () => { + token = await deployContract(wallet, 'TestInterchainTokenStandard', ['Test', 'TEST', decimals, service.address, HashZero]); + }); + + it('Should revert on registering token metadata with empty token address', async () => { + await expectRevert((gasOptions) => service.registerTokenMetadata(AddressZero, 0, gasOptions), service, 'EmptyTokenAddress'); + }); + + it('Should revert if ITS Hub chain is not trusted', async () => { + await expectRevert((gasOptions) => service.registerTokenMetadata(token.address, 0, gasOptions), service, 'UntrustedChain'); + }); + + it('Should successfully register token metadata', async () => { + const gasValue = 0; + const expectedPayload = defaultAbiCoder.encode( + ['uint256', 'bytes', 'uint8'], + [MESSAGE_TYPE_REGISTER_TOKEN_METADATA, token.address, decimals], + ); + + await service.setTrustedAddress(ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS).then((tx) => tx.wait); + + await expect(reportGas(service.registerTokenMetadata(token.address, gasValue), 'registerTokenMetadata')) + .to.emit(service, 'TokenMetadataRegistered') + .withArgs(token.address, decimals) + .to.emit(gateway, 'ContractCall') + .withArgs(service.address, ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS, keccak256(expectedPayload), expectedPayload); + }); + }); + describe('Custom Token Manager Deployment', () => { const tokenName = 'Token Name'; const tokenSymbol = 'TN'; @@ -874,35 +895,38 @@ describe('Interchain Token Service', () => { }); it('Should revert on deploying an invalid token manager', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - - await expectRevert((gasOptions) => service.deployTokenManager(salt, '', 6, params, 0, gasOptions)); + await expectRevert((gasOptions) => service.linkToken(salt, '', token.address, 6, wallet.address, 0, gasOptions)); }); it('Should revert on deploying a local token manager with invalid params', async () => { await expectRevert( - (gasOptions) => service.deployTokenManager(salt, '', NATIVE_INTERCHAIN_TOKEN, '0x', 0, gasOptions), + (gasOptions) => service.linkToken(salt, '', token.address, NATIVE_INTERCHAIN_TOKEN, '0x', 0, gasOptions), service, - 'EmptyParams', + 'CannotDeploy', ); }); it('Should revert on deploying a local token manager with interchain token manager type', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - await expectRevert( - (gasOptions) => service.deployTokenManager(salt, '', NATIVE_INTERCHAIN_TOKEN, params, 0, gasOptions), + (gasOptions) => service.linkToken(salt, '', token.address, NATIVE_INTERCHAIN_TOKEN, wallet.address, 0, gasOptions), service, 'CannotDeploy', [NATIVE_INTERCHAIN_TOKEN], ); }); - it('Should revert on deploying a remote token manager with interchain token manager type', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); + it('Should revert on linking a token with empty token address', async () => { + await expectRevert( + (gasOptions) => service.linkToken(salt, '', '0x', MINT_BURN, wallet.address, 0, gasOptions), + service, + 'EmptyTokenAddress', + ); + }); + it('Should revert on deploying a remote token manager with interchain token manager type', async () => { await expectRevert( - (gasOptions) => service.deployTokenManager(salt, destinationChain, NATIVE_INTERCHAIN_TOKEN, params, 0, gasOptions), + (gasOptions) => + service.linkToken(salt, destinationChain, token.address, NATIVE_INTERCHAIN_TOKEN, wallet.address, 0, gasOptions), service, 'CannotDeploy', [NATIVE_INTERCHAIN_TOKEN], @@ -910,10 +934,8 @@ describe('Interchain Token Service', () => { }); it('Should revert on deploying a token manager if token handler post deploy fails', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, AddressZero]); - await expectRevert( - (gasOptions) => service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0, gasOptions), + (gasOptions) => service.linkToken(salt, '', AddressZero, LOCK_UNLOCK, wallet.address, 0, gasOptions), service, 'PostDeployFailed', ); @@ -923,7 +945,12 @@ describe('Interchain Token Service', () => { const tokenManagerAddress = await service.tokenManagerAddress(tokenId); const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - await expect(reportGas(service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0), 'Call deployTokenManager on source chain')) + await expect( + reportGas( + service.linkToken(salt, '', token.address, LOCK_UNLOCK, wallet.address, 0), + 'Call deployTokenManager on source chain', + ), + ) .to.emit(service, 'InterchainTokenIdClaimed') .withArgs(tokenId, wallet.address, salt) .to.emit(service, 'TokenManagerDeployed') @@ -948,10 +975,9 @@ describe('Interchain Token Service', () => { }); it('Should revert when deploying a custom token manager twice', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); const revertData = keccak256(toUtf8Bytes('AlreadyDeployed()')).substring(0, 10); await expectRevert( - (gasOptions) => service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0, gasOptions), + (gasOptions) => service.linkToken(salt, '', token.address, LOCK_UNLOCK, wallet.address, 0, gasOptions), service, 'TokenManagerDeploymentFailed', [revertData], @@ -987,7 +1013,7 @@ describe('Interchain Token Service', () => { ]); const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - const tx = service.deployTokenManager(salt, '', MINT_BURN, params, 0); + const tx = service.linkToken(salt, '', token.address, MINT_BURN, wallet.address, 0); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); await expect(tx).to.emit(service, 'TokenManagerDeployed').withArgs(tokenId, expectedTokenManagerAddress, MINT_BURN, params); @@ -1022,7 +1048,7 @@ describe('Interchain Token Service', () => { ]); const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - const tx = service.deployTokenManager(salt, '', MINT_BURN_FROM, params, 0); + const tx = service.linkToken(salt, '', token.address, MINT_BURN_FROM, wallet.address, 0); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); await expect(tx) .to.emit(service, 'TokenManagerDeployed') @@ -1059,7 +1085,7 @@ describe('Interchain Token Service', () => { ]); const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - const tx = service.deployTokenManager(salt, '', LOCK_UNLOCK_FEE_ON_TRANSFER, params, 0); + const tx = service.linkToken(salt, '', token.address, LOCK_UNLOCK_FEE_ON_TRANSFER, wallet.address, 0); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); await expect(tx) .to.emit(service, 'TokenManagerDeployed') @@ -1086,50 +1112,30 @@ describe('Interchain Token Service', () => { it('Should revert when deploying a custom token manager if paused', async () => { await service.setPauseStatus(true).then((tx) => tx.wait); - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - - await expectRevert((gasOptions) => service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0, gasOptions), service, 'Pause'); - - await service.setPauseStatus(false).then((tx) => tx.wait); - }); - - it('Should revert with NotSupported when deploying a token manager on its hub chain', async () => { - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - - await expect(service.setTrustedAddress(chainName, ITS_HUB_ROUTING_IDENTIFIER)) - .to.emit(service, 'TrustedAddressSet') - .withArgs(chainName, ITS_HUB_ROUTING_IDENTIFIER); - await expectRevert( - (gasOptions) => service.deployTokenManager(salt, '', LOCK_UNLOCK, params, 0, gasOptions), + (gasOptions) => service.linkToken(salt, '', token.address, LOCK_UNLOCK, wallet.address, 0, gasOptions), service, - 'NotSupported', + 'Pause', ); - await expect(service.removeTrustedAddress(chainName)).to.emit(service, 'TrustedAddressRemoved').withArgs(chainName); + await service.setPauseStatus(false).then((tx) => tx.wait); }); }); describe('Initialize remote custom token manager deployment', () => { it('Should initialize a remote custom token manager deployment', async () => { const salt = getRandomBytes32(); + const tokenAddress = wallet.address; - await ( - await service.deployTokenManager( - salt, - '', - MINT_BURN, - defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), - 0, - ) - ).wait(); + await service.linkToken(salt, '', tokenAddress, MINT_BURN, '0x', 0).then((tx) => tx.wait); const tokenId = await service.interchainTokenId(wallet.address, salt); - const params = '0x1234'; + const remoteTokenAddress = '0x1234'; + const minter = '0x5789'; const type = LOCK_UNLOCK; const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, type, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [MESSAGE_TYPE_LINK_TOKEN, tokenId, type, tokenAddress, remoteTokenAddress, minter], ); const tokenManager = await getContractAt('TokenManager', await service.deployedTokenManager(tokenId), wallet); @@ -1140,14 +1146,21 @@ describe('Interchain Token Service', () => { await expect( reportGas( - service.deployTokenManager(salt, destinationChain, type, params, gasValue, { value: gasValue }), + service.linkToken(salt, destinationChain, remoteTokenAddress, type, minter, gasValue, { value: gasValue }), 'Send deployTokenManager to remote chain', ), ) .to.emit(service, 'InterchainTokenIdClaimed') .withArgs(tokenId, wallet.address, salt) - .to.emit(service, 'TokenManagerDeploymentStarted') - .withArgs(tokenId, destinationChain, type, params) + .to.emit(service, 'LinkTokenStarted') + .withArgs( + tokenId, + destinationChain, + tokenAddress.toLowerCase(), + remoteTokenAddress.toLowerCase(), + type, + minter.toLowerCase(), + ) .and.to.emit(gasService, 'NativeGasPaidForContractCall') .withArgs(service.address, destinationChain, service.address, keccak256(payload), gasValue, wallet.address) .and.to.emit(gateway, 'ContractCall') @@ -1157,11 +1170,12 @@ describe('Interchain Token Service', () => { it('Should revert on a remote custom token manager deployment if the token manager does does not exist', async () => { const salt = getRandomBytes32(); const tokenId = await service.interchainTokenId(wallet.address, salt); - const params = '0x1234'; + const tokenAddress = '0x1234'; + const minter = '0x5678'; const type = LOCK_UNLOCK; await expect( - service.deployTokenManager(salt, destinationChain, type, params, gasValue, { value: gasValue }), + service.linkToken(salt, destinationChain, tokenAddress, type, minter, gasValue, { value: gasValue }), ).to.be.revertedWithCustomError(service, 'TokenManagerDoesNotExist', [tokenId]); }); @@ -1169,12 +1183,13 @@ describe('Interchain Token Service', () => { await service.setPauseStatus(true).then((tx) => tx.wait); const salt = getRandomBytes32(); - const params = '0x1234'; + const tokenAddress = '0x1234'; + const minter = '0x5678'; const type = LOCK_UNLOCK; await expectRevert( (gasOptions) => - service.deployTokenManager(salt, destinationChain, type, params, gasValue, { + service.linkToken(salt, destinationChain, tokenAddress, type, minter, gasValue, { ...gasOptions, value: gasValue, }), @@ -1184,34 +1199,6 @@ describe('Interchain Token Service', () => { await service.setPauseStatus(false).then((tx) => tx.wait); }); - - it('Should revert with NotSupported on deploying a remote custom token manager via its hub', async () => { - const salt = getRandomBytes32(); - - await ( - await service.deployTokenManager( - salt, - '', - MINT_BURN, - defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), - 0, - ) - ).wait(); - - const params = '0x1234'; - const type = LOCK_UNLOCK; - const destinationChainItsHub = 'hub chain 1'; - - await expect(service.setTrustedAddress(destinationChainItsHub, ITS_HUB_ROUTING_IDENTIFIER)) - .to.emit(service, 'TrustedAddressSet') - .withArgs(destinationChainItsHub, ITS_HUB_ROUTING_IDENTIFIER); - - await expectRevert( - (gasOptions) => service.deployTokenManager(salt, destinationChainItsHub, type, params, gasValue, gasOptions), - service, - 'NotSupported', - ); - }); }); describe('Receive Remote Token Manager Deployment', () => { @@ -1227,6 +1214,10 @@ describe('Interchain Token Service', () => { it('Should be able to receive a remote lock/unlock token manager deployment', async () => { const tokenId = getRandomBytes32(); const tokenManagerAddress = await service.tokenManagerAddress(tokenId); + const tokenManagerType = LOCK_UNLOCK; + const sourceTokenAddress = '0x1234'; + const minter = wallet.address; + const token = await deployContract(wallet, 'TestInterchainTokenStandard', [ tokenName, tokenSymbol, @@ -1237,15 +1228,15 @@ describe('Interchain Token Service', () => { const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, LOCK_UNLOCK, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [MESSAGE_TYPE_LINK_TOKEN, tokenId, tokenManagerType, sourceTokenAddress, token.address, minter], ); const commandId = await approveContractCall(gateway, sourceChain, sourceAddress, service.address, payload); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); await expect(reportGas(service.execute(commandId, sourceChain, sourceAddress, payload), 'Receive GMP DEPLOY_TOKEN_MANAGER')) .to.emit(service, 'TokenManagerDeployed') - .withArgs(tokenId, expectedTokenManagerAddress, LOCK_UNLOCK, params); + .withArgs(tokenId, expectedTokenManagerAddress, tokenManagerType, params); const tokenManager = await getContractAt('TokenManager', tokenManagerAddress, wallet); expect(await tokenManager.tokenAddress()).to.equal(token.address); @@ -1255,6 +1246,10 @@ describe('Interchain Token Service', () => { it('Should be able to receive a remote mint/burn token manager deployment', async () => { const tokenId = getRandomBytes32(); const tokenManagerAddress = await service.tokenManagerAddress(tokenId); + const tokenManagerType = MINT_BURN; + const sourceTokenAddress = '0x1234'; + const minter = wallet.address; + const token = await deployContract(wallet, 'TestInterchainTokenStandard', [ tokenName, tokenSymbol, @@ -1265,15 +1260,15 @@ describe('Interchain Token Service', () => { const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, MINT_BURN, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [MESSAGE_TYPE_LINK_TOKEN, tokenId, tokenManagerType, sourceTokenAddress, token.address, minter], ); const commandId = await approveContractCall(gateway, sourceChain, sourceAddress, service.address, payload); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); await expect(service.execute(commandId, sourceChain, sourceAddress, payload)) .to.emit(service, 'TokenManagerDeployed') - .withArgs(tokenId, expectedTokenManagerAddress, MINT_BURN, params); + .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; @@ -1281,6 +1276,10 @@ describe('Interchain Token Service', () => { it('Should not be able to receive a remote interchain token manager deployment', async () => { const tokenId = getRandomBytes32(); + const tokenManagerType = NATIVE_INTERCHAIN_TOKEN; + const sourceTokenAddress = '0x1234'; + const minter = wallet.address; + const token = await deployContract(wallet, 'TestInterchainTokenStandard', [ tokenName, tokenSymbol, @@ -1289,10 +1288,9 @@ describe('Interchain Token Service', () => { tokenId, ]); - const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, NATIVE_INTERCHAIN_TOKEN, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [MESSAGE_TYPE_LINK_TOKEN, tokenId, tokenManagerType, sourceTokenAddress, token.address, minter], ); const commandId = await approveContractCall(gateway, sourceChain, sourceAddress, service.address, payload); @@ -1346,15 +1344,7 @@ describe('Interchain Token Service', () => { }); it('Should revert on initiating an interchain token transfer for lockUnlockFee with reentrant token', async () => { - const [, , tokenId] = await deployFunctions.lockUnlockFee( - service, - 'Test Token lockUnlockFee', - 'TT', - 12, - amount, - false, - 'reentrant', - ); + const [, , tokenId] = await deployFunctions.lockUnlockFee(service, 'Test Token lockUnlockFee', 'TT', 12, amount, 'reentrant'); const revertData = keccak256(toUtf8Bytes('TokenTransferFailed()')).substring(0, 10); @@ -2160,42 +2150,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(); + 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.deployTokenManager( - salt, - '', - MINT_BURN, - defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), - 0, - ) - ).wait(); + const token = await deployContract(wallet, 'TestInterchainTokenStandard', [ + tokenName, + tokenSymbol, + tokenDecimals, + service.address, + tokenId, + ]); - const tokenId = await service.interchainTokenId(wallet.address, salt); - const params = '0x1234'; + 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 remoteTokenAddress = '0x1234'; const type = LOCK_UNLOCK; const sourceChain = 'hub chain 1'; const itsMessage = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, type, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [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 () => { diff --git a/test/InterchainTokenServiceFullFlow.js b/test/InterchainTokenServiceFullFlow.js index 2d177f28..3dbf4ac2 100644 --- a/test/InterchainTokenServiceFullFlow.js +++ b/test/InterchainTokenServiceFullFlow.js @@ -18,7 +18,7 @@ const { approveContractCall } = require('../scripts/utils'); const { MESSAGE_TYPE_INTERCHAIN_TRANSFER, MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, - MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, + MESSAGE_TYPE_LINK_TOKEN, MESSAGE_TYPE_SEND_TO_HUB, MESSAGE_TYPE_RECEIVE_FROM_HUB, NATIVE_INTERCHAIN_TOKEN, @@ -355,7 +355,7 @@ describe('Interchain Token Service Full Flow', () => { const tokenManagerImplementation = await getContractAt('TokenManager', tokenManagerImplementationAddress, wallet); const params = await tokenManagerImplementation.params(wallet.address, token.address); - let tx = await service.populateTransaction.deployTokenManager(salt, '', MINT_BURN, params, 0); + let tx = await service.populateTransaction.linkToken(salt, '', token.address, MINT_BURN, wallet.address, 0); const calls = [tx.data]; let value = 0; @@ -363,29 +363,50 @@ describe('Interchain Token Service Full Flow', () => { for (const i in otherChains) { // This should be replaced with the existing token address on each chain being linked const remoteTokenAddress = token.address; - const params = await tokenManagerImplementation.params(wallet.address, remoteTokenAddress); - tx = await service.populateTransaction.deployTokenManager(salt, otherChains[i], MINT_BURN, params, gasValues[i]); + tx = await service.populateTransaction.linkToken( + salt, + otherChains[i], + remoteTokenAddress, + MINT_BURN, + wallet.address, + gasValues[i], + ); calls.push(tx.data); value += gasValues[i]; } const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'uint256', 'bytes'], - [MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, MINT_BURN, params], + ['uint256', 'bytes32', 'uint256', 'bytes', 'bytes', 'bytes'], + [MESSAGE_TYPE_LINK_TOKEN, tokenId, MINT_BURN, token.address, token.address, wallet.address], ); const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId); + await expect(service.multicall(calls, { value })) .to.emit(service, 'TokenManagerDeployed') .withArgs(tokenId, expectedTokenManagerAddress, MINT_BURN, params) - .and.to.emit(service, 'TokenManagerDeploymentStarted') - .withArgs(tokenId, otherChains[0], MINT_BURN, params) + .and.to.emit(service, 'LinkTokenStarted') + .withArgs( + tokenId, + otherChains[0], + token.address.toLowerCase(), + token.address.toLowerCase(), + MINT_BURN, + wallet.address.toLowerCase(), + ) .and.to.emit(gasService, 'NativeGasPaidForContractCall') .withArgs(service.address, otherChains[0], service.address, keccak256(payload), gasValues[0], wallet.address) .and.to.emit(gateway, 'ContractCall') .withArgs(service.address, otherChains[0], service.address, keccak256(payload), payload) - .and.to.emit(service, 'TokenManagerDeploymentStarted') - .withArgs(tokenId, otherChains[1], MINT_BURN, params) + .and.to.emit(service, 'LinkTokenStarted') + .withArgs( + tokenId, + otherChains[1], + token.address.toLowerCase(), + token.address.toLowerCase(), + MINT_BURN, + wallet.address.toLowerCase(), + ) .and.to.emit(gasService, 'NativeGasPaidForContractCall') .withArgs(service.address, otherChains[1], service.address, keccak256(payload), gasValues[1], wallet.address) .and.to.emit(gateway, 'ContractCall') diff --git a/test/InterchainTokenServiceUpgradeFlow.js b/test/InterchainTokenServiceUpgradeFlow.js index ce5a2965..69befd4e 100644 --- a/test/InterchainTokenServiceUpgradeFlow.js +++ b/test/InterchainTokenServiceUpgradeFlow.js @@ -52,7 +52,7 @@ describe('Interchain Token Service Upgrade Flow', () => { ]); const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]); - await expect(service.deployTokenManager(salt, '', MINT_BURN, params, 0)) + await expect(service.linkToken(salt, '', token.address, MINT_BURN, wallet.address, 0)) .to.emit(service, 'TokenManagerDeployed') .withArgs(tokenId, tokenManager.address, MINT_BURN, params); } diff --git a/test/constants.js b/test/constants.js index a4fef332..ccc90864 100644 --- a/test/constants.js +++ b/test/constants.js @@ -5,7 +5,9 @@ const MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN = 1; const MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2; const MESSAGE_TYPE_SEND_TO_HUB = 3; const MESSAGE_TYPE_RECEIVE_FROM_HUB = 4; -const INVALID_MESSAGE_TYPE = 5; +const MESSAGE_TYPE_LINK_TOKEN = 5; +const MESSAGE_TYPE_REGISTER_TOKEN_METADATA = 6; +const INVALID_MESSAGE_TYPE = 7; const NATIVE_INTERCHAIN_TOKEN = 0; const MINT_BURN_FROM = 1; @@ -28,6 +30,8 @@ module.exports = { MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, MESSAGE_TYPE_SEND_TO_HUB, MESSAGE_TYPE_RECEIVE_FROM_HUB, + MESSAGE_TYPE_LINK_TOKEN, + MESSAGE_TYPE_REGISTER_TOKEN_METADATA, INVALID_MESSAGE_TYPE, NATIVE_INTERCHAIN_TOKEN, MINT_BURN_FROM,