-
Notifications
You must be signed in to change notification settings - Fork 17
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(its): add custom token support with auto scaling #311
base: main
Are you sure you want to change the base?
Changes from all commits
5dad96a
d06d6f1
c4b30ab
0f3b53a
0c1dc92
6d65c01
d2280c3
2b7104e
29da692
0c29295
e14555b
6092f14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ import { IInterchainTokenExecutable } from './interfaces/IInterchainTokenExecuta | |
import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainTokenExpressExecutable.sol'; | ||
import { ITokenManager } from './interfaces/ITokenManager.sol'; | ||
import { IGatewayCaller } from './interfaces/IGatewayCaller.sol'; | ||
import { IERC20Named } from './interfaces/IERC20Named.sol'; | ||
import { Create3AddressFixed } from './utils/Create3AddressFixed.sol'; | ||
import { Operator } from './utils/Operator.sol'; | ||
|
||
|
@@ -79,9 +80,12 @@ contract InterchainTokenService is | |
|
||
uint256 private constant MESSAGE_TYPE_INTERCHAIN_TRANSFER = 0; | ||
uint256 private constant MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN = 1; | ||
uint256 private constant MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2; | ||
// Deprecated | ||
// uint256 private constant MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2; | ||
uint256 private constant MESSAGE_TYPE_SEND_TO_HUB = 3; | ||
uint256 private constant MESSAGE_TYPE_RECEIVE_FROM_HUB = 4; | ||
uint256 private constant MESSAGE_TYPE_LINK_TOKEN = 5; | ||
uint256 private constant MESSAGE_TYPE_REGISTER_TOKEN_METADATA = 6; | ||
|
||
/** | ||
* @dev Tokens and token managers deployed via the Token Factory contract use a special deployer address. | ||
|
@@ -279,6 +283,36 @@ contract InterchainTokenService is | |
USER FUNCTIONS | ||
\************/ | ||
|
||
/** | ||
* @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens. | ||
* @param tokenAddress The address of the token. | ||
* @param gasValue The gas value for deployment. | ||
*/ | ||
function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable { | ||
if (tokenAddress == address(0)) revert ZeroAddress(); | ||
|
||
uint8 decimals = IERC20Named(tokenAddress).decimals(); | ||
|
||
bytes memory payload = abi.encode(MESSAGE_TYPE_REGISTER_TOKEN_METADATA, tokenAddress.toBytes(), decimals); | ||
|
||
string memory destinationAddress = trustedAddress(ITS_HUB_CHAIN_NAME); | ||
// Check whether no trusted address was set for ITS Hub chain | ||
if (bytes(destinationAddress).length == 0) revert UntrustedChain(); | ||
|
||
(bool success, bytes memory returnData) = gatewayCaller.delegatecall( | ||
abi.encodeWithSelector( | ||
IGatewayCaller.callContract.selector, | ||
ITS_HUB_CHAIN_NAME, | ||
destinationAddress, | ||
payload, | ||
IGatewayCaller.MetadataVersion.CONTRACT_CALL, | ||
gasValue | ||
) | ||
); | ||
|
||
if (!success) revert GatewayCallFailed(returnData); | ||
} | ||
|
||
/** | ||
* @notice Used to deploy remote custom TokenManagers. | ||
* @dev At least the `gasValue` amount of native token must be passed to the function call. `gasValue` exists because this function can be | ||
|
@@ -301,31 +335,46 @@ contract InterchainTokenService is | |
TokenManagerType tokenManagerType, | ||
bytes calldata params, | ||
uint256 gasValue | ||
) external payable whenNotPaused returns (bytes32 tokenId) { | ||
if (bytes(params).length == 0) revert EmptyParams(); | ||
) external payable returns (bytes32 tokenId) { | ||
(bytes memory linkParams, address destinationTokenAddress) = abi.decode(params, (bytes, address)); | ||
|
||
tokenId = linkToken(salt, destinationChain, destinationTokenAddress.toBytes(), tokenManagerType, linkParams, gasValue); | ||
} | ||
|
||
function linkToken( | ||
bytes32 salt, | ||
string calldata destinationChain, | ||
bytes memory destinationTokenAddress, | ||
TokenManagerType tokenManagerType, | ||
bytes memory linkParams, | ||
uint256 gasValue | ||
) public payable whenNotPaused returns (bytes32 tokenId) { | ||
if (destinationTokenAddress.length == 0) revert EmptyDestinationAddress(); | ||
|
||
// TODO: Should we only allow mint/burn or lock/unlock for remote linking for simplicity? Makes it easier for external chains | ||
// Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens | ||
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); | ||
|
||
address deployer = msg.sender; | ||
|
||
if (deployer == interchainTokenFactory) { | ||
if (msg.sender == interchainTokenFactory) { | ||
deployer = TOKEN_FACTORY_DEPLOYER; | ||
} else if (bytes(destinationChain).length == 0 && trustedAddressHash(chainName()) == ITS_HUB_ROUTING_IDENTIFIER_HASH) { | ||
// Restricted on ITS contracts deployed to Amplifier chains until ITS Hub adds support | ||
revert NotSupported(); | ||
} else if (bytes(destinationChain).length == 0) { | ||
// TODO: Only support linking new tokens via ITS factory, to include chain name in token id derivation | ||
// Custom token usage needs to be moved to ITS factory tests | ||
// revert NotSupported(); | ||
} | ||
|
||
tokenId = interchainTokenId(deployer, salt); | ||
|
||
emit InterchainTokenIdClaimed(tokenId, deployer, salt); | ||
|
||
if (bytes(destinationChain).length == 0) { | ||
_deployTokenManager(tokenId, tokenManagerType, params); | ||
_deployTokenManager(tokenId, tokenManagerType, destinationTokenAddress.toAddress(), linkParams); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. linkParams are just the operator for local deployments it seems. |
||
} else { | ||
if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); | ||
|
||
_deployRemoteTokenManager(tokenId, destinationChain, gasValue, tokenManagerType, params); | ||
_linkToken(tokenId, destinationChain, destinationTokenAddress, tokenManagerType, linkParams, gasValue); | ||
} | ||
} | ||
|
||
|
@@ -370,7 +419,7 @@ contract InterchainTokenService is | |
if (bytes(destinationChain).length == 0) { | ||
address tokenAddress = _deployInterchainToken(tokenId, minter, name, symbol, decimals); | ||
|
||
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minter, tokenAddress)); | ||
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minter); | ||
} else { | ||
if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); | ||
|
||
|
@@ -731,15 +780,13 @@ contract InterchainTokenService is | |
/** | ||
* @notice Processes a deploy token manager payload. | ||
*/ | ||
function _processDeployTokenManagerPayload(bytes memory payload) internal { | ||
(, bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) = abi.decode( | ||
payload, | ||
(uint256, bytes32, TokenManagerType, bytes) | ||
); | ||
function _processLinkTokenPayload(bytes memory payload) internal { | ||
(, bytes32 tokenId, TokenManagerType tokenManagerType, , bytes memory destinationTokenAddress, , bytes memory linkParams) = abi | ||
.decode(payload, (uint256, bytes32, TokenManagerType, bytes, bytes, bool, bytes)); | ||
|
||
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); | ||
|
||
_deployTokenManager(tokenId, tokenManagerType, params); | ||
_deployTokenManager(tokenId, tokenManagerType, destinationTokenAddress.toAddress(), linkParams); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think even if the only param left in EVM is the operator, we should have it be abi encoded when it is passed as params. |
||
} | ||
|
||
/** | ||
|
@@ -755,7 +802,7 @@ contract InterchainTokenService is | |
|
||
tokenAddress = _deployInterchainToken(tokenId, minterBytes, name, symbol, decimals); | ||
|
||
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minterBytes, tokenAddress)); | ||
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minterBytes); | ||
} | ||
|
||
/** | ||
|
@@ -804,9 +851,6 @@ contract InterchainTokenService is | |
|
||
// Check whether the ITS call should be routed via ITS hub for this destination chain | ||
if (keccak256(abi.encodePacked(destinationAddress)) == ITS_HUB_ROUTING_IDENTIFIER_HASH) { | ||
// Prevent deploy token manager to be usable on ITS hub | ||
if (_getMessageType(payload) == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) revert NotSupported(); | ||
|
||
// Wrap ITS message in an ITS Hub message | ||
payload = abi.encode(MESSAGE_TYPE_SEND_TO_HUB, destinationChain, payload); | ||
destinationChain = ITS_HUB_CHAIN_NAME; | ||
|
@@ -833,10 +877,10 @@ contract InterchainTokenService is | |
if (messageType == MESSAGE_TYPE_INTERCHAIN_TRANSFER) { | ||
address expressExecutor = _getExpressExecutorAndEmitEvent(commandId, sourceChain, sourceAddress, payloadHash); | ||
_processInterchainTransferPayload(commandId, expressExecutor, originalSourceChain, payload); | ||
} else if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) { | ||
_processDeployTokenManagerPayload(payload); | ||
} else if (messageType == MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) { | ||
_processDeployInterchainTokenPayload(payload); | ||
} else if (messageType == MESSAGE_TYPE_LINK_TOKEN) { | ||
_processLinkTokenPayload(payload); | ||
} else { | ||
revert InvalidMessageType(messageType); | ||
} | ||
|
@@ -875,9 +919,6 @@ contract InterchainTokenService is | |
|
||
// Get message type of the inner ITS message | ||
messageType = _getMessageType(payload); | ||
|
||
// Prevent deploy token manager to be usable on ITS hub | ||
if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) revert NotSupported(); | ||
} else { | ||
// Prevent receiving a direct message from the ITS Hub. This is not supported yet. | ||
if (keccak256(abi.encodePacked(sourceChain)) == ITS_HUB_CHAIN_NAME_HASH) revert UntrustedChain(); | ||
|
@@ -890,23 +931,32 @@ contract InterchainTokenService is | |
* @notice Deploys a token manager on a destination chain. | ||
* @param tokenId The ID of the token. | ||
* @param destinationChain The chain where the token manager will be deployed. | ||
* @param gasValue The amount of gas to be paid for the transaction. | ||
* @param destinationTokenAddress The address of the token on the destination chain. | ||
* @param tokenManagerType The type of token manager to be deployed. | ||
* @param params Additional parameters for the token manager deployment. | ||
* @param params Additional parameters for the token linking. | ||
* @param gasValue The amount of gas to be paid for the transaction. | ||
*/ | ||
function _deployRemoteTokenManager( | ||
function _linkToken( | ||
bytes32 tokenId, | ||
string calldata destinationChain, | ||
uint256 gasValue, | ||
bytes memory destinationTokenAddress, | ||
TokenManagerType tokenManagerType, | ||
bytes calldata params | ||
bytes memory params, | ||
uint256 gasValue | ||
) internal { | ||
// slither-disable-next-line unused-return | ||
deployedTokenManager(tokenId); | ||
bytes memory sourceTokenAddress = registeredTokenAddress(tokenId).toBytes(); | ||
|
||
emit TokenManagerDeploymentStarted(tokenId, destinationChain, tokenManagerType, params); | ||
emit LinkTokenStarted(tokenId, destinationChain, sourceTokenAddress, destinationTokenAddress, tokenManagerType, params); | ||
|
||
bytes memory payload = abi.encode(MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params); | ||
bytes memory payload = abi.encode( | ||
MESSAGE_TYPE_LINK_TOKEN, | ||
tokenId, | ||
tokenManagerType, | ||
sourceTokenAddress, | ||
destinationTokenAddress, | ||
params | ||
); | ||
|
||
_callContract(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); | ||
} | ||
|
@@ -948,9 +998,13 @@ contract InterchainTokenService is | |
* @notice Deploys a token manager. | ||
* @param tokenId The ID of the token. | ||
* @param tokenManagerType The type of the token manager to be deployed. | ||
* @param params Additional parameters for the token manager deployment. | ||
* @param tokenAddress The address of the token to be managed. | ||
* @param operator The operator of the token manager. | ||
*/ | ||
function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) internal { | ||
function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, address tokenAddress, bytes memory operator) internal { | ||
// TokenManagerProxy params | ||
bytes memory params = abi.encode(operator, tokenAddress); | ||
|
||
(bool success, bytes memory returnData) = tokenManagerDeployer.delegatecall( | ||
abi.encodeWithSelector(ITokenManagerDeployer.deployTokenManager.selector, tokenId, tokenManagerType, params) | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens to already deployed custom tokens?