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(token-handler)!: added automatic token migration #317

Merged
merged 35 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5b1b544
Added the basic changes.
Foivos Dec 23, 2024
3be2fda
made lint happy
Foivos Jan 2, 2025
8e0c290
Added some functions to migrate tokens
Foivos Jan 6, 2025
58e956e
added automatic token migration
Foivos Jan 6, 2025
af995b8
Merge branch 'main' into feat/token-manager-mint-interchain-tokens-2
milapsheth Jan 7, 2025
4c7e551
Merge branch 'feat/token-manager-mint-interchain-tokens-2' into feat/…
milapsheth Jan 7, 2025
6cd5926
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
milapsheth Jan 7, 2025
a833844
Update contracts/InterchainTokenService.sol
Foivos Jan 9, 2025
123c9c1
removed migrate legacy token and added tests
Foivos Jan 9, 2025
3c63b22
added a docstring
Foivos Jan 9, 2025
70a0796
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
Foivos Jan 9, 2025
b2b367b
fixed tests
Foivos Jan 9, 2025
716fb46
made lint happy
Foivos Jan 9, 2025
de53714
addressed comments
Foivos Jan 10, 2025
5bcb97a
prettier
Foivos Jan 10, 2025
e4f3152
Merge branch 'feat/token-manager-mint-interchain-tokens-2' into feat/…
Foivos Jan 10, 2025
1f6eb3f
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
Foivos Jan 10, 2025
9c9f5f2
fix tests
Foivos Jan 10, 2025
2a606ec
Apply suggestions from code review
milapsheth Jan 10, 2025
09de417
Merge branch 'main' into feat/token-manager-mint-interchain-tokens-2
milapsheth Jan 13, 2025
cd4341f
Merge branch 'main' into feat/token-manager-mint-interchain-tokens-2
milapsheth Jan 15, 2025
bc6d739
Merge branch 'feat/token-manager-mint-interchain-tokens-2' into feat/…
milapsheth Jan 15, 2025
69f8dc6
cleanup
milapsheth Jan 15, 2025
d7e6c39
cs
milapsheth Jan 15, 2025
ac912cf
fmt
milapsheth Jan 15, 2025
a21d366
stash
Foivos Jan 15, 2025
7edef20
Merge branch 'main' into feat/manual-migrate-tokens
Foivos Jan 16, 2025
379a08c
Fixed tests
Foivos Jan 16, 2025
cd3826a
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
Foivos Jan 16, 2025
c3ae998
made lint happy
Foivos Jan 16, 2025
9a729bd
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
Foivos Jan 16, 2025
a5ad995
fixed tests
Foivos Jan 16, 2025
ba0334d
prettier
Foivos Jan 16, 2025
0d7dbfc
Merge branch 'feat/manual-migrate-tokens' into feat/automcatic-migrat…
Foivos Jan 16, 2025
593c674
Merge branch 'main' into feat/automcatic-migrate-tokens
milapsheth Jan 16, 2025
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
5 changes: 5 additions & 0 deletions .changeset/quick-ants-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axelar-network/interchain-token-service': minor
---

Add auto-migration of minter for native interchain tokens
32 changes: 22 additions & 10 deletions contracts/TokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea

(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();

_migrateToken(tokenManager, tokenAddress, tokenManagerType);

/// @dev Track the flow amount being received via the message
ITokenManager(tokenManager).addFlowIn(amount);
milapsheth marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -44,20 +46,15 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)
) {
_mintToken(ITokenManager(tokenManager), tokenAddress, to, amount);
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, tokenManager, to, amount);
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, tokenManager, to, amount);
return (amount, tokenAddress);
} else {
revert UnsupportedTokenManagerType(tokenManagerType);
}

revert UnsupportedTokenManagerType(tokenManagerType);
return (amount, tokenAddress);
}

/**
Expand All @@ -75,6 +72,8 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea

if (tokenOnly && msg.sender != tokenAddress) revert NotToken(msg.sender, tokenAddress);

_migrateToken(tokenManager, tokenAddress, tokenManagerType);

if (
tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN)
) {
Expand Down Expand Up @@ -176,4 +175,17 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
function _burnTokenFrom(address tokenAddress, address from, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20BurnableFrom.burnFrom.selector, from, amount));
}

/**
* @notice This transfers mintership of a native Interchain token to the tokenManager if ITS is still its minter.
* It does nothing if ITS is not the minter. This ensures that interchain tokens are auto-migrated without requiring a downtime for ITS.
* @param tokenManager The token manager address to transfer mintership to.
* @param tokenAddress The address of the token to transfer mintership of.
* @param tokenManagerType The token manager type for the token.
*/
function _migrateToken(address tokenManager, address tokenAddress, uint256 tokenManagerType) internal {
if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) && IMinter(tokenAddress).isMinter(address(this))) {
IMinter(tokenAddress).transferMintership(tokenManager);
}
}
milapsheth marked this conversation as resolved.
Show resolved Hide resolved
}
56 changes: 56 additions & 0 deletions test/InterchainTokenService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2981,6 +2981,62 @@ describe('Interchain Token Service', () => {
'NotOwner',
);
});

it('Should migrate a token automatically when sending token only once', async () => {
const name = 'migrated token';
const symbol = 'MT';
const decimals = 53;
const amount1 = 123;
const amount2 = 456;
const destinationAddress = '0x1234';

const [token, tokenManager, tokenId] = await deployFunctions.interchainToken(
service,
name,
symbol,
decimals,
service.address,
amount1 + amount2,
);

await expect(service.interchainTransfer(tokenId, destinationChain, destinationAddress, amount1, '0x', 0))
.to.emit(token, 'RolesRemoved')
.withArgs(service.address, 1 << MINTER_ROLE)
.to.emit(token, 'RolesAdded')
.withArgs(tokenManager.address, 1 << MINTER_ROLE);

await expect(service.interchainTransfer(tokenId, destinationChain, destinationAddress, amount2, '0x', 0))
.to.not.emit(token, 'RolesRemoved')
.to.not.emit(token, 'RolesAdded');
});

it('Should migrate a token automatically when receiving token', async () => {
const name = 'migrated token';
const symbol = 'MT';
const decimals = 53;
const amount = 123;
const sourceAddress = '0x1234';

const [token, tokenManager, tokenId] = await deployFunctions.interchainToken(service, name, symbol, decimals, service.address);

const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', 'bytes'],
[MESSAGE_TYPE_INTERCHAIN_TRANSFER, tokenId, sourceAddress, wallet.address, amount, '0x'],
);
let commandId = await approveContractCall(gateway, destinationChain, service.address, service.address, payload);

await expect(service.execute(commandId, destinationChain, service.address, payload))
.to.emit(token, 'RolesRemoved')
.withArgs(service.address, 1 << MINTER_ROLE)
.to.emit(token, 'RolesAdded')
.withArgs(tokenManager.address, 1 << MINTER_ROLE);

commandId = await approveContractCall(gateway, destinationChain, service.address, service.address, payload);

await expect(service.execute(commandId, destinationChain, service.address, payload))
.to.not.emit(token, 'RolesRemoved')
.to.not.emit(token, 'RolesAdded');
});
});

describe('Bytecode checks [ @skip-on-coverage ]', () => {
Expand Down