// Source: contracts/TokenHandler.sol pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol@v6.0.4 // General interface for upgradable contracts interface IContractIdentifier { /** * @notice Returns the contract ID. It can be used as a check during upgrades. * @dev Meant to be overridden in derived contracts. * @return bytes32 The contract ID */ function contractId() external pure returns (bytes32); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol@v6.0.4 interface IImplementation is IContractIdentifier { error NotProxy(); function setup(bytes calldata data) external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol@v6.0.4 /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { error InvalidAccount(); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol@v6.0.4 error TokenTransferFailed(); /* * @title SafeTokenCall * @dev This library is used for performing safe token transfers. */ library SafeTokenCall { /* * @notice Make a safe call to a token contract. * @param token The token contract to interact with. * @param callData The function call data. * @throws TokenTransferFailed error if transfer of token is not successful. */ function safeCall(IERC20 token, bytes memory callData) internal { (bool success, bytes memory returnData) = address(token).call(callData); bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool))); if (!transferred || address(token).code.length == 0) revert TokenTransferFailed(); } } /* * @title SafeTokenTransfer * @dev This library safely transfers tokens from the contract to a recipient. */ library SafeTokenTransfer { /* * @notice Transfer tokens to a recipient. * @param token The token contract. * @param receiver The recipient of the tokens. * @param amount The amount of tokens to transfer. */ function safeTransfer(IERC20 token, address receiver, uint256 amount) internal { SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount)); } } /* * @title SafeTokenTransferFrom * @dev This library helps to safely transfer tokens on behalf of a token holder. */ library SafeTokenTransferFrom { /* * @notice Transfer tokens on behalf of a token holder. * @param token The token contract. * @param from The address of the token holder. * @param to The address the tokens are to be sent to. * @param amount The amount of tokens to be transferred. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal { SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount)); } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IReentrancyGuard.sol@v6.0.4 /** * @title ReentrancyGuard * @notice This contract provides a mechanism to halt the execution of specific functions * if a pause condition is activated. */ interface IReentrancyGuard { error ReentrantCall(); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/ReentrancyGuard.sol@v6.0.4 /** * @title ReentrancyGuard * @notice This contract provides a mechanism to halt the execution of specific functions * if a pause condition is activated. */ contract ReentrancyGuard is IReentrancyGuard { // uint256(keccak256('ReentrancyGuard:entered')) - 1 uint256 internal constant ENTERED_SLOT = 0x1a771c70cada93a906f955a7dec24a83d7954ba2f75256be4febcf62b395d532; uint256 internal constant NOT_ENTERED = 1; uint256 internal constant ENTERED = 2; /** * @notice A modifier that throws a ReEntrancy custom error if the contract is entered * @dev This modifier should be used with functions that can be entered twice */ modifier noReEntrancy() { if (_hasEntered()) revert ReentrantCall(); _setEntered(ENTERED); _; _setEntered(NOT_ENTERED); } /** * @notice Check if the contract is already executing. * @return entered A boolean representing the entered status. True if already executing, false otherwise. */ function _hasEntered() internal view returns (bool entered) { assembly { entered := eq(sload(ENTERED_SLOT), ENTERED) } } /** * @notice Sets the entered status of the contract * @param entered A boolean representing the entered status. True if already executing, false otherwise. */ function _setEntered(uint256 entered) internal { assembly { sstore(ENTERED_SLOT, entered) } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol@v6.0.4 /** * @title IRolesBase Interface * @notice IRolesBase is an interface that abstracts the implementation of a * contract with role control internal functions. */ interface IRolesBase { error MissingRole(address account, uint8 role); error MissingAllRoles(address account, uint256 accountRoles); error MissingAnyOfRoles(address account, uint256 accountRoles); error InvalidProposedRoles(address fromAccount, address toAccount, uint256 accountRoles); event RolesProposed(address indexed fromAccount, address indexed toAccount, uint256 accountRoles); event RolesAdded(address indexed account, uint256 accountRoles); event RolesRemoved(address indexed account, uint256 accountRoles); /** * @notice Checks if an account has a role. * @param account The address to check * @param role The role to check * @return True if the account has the role, false otherwise */ function hasRole(address account, uint8 role) external view returns (bool); } // File contracts/interfaces/IMinter.sol /** * @title IMinter Interface * @notice An interface for a contract module which provides a basic access control mechanism, where * there is an account (a minter) that can be granted exclusive access to specific functions. */ interface IMinter is IRolesBase { /** * @notice Change the minter of the contract. * @dev Can only be called by the current minter. * @param minter_ The address of the new minter. */ function transferMintership(address minter_) external; /** * @notice Proposed a change of the minter of the contract. * @dev Can only be called by the current minter. * @param minter_ The address of the new minter. */ function proposeMintership(address minter_) external; /** * @notice Accept a change of the minter of the contract. * @dev Can only be called by the proposed minter. * @param fromMinter The previous minter. */ function acceptMintership(address fromMinter) external; /** * @notice Query if an address is a minter * @param addr the address to query for * @return bool Boolean value representing whether or not the address is a minter. */ function isMinter(address addr) external view returns (bool); } // File contracts/interfaces/IOperator.sol /** * @title IOperator Interface * @notice An interface for a contract module which provides a basic access control mechanism, where * there is an account (a operator) that can be granted exclusive access to specific functions. */ interface IOperator is IRolesBase { /** * @notice Change the operator of the contract. * @dev Can only be called by the current operator. * @param operator_ The address of the new operator. */ function transferOperatorship(address operator_) external; /** * @notice Proposed a change of the operator of the contract. * @dev Can only be called by the current operator. * @param operator_ The address of the new operator. */ function proposeOperatorship(address operator_) external; /** * @notice Accept a proposed change of operatorship. * @dev Can only be called by the proposed operator. * @param fromOperator The previous operator of the contract. */ function acceptOperatorship(address fromOperator) external; /** * @notice Query if an address is a operator. * @param addr The address to query for. * @return bool Boolean value representing whether or not the address is an operator. */ function isOperator(address addr) external view returns (bool); } // File contracts/interfaces/IBaseTokenManager.sol /** * @title IBaseTokenManager * @notice This contract is defines the base token manager interface implemented by all token managers. */ interface IBaseTokenManager { /** * @notice A function that returns the token id. */ function interchainTokenId() external view returns (bytes32); /** * @notice A function that should return the address of the token. * Must be overridden in the inheriting contract. * @return address address of the token. */ function tokenAddress() external view returns (address); /** * @notice A function that should return the token address from the init params. */ function getTokenAddressFromParams(bytes calldata params) external pure returns (address); } // File contracts/interfaces/IFlowLimit.sol /** * @title FlowLimit Interface * @notice Interface for flow limit logic for interchain token transfers. */ interface IFlowLimit { error FlowLimitExceeded(uint256 limit, uint256 flowAmount, address tokenManager); error FlowAdditionOverflow(uint256 flowAmount, uint256 flowToAdd, address tokenManager); error FlowLimitOverflow(uint256 flowLimit, uint256 flowToCompare, address tokenManager); event FlowLimitSet(bytes32 indexed tokenId, address operator, uint256 flowLimit_); /** * @notice Returns the current flow limit. * @return flowLimit_ The current flow limit value. */ function flowLimit() external view returns (uint256 flowLimit_); /** * @notice Returns the current flow out amount. * @return flowOutAmount_ The current flow out amount. */ function flowOutAmount() external view returns (uint256 flowOutAmount_); /** * @notice Returns the current flow in amount. * @return flowInAmount_ The current flow in amount. */ function flowInAmount() external view returns (uint256 flowInAmount_); } // File contracts/interfaces/ITokenManager.sol /** * @title ITokenManager Interface * @notice This contract is responsible for managing tokens, such as setting locking token balances, or setting flow limits, for interchain transfers. */ interface ITokenManager is IBaseTokenManager, IOperator, IFlowLimit, IImplementation { error TokenLinkerZeroAddress(); error NotService(address caller); error TakeTokenFailed(); error GiveTokenFailed(); error NotToken(address caller); error ZeroAddress(); error AlreadyFlowLimiter(address flowLimiter); error NotFlowLimiter(address flowLimiter); error NotSupported(); /** * @notice Returns implementation type of this token manager. * @return uint256 The implementation type of this token manager. */ function implementationType() external view returns (uint256); function addFlowIn(uint256 amount) external; function addFlowOut(uint256 amount) external; /** * @notice This function adds a flow limiter for this TokenManager. * @dev Can only be called by the operator. * @param flowLimiter the address of the new flow limiter. */ function addFlowLimiter(address flowLimiter) external; /** * @notice This function removes a flow limiter for this TokenManager. * @dev Can only be called by the operator. * @param flowLimiter the address of an existing flow limiter. */ function removeFlowLimiter(address flowLimiter) external; /** * @notice Query if an address is a flow limiter. * @param addr The address to query for. * @return bool Boolean value representing whether or not the address is a flow limiter. */ function isFlowLimiter(address addr) external view returns (bool); /** * @notice This function sets the flow limit for this TokenManager. * @dev Can only be called by the flow limiters. * @param flowLimit_ The maximum difference between the tokens flowing in and/or out at any given interval of time (6h). */ function setFlowLimit(uint256 flowLimit_) external; /** * @notice A function to renew approval to the service if we need to. */ function approveService() external; /** * @notice Getter function for the parameters of a lock/unlock TokenManager. * @dev This function will be mainly used by frontends. * @param operator_ The operator of the TokenManager. * @param tokenAddress_ The token to be managed. * @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; } // File contracts/interfaces/ITokenHandler.sol /** * @title ITokenHandler Interface * @notice This interface is responsible for handling tokens before initiating an interchain token transfer, or after receiving one. */ interface ITokenHandler { error UnsupportedTokenManagerType(uint256 tokenManagerType); error NotToken(address caller, address token); /** * @notice This function gives token to a specified address from 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. */ function giveToken(bytes32 tokenId, address to, uint256 amount) external returns (uint256, address); /** * @notice This function takes token from a specified address to the token manager. * @param tokenId The tokenId for the token. * @param tokenOnly can only 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. */ // slither-disable-next-line locked-ether function takeToken(bytes32 tokenId, bool tokenOnly, address from, uint256 amount) external payable returns (uint256); /** * @notice This function transfers token from and to a specified address. * @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. */ function transferTokenFrom(bytes32 tokenId, address from, address to, uint256 amount) external returns (uint256, address); /** * @notice This function prepares a token manager after it is deployed * @param tokenManagerType The token manager type. * @param tokenManager The address of the token manager. */ function postTokenManagerDeploy(uint256 tokenManagerType, ITokenManager tokenManager) external payable; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.sol@v6.0.4 // General interface for upgradable contracts interface IProxy { error InvalidOwner(); error InvalidImplementation(); error SetupFailed(); error NotOwner(); error AlreadyInitialized(); function implementation() external view returns (address); function setup(bytes calldata setupParams) external; } // File contracts/interfaces/ITokenManagerProxy.sol /** * @title ITokenManagerProxy Interface * @notice This interface is for a proxy for token manager contracts. */ interface ITokenManagerProxy is IProxy { error ZeroAddress(); /** * @notice Returns implementation type of this token manager. * @return uint256 The implementation type of this token manager. */ function implementationType() external view returns (uint256); /** * @notice Returns the interchain token ID of the token manager. * @return bytes32 The interchain token ID of the token manager. */ function interchainTokenId() external view returns (bytes32); /** * @notice Returns token address that this token manager manages. * @return address The token address. */ function tokenAddress() external view returns (address); /** * @notice Returns implementation type and token address. * @return uint256 The implementation type. * @return address The token address. */ function getImplementationTypeAndTokenAddress() external view returns (uint256, address); } // File contracts/interfaces/IERC20BurnableFrom.sol /** * @title IERC20BurnableFrom Interface * @notice Interface of the ERC20 standard as defined in the EIP. */ interface IERC20BurnableFrom { /** * @notice Function to burn tokens. * @dev Requires the caller to have allowance for `amount` on `from`. * Can only be called by the minter address. * @param from The address that will have its tokens burnt. * @param amount The amount of tokens to burn. */ function burnFrom(address from, uint256 amount) external; } // File contracts/interfaces/ITokenManagerType.sol /** * @title ITokenManagerType Interface * @notice A simple interface that defines all the token manager types. */ interface ITokenManagerType { enum TokenManagerType { 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. } } // File contracts/utils/Create3AddressFixed.sol /** * @title Create3AddressFixed contract * @notice This contract can be used to predict the deterministic deployment address of a contract deployed with the `CREATE3` technique. * It is equivalent to the Create3Address found in axelar-gmp-sdk-solidity repo but uses a fixed bytecode for CreateDeploy, * which allows changing compilation options (like number of runs) without affecting the future deployment addresses. */ contract Create3AddressFixed { // slither-disable-next-line too-many-digits bytes internal constant CREATE_DEPLOY_BYTECODE = hex'608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001d5760003560e01c806277436014610022575b600080fd5b61003561003036600461007b565b610037565b005b8051602082016000f061004957600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561008d57600080fd5b813567ffffffffffffffff808211156100a557600080fd5b818401915084601f8301126100b957600080fd5b8135818111156100cb576100cb61004c565b604051601f8201601f19908116603f011681019083821181831017156100f3576100f361004c565b8160405282815287602084870101111561010c57600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122094780ce55d28f1d568f4e0ab1b9dc230b96e952b73d2e06456fbff2289fa27f464736f6c63430008150033'; bytes32 internal constant CREATE_DEPLOY_BYTECODE_HASH = keccak256(CREATE_DEPLOY_BYTECODE); /** * @notice Compute the deployed address that will result from the `CREATE3` method. * @param deploySalt A salt to influence the contract address * @return deployed The deterministic contract address if it was deployed */ function _create3Address(bytes32 deploySalt) internal view returns (address deployed) { address deployer = address( uint160(uint256(keccak256(abi.encodePacked(hex'ff', address(this), deploySalt, CREATE_DEPLOY_BYTECODE_HASH)))) ); deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01'))))); } } // File contracts/TokenHandler.sol /** * @title TokenHandler * @notice This contract is responsible for handling tokens before initiating an interchain token transfer, or after receiving one. */ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Create3AddressFixed { using SafeTokenTransferFrom for IERC20; using SafeTokenCall for IERC20; /** * @notice This function gives token to a specified address from 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. */ function giveToken(bytes32 tokenId, address to, uint256 amount) external returns (uint256, address) { address tokenManager = _create3Address(tokenId); (uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress(); _migrateToken(tokenManager, tokenAddress, tokenManagerType); /// @dev Track the flow amount being received via the message ITokenManager(tokenManager).addFlowIn(amount); if ( tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM) ) { _mintToken(ITokenManager(tokenManager), tokenAddress, to, amount); } else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) { _transferTokenFrom(tokenAddress, tokenManager, to, amount); } else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) { amount = _transferTokenFromWithFee(tokenAddress, tokenManager, to, amount); } else { revert UnsupportedTokenManagerType(tokenManagerType); } return (amount, tokenAddress); } /** * @notice This function takes token from a specified address to the token manager. * @param tokenId The tokenId for the token. * @param tokenOnly can only 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. */ // slither-disable-next-line locked-ether function takeToken(bytes32 tokenId, bool tokenOnly, address from, uint256 amount) external payable returns (uint256) { address tokenManager = _create3Address(tokenId); (uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress(); if (tokenOnly && msg.sender != tokenAddress) revert NotToken(msg.sender, tokenAddress); _migrateToken(tokenManager, tokenAddress, tokenManagerType); if ( tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN) ) { _burnToken(ITokenManager(tokenManager), tokenAddress, from, amount); } else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) { _burnTokenFrom(tokenAddress, from, amount); } else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) { _transferTokenFrom(tokenAddress, from, tokenManager, amount); } else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) { amount = _transferTokenFromWithFee(tokenAddress, from, tokenManager, amount); } else { revert UnsupportedTokenManagerType(tokenManagerType); } /// @dev Track the flow amount being sent out as a message ITokenManager(tokenManager).addFlowOut(amount); return amount; } /** * @notice This function transfers token from and to a specified address. * @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. */ function transferTokenFrom(bytes32 tokenId, address from, address to, uint256 amount) external 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) ) { _transferTokenFrom(tokenAddress, from, to, amount); return (amount, tokenAddress); } if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) { amount = _transferTokenFromWithFee(tokenAddress, from, to, amount); return (amount, tokenAddress); } revert UnsupportedTokenManagerType(tokenManagerType); } /** * @notice This function prepares a token manager after it is deployed * @param tokenManagerType The token manager type. * @param tokenManager The address of the token manager. */ // slither-disable-next-line locked-ether function postTokenManagerDeploy(uint256 tokenManagerType, ITokenManager tokenManager) external payable { // For native interchain tokens, we transfer mintership to the token manager. // This is done here because the InterchainToken bytecode is preferred to be fixed to avoid having multiple versions of the Token code used in production. if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) { IMinter(tokenManager.tokenAddress()).transferMintership(address(tokenManager)); // For lock/unlock token managers, the ITS contract needs an approval from the token manager to transfer tokens on its behalf. } else if ( tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK) || tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE) ) { tokenManager.approveService(); } } function _transferTokenFrom(address tokenAddress, address from, address to, uint256 amount) internal { // slither-disable-next-line arbitrary-send-erc20 IERC20(tokenAddress).safeTransferFrom(from, to, amount); } function _transferTokenFromWithFee( address tokenAddress, address from, address to, uint256 amount ) internal noReEntrancy returns (uint256) { uint256 balanceBefore = IERC20(tokenAddress).balanceOf(to); _transferTokenFrom(tokenAddress, from, to, amount); uint256 diff = IERC20(tokenAddress).balanceOf(to) - balanceBefore; return diff < amount ? diff : amount; } function _mintToken(ITokenManager tokenManager, address tokenAddress, address to, uint256 amount) internal { tokenManager.mintToken(tokenAddress, to, amount); } function _burnToken(ITokenManager tokenManager, address tokenAddress, address from, uint256 amount) internal { tokenManager.burnToken(tokenAddress, from, amount); } function _burnTokenFrom(address tokenAddress, address from, uint256 amount) internal { IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20BurnableFrom.burnFrom.selector, from, amount)); } /** * @notice This transfers mintership of a native Interchain token to the tokenManager if ITS is still its minter. * It does nothing if ITS is not the minter. This ensures that interchain tokens are auto-migrated without requiring a downtime for ITS. * @param tokenManager The token manager address to transfer mintership to. * @param tokenAddress The address of the token to transfer mintership of. * @param tokenManagerType The token manager type for the token. */ function _migrateToken(address tokenManager, address tokenAddress, uint256 tokenManagerType) internal { if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) && IMinter(tokenAddress).isMinter(address(this))) { IMinter(tokenAddress).transferMintership(tokenManager); } } }