Skip to content

Commit

Permalink
updated
Browse files Browse the repository at this point in the history
  • Loading branch information
ahramy committed Sep 18, 2024
1 parent ae08d80 commit f3f8f98
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 123 deletions.
120 changes: 61 additions & 59 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,26 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

/**
* @notice Deploys a remote interchain token on a specified destination chain.
* @param salt The unique salt for deploying the token.
* @param minter The address to receive the minter and operator role of the token, in addition to ITS. If the address is `address(0)`,
* no additional minter is set on the token. Reverts if the minter does not have mint permission for the token.
* @param destinationChain The name of the destination chain.
* @param gasValue The amount of gas to send for the deployment.
* @return tokenId The tokenId corresponding to the deployed InterchainToken.
*/
function deployRemoteInterchainToken(
bytes32 salt,
address minter,
string memory destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
tokenId = _deployRemoteInterchainToken(chainNameHash, salt, minter, destinationChain, gasValue);
}

/**
* @notice Deploys a remote interchain token on a specified destination chain.
* @dev originalChainName is only allowed to be '', i.e the current chain.
* Other source chains are not supported anymore to simplify ITS deployment behaviour.
* @param originalChainName The name of the chain where the token originally exists.
* @param salt The unique salt for deploying the token.
* @param minter The address to receive the minter and operator role of the token, in addition to ITS. If the address is `address(0)`,
Expand All @@ -181,64 +201,42 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
string memory destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
string memory tokenName;
string memory tokenSymbol;
uint8 tokenDecimals;
bytes memory minter_ = new bytes(0);

{
bytes32 chainNameHash_;
if (bytes(originalChainName).length == 0) {
chainNameHash_ = chainNameHash;
} else {
chainNameHash_ = keccak256(bytes(originalChainName));
}

address sender = msg.sender;
salt = interchainTokenSalt(chainNameHash_, sender, salt);
tokenId = interchainTokenService.interchainTokenId(TOKEN_FACTORY_DEPLOYER, salt);

IInterchainToken token = IInterchainToken(interchainTokenService.interchainTokenAddress(tokenId));
bytes32 chainNameHash_;

tokenName = token.name();
tokenSymbol = token.symbol();
tokenDecimals = token.decimals();

if (minter != address(0)) {
if (!token.isMinter(minter)) revert NotMinter(minter);
if (minter == address(interchainTokenService)) revert InvalidMinter(minter);

minter_ = minter.toBytes();
}
if (bytes(originalChainName).length == 0) {
chainNameHash_ = chainNameHash;
} else {
revert NotSupported();
}

tokenId = _deployInterchainToken(salt, destinationChain, tokenName, tokenSymbol, tokenDecimals, minter_, gasValue);
tokenId = _deployRemoteInterchainToken(chainNameHash_, salt, minter, destinationChain, gasValue);
}

/**
* @notice The deployRemoteInterchainToken with originalChainName allows deploying a remote interchain token back to the same chain, which could cause issues with ITS Hub balance tracking.
* To fix this, the deployRemoteInterchainToken function was overloaded to prevent self-deployemnt while still allowing self-transfers.
* @notice Deploys a remote interchain token on a specified destination chain.
* @param chainNameHash_ Hash of the chain name.
* @param salt The unique salt for deploying the token.
* @param minter The address to receive the minter and operator role of the token, in addition to ITS. If the address is `address(0)`,
* no additional minter is set on the token. Reverts if the minter does not have mint permission for the token.
* @param destinationChain The name of the destination chain.
* @param gasValue The amount of gas to send for the deployment.
* @return tokenId The tokenId corresponding to the deployed InterchainToken.
*/
function deployRemoteInterchainToken(
function _deployRemoteInterchainToken(
bytes32 chainNameHash_,
bytes32 salt,
address minter,
string memory destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
) internal returns (bytes32 tokenId) {
string memory tokenName;
string memory tokenSymbol;
uint8 tokenDecimals;
bytes memory minter_ = new bytes(0);

{
address sender = msg.sender;
salt = interchainTokenSalt(chainNameHash, sender, salt);
salt = interchainTokenSalt(chainNameHash_, sender, salt);
tokenId = interchainTokenService.interchainTokenId(TOKEN_FACTORY_DEPLOYER, salt);

IInterchainToken token = IInterchainToken(interchainTokenService.interchainTokenAddress(tokenId));
Expand Down Expand Up @@ -306,6 +304,23 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

/**
* @notice Deploys a canonical interchain token on a remote chain.
* @param originalTokenAddress The address of the original token on the original chain.
* @param destinationChain The name of the chain where the token will be deployed.
* @param gasValue The gas amount to be sent for deployment.
* @return tokenId The tokenId corresponding to the deployed InterchainToken.
*/
function deployRemoteCanonicalInterchainToken(
address originalTokenAddress,
string calldata destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
tokenId = _deployRemoteCanonicalInterchainToken(chainNameHash, originalTokenAddress, destinationChain, gasValue);
}

/**
* @notice Deploys a canonical interchain token on a remote chain.
* @dev originalChain is only allowed to be '', i.e the current chain.
* Other source chains are not supported anymore to simplify ITS deployment behaviour.
* @param originalChain The name of the chain where the token originally exists.
* @param originalTokenAddress The address of the original token on the original chain.
* @param destinationChain The name of the chain where the token will be deployed.
Expand All @@ -318,50 +333,37 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
string calldata destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
bytes32 salt;
IInterchainToken token;

{
bytes32 chainNameHash_;
if (bytes(originalChain).length == 0) {
chainNameHash_ = chainNameHash;
} else {
chainNameHash_ = keccak256(bytes(originalChain));
}
bytes32 chainNameHash_;

// This ensures that the token manager has been deployed by this address, so it's safe to trust it.
salt = canonicalInterchainTokenSalt(chainNameHash_, originalTokenAddress);
tokenId = interchainTokenService.interchainTokenId(TOKEN_FACTORY_DEPLOYER, salt);
token = IInterchainToken(interchainTokenService.validTokenAddress(tokenId));
if (bytes(originalChain).length == 0) {
chainNameHash_ = chainNameHash;
} else {
revert NotSupported();
}

// The 3 lines below will revert if the token does not exist.
string memory tokenName = token.name();
string memory tokenSymbol = token.symbol();
uint8 tokenDecimals = token.decimals();

tokenId = _deployInterchainToken(salt, destinationChain, tokenName, tokenSymbol, tokenDecimals, '', gasValue);
tokenId = _deployRemoteCanonicalInterchainToken(chainNameHash_, originalTokenAddress, destinationChain, gasValue);
}

/**
* @notice The deployRemoteCanonicalInterchainToken with originalChain allows deploying a canonical interchain token back to the same chain, which could cause issues with ITS Hub balance tracking.
* To fix this, the deployRemoteCanonicalInterchainToken function was overloaded to prevent self-deployemnt while still allowing self-transfers.
* @notice Deploys a canonical interchain token on a remote chain.
* @param chainNameHash_ Hash of the chain name.
* @param originalTokenAddress The address of the original token on the original chain.
* @param destinationChain The name of the chain where the token will be deployed.
* @param gasValue The gas amount to be sent for deployment.
* @return tokenId The tokenId corresponding to the deployed InterchainToken.
*/
function deployRemoteCanonicalInterchainToken(
function _deployRemoteCanonicalInterchainToken(
bytes32 chainNameHash_,
address originalTokenAddress,
string calldata destinationChain,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
) internal returns (bytes32 tokenId) {
bytes32 salt;
IInterchainToken token;

{
// This ensures that the token manager has been deployed by this address, so it's safe to trust it.
salt = canonicalInterchainTokenSalt(chainNameHash, originalTokenAddress);
salt = canonicalInterchainTokenSalt(chainNameHash_, originalTokenAddress);
tokenId = interchainTokenService.interchainTokenId(TOKEN_FACTORY_DEPLOYER, salt);
token = IInterchainToken(interchainTokenService.validTokenAddress(tokenId));
}
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface IInterchainTokenFactory is IUpgradable, IMulticall {
error GatewayToken(address tokenAddress);
error NotServiceOwner(address sender);
error NotGatewayToken(string symbol);
error NotSupported();

/**
* @notice Returns the address of the interchain token service.
Expand Down
89 changes: 33 additions & 56 deletions test/InterchainTokenFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,20 @@ describe('InterchainTokenFactory', () => {
[MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, '0x'],
);

await expect(
tokenFactory['deployRemoteCanonicalInterchainToken(string,address,string,uint256)'](
chainName,
token.address,
destinationChain,
gasValue,
{
value: gasValue,
},
),
)
.to.emit(service, 'InterchainTokenDeploymentStarted')
.withArgs(tokenId, name, symbol, decimals, '0x', destinationChain)
.and.to.emit(gasService, 'NativeGasPaidForContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), gasValue, wallet.address)
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);
await expectRevert(
(gasOptions) =>
tokenFactory['deployRemoteCanonicalInterchainToken(string,address,string,uint256)'](
chainName,
token.address,
destinationChain,
gasValue,
{
value: gasValue,
},
),
tokenFactory,
'NotSupported',
);

await expect(
tokenFactory['deployRemoteCanonicalInterchainToken(address,string,uint256)'](token.address, destinationChain, gasValue, {
Expand Down Expand Up @@ -449,14 +446,31 @@ describe('InterchainTokenFactory', () => {
},
),
tokenFactory,
'NotSupported',
);

await expectRevert(
(gasOptions) =>
tokenFactory['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
'',
salt,
otherWallet.address,
destinationChain,
gasValue,
{
...gasOptions,
value: gasValue,
},
),
tokenFactory,
'NotMinter',
[otherWallet.address],
);

await expectRevert(
(gasOptions) =>
tokenFactory['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
'',
salt,
service.address,
destinationChain,
Expand Down Expand Up @@ -490,25 +504,6 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);

await expect(
tokenFactory['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
salt,
wallet.address,
destinationChain,
gasValue,
{
value: gasValue,
},
),
)
.to.emit(service, 'InterchainTokenDeploymentStarted')
.withArgs(tokenId, name, symbol, decimals, wallet.address.toLowerCase(), destinationChain)
.and.to.emit(gasService, 'NativeGasPaidForContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), gasValue, wallet.address)
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);

await expectRevert(
(gasOptions) =>
tokenFactory['deployRemoteInterchainToken(bytes32,address,string,uint256)'](
Expand Down Expand Up @@ -560,24 +555,6 @@ describe('InterchainTokenFactory', () => {
.withArgs(service.address, destinationChain, service.address, keccak256(payload), gasValue, wallet.address)
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);

await expect(
tokenFactory['deployRemoteInterchainToken(bytes32,address,string,uint256)'](
salt,
wallet.address,
destinationChain,
gasValue,
{
value: gasValue,
},
),
)
.to.emit(service, 'InterchainTokenDeploymentStarted')
.withArgs(tokenId, name, symbol, decimals, wallet.address.toLowerCase(), destinationChain)
.and.to.emit(gasService, 'NativeGasPaidForContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), gasValue, wallet.address)
.and.to.emit(gateway, 'ContractCall')
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);
});

it('Should initiate a remote interchain token deployment without the same minter', async () => {
Expand Down Expand Up @@ -617,7 +594,7 @@ describe('InterchainTokenFactory', () => {

await expect(
tokenFactory['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
'',
salt,
AddressZero,
destinationChain,
Expand Down
12 changes: 4 additions & 8 deletions test/InterchainTokenServiceFullFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ describe('Interchain Token Service Full Flow', () => {
let value = 0;

for (const i in otherChains) {
tx = await tokenFactory.populateTransaction['deployRemoteCanonicalInterchainToken(string,address,string,uint256)'](
chainName,
tx = await tokenFactory.populateTransaction['deployRemoteCanonicalInterchainToken(address,string,uint256)'](
token.address,
otherChains[i],
gasValues[i],
Expand Down Expand Up @@ -177,8 +176,7 @@ describe('Interchain Token Service Full Flow', () => {

// Deploy a linked Interchain token to remote chains.
for (const i in otherChains) {
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(bytes32,address,string,uint256)'](
salt,
wallet.address,
otherChains[i],
Expand Down Expand Up @@ -485,8 +483,7 @@ describe('Interchain Token Service Full Flow', () => {

// Deploy a linked Interchain token to remote chains.
for (const i in otherChains) {
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(bytes32,address,string,uint256)'](
salt,
AddressZero,
otherChains[i],
Expand Down Expand Up @@ -703,8 +700,7 @@ describe('Interchain Token Service Full Flow', () => {

// Deploy a linked Interchain token to remote chains.
for (const i in otherChains) {
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(string,bytes32,address,string,uint256)'](
chainName,
tx = await tokenFactory.populateTransaction['deployRemoteInterchainToken(bytes32,address,string,uint256)'](
salt,
wallet.address,
otherChains[i],
Expand Down

0 comments on commit f3f8f98

Please sign in to comment.