Skip to content

Commit

Permalink
refactor(its-factory)!: remove auto-registration of token metadata (#326
Browse files Browse the repository at this point in the history
)
  • Loading branch information
milapsheth authored Jan 21, 2025
1 parent 869b412 commit d61ef1b
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-dogs-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axelar-network/interchain-token-service': minor
---

Remove auto-registration of token metadata in ITS factory registerCustomToken. registerTokenMetadata on ITS should be called instead for every token being linked explicitly.
8 changes: 3 additions & 5 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -505,18 +505,18 @@ contract InterchainTokenFactory is IInterchainTokenFactory, Multicall, Upgradabl
/**
* @notice Register an existing ERC20 token under a `tokenId` computed from the provided `salt`.
* A token metadata registration message will also be sent to the ITS Hub.
* This token can then be linked to remote tokens on different chains by submitting the `linkToken` function from the same `msg.sender` and using the same `salt`.
* @dev This function is marked as payable since it can be called within a multicall with other payable methods.
* @param salt The salt used to derive the tokenId for the custom token registration. The same salt must be used when linking this token on other chains under the same tokenId.
* @param tokenAddress The token address of the token being registered.
* @param tokenManagerType The token manager type used for the token link.
* @param operator The operator of the token manager.
* @param gasValue The cross-chain gas value used to register the token metadata on the ITS Hub.
*/
function registerCustomToken(
bytes32 salt,
address tokenAddress,
TokenManagerType tokenManagerType,
address operator,
uint256 gasValue
address operator
) external payable returns (bytes32 tokenId) {
bytes32 deploySalt = linkedTokenDeploySalt(msg.sender, salt);
bytes memory linkParams = '';
Expand All @@ -525,8 +525,6 @@ contract InterchainTokenFactory is IInterchainTokenFactory, Multicall, Upgradabl
}

tokenId = interchainTokenService.registerCustomToken(deploySalt, tokenAddress, tokenManagerType, linkParams);

interchainTokenService.registerTokenMetadata{ value: gasValue }(tokenAddress, gasValue);
}

/**
Expand Down
7 changes: 3 additions & 4 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -234,20 +234,19 @@ interface IInterchainTokenFactory is ITokenManagerType, IUpgradable, IMulticall

/**
* @notice Register an existing ERC20 token under a `tokenId` computed from the provided `salt`.
* A token metadata registration message will also be sent to the ITS Hub.
* The token metadata must have been registered for tokens on each chain via `InterchainTokenService.registerTokenMetadata`.
* This token can then be linked to remote tokens on different chains by submitting the `linkToken` function from the same `msg.sender` and using the same `salt`.
* @dev This function is marked as payable since it can be called within a multicall with other payable methods.
* @param salt The salt used to derive the tokenId for the custom token registration. The same salt must be used when linking this token on other chains under the same tokenId.
* @param tokenAddress The token address of the token being registered.
* @param tokenManagerType The token manager type used for the token link.
* @param operator The operator of the token manager.
* @param gasValue The cross-chain gas value used to register the token metadata on the ITS Hub.
*/
function registerCustomToken(
bytes32 salt,
address tokenAddress,
TokenManagerType tokenManagerType,
address operator,
uint256 gasValue
address operator
) external payable returns (bytes32 tokenId);

/**
Expand Down
36 changes: 12 additions & 24 deletions test/InterchainTokenFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,13 +673,13 @@ describe('InterchainTokenFactory', () => {
});

it('Should revert on deploying an invalid token manager', async () => {
await expectRevert((gasOptions) => tokenFactory.registerCustomToken(salt, token.address, 6, wallet.address, 0, gasOptions));
await expectRevert((gasOptions) => tokenFactory.registerCustomToken(salt, token.address, 6, wallet.address, gasOptions));
});

it('Should revert on deploying a local token manager with interchain token manager type', async () => {
await expectRevert(
(gasOptions) =>
tokenFactory.registerCustomToken(salt, token.address, NATIVE_INTERCHAIN_TOKEN, wallet.address, 0, gasOptions),
tokenFactory.registerCustomToken(salt, token.address, NATIVE_INTERCHAIN_TOKEN, wallet.address, gasOptions),
service,
'CannotDeploy',
[NATIVE_INTERCHAIN_TOKEN],
Expand All @@ -706,24 +706,19 @@ describe('InterchainTokenFactory', () => {

it('Should revert on deploying a token manager if token handler post deploy fails', async () => {
await expectRevert(
(gasOptions) => tokenFactory.registerCustomToken(salt, AddressZero, LOCK_UNLOCK, wallet.address, 0, gasOptions),
(gasOptions) => tokenFactory.registerCustomToken(salt, AddressZero, LOCK_UNLOCK, wallet.address, gasOptions),
service,
'PostDeployFailed',
);
});

it('Should revert when deploying a custom token when the service is paused', async () => {
const salt = getRandomBytes32();
const gasValue = 1;

await service.setPauseStatus(true).then((tx) => tx.wait);

await expectRevert(
(gasOptions) =>
tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, AddressZero, gasValue, {
value: gasValue,
...gasOptions,
}),
(gasOptions) => tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, AddressZero, gasOptions),
service,
'Pause',
);
Expand All @@ -736,10 +731,9 @@ describe('InterchainTokenFactory', () => {
const tokenId = await tokenFactory.linkedTokenId(wallet.address, salt);
const deploySalt = await tokenFactory.linkedTokenDeploySalt(wallet.address, salt);
const tokenManagerAddress = await service.tokenManagerAddress(tokenId);
const gasValue = 1;
const params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);

await expect(tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, AddressZero, gasValue, { value: gasValue }))
await expect(tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, AddressZero))
.to.emit(service, 'InterchainTokenIdClaimed')
.withArgs(tokenId, AddressZero, deploySalt)
.to.emit(service, 'TokenManagerDeployed')
Expand All @@ -751,14 +745,11 @@ describe('InterchainTokenFactory', () => {
const tokenId = await tokenFactory.linkedTokenId(wallet.address, salt);
const deploySalt = await tokenFactory.linkedTokenDeploySalt(wallet.address, salt);
const tokenManagerAddress = await service.tokenManagerAddress(tokenId);
const gasValue = 1;
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

await service.setTrustedAddress(ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS).then((tx) => tx.wait);

await expect(
tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address, gasValue, { value: gasValue }),
)
await expect(tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address))
.to.emit(service, 'InterchainTokenIdClaimed')
.withArgs(tokenId, AddressZero, deploySalt)
.to.emit(service, 'TokenManagerDeployed')
Expand All @@ -768,11 +759,10 @@ describe('InterchainTokenFactory', () => {
it('Should register a token with lock_unlock type', async () => {
const tokenManagerAddress = await service.tokenManagerAddress(tokenId);
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);
const gasValue = 1;

await expect(
reportGas(
tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address, gasValue, { value: gasValue }),
tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address),
'Call deployTokenManager on source chain',
),
)
Expand Down Expand Up @@ -802,7 +792,7 @@ describe('InterchainTokenFactory', () => {
it('Should revert when linking a token twice', async () => {
const revertData = keccak256(toUtf8Bytes('AlreadyDeployed()')).substring(0, 10);
await expectRevert(
(gasOptions) => tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address, 0, gasOptions),
(gasOptions) => tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK, wallet.address, gasOptions),
service,
'TokenManagerDeploymentFailed',
[revertData],
Expand Down Expand Up @@ -838,7 +828,7 @@ describe('InterchainTokenFactory', () => {
]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

const tx = tokenFactory.registerCustomToken(salt, token.address, MINT_BURN, wallet.address, 0);
const tx = tokenFactory.registerCustomToken(salt, token.address, MINT_BURN, wallet.address);
const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId);
await expect(tx).to.emit(service, 'TokenManagerDeployed').withArgs(tokenId, expectedTokenManagerAddress, MINT_BURN, params);

Expand Down Expand Up @@ -873,7 +863,7 @@ describe('InterchainTokenFactory', () => {
]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

const tx = tokenFactory.registerCustomToken(salt, token.address, MINT_BURN_FROM, wallet.address, 0);
const tx = tokenFactory.registerCustomToken(salt, token.address, MINT_BURN_FROM, wallet.address);
const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId);
await expect(tx)
.to.emit(service, 'TokenManagerDeployed')
Expand Down Expand Up @@ -910,7 +900,7 @@ describe('InterchainTokenFactory', () => {
]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

const tx = tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK_FEE_ON_TRANSFER, wallet.address, 0);
const tx = tokenFactory.registerCustomToken(salt, token.address, LOCK_UNLOCK_FEE_ON_TRANSFER, wallet.address);
const expectedTokenManagerAddress = await service.tokenManagerAddress(tokenId);
await expect(tx)
.to.emit(service, 'TokenManagerDeployed')
Expand Down Expand Up @@ -965,9 +955,7 @@ describe('InterchainTokenFactory', () => {
]);

tokenId = await tokenFactory.linkedTokenId(wallet.address, salt);
await tokenFactory
.registerCustomToken(salt, token.address, tokenManagerType, operator, gasValue, { value: gasValue })
.then((tx) => tx.wait);
await tokenFactory.registerCustomToken(salt, token.address, tokenManagerType, operator).then((tx) => tx.wait);
await token.setTokenId(tokenId).then((tx) => tx.wait);
}

Expand Down
36 changes: 24 additions & 12 deletions test/InterchainTokenServiceFullFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
ITS_HUB_CHAIN_NAME,
ITS_HUB_ROUTING_IDENTIFIER,
ITS_HUB_ADDRESS,
MESSAGE_TYPE_REGISTER_TOKEN_METADATA,
} = require('./constants');

describe('Interchain Token Service Full Flow', () => {
Expand Down Expand Up @@ -334,16 +335,17 @@ describe('Interchain Token Service Full Flow', () => {

/**
* This test creates a token link between pre-existing tokens by giving mint/burn permission to ITS.
* - Deploy a normal mint/burn ERC20 registered with an owner
* - Deploy a mint/burn token manager for the token on all chains
* - Transfer mint/burn permission (minter role) to ITS
* - Choose the tokens being linked across chains. For the test, a mint/burn ERC20 is deployed for the source chain.
* - Register token metadata of the token on each chain being linked via ITS
* - Link the source chain token to each remote token via ITS Factory
* - Give/transfer mint/burn permission to the corresponding token manager on each chain
* - Transfer tokens via ITS between chains
*/
describe('Pre-existing Token as Mint/Burn', () => {
let token;
const otherChains = ['chain 1', 'chain 2'];
const gasValues = [1234, 5678];
const registrationGasValue = 9012;
const registrationGasValue = 1234;
const tokenCap = 1e9;
const salt = getRandomBytes32();

Expand All @@ -354,20 +356,30 @@ describe('Interchain Token Service Full Flow', () => {
await token.mint(wallet.address, tokenCap).then((tx) => tx.wait);
});

it('Should register token metadata', async () => {
const payload = defaultAbiCoder.encode(
['uint256', 'bytes', 'uint8'],
[MESSAGE_TYPE_REGISTER_TOKEN_METADATA, token.address, decimals],
);
const payloadHash = keccak256(payload);

// Register token metadata being linked from the source chain
// Similarly, submit this registration from ITS contract of all chains for the corresponding token addresses being linked
await expect(service.registerTokenMetadata(token.address, registrationGasValue, { value: registrationGasValue }))
.to.emit(service, 'TokenMetadataRegistered')
.withArgs(token.address, decimals)
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, ITS_HUB_CHAIN_NAME, ITS_HUB_ADDRESS, payloadHash, payload);
});

it('Should register the token and initiate its deployment on other chains', async () => {
const tokenManagerImplementationAddress = await service.tokenManager();
const tokenManagerImplementation = await getContractAt('TokenManager', tokenManagerImplementationAddress, wallet);

const params = await tokenManagerImplementation.params(wallet.address, token.address);
let tx = await tokenFactory.populateTransaction.registerCustomToken(
salt,
token.address,
MINT_BURN,
wallet.address,
registrationGasValue,
);
let tx = await tokenFactory.populateTransaction.registerCustomToken(salt, token.address, MINT_BURN, wallet.address);
const calls = [tx.data];
let value = registrationGasValue;
let value = 0;

for (const i in otherChains) {
// This should be replaced with the existing token address on each chain being linked
Expand Down
2 changes: 1 addition & 1 deletion test/InterchainTokenServiceUpgradeFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('Interchain Token Service Upgrade Flow', () => {
]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, token.address]);

await expect(tokenFactory.registerCustomToken(salt, token.address, MINT_BURN, wallet.address, 0))
await expect(tokenFactory.registerCustomToken(salt, token.address, MINT_BURN, wallet.address))
.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManager.address, MINT_BURN, params);
}
Expand Down

0 comments on commit d61ef1b

Please sign in to comment.