Line data Source code
1 : // SPDX-License-Identifier: MIT 2 : 3 : pragma solidity ^0.6.12; 4 : pragma experimental ABIEncoderV2; 5 : 6 : // OZ Imports 7 : 8 : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 9 : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 10 : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol"; 11 : 12 : // Tornado Imports 13 : 14 : import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol"; 15 : 16 : // Local imports 17 : 18 : import { ENSResolver } from "./libraries/ENSResolver.sol"; 19 : import { NameEncoder } from "./libraries/NameEncoder.sol"; 20 : import { TornadoRouter } from "./TornadoRouter.sol"; 21 : 22 : /** 23 : * @title InstanceRegistry 24 : * @author AlienTornadosaurusHex 25 : * @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding 26 : * them. 27 : * @dev This contract will help us layout storage properly for a proxy upgrade for the impl 28 : * InstanceRegistry. 29 : */ 30 : contract InstanceRegistryLegacyStorage { 31 : /* From first contract, just so right uint type is chosen */ 32 : enum LegacyStatePlaceholder { 33 : DISABLED, 34 : ENABLED 35 : } 36 : 37 : /* From first contract, just if necessary to be able to properly unpack and determine value storage 38 : addresses and offsets */ 39 : struct LegacyInstanceStructPlaceholder { 40 : bool deprecatedIsERC20; 41 : address deprecatedToken; 42 : LegacyStatePlaceholder deprecatedState; 43 : uint24 deprecatedUniswapPoolSwappingFee; 44 : uint32 deprecatedProtocolFeePercentage; 45 : } 46 : 47 : /** 48 : * @dev From Initializable.sol of first contract 49 : */ 50 : bool private _deprecatedInitialized; 51 : 52 : /** 53 : * @dev From Initializable.sol of first contract 54 : */ 55 : bool private _deprecatedInitializing; 56 : 57 : /** 58 : * @dev From first contract 59 : */ 60 : address private _deprecatedRouterAddress; 61 : 62 : /** 63 : * @dev From first contract 64 : */ 65 : mapping(address => LegacyInstanceStructPlaceholder) private _deprecatedInstances; 66 : 67 : /** 68 : * @dev From first contract 69 : */ 70 : ITornadoInstance[] private _deprecatedInstanceIds; 71 : } 72 : 73 : /** 74 : * @dev This struct holds barebones information regarding an instance and its data location in storage. 75 : */ 76 : struct InstanceState { 77 : IERC20 token; 78 : uint80 index; 79 : bool isERC20; 80 : bool isEnabled; 81 : } 82 : 83 : /** 84 : * @title InstanceRegistry 85 : * @author AlienTornadosaurusHex 86 : * @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding 87 : * them. 88 : * @dev This is an improved version of the InstanceRegistry with a modified design from the original contract. 89 : */ 90 : contract InstanceRegistry is InstanceRegistryLegacyStorage, ENSResolver, Initializable { 91 : using SafeERC20 for IERC20; 92 : 93 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 94 : 95 : /** 96 : * @notice The address of the Governance proxy 97 : */ 98 : address public immutable governanceProxyAddress; 99 : 100 : /** 101 : * @notice Essential data regarding instances, see struct above 102 : */ 103 : mapping(ITornadoInstance => InstanceState) public instanceData; 104 : 105 : /** 106 : * @notice All instances enumerable 107 : */ 108 : ITornadoInstance[] public instances; 109 : 110 : /** 111 : * @notice The router which processes txs which we must command to approve tokens 112 : */ 113 : TornadoRouter public router; 114 : 115 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 116 : 117 : event RouterRegistered(address newRouterAddress); 118 : event InstanceAdded(address indexed instance, uint80 dataIndex, bool isERC20); 119 : event InstanceRemoved(address indexed instance); 120 : 121 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 122 : 123 : constructor(address _governanceProxyAddress) public { 124 : governanceProxyAddress = _governanceProxyAddress; 125 : } 126 : 127 : modifier onlyGovernance() { 128 : require(msg.sender == governanceProxyAddress, "InstanceRegistry: onlyGovernance"); 129 : _; 130 : } 131 : 132 : function version() public pure virtual returns (string memory) { 133 2 : return "v2-oracle-manager"; 134 : } 135 : 136 : function initialize(ITornadoInstance[] memory _instances, TornadoRouter _router) 137 : external 138 : onlyGovernance 139 : initializer 140 : { 141 4 : uint256 numInstances = _instances.length; 142 : 143 : // Router must be initialized before otherwise below will fail 144 4 : router = _router; 145 : 146 4 : for (uint256 i = 0; i < numInstances; i++) { 147 68 : addInstance(_instances[i]); 148 : } 149 : } 150 : 151 : function addInstance(ITornadoInstance _instance) public virtual onlyGovernance { 152 : // The instance may not already be enabled 153 : 154 80 : bool isEnabled = instanceData[_instance].isEnabled; 155 : 156 80 : require(!isEnabled, "InstanceRegistry: can't add the same instance."); 157 : 158 : // Determine whether it is an ERC20 or not 159 : 160 80 : IERC20 token = IERC20(address(0)); 161 : 162 80 : bool isERC20 = false; 163 : 164 : // ETH instances do not know of a `token()` call 165 80 : try _instance.token() returns (address _tokenAddress) { 166 : token = IERC20(_tokenAddress); 167 : isERC20 = true; 168 : } catch { 169 : /* It's an ETH instance, do nothing */ 170 : } 171 : 172 : // If it is ERC20 then make the router give an approval for the Tornado instance to allow the token 173 : // amount, if it hasn't already done so 174 80 : if (isERC20) { 175 63 : uint256 routerAllowanceForInstance = token.allowance(address(router), address(_instance)); 176 : 177 63 : if (routerAllowanceForInstance == 0) { 178 63 : router.approveTokenForInstance(token, address(_instance), type(uint256).max); 179 : } 180 : } 181 : 182 : // Add it to the enumerable 183 80 : instances.push(_instance); 184 : 185 : // Read out the index of the instance in the enumerable 186 80 : uint64 instanceIndex = uint64(instances.length - 1); 187 : 188 : // Store the collected data of the instance 189 80 : instanceData[_instance] = 190 : InstanceState({ token: token, index: instanceIndex, isERC20: isERC20, isEnabled: true }); 191 : 192 : // Log 193 80 : emit InstanceAdded(address(_instance), instanceIndex, isERC20); 194 : } 195 : 196 : /** 197 : * @notice Remove an instance, only callable by Governance. 198 : * @dev The access modifier is in the internal call. 199 : * @param _instanceIndex The index of the instance to remove. 200 : */ 201 : function removeInstanceByIndex(uint256 _instanceIndex) public virtual { 202 1 : _removeInstanceByAddress(address(instances[_instanceIndex])); 203 : } 204 : 205 : /** 206 : * @notice Remove an instance, only callable by Governance. 207 : * @dev The access modifier is in the internal call. 208 : * @param _instanceAddress The adress of the instance to remove. 209 : */ 210 : function removeInstanceByAddress(address _instanceAddress) public virtual { 211 1 : _removeInstanceByAddress(_instanceAddress); 212 : } 213 : 214 : function _removeInstanceByAddress(address _instanceAddress) internal virtual onlyGovernance { 215 : // Grab data needed to remove the instance 216 : 217 2 : ITornadoInstance instance = ITornadoInstance(_instanceAddress); 218 2 : InstanceState memory data = instanceData[instance]; 219 : 220 : // Checks whether already removed, so allowance can't be killed 221 : 222 2 : require(data.isEnabled, "InstanceRegistry: already removed"); 223 : 224 : // Kill the allowance of the router first (arbitrary order) 225 : 226 2 : if (data.isERC20) { 227 1 : uint256 routerAllowanceForInstance = data.token.allowance(address(router), _instanceAddress); 228 : 229 1 : if (routerAllowanceForInstance != 0) { 230 1 : router.approveTokenForInstance(data.token, _instanceAddress, 0); 231 : } 232 : } 233 : 234 : // We need to revert all changes, meaning modify position of last instance and change index in data 235 : 236 2 : uint64 lastInstanceIndex = uint64(instances.length - 1); 237 : 238 2 : ITornadoInstance lastInstance = instances[lastInstanceIndex]; 239 : 240 : // Swap position of last instance with old 241 : 242 2 : instances[data.index] = lastInstance; 243 : 244 2 : instanceData[lastInstance].index = data.index; 245 : 246 2 : instances.pop(); 247 : 248 : // Delete old instance data 249 : 250 2 : delete instanceData[instance]; 251 : 252 : // Log 253 : 254 2 : emit InstanceRemoved(_instanceAddress); 255 : } 256 : 257 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 258 : 259 : function setTornadoRouter(address _newRouterAddress) external onlyGovernance { 260 1 : router = TornadoRouter(_newRouterAddress); 261 1 : emit RouterRegistered(_newRouterAddress); 262 : } 263 : 264 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 265 : 266 : function getAllInstances() public view virtual returns (ITornadoInstance[] memory allInstances) { 267 5 : return getInstances(0, instances.length - 1); 268 : } 269 : 270 : function getInstances(uint256 _inclusiveStartIndex, uint256 _inclusiveEndIndex) 271 : public 272 : view 273 : virtual 274 : returns (ITornadoInstance[] memory allInstances) 275 : { 276 5 : allInstances = new ITornadoInstance[](1 + _inclusiveEndIndex - _inclusiveStartIndex); 277 : 278 5 : for (uint256 i = _inclusiveStartIndex; i < _inclusiveEndIndex + 1; i++) { 279 95 : allInstances[i - _inclusiveStartIndex] = instances[i]; 280 : } 281 : } 282 : 283 : function getAllInstanceStates() public view virtual returns (InstanceState[] memory data) { 284 1 : return getInstanceState(0, instances.length - 1); 285 : } 286 : 287 : function getInstanceState(uint256 _inclusiveStartIndex, uint256 _inclusiveEndIndex) 288 : public 289 : view 290 : virtual 291 : returns (InstanceState[] memory data) 292 : { 293 2 : data = new InstanceState[](1 + _inclusiveEndIndex - _inclusiveStartIndex); 294 : 295 2 : for (uint256 i = _inclusiveStartIndex; i < _inclusiveEndIndex + 1; i++) { 296 26 : data[i - _inclusiveStartIndex] = instanceData[instances[i]]; 297 : } 298 : } 299 : 300 : function getInstanceState(uint256 _index) public view virtual returns (InstanceState memory data) { 301 2 : return instanceData[instances[_index]]; 302 : } 303 : 304 : function getInstanceState(ITornadoInstance _instance) 305 : public 306 : view 307 : virtual 308 : returns (InstanceState memory data) 309 : { 310 137 : return instanceData[_instance]; 311 : } 312 : 313 : function getInstanceToken(ITornadoInstance _instance) public view virtual returns (IERC20) { 314 2 : return instanceData[_instance].token; 315 : } 316 : 317 : function getInstanceIndex(ITornadoInstance _instance) public view virtual returns (uint80) { 318 1 : return instanceData[_instance].index; 319 : } 320 : 321 : function isRegisteredInstance(ITornadoInstance _instance) public view virtual returns (bool) { 322 2 : return isEnabledInstance(_instance); 323 : } 324 : 325 : function isEnabledInstance(ITornadoInstance _instance) public view virtual returns (bool) { 326 4 : return instanceData[_instance].isEnabled; 327 : } 328 : 329 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ENS GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 330 : 331 : function isRegisteredInstanceByENSName(string memory _instanceENSName) 332 : public 333 : view 334 : virtual 335 : returns (bool) 336 : { 337 0 : return isEnabledInstanceByENSName(_instanceENSName); 338 : } 339 : 340 : function isEnabledInstanceByENSName(string memory _instanceENSName) public view virtual returns (bool) { 341 0 : return isEnabledInstance(getInstanceByENSName(_instanceENSName)); 342 : } 343 : 344 : function getInstanceByENSName(string memory _instanceENSName) 345 : public 346 : view 347 : virtual 348 : returns (ITornadoInstance) 349 : { 350 1 : return ITornadoInstance(resolveByName(_instanceENSName)); 351 : } 352 : }