Mostly finalize design

Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
AlienTornadosaurusHex 2023-06-20 12:47:48 +00:00
parent 7d6749a6f6
commit c11658265c
8 changed files with 118 additions and 205 deletions

@ -31,7 +31,7 @@ import { TornadoRouter } from "../v2/TornadoRouter.sol";
* registry such that multiple oracles can be used and so the DAO can add more instances. * registry such that multiple oracles can be used and so the DAO can add more instances.
*/ */
contract InfrastructureUpgradeProposal { contract InfrastructureUpgradeProposal {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ADDRESSES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* @dev The address of the current FeeManager proxy, future FeeOracleManager */ /* @dev The address of the current FeeManager proxy, future FeeOracleManager */
address payable public constant feeManagerProxyAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7; address payable public constant feeManagerProxyAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7;
@ -45,9 +45,6 @@ contract InfrastructureUpgradeProposal {
/* @dev The address of the current TornadoStakingRewards proxy, this will be upgraded */ /* @dev The address of the current TornadoStakingRewards proxy, this will be upgraded */
address payable public constant stakingProxyAddress = 0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29; address payable public constant stakingProxyAddress = 0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29;
/* @dev The ENS resolver address */
address public constant ensAddress = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e;
/* @dev This is the Uniswap Oracle which we will use for all of our traditional instances, but it will /* @dev This is the Uniswap Oracle which we will use for all of our traditional instances, but it will
also help the CurveFeeOracle, the former must have been deployed witht the address of this */ also help the CurveFeeOracle, the former must have been deployed witht the address of this */
address public immutable deployedUniswapFeeOracleAddress; address public immutable deployedUniswapFeeOracleAddress;
@ -108,12 +105,7 @@ contract InfrastructureUpgradeProposal {
// Initialize V2 // Initialize V2
RelayerRegistry(relayerRegistryProxyAddress).initialize( RelayerRegistry(relayerRegistryProxyAddress).initialize(
ensAddress, deployedTornadoRouterAddress, stakingProxyAddress, bytes20(_getMinimumStakeAmount())
deployedTornadoRouterAddress,
feeManagerProxyAddress,
stakingProxyAddress,
bytes20(uint160(2000 ether)),
5000
); );
// Upgrade TornadoStakingRewards (V1) Proxy to TornadoStakingRewards (V2) // Upgrade TornadoStakingRewards (V1) Proxy to TornadoStakingRewards (V2)
@ -135,6 +127,7 @@ contract InfrastructureUpgradeProposal {
FeeOracleManager(feeManagerProxyAddress).initialize( FeeOracleManager(feeManagerProxyAddress).initialize(
deployedUniswapFeeOracleAddress, deployedUniswapFeeOracleAddress,
instanceRegistryProxyAddress, instanceRegistryProxyAddress,
_getTWAPUpdateInterval(),
_getAllInstances(), _getAllInstances(),
_getAllInstanceFeePercents() _getAllInstanceFeePercents()
); );
@ -217,6 +210,16 @@ contract InfrastructureUpgradeProposal {
_uniswapFeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8)); _uniswapFeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
} }
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PARAMETERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function _getMinimumStakeAmount() internal pure returns (uint160) {
return 2000 ether;
}
function _getTWAPUpdateInterval() internal pure returns (uint32) {
return 6 hours;
}
function _getAllInstances() internal pure returns (ITornadoInstance[] memory _addresses) { function _getAllInstances() internal pure returns (ITornadoInstance[] memory _addresses) {
_addresses = new ITornadoInstance[](17); _addresses = new ITornadoInstance[](17);

@ -123,7 +123,7 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
} }
function version() public pure virtual returns (string memory) { function version() public pure virtual returns (string memory) {
return "v2-slashing-and-oracles"; return "v2-oracle-manager";
} }
/** /**
@ -133,6 +133,7 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
function initialize( function initialize(
address _uniswapFeeOracle, address _uniswapFeeOracle,
address _instanceRegistryAddress, address _instanceRegistryAddress,
uint32 _feeUpdateInterval,
ITornadoInstance[] memory _instances, ITornadoInstance[] memory _instances,
uint256[] memory _percents uint256[] memory _percents
) external onlyGovernance initializer { ) external onlyGovernance initializer {
@ -147,7 +148,7 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
feesByInstance[instance] = FeeData({ feesByInstance[instance] = FeeData({
amount: _oldFeesForInstance[instance], amount: _oldFeesForInstance[instance],
percent: uint32(_percents[i]), percent: uint32(_percents[i]),
updateInterval: 6 hours, // TODO: Bubble this up to proposal updateInterval: _feeUpdateInterval,
lastUpdateTime: uint32(_oldFeesForInstanceUpdateTime[instance]) lastUpdateTime: uint32(_oldFeesForInstanceUpdateTime[instance])
}); });

@ -130,7 +130,7 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
} }
function version() public pure virtual returns (string memory) { function version() public pure virtual returns (string memory) {
return "v2-slashing-and-oracles"; return "v2-oracle-manager";
} }
function initialize(ITornadoInstance[] memory _instances, TornadoRouter _router) function initialize(ITornadoInstance[] memory _instances, TornadoRouter _router)

@ -14,7 +14,6 @@ import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
// Tornado imports // Tornado imports
import { TORN } from "torn-token/contracts/TORN.sol"; import { TORN } from "torn-token/contracts/TORN.sol";
import { EnsResolve } from "torn-token/contracts/ENS.sol";
// Local imports // Local imports
@ -22,6 +21,8 @@ import { IENS } from "./interfaces/IENS.sol";
import { ENSNamehash } from "./libraries/ENSNamehash.sol"; import { ENSNamehash } from "./libraries/ENSNamehash.sol";
import { EnsResolver } from "./libraries/ENSResolver.sol";
import { TornadoStakingRewards } from "./TornadoStakingRewards.sol"; import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
import { FeeOracleManager } from "./FeeOracleManager.sol"; import { FeeOracleManager } from "./FeeOracleManager.sol";
@ -71,7 +72,7 @@ struct RelayerMetadata {
bytes32 node; bytes32 node;
} }
contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable { contract RelayerRegistry is RegistryLegacyStorage, EnsResolver, Initializable {
using SafeMath for uint256; using SafeMath for uint256;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@ -92,16 +93,6 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
*/ */
TORN public immutable torn; TORN public immutable torn;
/**
* @notice The ENS Registry.
*/
IENS public ens;
/**
* @notice The Fee Oracle Manager contract.
*/
FeeOracleManager public feeOracleManager;
/** /**
* @notice The Staking Rewards contract. * @notice The Staking Rewards contract.
*/ */
@ -118,30 +109,29 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
*/ */
bytes20 public minStakeAmountOracle; bytes20 public minStakeAmountOracle;
/**
* @notice What the kickback to stakers is on a slash
*/
uint256 public stakerKickbackBips;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// Contract general
event ENSUpdated(address ens); event ENSUpdated(address ens);
event RouterAddressUpdated(address routerAddress); event RouterAddressUpdated(address routerAddress);
event StakingRewardsUpdated(address stakingRewards); event StakingRewardsUpdated(address stakingRewards);
event FeeOracleManagerUpdated(address feeOracleManagerAddress); event FeeOracleManagerUpdated(address feeOracleManagerAddress);
event MinimumStakingAmountOracleUpdated(bytes20 oracle, bool isContract); event MinimumStakingAmountOracleUpdated(bytes20 oracle, bool isContract);
event WorkerRegistered(address relayer, address worker); // Relayer metadata
event WorkerUnregistered(address relayer, address worker);
event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
event StakeBurned(address relayer, uint256 amountBurned);
event RelayerRegistered(string ensName, bytes32 relayer, address relayerAddress, uint256 stakedAmount); event RelayerRegistered(string ensName, bytes32 relayer, address relayerAddress, uint256 stakedAmount);
event RelayerSlashed( event RelayerSlashed(
address indexed relayer, address beneficiary, uint256 slashedAmount, uint256 stakerKickbackBips address indexed relayer, address beneficiary, uint256 slashedAmount, uint256 stakerKickbackBips
); );
// Relayer Stake
event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
event StakeBurned(address relayer, uint256 amountBurned);
// Relayers workers
event WorkerRegistered(address relayer, address worker);
event WorkerUnregistered(address relayer, address worker);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address _governanceProxyAddress, address _tornTokenAddress) public { constructor(address _governanceProxyAddress, address _tornTokenAddress) public {
@ -160,33 +150,27 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
} }
modifier onlyRelayer(address _possibleRelayer, address _possibleWorker) { modifier onlyRelayer(address _possibleRelayer, address _possibleWorker) {
require(isRegisteredRelayer(_possibleRelayer, _possibleWorker), "RelayerRegistry: onlyRelayer"); require(isWorkerOfRelayer(_possibleRelayer, _possibleWorker), "RelayerRegistry: onlyRelayer");
_; _;
} }
modifier onlyENSOwner(address _relayer, bytes32 _node) { modifier onlyENSOwner(address _relayer, bytes32 _node) {
require(_relayer == ens.owner(_node), "RelayerRegistry: onlyENSOwner"); require(_relayer == owner(_node), "RelayerRegistry: onlyENSOwner");
_; _;
} }
function version() public pure virtual returns (string memory) { function version() public pure virtual returns (string memory) {
return "v2-slashing-and-oracles"; return "v2-oracle-manager";
} }
function initialize( function initialize(address _routerAddress, address _stakingRewardsAddress, bytes20 _minStakeAmountOracle)
address _ensAddress, public
address _routerAddress, virtual
address _feeOracleManagerAddress, initializer
address _stakingRewardsAddress, {
bytes20 _minStakeAmountOracle, routerAddress = _routerAddress;
uint256 _stakerKickbackBips
) public virtual initializer {
feeOracleManager = FeeOracleManager(_feeOracleManagerAddress);
stakingRewards = TornadoStakingRewards(_stakingRewardsAddress); stakingRewards = TornadoStakingRewards(_stakingRewardsAddress);
minStakeAmountOracle = _minStakeAmountOracle; minStakeAmountOracle = _minStakeAmountOracle;
stakerKickbackBips = _stakerKickbackBips;
routerAddress = _routerAddress;
ens = IENS(_ensAddress);
} }
function register(string calldata _name, uint256 _staked, address[] calldata _workers) public virtual { function register(string calldata _name, uint256 _staked, address[] calldata _workers) public virtual {
@ -221,10 +205,13 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
"RelayerRegistry: already registered" "RelayerRegistry: already registered"
); );
// Check if a sufficient amount of TORN is to be staked
require(getMinimumStakingAmount() <= _staked, "RelayerRegistry: stake too low"); require(getMinimumStakingAmount() <= _staked, "RelayerRegistry: stake too low");
// Then transfer it here
IERC20(torn).safeTransferFrom(_relayer, address(stakingRewards), _staked); IERC20(torn).safeTransferFrom(_relayer, address(stakingRewards), _staked);
// Then store metadata ("register")
metadata[_relayer] = RelayerMetadata({ balance: _staked, node: _node }); metadata[_relayer] = RelayerMetadata({ balance: _staked, node: _node });
workers[_relayer] = _relayer; workers[_relayer] = _relayer;
@ -251,9 +238,10 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
} }
function unregisterWorker(address _worker) public virtual { function unregisterWorker(address _worker) public virtual {
if (msg.sender != _worker) { require(
require(workers[_worker] == msg.sender, "RelayerRegistry: sender must own worker"); msg.sender == _worker || isWorkerOfRelayer(msg.sender, _worker),
} "RelayerRegistry: sender must own worker"
);
require(workers[_worker] != _worker, "RelayerRegistry: cant remove owner"); require(workers[_worker] != _worker, "RelayerRegistry: cant remove owner");
@ -299,31 +287,8 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
emit StakeBurned(_relayer, _burned); emit StakeBurned(_relayer, _burned);
} }
function slashRelayer(address _beneficiary, address _relayer) public virtual onlyRouter {
// Store to reward
uint256 balToSlash = metadata[_relayer].balance;
// Slash
metadata[_relayer].balance = 0;
// We can't give the slasher the full balance because in that case the relayer will just slash himself
stakingRewards.rewardSlasher(
_beneficiary, (balToSlash * (BIP_DIVISOR - stakerKickbackBips)) / BIP_DIVISOR
);
// Give rest to Gov stakers
stakingRewards.addBurnRewards((balToSlash * stakerKickbackBips) / BIP_DIVISOR);
emit RelayerSlashed(_relayer, _beneficiary, balToSlash, stakerKickbackBips);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function setENS(address _newENSAddress) public virtual onlyGovernance {
ens = IENS(_newENSAddress);
emit ENSUpdated(_newENSAddress);
}
function setRouterAddress(address _newRouterAddress) public virtual onlyGovernance { function setRouterAddress(address _newRouterAddress) public virtual onlyGovernance {
routerAddress = _newRouterAddress; routerAddress = _newRouterAddress;
emit RouterAddressUpdated(_newRouterAddress); emit RouterAddressUpdated(_newRouterAddress);
@ -334,22 +299,13 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
emit StakingRewardsUpdated(_newStakingRewardsAddress); emit StakingRewardsUpdated(_newStakingRewardsAddress);
} }
function setFeeOracleManager(address _newFeeOracleManagerAddress) public virtual onlyGovernance {
feeOracleManager = FeeOracleManager(_newFeeOracleManagerAddress);
emit FeeOracleManagerUpdated(_newFeeOracleManagerAddress);
}
function setMinimumStakingAmountOracle(bytes20 _newOracle, bool _isContractOracle) function setMinimumStakingAmountOracle(bytes20 _newOracle, bool _isContractOracle)
public public
virtual virtual
onlyGovernance onlyGovernance
{ {
bool oracleIsContract = Address.isContract(address(_newOracle)); bool oracleIsContract = Address.isContract(address(_newOracle));
if (_isContractOracle) { require(_isContractOracle == oracleIsContract, "RelayerRegistry: oracle not expected type");
require(oracleIsContract, "RelayerRegistry: oracle is not contract");
} else {
require(!oracleIsContract, "RelayerRegistry: oracle is contract");
}
minStakeAmountOracle = _newOracle; minStakeAmountOracle = _newOracle;
emit MinimumStakingAmountOracleUpdated(_newOracle, oracleIsContract); emit MinimumStakingAmountOracleUpdated(_newOracle, oracleIsContract);
} }
@ -360,7 +316,7 @@ contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
return workers[_possibleWorker] != address(0); return workers[_possibleWorker] != address(0);
} }
function isRegisteredRelayer(address _possibleRelayer, address _possibleWorker) function isWorkerOfRelayer(address _possibleRelayer, address _possibleWorker)
public public
view view
virtual virtual

@ -52,11 +52,6 @@ contract TornadoRouter is Initializable {
*/ */
FeeOracleManager public feeOracleManager; FeeOracleManager public feeOracleManager;
/**
* @notice Note down proofs for slashing relayers
*/
mapping(bytes32 => bool) public slashProofs;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
event EncryptedNote(address indexed sender, bytes encryptedNote); event EncryptedNote(address indexed sender, bytes encryptedNote);
@ -87,7 +82,7 @@ contract TornadoRouter is Initializable {
} }
function version() public pure virtual returns (string memory) { function version() public pure virtual returns (string memory) {
return "v2-slashing-and-oracles"; return "v2-oracle-manager";
} }
function initialize( function initialize(
@ -127,83 +122,9 @@ contract TornadoRouter is Initializable {
} }
/** /**
* @notice Withdrawal function for Governance relayers by which they can safely process a withdrawal * @notice Withdraw from a Tornado Instance. The registry proxy will be set to 0, this will then
* without getting slashed (which kicks them off the frontend). * guarantee that the contract is not upgradeable and thus that it will never be able to block
* @param _tornado The Tornado instance to withdraw from. * withdrawals.
* @param _proof Bytes proof data.
* @param _root A current or historical bytes32 root of the Merkle Tree within the proofs context.
* @param _nullifierHash The bytes32 nullifierHash for the deposit.
* @param _recipient The address of recipient for withdrawn funds.
* @param _relayer The address of the relayer which will be making the withdrawal.
* @param _fee The fee in bips to pay the relayer.
* @param _refund If swapping into ETH on the other side, use this to specify how much should be paid for
*/
function processWithdrawAsGovernanceRelayer(
ITornadoInstance _tornado,
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) public payable virtual {
// Deduct balance immediately
relayerRegistry.deductBalance(msg.sender, _relayer, feeOracleManager.updateFee(_tornado, true));
// Then call the regular withdraw function which will make the relayer slashable
withdraw(_tornado, _proof, _root, _nullifierHash, _recipient, _relayer, _fee, _refund);
// But then undo this and get gas refund
slashProofs[keccak256(abi.encode(msg.sender, _tornado, _relayer, _nullifierHash))] = false;
}
/**
* @notice Slash a relayer by proving that there is a nullifier hash for which he has not been deducted,
* get tokens back for it.
* @dev Events are emitted when withdrawals are processed, that is how you can find hashes.
* @param _relayer The relayer to be slashed
* @param _instanceAddress The instance for which nullifier hash is valid for
* @param _nullifierHash The nullifier hash
*/
function slashRelayer(address _sender, address _relayer, address _instanceAddress, bytes32 _nullifierHash)
public
virtual
{
// Construct the proof, msg.sender can't lie about _sender, because proof will be invalid
// And _sender was withdraw msg.sender
bytes32 possibleSlashProof =
keccak256(abi.encode(_sender, _relayer, _instanceAddress, _nullifierHash));
// Proof must be valid
require(slashProofs[possibleSlashProof], "TornadoRouter: invalid proof");
// Since the proof is valid, we know the _sender is correct
// First step against multicall
require(msg.sender != _sender, "TornadoRouter: slasher is withdrawer");
// Also require the sender is not a contract, multicall could proxy over one
require(!Address.isContract(msg.sender), "TornadoRouter: sender is contract");
// Further it doesn't make sense then to really assert it's not a worker,
// since relayer could use a different account.
//
// In any case searchers and the balance not being returned in full should prevent relayers from not
// behaving well, if they are listed on the frontend
//
// Otherwise they are on their own
// Slash relayer first, it also logs the slash event
relayerRegistry.slashRelayer(msg.sender, _relayer);
// Then set this back false
slashProofs[possibleSlashProof] = false;
}
/**
* @notice Withdraw from a Tornado Instance. Note that the withdraw function is totally independent of any
* Governance contracts, that means, even if the instances demand that only Router is the caller, and the
* Governance contracts get fucked, depositors won't lose their money AND third-party relayers will still
* have the opportunity to operate. Relayers not affiliated with Governance, sorry for that one extra
* SSTORE and emit!
* @param _tornado The Tornado instance to withdraw from. * @param _tornado The Tornado instance to withdraw from.
* @param _proof Bytes proof data. * @param _proof Bytes proof data.
* @param _root A current or historical bytes32 root of the Merkle Tree within the proofs context. * @param _root A current or historical bytes32 root of the Merkle Tree within the proofs context.
@ -224,9 +145,12 @@ contract TornadoRouter is Initializable {
uint256 _fee, uint256 _fee,
uint256 _refund uint256 _refund
) public payable virtual { ) public payable virtual {
if (_relayer != address(0)) { // The registry proxy admin has been tossed meaning that below function can't be
slashProofs[keccak256(abi.encode(msg.sender, _relayer, address(_tornado), _nullifierHash))] = true; // upgraded to break withdrawals from the instances
emit WithdrawalWithRelayer(msg.sender, _relayer, address(_tornado), _nullifierHash); if (relayerRegistry.isWorker(_relayer)) {
require(relayerRegistry.isWorkerOfRelayer(_relayer, msg.sender), "TornadoRouter: invalid relayer");
require(instanceRegistry.isEnabledInstance(_tornado), "TornadoRouter: instance not enabled");
relayerRegistry.deductBalance(msg.sender, _relayer, feeOracleManager.updateFee(_tornado, true));
} }
_tornado.withdraw{ value: msg.value }( _tornado.withdraw{ value: msg.value }(
@ -279,11 +203,6 @@ contract TornadoRouter is Initializable {
emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress); emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress);
} }
function setRelayerRegistry(address _newRelayerRegistryProxyAddress) external onlyGovernance {
relayerRegistry = RelayerRegistry(_newRelayerRegistryProxyAddress);
emit RelayerRegistryUpdated(_newRelayerRegistryProxyAddress);
}
function setFeeOracleManager(address _newFeeOracleManagerProxyAddress) external onlyGovernance { function setFeeOracleManager(address _newFeeOracleManagerProxyAddress) external onlyGovernance {
feeOracleManager = FeeOracleManager(_newFeeOracleManagerProxyAddress); feeOracleManager = FeeOracleManager(_newFeeOracleManagerProxyAddress);
emit FeeOracleManagerUpdated(_newFeeOracleManagerProxyAddress); emit FeeOracleManagerUpdated(_newFeeOracleManagerProxyAddress);

@ -31,11 +31,15 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
using SafeMath for uint256; using SafeMath for uint256;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMMUTABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/// @notice 1e25 /// @notice 1e25
uint256 public immutable ratioConstant; uint256 public immutable ratioConstant;
ITornadoGovernance public immutable Governance; ITornadoGovernance public immutable Governance;
IERC20 public immutable torn; IERC20 public immutable torn;
address public immutable relayerRegistry; address public immutable router;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MUTABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/// @notice the sum torn_burned_i/locked_amount_i*coefficient where i is incremented at each burn /// @notice the sum torn_burned_i/locked_amount_i*coefficient where i is incremented at each burn
uint256 public accumulatedRewardPerTorn; uint256 public accumulatedRewardPerTorn;
@ -47,24 +51,26 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
event RewardsUpdated(address indexed account, uint256 rewards); event RewardsUpdated(address indexed account, uint256 rewards);
event RewardsClaimed(address indexed account, uint256 rewardsClaimed); event RewardsClaimed(address indexed account, uint256 rewardsClaimed);
modifier onlyGovernance() { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
require(msg.sender == address(Governance), "only governance");
_;
}
modifier onlyRelayerRegistry() {
require(msg.sender == relayerRegistry, "only relayer registry");
_;
}
// Minor code change here we won't resolve the registry by ENS // Minor code change here we won't resolve the registry by ENS
constructor(address governanceAddress, address tornAddress, address _relayerRegistry) public { constructor(address governanceAddress, address tornAddress, address _router) public {
Governance = ITornadoGovernance(governanceAddress); Governance = ITornadoGovernance(governanceAddress);
torn = IERC20(tornAddress); torn = IERC20(tornAddress);
relayerRegistry = _relayerRegistry; router = _router;
ratioConstant = IERC20(tornAddress).totalSupply(); ratioConstant = IERC20(tornAddress).totalSupply();
} }
modifier onlyGovernance() {
require(msg.sender == address(Governance), "TornadoStakingRewards: onlyGovernance");
_;
}
modifier onlyRouter() {
require(msg.sender == router, "TornadoStakingRewards: onlyRouter");
_;
}
/** /**
* @notice This function should safely send a user his rewards. * @notice This function should safely send a user his rewards.
* @dev IMPORTANT FUNCTION: * @dev IMPORTANT FUNCTION:
@ -89,7 +95,10 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
* @param amount amount to add to the rewards * @param amount amount to add to the rewards
*/ */
function addBurnRewards(uint256 amount) external { function addBurnRewards(uint256 amount) external {
require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized"); require(
msg.sender == address(Governance) || msg.sender == router,
"TornadoStakingRewards: only gov or router"
);
accumulatedRewardPerTorn = accumulatedRewardPerTorn.add( accumulatedRewardPerTorn = accumulatedRewardPerTorn.add(
amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault()))) amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault())))
); );
@ -118,13 +127,6 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
torn.safeTransfer(address(Governance), amount); torn.safeTransfer(address(Governance), amount);
} }
/**
* @notice This function may be used to reward a slasher (since it is registry gated)
*/
function rewardSlasher(address _slasher, uint256 _amount) external onlyRelayerRegistry {
torn.safeTransfer(_slasher, _amount);
}
/** /**
* @notice This function should calculated the proper amount of rewards attributed to user since the last * @notice This function should calculated the proper amount of rewards attributed to user since the last
* update * update
@ -150,6 +152,8 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
emit RewardsUpdated(account, claimed); emit RewardsUpdated(account, claimed);
} }
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/** /**
* @notice This function should show a user his rewards. * @notice This function should show a user his rewards.
* @param account address of account to calculate rewards for * @param account address of account to calculate rewards for

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
interface ENS {
function resolver(bytes32 node) external view returns (Resolver);
function owner(bytes32 node) external view returns (address);
}
interface Resolver {
function addr(bytes32 node) external view returns (address);
}
/**
* @dev The addresses below only work on Mainnet.
*/
contract EnsResolver {
address internal constant _ENS_ADDRESS = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e;
address internal constant _ENS_NAMEWRAPPER_ADDRESS = 0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401;
function owner(bytes32 node) public view virtual returns (address) {
ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
address _owner = ens.owner(node);
return _owner == _ENS_NAMEWRAPPER_ADDRESS ? Resolver(_owner).addr(node) : _owner;
}
function resolve(bytes32 node) public view virtual returns (address) {
ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
return ens.resolver(node).addr(node);
}
}

@ -140,16 +140,15 @@ contract ProposalTests is Instances, TornadoProposalTest {
curveFeeOracle.setUniswapFeeOracle(uniswapFeeOracle); curveFeeOracle.setUniswapFeeOracle(uniswapFeeOracle);
router = new TornadoRouter(address(governance));
implRegistry = new RelayerRegistry(address(governance), address(TORN)); implRegistry = new RelayerRegistry(address(governance), address(TORN));
implInstanceRegistry = new InstanceRegistry(address(governance)); implInstanceRegistry = new InstanceRegistry(address(governance));
implFeeOracleManager = new FeeOracleManager(address(TORN), address(governance)); implFeeOracleManager = new FeeOracleManager(address(TORN), address(governance));
implStaking = implStaking = new TornadoStakingRewards(address(governance), address(TORN), address(router));
new TornadoStakingRewards(address(governance), address(TORN), address(relayerRegistryProxyAddress));
router = new TornadoRouter(address(governance));
_advanceTORNETHMarket(); _advanceTORNETHMarket();
} }
@ -350,12 +349,12 @@ contract ProposalTests is Instances, TornadoProposalTest {
vm.expectRevert(); vm.expectRevert();
vm.prank(address(governance)); vm.prank(address(governance));
feeOracleManager.initialize( feeOracleManager.initialize(
address(uniswapFeeOracle), address(instanceRegistry), _toUpdate, feeArrayForTesting_1() address(uniswapFeeOracle), address(instanceRegistry), 6 hours, _toUpdate, feeArrayForTesting_1()
); );
vm.expectRevert(); vm.expectRevert();
feeOracleManager.initialize( feeOracleManager.initialize(
address(uniswapFeeOracle), address(instanceRegistry), _toUpdate, feeArrayForTesting_1() address(uniswapFeeOracle), address(instanceRegistry), 6 hours, _toUpdate, feeArrayForTesting_1()
); );
// Must not be able to re-initialize the router... the contract addresses are unimportant // Must not be able to re-initialize the router... the contract addresses are unimportant