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 { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
9 : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
10 : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
11 : import { Address } from "@openzeppelin/contracts/utils/Address.sol";
12 : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
13 :
14 : // Tornado imports
15 :
16 : import { TORN } from "torn-token/contracts/TORN.sol";
17 : import { EnsResolve } from "torn-token/contracts/ENS.sol";
18 :
19 : // Local imports
20 :
21 : import { IENS } from "./interfaces/IENS.sol";
22 :
23 : import { ENSNamehash } from "./libraries/ENSNamehash.sol";
24 :
25 : import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
26 :
27 : import { FeeOracleManager } from "./FeeOracleManager.sol";
28 :
29 : contract RegistryLegacyStorage {
30 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LEGACY STORAGE, ABANDONED ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
31 :
32 : /**
33 : * @dev From Initializable.sol of first contract
34 : */
35 : bool private _deprecatedInitialized;
36 :
37 : /**
38 : * @dev From Initializable.sol of first contract
39 : */
40 : bool private _deprecatedInitializing;
41 :
42 : /**
43 : * @dev This one will be moved for visibility
44 : */
45 : address private _deprecatedRouterAddress;
46 :
47 : /**
48 : * @dev We are not using this one because we will pull some magic
49 : */
50 : uint256 private _deprecatedMinStakeAmount;
51 :
52 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LEGACY STORAGE, IN USE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
53 :
54 : /**
55 : * @notice Relayer metadata: their (non-refundable) TORN balances and ENS node
56 : */
57 : mapping(address => RelayerMetadata) public metadata;
58 :
59 : /**
60 : * @notice Subaddresses which may work for a given relayer
61 : */
62 : mapping(address => address) public workers;
63 : }
64 :
65 : interface MinimumStakingAmountOracle {
66 : function calculateMinimumStakingAmount() external view returns (uint256);
67 : }
68 :
69 : struct RelayerMetadata {
70 : uint256 balance;
71 : bytes32 node;
72 : }
73 :
74 : contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
75 : using SafeMath for uint256;
76 : using SafeERC20 for IERC20;
77 :
78 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NEW STORAGE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
79 :
80 : /**
81 : * @notice Basis points are 1/100th of a %
82 : */
83 : uint256 public constant BIP_DIVISOR = 10_000;
84 :
85 : /**
86 : * @notice The address of the Governance proxy.
87 : */
88 : address public immutable governanceProxyAddress;
89 :
90 : /**
91 : * @notice The TORN token.
92 : */
93 : TORN public immutable torn;
94 :
95 : /**
96 : * @notice The ENS Registry.
97 : */
98 : IENS public ens;
99 :
100 : /**
101 : * @notice The Fee Oracle Manager contract.
102 : */
103 : FeeOracleManager public feeOracleManager;
104 :
105 : /**
106 : * @notice The Staking Rewards contract.
107 : */
108 : TornadoStakingRewards public stakingRewards;
109 :
110 : /**
111 : * @notice The address from which slashes may come from.
112 : */
113 : address public routerAddress;
114 :
115 : /**
116 : * @notice Since registrations are maybe relatively frequent and this boils down to a view getter, we can
117 : * use a bytes20 to either read out a min stake amount as a number or to fetch it from an address.
118 : */
119 : bytes20 public minStakeAmountOracle;
120 :
121 : /**
122 : * @notice What the kickback to stakers is on a slash
123 : */
124 : uint256 public stakerKickbackBips;
125 :
126 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
127 :
128 : event ENSUpdated(address ens);
129 : event RouterAddressUpdated(address routerAddress);
130 : event StakingRewardsUpdated(address stakingRewards);
131 : event FeeOracleManagerUpdated(address feeOracleManagerAddress);
132 : event MinimumStakingAmountOracleUpdated(bytes20 oracle, bool isContract);
133 :
134 : event WorkerRegistered(address relayer, address worker);
135 : event WorkerUnregistered(address relayer, address worker);
136 :
137 : event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
138 : event StakeBurned(address relayer, uint256 amountBurned);
139 :
140 : event RelayerRegistered(string ensName, bytes32 relayer, address relayerAddress, uint256 stakedAmount);
141 : event RelayerSlashed(
142 : address indexed relayer, address beneficiary, uint256 slashedAmount, uint256 stakerKickbackBips
143 : );
144 :
145 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
146 :
147 : constructor(address _governanceProxyAddress, address _tornTokenAddress) public {
148 : governanceProxyAddress = _governanceProxyAddress;
149 : torn = TORN(_tornTokenAddress);
150 : }
151 :
152 : modifier onlyGovernance() {
153 : require(msg.sender == governanceProxyAddress, "RelayerRegistry: onlyGovernance");
154 : _;
155 : }
156 :
157 : modifier onlyRouter() {
158 : require(msg.sender == routerAddress, "RelayerRegistry: onlyRouter");
159 : _;
160 : }
161 :
162 : modifier onlyRelayer(address _possibleRelayer, address _possibleWorker) {
163 : require(isRegisteredRelayer(_possibleRelayer, _possibleWorker), "RelayerRegistry: onlyRelayer");
164 : _;
165 : }
166 :
167 : modifier onlyENSOwner(address _relayer, bytes32 _node) {
168 : require(_relayer == ens.owner(_node), "RelayerRegistry: onlyENSOwner");
169 : _;
170 : }
171 :
172 : function version() public pure virtual returns (string memory) {
173 0 : return "v2-slashing-and-oracles";
174 : }
175 :
176 : function initialize(
177 : address _ensAddress,
178 : address _routerAddress,
179 : address _feeOracleManagerAddress,
180 : address _stakingRewardsAddress,
181 : bytes20 _minStakeAmountOracle,
182 : uint256 _stakerKickbackBips
183 : ) public virtual initializer {
184 4 : feeOracleManager = FeeOracleManager(_feeOracleManagerAddress);
185 4 : stakingRewards = TornadoStakingRewards(_stakingRewardsAddress);
186 4 : minStakeAmountOracle = _minStakeAmountOracle;
187 4 : stakerKickbackBips = _stakerKickbackBips;
188 4 : routerAddress = _routerAddress;
189 4 : ens = IENS(_ensAddress);
190 : }
191 :
192 : function register(string calldata _name, uint256 _staked, address[] calldata _workers) public virtual {
193 0 : _register(_name, ENSNamehash.namehash(bytes(_name)), msg.sender, _staked, _workers);
194 : }
195 :
196 : function registerPermit(
197 : string calldata _name,
198 : uint256 _staked,
199 : address[] calldata _workers,
200 : address _relayer,
201 : uint256 _deadline,
202 : uint8 v,
203 : bytes32 r,
204 : bytes32 s
205 : ) public virtual {
206 0 : torn.permit(_relayer, address(this), _staked, _deadline, v, r, s);
207 0 : _register(_name, ENSNamehash.namehash(bytes(_name)), _relayer, _staked, _workers);
208 : }
209 :
210 : function _register(
211 : string memory _name,
212 : bytes32 _node,
213 : address _relayer,
214 : uint256 _staked,
215 : address[] calldata _workers
216 : ) internal onlyENSOwner(_relayer, _node) {
217 0 : bytes32 currentNodeValue = metadata[_relayer].node;
218 :
219 0 : require(
220 : workers[_relayer] == address(0) && currentNodeValue == bytes32(0),
221 : "RelayerRegistry: already registered"
222 : );
223 :
224 0 : require(getMinimumStakingAmount() <= _staked, "RelayerRegistry: stake too low");
225 :
226 0 : IERC20(torn).safeTransferFrom(_relayer, address(stakingRewards), _staked);
227 :
228 0 : metadata[_relayer] = RelayerMetadata({ balance: _staked, node: _node });
229 :
230 0 : workers[_relayer] = _relayer;
231 :
232 0 : for (uint256 i = 0; i < _workers.length; i++) {
233 0 : _registerWorker(_relayer, _workers[i]);
234 : }
235 :
236 0 : emit RelayerRegistered(_name, _node, _relayer, _staked);
237 : }
238 :
239 : function registerWorker(address _relayer, address _worker)
240 : public
241 : virtual
242 : onlyRelayer(_relayer, msg.sender)
243 : {
244 0 : _registerWorker(_relayer, _worker);
245 : }
246 :
247 : function _registerWorker(address _relayer, address _worker) internal {
248 0 : require(workers[_worker] == address(0), "RelayerRegistry: already registered");
249 0 : workers[_worker] = _relayer;
250 0 : emit WorkerRegistered(_relayer, _worker);
251 : }
252 :
253 : function unregisterWorker(address _worker) public virtual {
254 0 : if (msg.sender != _worker) {
255 0 : require(workers[_worker] == msg.sender, "RelayerRegistry: sender must own worker");
256 : }
257 :
258 0 : require(workers[_worker] != _worker, "RelayerRegistry: cant remove owner");
259 :
260 0 : workers[_worker] = address(0);
261 :
262 0 : emit WorkerUnregistered(workers[_worker], _worker);
263 : }
264 :
265 : function stakeToRelayer(address _relayer, uint256 _staked) public virtual {
266 0 : _addStake(msg.sender, _relayer, _staked);
267 : }
268 :
269 : function stakeToRelayerPermit(
270 : address _relayer,
271 : uint256 _staked,
272 : address _staker,
273 : uint256 _deadline,
274 : uint8 v,
275 : bytes32 r,
276 : bytes32 s
277 : ) public virtual {
278 0 : torn.permit(_staker, address(this), _staked, _deadline, v, r, s);
279 0 : _addStake(_staker, _relayer, _staked);
280 : }
281 :
282 : function _addStake(address _staker, address _relayer, uint256 _staked)
283 : internal
284 : onlyRelayer(_relayer, _relayer)
285 : {
286 0 : IERC20(torn).safeTransferFrom(_staker, address(stakingRewards), _staked);
287 0 : metadata[_relayer].balance = _staked.add(metadata[_relayer].balance);
288 0 : emit StakeAddedToRelayer(_relayer, _staked);
289 : }
290 :
291 : function deductBalance(address _sender, address _relayer, uint256 _burned)
292 : public
293 : virtual
294 : onlyRouter
295 : onlyRelayer(_relayer, _sender)
296 : {
297 0 : metadata[_relayer].balance = metadata[_relayer].balance.sub(_burned);
298 0 : stakingRewards.addBurnRewards(_burned);
299 0 : emit StakeBurned(_relayer, _burned);
300 : }
301 :
302 : function slashRelayer(address _beneficiary, address _relayer) public virtual onlyRouter {
303 : // Store to reward
304 0 : uint256 balToSlash = metadata[_relayer].balance;
305 :
306 : // Slash
307 0 : metadata[_relayer].balance = 0;
308 :
309 : // We can't give the slasher the full balance because in that case the relayer will just slash himself
310 0 : stakingRewards.rewardSlasher(
311 : _beneficiary, (balToSlash * (BIP_DIVISOR - stakerKickbackBips)) / BIP_DIVISOR
312 : );
313 :
314 : // Give rest to Gov stakers
315 0 : stakingRewards.addBurnRewards((balToSlash * stakerKickbackBips) / BIP_DIVISOR);
316 :
317 0 : emit RelayerSlashed(_relayer, _beneficiary, balToSlash, stakerKickbackBips);
318 : }
319 :
320 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
321 :
322 : function setENS(address _newENSAddress) public virtual onlyGovernance {
323 0 : ens = IENS(_newENSAddress);
324 0 : emit ENSUpdated(_newENSAddress);
325 : }
326 :
327 : function setRouterAddress(address _newRouterAddress) public virtual onlyGovernance {
328 0 : routerAddress = _newRouterAddress;
329 0 : emit RouterAddressUpdated(_newRouterAddress);
330 : }
331 :
332 : function setStakingRewards(address _newStakingRewardsAddress) public virtual onlyGovernance {
333 0 : stakingRewards = TornadoStakingRewards(_newStakingRewardsAddress);
334 0 : emit StakingRewardsUpdated(_newStakingRewardsAddress);
335 : }
336 :
337 : function setFeeOracleManager(address _newFeeOracleManagerAddress) public virtual onlyGovernance {
338 0 : feeOracleManager = FeeOracleManager(_newFeeOracleManagerAddress);
339 0 : emit FeeOracleManagerUpdated(_newFeeOracleManagerAddress);
340 : }
341 :
342 : function setMinimumStakingAmountOracle(bytes20 _newOracle, bool _isContractOracle)
343 : public
344 : virtual
345 : onlyGovernance
346 : {
347 0 : bool oracleIsContract = Address.isContract(address(_newOracle));
348 0 : if (_isContractOracle) {
349 0 : require(oracleIsContract, "RelayerRegistry: oracle is not contract");
350 : } else {
351 0 : require(!oracleIsContract, "RelayerRegistry: oracle is contract");
352 : }
353 0 : minStakeAmountOracle = _newOracle;
354 0 : emit MinimumStakingAmountOracleUpdated(_newOracle, oracleIsContract);
355 : }
356 :
357 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
358 :
359 : function isWorker(address _possibleWorker) public view virtual returns (bool) {
360 0 : return workers[_possibleWorker] != address(0);
361 : }
362 :
363 : function isRegisteredRelayer(address _possibleRelayer, address _possibleWorker)
364 : public
365 : view
366 : virtual
367 : returns (bool)
368 : {
369 0 : return _possibleRelayer == workers[_possibleWorker];
370 : }
371 :
372 : function getRelayerENSNode(address _relayer) public view virtual returns (bytes32) {
373 0 : return metadata[workers[_relayer]].node;
374 : }
375 :
376 : function getRelayerBalance(address _relayer) public view virtual returns (uint256) {
377 0 : return metadata[workers[_relayer]].balance;
378 : }
379 :
380 : function getOwningRelayerOfWorker(address _worker) public view virtual returns (address) {
381 0 : return workers[_worker];
382 : }
383 :
384 : function getMinimumStakingAmount() public view virtual returns (uint256) {
385 0 : bytes20 _minStakeAmountOracle = minStakeAmountOracle;
386 0 : if (Address.isContract(address(_minStakeAmountOracle))) {
387 0 : return MinimumStakingAmountOracle(address(_minStakeAmountOracle)).calculateMinimumStakingAmount();
388 : }
389 0 : return uint160(_minStakeAmountOracle);
390 : }
391 : }
|