Contract Name:
TokenManagerDeployer
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IDeploy } from '../interfaces/IDeploy.sol';
import { ContractAddress } from '../libs/ContractAddress.sol';
import { CreateDeploy } from './CreateDeploy.sol';
import { Create3Address } from './Create3Address.sol';
/**
* @title Create3 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.
*/
contract Create3 is Create3Address, 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 create = new CreateDeploy{ salt: deploySalt }();
if (address(create) == address(0)) revert DeployFailed();
// Deploy using create
create.deploy(bytecode);
}
}
// SPDX-License-Identifier: MIT
import { CreateDeploy } from './CreateDeploy.sol';
pragma solidity ^0.8.0;
/**
* @title Create3Address contract
* @notice This contract can be used to predict the deterministic deployment address of a contract deployed with the `CREATE3` technique.
*/
contract Create3Address {
/// @dev bytecode hash of the CreateDeploy helper contract
bytes32 internal immutable createDeployBytecodeHash;
constructor() {
createDeployBytecodeHash = keccak256(type(CreateDeploy).creationCode);
}
/**
* @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, createDeployBytecodeHash))))
);
deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01')))));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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)
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '../interfaces/IProxy.sol';
/**
* @title BaseProxy Contract
* @dev This abstract contract implements a basic proxy that stores an implementation address. Fallback function
* calls are delegated to the implementation. This contract is meant to be inherited by other proxy contracts.
*/
abstract contract BaseProxy is IProxy {
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// keccak256('owner')
bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
/**
* @dev Returns the current implementation address.
* @return implementation_ The address of the current implementation contract
*/
function implementation() public view virtual returns (address implementation_) {
assembly {
implementation_ := sload(_IMPLEMENTATION_SLOT)
}
}
/**
* @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy.
* @param params The setup parameters for the implementation contract.
*/
function setup(bytes calldata params) external {}
/**
* @dev Returns the contract ID. It can be used as a check during upgrades. Meant to be implemented in derived contracts.
* @return bytes32 The contract ID
*/
function contractId() internal pure virtual returns (bytes32);
/**
* @dev Fallback function. Delegates the call to the current implementation contract.
*/
fallback() external payable virtual {
address implementation_ = implementation();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev Payable fallback function. Can be overridden in derived contracts.
*/
receive() external payable virtual {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ITokenManagerDeployer Interface
* @notice This interface is used to deploy new instances of the TokenManagerProxy contract.
*/
interface ITokenManagerDeployer {
error AddressZero();
error TokenManagerDeploymentFailed();
/**
* @notice Deploys a new instance of the TokenManagerProxy contract.
* @param tokenId The token ID.
* @param implementationType Token manager implementation type.
* @param params Additional parameters used in the setup of the token manager.
* @return tokenManager Address of the deployed tokenManager.
*/
function deployTokenManager(
bytes32 tokenId,
uint256 implementationType,
bytes calldata params
) external payable returns (address tokenManager);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ITokenManagerImplementation Interface
* @notice Interface for returning the token manager implementation type.
*/
interface ITokenManagerImplementation {
/**
* @notice Returns the implementation address for a given token manager type.
* @param tokenManagerType The type of token manager.
* @return tokenManagerAddress_ The address of the token manager implementation.
*/
function tokenManagerImplementation(uint256 tokenManagerType) external view returns (address tokenManagerAddress_);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.sol';
import { BaseProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/BaseProxy.sol';
import { IBaseTokenManager } from '../interfaces/IBaseTokenManager.sol';
import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol';
import { ITokenManagerImplementation } from '../interfaces/ITokenManagerImplementation.sol';
/**
* @title TokenManagerProxy
* @notice This contract is a proxy for token manager contracts.
* @dev This contract implements BaseProxy and ITokenManagerProxy.
*/
contract TokenManagerProxy is BaseProxy, ITokenManagerProxy {
bytes32 private constant CONTRACT_ID = keccak256('token-manager');
address public immutable interchainTokenService;
uint256 public immutable implementationType;
bytes32 public immutable interchainTokenId;
address public immutable tokenAddress;
/**
* @notice Constructs the TokenManagerProxy contract.
* @param interchainTokenService_ The address of the interchain token service.
* @param implementationType_ The token manager type.
* @param tokenId The identifier for the token.
* @param params The initialization parameters for the token manager contract.
*/
constructor(address interchainTokenService_, uint256 implementationType_, bytes32 tokenId, bytes memory params) {
if (interchainTokenService_ == address(0)) revert ZeroAddress();
interchainTokenService = interchainTokenService_;
implementationType = implementationType_;
interchainTokenId = tokenId;
address implementation_ = _tokenManagerImplementation(interchainTokenService_, implementationType_);
if (implementation_ == address(0)) revert InvalidImplementation();
(bool success, ) = implementation_.delegatecall(abi.encodeWithSelector(IProxy.setup.selector, params));
if (!success) revert SetupFailed();
tokenAddress = IBaseTokenManager(implementation_).getTokenAddressFromParams(params);
}
/**
* @notice Getter for the contract id.
* @return bytes32 The contract id.
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
/**
* @notice Returns implementation type and token address.
* @return implementationType_ The implementation type.
* @return tokenAddress_ The token address.
*/
function getImplementationTypeAndTokenAddress() external view returns (uint256 implementationType_, address tokenAddress_) {
implementationType_ = implementationType;
tokenAddress_ = tokenAddress;
}
/**
* @notice Returns the address of the current implementation.
* @return implementation_ The address of the current implementation.
*/
function implementation() public view override(BaseProxy, IProxy) returns (address implementation_) {
implementation_ = _tokenManagerImplementation(interchainTokenService, implementationType);
}
/**
* @notice Returns the implementation address from the interchain token service for the provided type.
* @param interchainTokenService_ The address of the interchain token service.
* @param implementationType_ The token manager type.
* @return implementation_ The address of the implementation.
*/
function _tokenManagerImplementation(
address interchainTokenService_,
uint256 implementationType_
) internal view returns (address implementation_) {
implementation_ = ITokenManagerImplementation(interchainTokenService_).tokenManagerImplementation(implementationType_);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Create3 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3.sol';
import { ITokenManagerDeployer } from '../interfaces/ITokenManagerDeployer.sol';
import { TokenManagerProxy } from '../proxies/TokenManagerProxy.sol';
/**
* @title TokenManagerDeployer
* @notice This contract is used to deploy new instances of the TokenManagerProxy contract.
*/
contract TokenManagerDeployer is ITokenManagerDeployer, Create3 {
/**
* @notice Deploys a new instance of the TokenManagerProxy contract
* @param tokenId The unique identifier for the token
* @param implementationType Token manager implementation type
* @param params Additional parameters used in the setup of the token manager
* @return tokenManager The address of the deployed tokenManager
*/
// slither-disable-next-line locked-ether
function deployTokenManager(
bytes32 tokenId,
uint256 implementationType,
bytes calldata params
) external payable returns (address tokenManager) {
bytes memory args = abi.encode(address(this), implementationType, tokenId, params);
// slither-disable-next-line too-many-digits
bytes memory bytecode = abi.encodePacked(type(TokenManagerProxy).creationCode, args);
tokenManager = _create3(bytecode, tokenId);
if (tokenManager.code.length == 0) revert TokenManagerDeploymentFailed();
}
}