From a8338448bafd10446cf0b2acb7a7a020a15e5f84 Mon Sep 17 00:00:00 2001 From: Foivos Date: Thu, 9 Jan 2025 16:13:06 +0200 Subject: [PATCH 1/2] Update contracts/InterchainTokenService.sol Co-authored-by: Milap Sheth --- contracts/InterchainTokenService.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/InterchainTokenService.sol b/contracts/InterchainTokenService.sol index 9857b945..bf6f205e 100644 --- a/contracts/InterchainTokenService.sol +++ b/contracts/InterchainTokenService.sol @@ -658,7 +658,7 @@ contract InterchainTokenService is } /** - * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically. + * @notice Allows the owner to migrate minter of native interchain tokens from ITS to the corresponding token manager. * @param tokenId the tokenId of the registered token. */ function migrateInterchainToken(bytes32 tokenId) external onlyOwner { From 123c9c10abed4a8dbeb9a6c587b14577b2e1cce5 Mon Sep 17 00:00:00 2001 From: Foivos Date: Thu, 9 Jan 2025 17:24:29 +0200 Subject: [PATCH 2/2] removed migrate legacy token and added tests --- contracts/InterchainTokenService.sol | 13 ---- .../interfaces/IInterchainTokenService.sol | 8 --- test/InterchainTokenService.js | 67 +++++++++++++++++++ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/contracts/InterchainTokenService.sol b/contracts/InterchainTokenService.sol index bf6f205e..9cfaa047 100644 --- a/contracts/InterchainTokenService.sol +++ b/contracts/InterchainTokenService.sol @@ -644,19 +644,6 @@ contract InterchainTokenService is } } - /** - * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically. - * @param tokenId the tokenId of the registered token. - * @param data the data to pass to migrate the token. - */ - function migrateLegacyToken(bytes32 tokenId, bytes calldata data) external onlyOwner { - address tokenAddress = registeredTokenAddress(tokenId); - (bool success, bytes memory returnData) = tokenAddress.call(data); - if (!success) { - revert TokenMigrateFailed(returnData); - } - } - /** * @notice Allows the owner to migrate minter of native interchain tokens from ITS to the corresponding token manager. * @param tokenId the tokenId of the registered token. diff --git a/contracts/interfaces/IInterchainTokenService.sol b/contracts/interfaces/IInterchainTokenService.sol index e7474889..c10dffb7 100644 --- a/contracts/interfaces/IInterchainTokenService.sol +++ b/contracts/interfaces/IInterchainTokenService.sol @@ -54,7 +54,6 @@ interface IInterchainTokenService is error EmptyParams(); error EmptyDestinationAddress(); error NotSupported(); - error TokenMigrateFailed(bytes returnData); event InterchainTransfer( bytes32 indexed tokenId, @@ -276,13 +275,6 @@ interface IInterchainTokenService is */ function setPauseStatus(bool paused) external; - /** - * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically. - * @param tokenId the tokenId of the registered token. - * @param data the data to pass to migrate the token. - */ - function migrateLegacyToken(bytes32 tokenId, bytes calldata data) external; - /** * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically. * @param tokenId the tokenId of the registered token. diff --git a/test/InterchainTokenService.js b/test/InterchainTokenService.js index 401edf33..93eed02f 100644 --- a/test/InterchainTokenService.js +++ b/test/InterchainTokenService.js @@ -30,6 +30,7 @@ const { ITS_HUB_CHAIN_NAME, ITS_HUB_ROUTING_IDENTIFIER, ITS_HUB_ADDRESS, + MINTER_ROLE, } = require('./constants'); const reportGas = gasReporter('Interchain Token Service'); @@ -3235,6 +3236,72 @@ describe('Interchain Token Service', () => { }); }); + describe('Interchain Token Migration', () => { + it('Should migrate a token succesfully', async () => { + const salt = getRandomBytes32(); + const name = 'migrated token'; + const symbol = 'MT'; + const decimals = 53; + const tokenId = await service.interchainTokenId(wallet.address, salt); + const tokenManagerAddress = await service.tokenManagerAddress(tokenId); + + await interchainTokenDeployer.deployInterchainToken(salt, tokenId, service.address, name, symbol, decimals).then((tx) => tx.wait); + const tokenAddress = await interchainTokenDeployer.deployedAddress(salt); + const token = await getContractAt('InterchainToken', tokenAddress, wallet); + + const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, tokenAddress]); + + await service.deployTokenManager(salt, '', MINT_BURN, params, 0).then((tx) => tx.wait); + + await expect(service.migrateInterchainToken(tokenId)) + .to.emit(token, 'RolesRemoved') + .withArgs(service.address, 1 << MINTER_ROLE) + .to.emit(token, 'RolesAdded') + .withArgs(tokenManagerAddress, 1 << MINTER_ROLE); + }); + + it('Should not be able to migrate a token twice', async () => { + const salt = getRandomBytes32(); + const name = 'migrated token'; + const symbol = 'MT'; + const decimals = 53; + const tokenId = await service.interchainTokenId(wallet.address, salt); + const tokenManagerAddress = await service.tokenManagerAddress(tokenId); + + await interchainTokenDeployer.deployInterchainToken(salt, tokenId, service.address, name, symbol, decimals).then((tx) => tx.wait); + const tokenAddress = await interchainTokenDeployer.deployedAddress(salt); + const token = await getContractAt('InterchainToken', tokenAddress, wallet); + + const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, tokenAddress]); + + await service.deployTokenManager(salt, '', MINT_BURN, params, 0).then((tx) => tx.wait); + + await expect(service.migrateInterchainToken(tokenId)) + .to.emit(token, 'RolesRemoved') + .withArgs(service.address, 1 << MINTER_ROLE) + .to.emit(token, 'RolesAdded') + .withArgs(tokenManagerAddress, 1 << MINTER_ROLE); + + await expectRevert((gasOptions) => service.migrateInterchainToken(tokenId, { gasOptions} ), token, 'MissingRole', [service.address, MINTER_ROLE]); + }); + + it('Should not be able to migrate a token deployed after this upgrade', async () => { + const salt = getRandomBytes32(); + const name = 'migrated token'; + const symbol = 'MT'; + const decimals = 53; + const tokenId = await service.interchainTokenId(wallet.address, salt); + const tokenManagerAddress = await service.tokenManagerAddress(tokenId); + + + await service.deployInterchainToken(salt, '', name, symbol, decimals, AddressZero, 0).then((tx) => tx.wait); + const tokenAddress = await service.interchainTokenAddress(tokenId); + const token = await getContractAt('InterchainToken', tokenAddress, wallet); + + await expectRevert((gasOptions) => service.migrateInterchainToken(tokenId, { gasOptions} ), token, 'MissingRole', [service.address, MINTER_ROLE]); + }); + }) + describe('Bytecode checks [ @skip-on-coverage ]', () => { it('Should preserve the same proxy bytecode for each EVM', async () => { const proxyFactory = await ethers.getContractFactory('InterchainProxy', wallet);