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: merge new token managers #259

Merged
merged 20 commits into from
Apr 15, 2024
Merged
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
16 changes: 11 additions & 5 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,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 @@ -274,9 +274,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 @@ -321,7 +326,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 @@ -756,14 +761,15 @@ contract InterchainTokenService is

/**
* @notice Processes a deploy token manager payload.
* @param payload The encoded data payload to be processed
*/
function _processDeployTokenManagerPayload(bytes calldata 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 @@ -780,7 +786,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
Loading