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 #255

Closed
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
62 changes: 18 additions & 44 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ import { Pausable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/util
import { InterchainAddressTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/InterchainAddressTracker.sol';

import { IInterchainTokenService } from './interfaces/IInterchainTokenService.sol';
import { ITokenManagerProxy } from './interfaces/ITokenManagerProxy.sol';
import { ITokenHandler } from './interfaces/ITokenHandler.sol';
import { ITokenManagerDeployer } from './interfaces/ITokenManagerDeployer.sol';
import { IInterchainTokenDeployer } from './interfaces/IInterchainTokenDeployer.sol';
import { IInterchainTokenExecutable } from './interfaces/IInterchainTokenExecutable.sol';
import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainTokenExpressExecutable.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { IERC20Named } from './interfaces/IERC20Named.sol';

import { Operator } from './utils/Operator.sol';

Expand Down Expand Up @@ -264,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 @@ -276,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 @@ -323,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 @@ -396,21 +399,11 @@ contract InterchainTokenService is

IERC20 token;
{
ITokenManager tokenManager_ = ITokenManager(tokenManagerAddress(tokenId));
token = IERC20(tokenManager_.tokenAddress());

(bool success, bytes memory returnData) = tokenHandler.delegatecall(
abi.encodeWithSelector(
ITokenHandler.transferTokenFrom.selector,
tokenManager_.implementationType(),
address(token),
msg.sender,
destinationAddress,
amount
)
abi.encodeWithSelector(ITokenHandler.transferTokenFrom.selector, tokenId, msg.sender, destinationAddress, amount)
);
if (!success) revert TokenHandlerFailed(returnData);
amount = abi.decode(returnData, (uint256));
(amount, token) = abi.decode(returnData, (uint256, IERC20));
}

// slither-disable-next-line reentrancy-events
Expand Down Expand Up @@ -738,14 +731,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 @@ -762,7 +756,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 Expand Up @@ -1073,44 +1067,24 @@ contract InterchainTokenService is
* @dev Takes token from a sender via the token service. `tokenOnly` indicates if the caller should be restricted to the token only.
*/
function _takeToken(bytes32 tokenId, address from, uint256 amount, bool tokenOnly) internal returns (uint256, string memory symbol) {
address tokenManager_ = tokenManagerAddress(tokenId);
uint256 tokenManagerType;
address tokenAddress;

(tokenManagerType, tokenAddress) = ITokenManagerProxy(tokenManager_).getImplementationTypeAndTokenAddress();

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

(bool success, bytes memory data) = tokenHandler.delegatecall(
abi.encodeWithSelector(ITokenHandler.takeToken.selector, tokenManagerType, tokenAddress, tokenManager_, from, amount)
abi.encodeWithSelector(ITokenHandler.takeToken.selector, tokenId, tokenOnly, from, amount)
);
if (!success) revert TakeTokenFailed(data);
amount = abi.decode(data, (uint256));
(amount, symbol) = abi.decode(data, (uint256, string));

/// @dev Track the flow amount being sent out as a message
ITokenManager(tokenManager_).addFlowOut(amount);
if (tokenManagerType == uint256(TokenManagerType.GATEWAY)) {
symbol = IERC20Named(tokenAddress).symbol();
}
return (amount, symbol);
}

/**
* @dev Gives token to recipient via the token service.
*/
function _giveToken(bytes32 tokenId, address to, uint256 amount) internal returns (uint256, address) {
address tokenManager_ = tokenManagerAddress(tokenId);

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

/// @dev Track the flow amount being received via the message
ITokenManager(tokenManager_).addFlowIn(amount);

function _giveToken(bytes32 tokenId, address to, uint256 amount) internal returns (uint256, address tokenAddress) {
(bool success, bytes memory data) = tokenHandler.delegatecall(
abi.encodeWithSelector(ITokenHandler.giveToken.selector, tokenManagerType, tokenAddress, tokenManager_, to, amount)
abi.encodeWithSelector(ITokenHandler.giveToken.selector, tokenId, to, amount)
);
if (!success) revert GiveTokenFailed(data);
amount = abi.decode(data, (uint256));
(amount, tokenAddress) = abi.decode(data, (uint256, address));

return (amount, tokenAddress);
}
Expand Down
116 changes: 72 additions & 44 deletions contracts/TokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import { ITokenHandler } from './interfaces/ITokenHandler.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { SafeTokenTransfer, SafeTokenTransferFrom, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { ReentrancyGuard } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/ReentrancyGuard.sol';
import { Create3Address } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Address.sol';

import { ITokenManagerType } from './interfaces/ITokenManagerType.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { ITokenManagerProxy } from './interfaces/ITokenManagerProxy.sol';
import { IERC20MintableBurnable } from './interfaces/IERC20MintableBurnable.sol';
import { IERC20BurnableFrom } from './interfaces/IERC20BurnableFrom.sol';
import { IERC20Named } from './interfaces/IERC20Named.sol';

/**
* @title TokenHandler
* @notice This interface is responsible for handling tokens before initiating an interchain token transfer, or after receiving one.
*/
contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard {
contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Create3Address {
using SafeTokenTransferFrom for IERC20;
using SafeTokenCall for IERC20;
using SafeTokenTransfer for IERC20;
Expand All @@ -32,119 +35,136 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard {

/**
* @notice This function gives token to a specified address from the token manager.
* @param tokenManagerType The token manager type.
* @param tokenAddress The address of the token to give.
* @param tokenManager The address of the token manager.
* @param tokenId The token id of the tokenManager.
* @param to The address to give tokens to.
* @param amount The amount of tokens to give.
* @return uint256 The amount of token actually given, which could be different for certain token type.
* @return address the address of the token.
*/
// slither-disable-next-line locked-ether
function giveToken(
uint256 tokenManagerType,
address tokenAddress,
address tokenManager,
address to,
uint256 amount
) external payable returns (uint256) {
function giveToken(bytes32 tokenId, address to, uint256 amount) external payable returns (uint256, address) {
Comment on lines -43 to +45
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make these new changes in a separate PR? This PR should be a simple cherry pick of the fix. Makes it easier to audit as well.

Was the issue bytecode size? if so, you can point this PR on the other PR that adds the bytecode fix but keep the token handler refactor separate?

address tokenManager = _create3Address(tokenId);

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

/// @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);
return amount;
_mintToken(tokenManager, tokenAddress, to, amount);
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, tokenManager, to, amount);
return amount;
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, tokenManager, to, amount);
return amount;
return (amount, tokenAddress);
}

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

revert UnsupportedTokenManagerType(tokenManagerType);
}

/**
* @notice This function takes token from a specified address to the token manager.
* @param tokenManagerType The token manager type.
* @param tokenAddress The address of the token to give.
* @param tokenManager The address of the token manager.
* @param tokenId The tokenId for the token.
* @param tokenOnly can onky be called from the token.
* @param from The address to take tokens from.
* @param amount The amount of token to take.
* @return uint256 The amount of token actually taken, which could be different for certain token type.
* @return symbol The symbol for the token, if not empty the token is a gateway token and a callContractWith token has to be made.
*/
// slither-disable-next-line locked-ether
function takeToken(
uint256 tokenManagerType,
address tokenAddress,
address tokenManager,
bytes32 tokenId,
bool tokenOnly,
address from,
uint256 amount
) external payable returns (uint256) {
) 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);

/// @dev Track the flow amount being sent out as a message
ITokenManager(tokenManager).addFlowOut(amount);
if (tokenManagerType == uint256(TokenManagerType.GATEWAY)) {
symbol = IERC20Named(tokenAddress).symbol();
}

if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_takeInterchainToken(tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_takeTokenMintBurn(tokenAddress, from, amount);
return amount;
_burnToken(tokenManager, tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_takeTokenMintBurnFrom(tokenAddress, from, amount);
return amount;
_burnTokenFrom(tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, from, tokenManager, amount);
return amount;
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, from, tokenManager, amount);
return amount;
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.GATEWAY)) {
_transferTokenFrom(tokenAddress, from, address(this), amount);
return amount;
return (amount, symbol);
}

revert UnsupportedTokenManagerType(tokenManagerType);
}

/**
* @notice This function transfers token from and to a specified address.
* @param tokenManagerType The token manager type.
* @param tokenAddress the address of the token to give.
* @param tokenId The token id of the token manager.
* @param from The address to transfer tokens from.
* @param to The address to transfer tokens to.
* @param amount The amount of token to transfer.
* @return uint256 The amount of token actually transferred, which could be different for certain token type.
* @return address The address of the token corresponding to the input tokenId.
*/
// slither-disable-next-line locked-ether
function transferTokenFrom(
uint256 tokenManagerType,
address tokenAddress,
address from,
address to,
uint256 amount
) external payable returns (uint256) {
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) ||
tokenManagerType == uint256(TokenManagerType.GATEWAY)
) {
_transferTokenFrom(tokenAddress, from, to, amount);
return amount;
return (amount, tokenAddress);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, from, to, amount);
return amount;
return (amount, tokenAddress);
}

revert UnsupportedTokenManagerType(tokenManagerType);
Expand Down Expand Up @@ -196,15 +216,23 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard {
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
4 changes: 2 additions & 2 deletions contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ interface IInterchainTokenService is
error InvalidChainName();
error NotRemoteService();
error TokenManagerDoesNotExist(bytes32 tokenId);
error NotToken(address caller, address token);
error ExecuteWithInterchainTokenFailed(address contractAddress);
error ExpressExecuteWithInterchainTokenFailed(address contractAddress);
error GatewayToken();
Expand All @@ -47,6 +46,7 @@ interface IInterchainTokenService is
error TokenHandlerFailed(bytes data);
error EmptyData();
error PostDeployFailed(bytes data);
error CannotDeploy(TokenManagerType);
error ZeroAmount();

event InterchainTransfer(
Expand Down Expand Up @@ -167,7 +167,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
Loading
Loading