// Source: contracts/utils/InterchainTokenDeployer.sol pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // 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 contracts/interfaces/IERC20Named.sol /** * @title IERC20Named Interface * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Named is IERC20 { /** * @notice Getter for the name of the token. * @return string Name of the token. */ function name() external view returns (string memory); /** * @notice Getter for the symbol of the token. * @return string The symbol of the token. */ function symbol() external view returns (string memory); /** * @notice Getter for the decimals of the token. * @return uint8 The decimals of the token. */ function decimals() external view returns (uint8); } // File contracts/interfaces/IERC20MintableBurnable.sol /** * @title IERC20MintableBurnable Interface * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20MintableBurnable { /** * @notice Function to mint new tokens. * @dev Can only be called by the minter address. * @param to The address that will receive the minted tokens. * @param amount The amount of tokens to mint. */ function mint(address to, uint256 amount) external; /** * @notice Function to burn tokens. * @dev 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 burn(address from, uint256 amount) external; } // File contracts/interfaces/IInterchainTokenStandard.sol /** * @title IInterchainTokenStandard interface * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IInterchainTokenStandard { /** * @notice Implementation of the interchainTransfer method. * @dev We chose to either pass `metadata` as raw data on a remote contract call, or if no data is passed, just do a transfer. * A different implementation could use metadata to specify a function to invoke, or for other purposes as well. * @param destinationChain The destination chain identifier. * @param recipient The bytes representation of the address of the recipient. * @param amount The amount of token to be transferred. * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract). */ function interchainTransfer( string calldata destinationChain, bytes calldata recipient, uint256 amount, bytes calldata metadata ) external payable; /** * @notice Implementation of the interchainTransferFrom method * @dev We chose to either pass `metadata` as raw data on a remote contract call, or, if no data is passed, just do a transfer. * A different implementation could use metadata to specify a function to invoke, or for other purposes as well. * @param sender The sender of the tokens. They need to have approved `msg.sender` before this is called. * @param destinationChain The string representation of the destination chain. * @param recipient The bytes representation of the address of the recipient. * @param amount The amount of token to be transferred. * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract.) */ function interchainTransferFrom( address sender, string calldata destinationChain, bytes calldata recipient, uint256 amount, bytes calldata metadata ) external payable; } // 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/IInterchainToken.sol /** * @title IInterchainToken interface * @dev Extends IInterchainTokenStandard and IMinter. */ interface IInterchainToken is IInterchainTokenStandard, IMinter, IERC20MintableBurnable, IERC20Named { error InterchainTokenServiceAddressZero(); error TokenIdZero(); error TokenNameEmpty(); error TokenSymbolEmpty(); error AlreadyInitialized(); /** * @notice Getter for the interchain token service contract. * @dev Needs to be overwitten. * @return interchainTokenServiceAddress The interchain token service address. */ function interchainTokenService() external view returns (address interchainTokenServiceAddress); /** * @notice Getter for the tokenId used for this token. * @dev Needs to be overwitten. * @return tokenId_ The tokenId for this token. */ function interchainTokenId() external view returns (bytes32 tokenId_); /** * @notice Setup function to initialize contract parameters. * @param tokenId_ The tokenId of the token. * @param minter The address of the token minter. * @param tokenName The name of the token. * @param tokenSymbol The symbopl of the token. * @param tokenDecimals The decimals of the token. */ function init(bytes32 tokenId_, address minter, string calldata tokenName, string calldata tokenSymbol, uint8 tokenDecimals) external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/CreateDeploy.sol@v6.0.4 /** * @title CreateDeploy Contract * @notice This contract deploys new contracts using the `CREATE` opcode and is used as part of * the `CREATE3` deployment method. */ contract CreateDeploy { /** * @dev Deploys a new contract with the specified bytecode using the `CREATE` opcode. * @param bytecode The bytecode of the contract to be deployed */ // slither-disable-next-line locked-ether function deploy(bytes memory bytecode) external payable { assembly { if iszero(create(0, add(bytecode, 32), mload(bytecode))) { revert(0, 0) } } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IDeploy.sol@v6.0.4 /** * @title IDeploy Interface * @notice This interface defines the errors for a contract that is responsible for deploying new contracts. */ interface IDeploy { error EmptyBytecode(); error AlreadyDeployed(); error DeployFailed(); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/libs/ContractAddress.sol@v6.0.4 library ContractAddress { function isContract(address contractAddress) internal view returns (bool) { bytes32 existingCodeHash = contractAddress.codehash; // https://eips.ethereum.org/EIPS/eip-1052 // keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 return existingCodeHash != bytes32(0) && existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; } } // 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/utils/Create3Fixed.sol /** * @title Create3Fixed contract * @notice This contract can be used to deploy a contract with a deterministic address that depends only on * the deployer address and deployment salt, not the contract bytecode and constructor parameters. * It uses a fixed bytecode to allow changing the compilation settings without affecting the deployment address in the future. */ contract Create3Fixed is Create3AddressFixed, IDeploy { using ContractAddress for address; /** * @notice Deploys a new contract using the `CREATE3` method. * @dev This function first deploys the CreateDeploy contract using * the `CREATE2` opcode and then utilizes the CreateDeploy to deploy the * new contract with the `CREATE` opcode. * @param bytecode The bytecode of the contract to be deployed * @param deploySalt A salt to influence the contract address * @return deployed The address of the deployed contract */ function _create3(bytes memory bytecode, bytes32 deploySalt) internal returns (address deployed) { deployed = _create3Address(deploySalt); if (bytecode.length == 0) revert EmptyBytecode(); if (deployed.isContract()) revert AlreadyDeployed(); // Deploy using create2 CreateDeploy createDeploy; bytes memory createDeployBytecode_ = CREATE_DEPLOY_BYTECODE; uint256 length = createDeployBytecode_.length; assembly { createDeploy := create2(0, add(createDeployBytecode_, 0x20), length, deploySalt) } if (address(createDeploy) == address(0)) revert DeployFailed(); // Deploy using create createDeploy.deploy(bytecode); } } // File contracts/interfaces/IInterchainTokenDeployer.sol /** * @title IInterchainTokenDeployer * @notice This interface is used to deploy new instances of the InterchainTokenProxy contract. */ interface IInterchainTokenDeployer { error AddressZero(); error TokenDeploymentFailed(); /** * @notice Returns the interchain token implementation address. * @return address The interchain token implementation address. */ function implementationAddress() external view returns (address); /** * @notice Returns the interchain token deployment address. * @param salt The deployment salt. * @return tokenAddress The token address. */ function deployedAddress(bytes32 salt) external view returns (address tokenAddress); /** * @notice Deploys a new instance of the InterchainTokenProxy contract. * @param salt The salt used by Create3Deployer. * @param tokenId tokenId of the token. * @param minter Address of the minter. * @param name Name of the token. * @param symbol Symbol of the token. * @param decimals Decimals of the token. * @return tokenAddress Address of the deployed token. */ function deployInterchainToken( bytes32 salt, bytes32 tokenId, address minter, string calldata name, string calldata symbol, uint8 decimals ) external returns (address tokenAddress); } // File contracts/utils/InterchainTokenDeployer.sol /** * @title InterchainTokenDeployer * @notice This contract is used to deploy new instances of the InterchainTokenProxy contract. */ contract InterchainTokenDeployer is IInterchainTokenDeployer, Create3Fixed { address public immutable implementationAddress; /** * @notice Constructor for the InterchainTokenDeployer contract. * @param implementationAddress_ Address of the InterchainToken contract. */ constructor(address implementationAddress_) { if (implementationAddress_ == address(0)) revert AddressZero(); implementationAddress = implementationAddress_; } /** * @notice Deploys a new instance of the InterchainTokenProxy contract. * @param salt The salt used by Create3Deployer. * @param tokenId TokenId for the token. * @param minter Address of the minter. * @param name Name of the token. * @param symbol Symbol of the token. * @param decimals Decimals of the token. * @return tokenAddress Address of the deployed token. */ function deployInterchainToken( bytes32 salt, bytes32 tokenId, address minter, string calldata name, string calldata symbol, uint8 decimals ) external returns (address tokenAddress) { // Use a minimal proxy for cheap token deployment and auto-verification on explorers // https://eips.ethereum.org/EIPS/eip-1167 // The minimal proxy bytecode is the same as https://github.com/OpenZeppelin/openzeppelin-contracts/blob/94697be8a3f0dfcd95dfb13ffbd39b5973f5c65d/contracts/proxy/Clones.sol#L28 // The minimal proxy bytecode is 0x37 = 55 bytes long bytes memory bytecode = new bytes(0x37); address implementation = implementationAddress; /// @solidity memory-safe-assembly assembly { // The first 0x20 = 32 bytes (0x00 - 0x19) are reserved for the length. // The next 0x14 = 20 bytes (0x20 - 0x33) are the ones below. mstore(add(bytecode, 0x20), shl(0x60, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)) // The next 0x14 = 20 bytes (0x34 - 0x47) are the implementation address. mstore(add(bytecode, 0x34), shl(0x60, implementation)) // The last 0x0f = 15 bytes (0x48 - 0x56) are the ones below. mstore(add(bytecode, 0x48), shl(0x88, 0x5af43d82803e903d91602b57fd5bf3)) } tokenAddress = _create3(bytecode, salt); if (tokenAddress.code.length == 0) revert TokenDeploymentFailed(); IInterchainToken(tokenAddress).init(tokenId, minter, name, symbol, decimals); } /** * @notice Returns the interchain token deployment address. * @param salt The deployment salt. * @return tokenAddress The token address. */ function deployedAddress(bytes32 salt) external view returns (address tokenAddress) { tokenAddress = _create3Address(salt); } }