Line data Source code
1 : // SPDX-License-Identifier: MIT
2 :
3 : pragma solidity ^0.6.12;
4 : pragma experimental ABIEncoderV2;
5 :
6 : import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
7 : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8 : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
9 : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
10 : import { EnsResolve } from "torn-token/contracts/ENS.sol";
11 : import { TORN } from "torn-token/contracts/TORN.sol";
12 : import { TornadoStakingRewards } from "../v1/staking/TornadoStakingRewards.sol";
13 :
14 : interface ITornadoInstance {
15 : function token() external view returns (address);
16 :
17 : function denomination() external view returns (uint256);
18 :
19 : function deposit(bytes32 commitment) external payable;
20 :
21 : function withdraw(
22 : bytes calldata proof,
23 : bytes32 root,
24 : bytes32 nullifierHash,
25 : address payable recipient,
26 : address payable relayer,
27 : uint256 fee,
28 : uint256 refund
29 : ) external payable;
30 : }
31 :
32 : interface IENS {
33 : function owner(bytes32 node) external view returns (address);
34 : }
35 :
36 : /*
37 : * @dev Solidity implementation of the ENS namehash algorithm.
38 : *
39 : * Warning! Does not normalize or validate names before hashing.
40 : * Original version can be found here https://github.com/JonahGroendal/ens-namehash/
41 : */
42 : library ENSNamehash {
43 : function namehash(bytes memory domain) internal pure returns (bytes32) {
44 0 : return namehash(domain, 0);
45 : }
46 :
47 : function namehash(bytes memory domain, uint256 i) internal pure returns (bytes32) {
48 0 : if (domain.length <= i) return 0x0000000000000000000000000000000000000000000000000000000000000000;
49 :
50 0 : uint256 len = labelLength(domain, i);
51 :
52 0 : return keccak256(abi.encodePacked(namehash(domain, i + len + 1), keccak(domain, i, len)));
53 : }
54 :
55 : function labelLength(bytes memory domain, uint256 i) private pure returns (uint256) {
56 0 : uint256 len;
57 0 : while (i + len != domain.length && domain[i + len] != 0x2e) {
58 0 : len++;
59 : }
60 0 : return len;
61 : }
62 :
63 : function keccak(bytes memory data, uint256 offset, uint256 len) private pure returns (bytes32 ret) {
64 0 : require(offset + len <= data.length);
65 : assembly {
66 0 : ret := keccak256(add(add(data, 32), offset), len)
67 : }
68 : }
69 : }
70 :
71 : interface IFeeManager {
72 : function instanceFeeWithUpdate(ITornadoInstance _instance) external returns (uint160);
73 : }
74 :
75 : struct RelayerState {
76 : uint256 balance;
77 : bytes32 ensHash;
78 : }
79 :
80 : /**
81 : * @notice Registry contract, one of the main contracts of this protocol upgrade.
82 : * The contract should store relayers' addresses and data attributed to the
83 : * master address of the relayer. This data includes the relayers stake and
84 : * his ensHash.
85 : * A relayers master address has a number of subaddresses called "workers",
86 : * these are all addresses which burn stake in communication with the proxy.
87 : * If a relayer is not registered, he is not displayed on the frontend.
88 : * @dev CONTRACT RISKS:
89 : * - if setter functions are compromised, relayer metadata would be at risk, including the noted amount
90 : * of his balance
91 : * - if burn function is compromised, relayers run the risk of being unable to handle withdrawals
92 : * - the above risk also applies to the nullify balance function
93 : *
94 : */
95 : contract RelayerRegistry is Initializable, EnsResolve {
96 : using SafeMath for uint256;
97 : using SafeERC20 for TORN;
98 : using ENSNamehash for bytes;
99 :
100 : TORN public immutable torn;
101 : address public immutable governance;
102 : IENS public immutable ens;
103 : TornadoStakingRewards public immutable staking;
104 : IFeeManager public immutable feeManager;
105 :
106 : address public tornadoRouter;
107 : uint256 public minStakeAmount;
108 :
109 : mapping(address => RelayerState) public relayers;
110 : mapping(address => address) public workers;
111 :
112 : event RelayerBalanceNullified(address relayer);
113 : event WorkerRegistered(address relayer, address worker);
114 : event WorkerUnregistered(address relayer, address worker);
115 : event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
116 : event StakeBurned(address relayer, uint256 amountBurned);
117 : event MinimumStakeAmount(uint256 minStakeAmount);
118 : event RouterRegistered(address tornadoRouter);
119 : event RelayerRegistered(bytes32 relayer, string ensName, address relayerAddress, uint256 stakedAmount);
120 :
121 : modifier onlyGovernance() {
122 : require(msg.sender == governance, "only governance");
123 : _;
124 : }
125 :
126 : modifier onlyTornadoRouter() {
127 : require(msg.sender == tornadoRouter, "only proxy");
128 : _;
129 : }
130 :
131 : modifier onlyRelayer(address sender, address relayer) {
132 : require(workers[sender] == relayer, "only relayer");
133 : _;
134 : }
135 :
136 : constructor(address _torn, address _governance, address _ens, address _staking, address _feeManager)
137 : public
138 : {
139 : torn = TORN(_torn);
140 : governance = _governance;
141 : ens = IENS(_ens);
142 : staking = TornadoStakingRewards(_staking);
143 : feeManager = IFeeManager(_feeManager);
144 : }
145 :
146 : /**
147 : * @notice initialize function for upgradeability
148 : * @dev this contract will be deployed behind a proxy and should not assign values at logic address,
149 : * params left out because self explainable
150 : *
151 : */
152 : function initialize(bytes32 _tornadoRouter) external initializer {
153 0 : tornadoRouter = resolve(_tornadoRouter);
154 : }
155 :
156 : /**
157 : * @notice This function should register a master address and optionally a set of workeres for a relayer +
158 : * metadata
159 : * @dev Relayer can't steal other relayers workers since they are registered, and a wallet (msg.sender
160 : * check) can always unregister itself
161 : * @param ensName ens name of the relayer
162 : * @param stake the initial amount of stake in TORN the relayer is depositing
163 : *
164 : */
165 : function register(string calldata ensName, uint256 stake, address[] calldata workersToRegister)
166 : external
167 : {
168 0 : _register(msg.sender, ensName, stake, workersToRegister);
169 : }
170 :
171 : /**
172 : * @dev Register function equivalent with permit-approval instead of regular approve.
173 : *
174 : */
175 : function registerPermit(
176 : string calldata ensName,
177 : uint256 stake,
178 : address[] calldata workersToRegister,
179 : address relayer,
180 : uint256 deadline,
181 : uint8 v,
182 : bytes32 r,
183 : bytes32 s
184 : ) external {
185 0 : torn.permit(relayer, address(this), stake, deadline, v, r, s);
186 0 : _register(relayer, ensName, stake, workersToRegister);
187 : }
188 :
189 : function _register(
190 : address relayer,
191 : string calldata ensName,
192 : uint256 stake,
193 : address[] calldata workersToRegister
194 : ) internal {
195 0 : bytes32 ensHash = bytes(ensName).namehash();
196 0 : require(relayer == ens.owner(ensHash), "only ens owner");
197 0 : require(workers[relayer] == address(0), "cant register again");
198 0 : RelayerState storage metadata = relayers[relayer];
199 :
200 0 : require(metadata.ensHash == bytes32(0), "registered already");
201 0 : require(stake >= minStakeAmount, "!min_stake");
202 :
203 0 : torn.safeTransferFrom(relayer, address(staking), stake);
204 0 : emit StakeAddedToRelayer(relayer, stake);
205 :
206 0 : metadata.balance = stake;
207 0 : metadata.ensHash = ensHash;
208 0 : workers[relayer] = relayer;
209 :
210 0 : for (uint256 i = 0; i < workersToRegister.length; i++) {
211 0 : address worker = workersToRegister[i];
212 0 : _registerWorker(relayer, worker);
213 : }
214 :
215 0 : emit RelayerRegistered(ensHash, ensName, relayer, stake);
216 : }
217 :
218 : /**
219 : * @notice This function should allow relayers to register more workeres
220 : * @param relayer Relayer which should send message from any worker which is already registered
221 : * @param worker Address to register
222 : *
223 : */
224 : function registerWorker(address relayer, address worker) external onlyRelayer(msg.sender, relayer) {
225 0 : _registerWorker(relayer, worker);
226 : }
227 :
228 : function _registerWorker(address relayer, address worker) internal {
229 0 : require(workers[worker] == address(0), "can't steal an address");
230 0 : workers[worker] = relayer;
231 0 : emit WorkerRegistered(relayer, worker);
232 : }
233 :
234 : /**
235 : * @notice This function should allow anybody to unregister an address they own
236 : * @dev designed this way as to allow someone to unregister themselves in case a relayer misbehaves
237 : * - this should be followed by an action like burning relayer stake
238 : * - there was an option of allowing the sender to burn relayer stake in case of malicious behaviour,
239 : * this feature was not included in the end
240 : * - reverts if trying to unregister master, otherwise contract would break. in general, there should
241 : * be no reason to unregister master at all
242 : *
243 : */
244 : function unregisterWorker(address worker) external {
245 0 : if (worker != msg.sender) require(workers[worker] == msg.sender, "only owner of worker");
246 0 : require(workers[worker] != worker, "cant unregister master");
247 0 : emit WorkerUnregistered(workers[worker], worker);
248 0 : workers[worker] = address(0);
249 : }
250 :
251 : /**
252 : * @notice This function should allow anybody to stake to a relayer more TORN
253 : * @param relayer Relayer main address to stake to
254 : * @param stake Stake to be added to relayer
255 : *
256 : */
257 : function stakeToRelayer(address relayer, uint256 stake) external {
258 0 : _stakeToRelayer(msg.sender, relayer, stake);
259 : }
260 :
261 : /**
262 : * @dev stakeToRelayer function equivalent with permit-approval instead of regular approve.
263 : * @param staker address from that stake is paid
264 : *
265 : */
266 : function stakeToRelayerPermit(
267 : address relayer,
268 : uint256 stake,
269 : address staker,
270 : uint256 deadline,
271 : uint8 v,
272 : bytes32 r,
273 : bytes32 s
274 : ) external {
275 0 : torn.permit(staker, address(this), stake, deadline, v, r, s);
276 0 : _stakeToRelayer(staker, relayer, stake);
277 : }
278 :
279 : function _stakeToRelayer(address staker, address relayer, uint256 stake) internal {
280 0 : require(workers[relayer] == relayer, "!registered");
281 0 : torn.safeTransferFrom(staker, address(staking), stake);
282 0 : relayers[relayer].balance = stake.add(relayers[relayer].balance);
283 0 : emit StakeAddedToRelayer(relayer, stake);
284 : }
285 :
286 : /**
287 : * @notice This function should burn some relayer stake on withdraw and notify staking of this
288 : * @dev IMPORTANT FUNCTION:
289 : * - This should be only called by the tornado proxy
290 : * - Should revert if relayer does not call proxy from valid worker
291 : * - Should not overflow
292 : * - Should underflow and revert (SafeMath) on not enough stake (balance)
293 : * @param sender worker to check sender == relayer
294 : * @param relayer address of relayer who's stake is being burned
295 : * @param pool instance to get fee for
296 : *
297 : */
298 : function burn(address sender, address relayer, ITornadoInstance pool) external onlyTornadoRouter {
299 0 : address masterAddress = workers[sender];
300 0 : if (masterAddress == address(0)) {
301 0 : require(workers[relayer] == address(0), "Only custom relayer");
302 0 : return;
303 : }
304 :
305 0 : require(masterAddress == relayer, "only relayer");
306 0 : uint256 toBurn = feeManager.instanceFeeWithUpdate(pool);
307 0 : relayers[relayer].balance = relayers[relayer].balance.sub(toBurn);
308 0 : staking.addBurnRewards(toBurn);
309 0 : emit StakeBurned(relayer, toBurn);
310 : }
311 :
312 : /**
313 : * @notice This function should allow governance to set the minimum stake amount
314 : * @param minAmount new minimum stake amount
315 : *
316 : */
317 : function setMinStakeAmount(uint256 minAmount) external onlyGovernance {
318 0 : minStakeAmount = minAmount;
319 0 : emit MinimumStakeAmount(minAmount);
320 : }
321 :
322 : /**
323 : * @notice This function should allow governance to set a new tornado proxy address
324 : * @param tornadoRouterAddress address of the new proxy
325 : *
326 : */
327 : function setTornadoRouter(address tornadoRouterAddress) external onlyGovernance {
328 0 : tornadoRouter = tornadoRouterAddress;
329 0 : emit RouterRegistered(tornadoRouterAddress);
330 : }
331 :
332 : /**
333 : * @notice This function should allow governance to nullify a relayers balance
334 : * @dev IMPORTANT FUNCTION:
335 : * - Should nullify the balance
336 : * - Adding nullified balance as rewards was refactored to allow for the flexibility of these funds
337 : * (for gov to operate with them)
338 : * @param relayer address of relayer who's balance is to nullify
339 : *
340 : */
341 : function nullifyBalance(address relayer) external onlyGovernance {
342 0 : address masterAddress = workers[relayer];
343 0 : require(relayer == masterAddress, "must be master");
344 0 : relayers[masterAddress].balance = 0;
345 0 : emit RelayerBalanceNullified(relayer);
346 : }
347 :
348 : /**
349 : * @notice This function should check if a worker is associated with a relayer
350 : * @param toResolve address to check
351 : * @return true if is associated
352 : *
353 : */
354 : function isRelayer(address toResolve) external view returns (bool) {
355 0 : return workers[toResolve] != address(0);
356 : }
357 :
358 : /**
359 : * @notice This function should check if a worker is registered to the relayer stated
360 : * @param relayer relayer to check
361 : * @param toResolve address to check
362 : * @return true if registered
363 : *
364 : */
365 : function isRelayerRegistered(address relayer, address toResolve) external view returns (bool) {
366 0 : return workers[toResolve] == relayer;
367 : }
368 :
369 : /**
370 : * @notice This function should get a relayers ensHash
371 : * @param relayer address to fetch for
372 : * @return relayer's ensHash
373 : *
374 : */
375 : function getRelayerEnsHash(address relayer) external view returns (bytes32) {
376 0 : return relayers[workers[relayer]].ensHash;
377 : }
378 :
379 : /**
380 : * @notice This function should get a relayers balance
381 : * @param relayer relayer who's balance is to fetch
382 : * @return relayer's balance
383 : *
384 : */
385 : function getRelayerBalance(address relayer) external view returns (uint256) {
386 0 : return relayers[workers[relayer]].balance;
387 : }
388 : }
|