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(interchainTokenFactory)!: add a function to link tokens as interchain tokens #327

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,34 @@ contract InterchainTokenFactory is IInterchainTokenFactory, Multicall, Upgradabl
);
}

/**
* @notice Links a remote token on `destinationChain` to a local token corresponding to the `tokenId` computed from the provided `salt`.
* A local token must have been registered first using the `registerCustomToken` function.
* @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 destinationChain The name of the destination chain.
* @param name The token name on destination chain.
* @param symbol The token symbol on destination chain.
* @param decimals The token decimals on destination chain.
* @param linkParams Additional parameters for the token link depending on the destination chain. For EVM destination chains, this is an optional custom operator address.
* @param gasValue The cross-chain gas value used to link the token on the destination chain.
* @return tokenId The tokenId corresponding to the linked token.
*/
function linkAsInterchainToken(
bytes32 salt,
string calldata destinationChain,
string calldata name,
string calldata symbol,
uint8 decimals,
bytes calldata linkParams,
uint256 gasValue
) external payable returns (bytes32 tokenId) {
bytes32 deploySalt = linkedTokenDeploySalt(msg.sender, salt);
if (bytes(destinationChain).length == 0) {
revert CannotLinkTokenToThisChain();
}
tokenId = _deployInterchainToken(deploySalt, destinationChain, name, symbol, decimals, linkParams, gasValue);
}

/********************\
|* Pure Key Getters *|
\********************/
Expand Down
23 changes: 23 additions & 0 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface IInterchainTokenFactory is ITokenManagerType, IUpgradable, IMulticall
error InvalidTokenId(bytes32 tokenId, bytes32 expectedTokenId);
error ZeroSupplyToken();
error NotToken(address tokenAddress);
error CannotLinkTokenToThisChain();

/// @notice Emitted when a minter approves a deployer for a remote interchain token deployment that uses a custom destinationMinter address.
event DeployRemoteInterchainTokenApproval(
Expand Down Expand Up @@ -268,4 +269,26 @@ interface IInterchainTokenFactory is ITokenManagerType, IUpgradable, IMulticall
bytes calldata linkParams,
uint256 gasValue
) external payable returns (bytes32 tokenId);

/**
* @notice Links a remote token on `destinationChain` to a local token corresponding to the `tokenId` computed from the provided `salt`.
* A local token must have been registered first using the `registerCustomToken` function.
* @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 destinationChain The name of the destination chain.
* @param name The token name on destination chain.
* @param symbol The token symbol on destination chain.
* @param decimals The token decimals on destination chain.
* @param linkParams Additional parameters for the token link depending on the destination chain. For EVM destination chains, this is an optional custom operator address.
* @param gasValue The cross-chain gas value used to link the token on the destination chain.
* @return tokenId The tokenId corresponding to the linked token.
*/
function linkAsInterchainToken(
bytes32 salt,
string calldata destinationChain,
string calldata name,
string calldata symbol,
uint8 decimals,
bytes calldata linkParams,
uint256 gasValue
) external payable returns (bytes32 tokenId);
}
33 changes: 33 additions & 0 deletions test/InterchainTokenFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,39 @@ describe('InterchainTokenFactory', () => {
.withArgs(service.address, destinationChain, service.address, keccak256(payload), payload);
});

it('Should initialize a remote link as interchain token deployment', async () => {
await deployAndRegisterToken();

const minter = '0x5789';
const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'string', 'string', 'uint8', 'bytes'],
[MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, minter],
);

const tokenManager = await getContractAt('TokenManager', await service.deployedTokenManager(tokenId), wallet);
expect(await tokenManager.isOperator(AddressZero)).to.be.true;
expect(await tokenManager.isOperator(service.address)).to.be.true;
expect(await tokenManager.isFlowLimiter(AddressZero)).to.be.true;
expect(await tokenManager.isFlowLimiter(service.address)).to.be.true;

await expect(
reportGas(
tokenFactory.linkAsInterchainToken(salt, destinationChain, name, symbol, decimals, minter, gasValue, {
value: gasValue,
}),
'Send deployInterchainToken to link a token to remote chain',
),
)
.to.emit(service, 'InterchainTokenIdClaimed')
.withArgs(tokenId, AddressZero, await tokenFactory.linkedTokenDeploySalt(wallet.address, salt))
.to.emit(service, 'InterchainTokenDeploymentStarted')
.withArgs(tokenId, name, symbol, decimals, minter.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 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);
Expand Down