// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; // OZ Imports import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol"; // Local imports import "./instances/ERC20TornadoCloneable.sol"; import "./instances/ETHTornadoCloneable.sol"; /** * @title MinimalInstanceFactory * @author AlienTornadosaurusHex * @notice A factory to deploy new Tornado Instances. */ contract MinimalInstanceFactory { using Clones for address; using Address for address; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** @notice The address of the verifier contract, no actual use in the contract but just to show which address was set in the instances */ address public immutable verifier; /** @notice The address of the hasher contract, no actual use in the contract but just to show which address was set in the instances */ address public immutable hasher; /** @notice The address of the router which will have relayers run their txs through */ address public immutable router; /** @notice The standard merkle tree height used for the instances */ uint32 public immutable merkleTreeHeight; /** @notice The implementation which will be used for the cloned ETH (!) instances */ address public immutable ethImpl; /** @notice The implementation which will be used for the cloned ERC20 (!) instances */ address public immutable ERC20Impl; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ event NewTreeHeightSet(uint32 indexed newTreeHeight); event NewInstanceCloneCreated(address indexed clone); event NewImplementationSet( address indexed ERC20Impl, address indexed ethImpl, address verifier, address hasher ); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * @notice This instance factory is designed to be always able to print new tornado instances. */ constructor( address _verifierAddress, address _hasherAddress, address _routerAddresss, uint32 _merkleTreeHeight ) { // Store instance parameters merkleTreeHeight = _merkleTreeHeight; verifier = _verifierAddress; hasher = _hasherAddress; // Store router addresses router = _routerAddresss; // Deploy implementations ETHTornadoCloneable ethImplContract = new ETHTornadoCloneable(_verifierAddress, _hasherAddress); ERC20TornadoCloneable ERC20ImplContract = new ERC20TornadoCloneable(_verifierAddress, _hasherAddress); // Store implementations ethImpl = address(ethImplContract); ERC20Impl = address(ERC20ImplContract); } /** * @notice Creates a new Tornado instance. If the registry is dead, you can immediately call `relayerRegistryIsDead()` on the cloned instance. * @param _denomination The denomination of the cloned instance. Must be in E18. * @param _token The token address of the new instance. then it will be ETH */ function createInstanceClone(uint256 _denomination, address _token) public virtual returns (address clone) { // Calculate salt for the deterministic deployment bytes32 salt = keccak256(abi.encodePacked(_denomination, _token)); // Check whether token is ETH if (_token == address(0)) { // Calc deployment address clone = ethImpl.predictDeterministicAddress(salt); // Check whether already exists, if so, don't deploy, just give address if (!clone.isContract()) { ethImpl.cloneDeterministic(salt); emit NewInstanceCloneCreated(clone); ETHTornadoCloneable(clone).initialize(_denomination, merkleTreeHeight); } } else { // Calc deployment address clone = ERC20Impl.predictDeterministicAddress(salt); // Check whether already exists, if so, don't deploy, just give address if (!clone.isContract()) { ERC20Impl.cloneDeterministic(salt); emit NewInstanceCloneCreated(clone); ERC20TornadoCloneable(clone).initialize(router, _token, _denomination, merkleTreeHeight); } } // Return address of the clone return clone; } /** * @notice Since addresses are deterministic, this function fetches the deterministic address to which an instance would be or is deployed, for a specific denomination and token. * @param _denomination The denomination to calculate the address for. Needs to be E18. * @param _token The token address to calculate the address for. */ function getInstanceAddress(uint256 _denomination, address _token) public view returns (address) { bytes32 salt = keccak256(abi.encodePacked(_denomination, _token)); if (_token == address(0)) { return ethImpl.predictDeterministicAddress(salt); } else { return ERC20Impl.predictDeterministicAddress(salt); } } }