Skip to content

Commit

Permalink
Merge branch 'feat/merge-new-token-managers' into feat/wrap-payload
Browse files Browse the repository at this point in the history
  • Loading branch information
Foivos committed Apr 15, 2024
2 parents c394e13 + f3b9c67 commit 0b0e606
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 96 deletions.
16 changes: 11 additions & 5 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ contract InterchainTokenService is
* part of a multicall involving multiple functions that could make remote contract calls.
* @param salt The salt to be used during deployment.
* @param destinationChain The name of the chain to deploy the TokenManager and standardized token to.
* @param tokenManagerType The type of TokenManager to be deployed.
* @param tokenManagerType The type of token manager to be deployed. Cannot be NATIVE_INTERCHAIN_TOKEN.
* @param params The params that will be used to initialize the TokenManager.
* @param gasValue The amount of native tokens to be used to pay for gas for the remote deployment.
* @return tokenId The tokenId corresponding to the deployed TokenManager.
Expand All @@ -278,9 +278,14 @@ contract InterchainTokenService is
bytes calldata params,
uint256 gasValue
) external payable whenNotPaused returns (bytes32 tokenId) {
// Custom token managers can't be deployed with Interchain token mint burn type, which is reserved for interchain tokens
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType);

address deployer = msg.sender;

if (deployer == interchainTokenFactory) deployer = TOKEN_FACTORY_DEPLOYER;
if (deployer == interchainTokenFactory) {
deployer = TOKEN_FACTORY_DEPLOYER;
}

tokenId = interchainTokenId(deployer, salt);

Expand Down Expand Up @@ -325,7 +330,7 @@ contract InterchainTokenService is
if (bytes(destinationChain).length == 0) {
address tokenAddress = _deployInterchainToken(tokenId, minter, name, symbol, decimals);

_deployTokenManager(tokenId, TokenManagerType.MINT_BURN, abi.encode(minter, tokenAddress));
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minter, tokenAddress));
} else {
_deployRemoteInterchainToken(tokenId, name, symbol, decimals, minter, destinationChain, gasValue);
}
Expand Down Expand Up @@ -760,14 +765,15 @@ contract InterchainTokenService is

/**
* @notice Processes a deploy token manager payload.
* @param payload The encoded data payload to be processed
*/
function _processDeployTokenManagerPayload(bytes memory payload) internal {
(, bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) = abi.decode(
payload,
(uint256, bytes32, TokenManagerType, bytes)
);

if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType);

_deployTokenManager(tokenId, tokenManagerType, params);
}

Expand All @@ -784,7 +790,7 @@ contract InterchainTokenService is

tokenAddress = _deployInterchainToken(tokenId, minterBytes, name, symbol, decimals);

_deployTokenManager(tokenId, TokenManagerType.MINT_BURN, abi.encode(minterBytes, tokenAddress));
_deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, abi.encode(minterBytes, tokenAddress));
}

/**
Expand Down
32 changes: 23 additions & 9 deletions contracts/TokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
/// @dev Track the flow amount being received via the message
ITokenManager(tokenManager).addFlowIn(amount);

if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_giveInterchainToken(tokenAddress, to, amount);
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_giveTokenMintBurn(tokenAddress, to, amount);
_mintToken(tokenManager, tokenAddress, to, amount);
return (amount, tokenAddress);
}

Expand Down Expand Up @@ -90,15 +95,16 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
uint256 amount
) external payable returns (uint256, string memory symbol) {
address tokenManager = _create3Address(tokenId);

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

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

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_takeTokenMintBurn(tokenAddress, from, amount);
if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_takeInterchainToken(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_burnToken(tokenManager, tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_takeTokenMintBurnFrom(tokenAddress, from, amount);
_burnTokenFrom(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, from, tokenManager, amount);
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
Expand Down Expand Up @@ -128,10 +134,10 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
// slither-disable-next-line locked-ether
function transferTokenFrom(bytes32 tokenId, address from, address to, uint256 amount) external payable returns (uint256, address) {
address tokenManager = _create3Address(tokenId);

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

if (
tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) ||
tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK) ||
tokenManagerType == uint256(TokenManagerType.MINT_BURN) ||
tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM) ||
Expand Down Expand Up @@ -195,15 +201,23 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
return amount;
}

function _giveTokenMintBurn(address tokenAddress, address to, uint256 amount) internal {
function _giveInterchainToken(address tokenAddress, address to, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.mint.selector, to, amount));
}

function _takeTokenMintBurn(address tokenAddress, address from, uint256 amount) internal {
function _takeInterchainToken(address tokenAddress, address from, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.burn.selector, from, amount));
}

function _takeTokenMintBurnFrom(address tokenAddress, address from, uint256 amount) internal {
function _mintToken(address tokenManager, address tokenAddress, address to, uint256 amount) internal {
ITokenManager(tokenManager).mintToken(tokenAddress, to, amount);
}

function _burnToken(address tokenManager, address tokenAddress, address from, uint256 amount) internal {
ITokenManager(tokenManager).burnToken(tokenAddress, from, amount);
}

function _burnTokenFrom(address tokenAddress, address from, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20BurnableFrom.burnFrom.selector, from, amount));
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface IInterchainTokenService is
error EmptyData();
error PostDeployFailed(bytes data);
error ZeroAmount();
error CannotDeploy(TokenManagerType);
error InvalidGatewayTokenTransfer(bytes32 tokenId, bytes payload, string tokenSymbol, uint256 amount);

event InterchainTransfer(
Expand Down Expand Up @@ -167,7 +168,7 @@ interface IInterchainTokenService is
* @notice Deploys a custom token manager contract on a remote chain.
* @param salt The salt used for token manager deployment.
* @param destinationChain The name of the destination chain.
* @param tokenManagerType The type of token manager.
* @param tokenManagerType The type of token manager. Cannot be NATIVE_INTERCHAIN_TOKEN.
* @param params The deployment parameters.
* @param gasValue The gas value for deployment.
* @return tokenId The tokenId associated with the token manager.
Expand Down
18 changes: 18 additions & 0 deletions contracts/interfaces/ITokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,22 @@ interface ITokenManager is IBaseTokenManager, IOperator, IFlowLimit, IImplementa
* @return params_ The resulting params to be passed to custom TokenManager deployments.
*/
function params(bytes calldata operator_, address tokenAddress_) external pure returns (bytes memory params_);

/**
* @notice External function to allow the service to mint tokens through the tokenManager
* @dev This function should revert if called by anyone but the service.
* @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager.
* @param to The recipient.
* @param amount The amount to mint.
*/
function mintToken(address tokenAddress_, address to, uint256 amount) external;

/**
* @notice External function to allow the service to burn tokens through the tokenManager
* @dev This function should revert if called by anyone but the service.
* @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager.
* @param from The address to burn the token from.
* @param amount The amount to burn.
*/
function burnToken(address tokenAddress_, address from, uint256 amount) external;
}
11 changes: 6 additions & 5 deletions contracts/interfaces/ITokenManagerType.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ pragma solidity ^0.8.0;
*/
interface ITokenManagerType {
enum TokenManagerType {
MINT_BURN,
MINT_BURN_FROM,
LOCK_UNLOCK,
LOCK_UNLOCK_FEE,
GATEWAY
NATIVE_INTERCHAIN_TOKEN, // This type is reserved for interchain tokens deployed by ITS, and can't be used by custom token managers.
MINT_BURN_FROM, // The token will be minted/burned on transfers. The token needs to give mint permission to the token manager, but burning happens via an approval.
LOCK_UNLOCK, // The token will be locked/unlocked at the token manager.
LOCK_UNLOCK_FEE, // The token will be locked/unlocked at the token manager, which will account for any fee-on-transfer behaviour.
MINT_BURN, // The token will be minted/burned on transfers. The token needs to give mint and burn permission to the token manager.
GATEWAY // The token will be sent throught the gateway via callContractWithToken
}
}
2 changes: 1 addition & 1 deletion contracts/test/TestInterchainTokenStandard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract TestInterchainTokenStandard is InterchainTokenStandard, Minter, ERC20,
_burn(account, amount);
}

function burnFrom(address account, uint256 amount) external onlyRole(uint8(Roles.MINTER)) {
function burnFrom(address account, uint256 amount) external {
uint256 currentAllowance = allowance[account][msg.sender];
if (currentAllowance < amount) revert AllowanceExceeded();
_approve(account, msg.sender, currentAllowance - amount);
Expand Down
23 changes: 23 additions & 0 deletions contracts/token-manager/TokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts
import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol';

import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IERC20MintableBurnable } from '../interfaces/IERC20MintableBurnable.sol';

import { Operator } from '../utils/Operator.sol';
import { FlowLimit } from '../utils/FlowLimit.sol';
Expand Down Expand Up @@ -190,4 +191,26 @@ contract TokenManager is ITokenManager, Operator, FlowLimit, Implementation, Mul
function params(bytes calldata operator_, address tokenAddress_) external pure returns (bytes memory params_) {
params_ = abi.encode(operator_, tokenAddress_);
}

/**
* @notice External function to allow the service to mint tokens through the tokenManager
* @dev This function should revert if called by anyone but the service.
* @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager.
* @param to The recipient.
* @param amount The amount to mint.
*/
function mintToken(address tokenAddress_, address to, uint256 amount) external onlyService {
IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.mint.selector, to, amount));
}

/**
* @notice External function to allow the service to burn tokens through the tokenManager
* @dev This function should revert if called by anyone but the service.
* @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager.
* @param from The address to burn the token from.
* @param amount The amount to burn.
*/
function burnToken(address tokenAddress_, address from, uint256 amount) external onlyService {
IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.burn.selector, from, amount));
}
}
Loading

0 comments on commit 0b0e606

Please sign in to comment.