first try - compiles
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
9aebb55bc1
commit
0e5cb316b0
@ -143,7 +143,7 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
|
|||||||
address proposer,
|
address proposer,
|
||||||
address target,
|
address target,
|
||||||
string memory description
|
string memory description
|
||||||
) internal override(Delegation) returns (uint256) {
|
) internal virtual override(Delegation) returns (uint256) {
|
||||||
uint256 votingPower = lockedBalance[proposer];
|
uint256 votingPower = lockedBalance[proposer];
|
||||||
require(votingPower >= PROPOSAL_THRESHOLD, "Governance::propose: proposer votes below proposal threshold");
|
require(votingPower >= PROPOSAL_THRESHOLD, "Governance::propose: proposer votes below proposal threshold");
|
||||||
// target should be a contract
|
// target should be a contract
|
||||||
@ -181,7 +181,7 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
|
|||||||
return proposalId;
|
return proposalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute(uint256 proposalId) external payable virtual {
|
function execute(uint256 proposalId) public payable virtual {
|
||||||
require(state(proposalId) == ProposalState.AwaitingExecution, "Governance::execute: invalid proposal state");
|
require(state(proposalId) == ProposalState.AwaitingExecution, "Governance::execute: invalid proposal state");
|
||||||
Proposal storage proposal = proposals[proposalId];
|
Proposal storage proposal = proposals[proposalId];
|
||||||
proposal.executed = true;
|
proposal.executed = true;
|
||||||
|
@ -1,33 +1,60 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.12;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../v1/Governance.sol";
|
import "../v1/Governance.sol";
|
||||||
import "../v3-relayer-registry/GovernanceStakingUpgrade.sol";
|
import "../v3-relayer-registry/GovernanceStakingUpgrade.sol";
|
||||||
|
|
||||||
contract GovernancePatchProposal is GovernanceStakingUpgrade {
|
contract GovernancePatchUpgrade is GovernanceStakingUpgrade {
|
||||||
mapping(uint256 => bytes32) public _proposalCodeHashes;
|
mapping(uint256 => bytes32) public proposalCodehashes;
|
||||||
|
|
||||||
event CodeHashDifferent(address target, bytes32 oldHash, bytes32 newHash);
|
event CodehashDifferent(address target, bytes32 oldHash, bytes32 newHash);
|
||||||
|
|
||||||
|
// The stakingRewardsAddress sets the immutable to the new staking contract
|
||||||
|
constructor(
|
||||||
|
address stakingRewardsAddress,
|
||||||
|
address gasCompLogic,
|
||||||
|
address userVaultAddress
|
||||||
|
) public GovernanceStakingUpgrade(stakingRewardsAddress, gasCompLogic, userVaultAddress) {}
|
||||||
|
|
||||||
|
// This should guarantee that the proposal extcodehashes are good
|
||||||
|
function execute(uint256 proposalId) public payable virtual override(Governance) {
|
||||||
|
require(msg.sender != address(this), "pseudo-external function");
|
||||||
|
|
||||||
function execute(uint256 proposalId) external payable virtual override(Governance) {
|
|
||||||
Proposal storage proposal = proposals[proposalId];
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
|
||||||
if (proposal.target.codehash == _proposalCodeHashes[proposalId]) {
|
address target = proposal.target;
|
||||||
|
|
||||||
|
bytes32 proposalCodehash;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
proposalCodehash := extcodehash(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proposalCodehash == proposalCodehashes[proposalId]) {
|
||||||
super.execute(proposalId);
|
super.execute(proposalId);
|
||||||
} else {
|
} else {
|
||||||
proposal.executed = true;
|
proposal.executed = true;
|
||||||
emit CodeHashDifferent(proposal.target, _proposalCodeHashes[proposalId], proposal.target.codehash);
|
emit CodehashDifferent(proposal.target, proposalCodehashes[proposalId], proposalCodehash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should store the proposal extcodehash
|
||||||
function _propose(
|
function _propose(
|
||||||
address proposer,
|
address proposer,
|
||||||
address target,
|
address target,
|
||||||
string memory description
|
string memory description
|
||||||
) internal virtual override(Governance) {
|
) internal virtual override(Governance) returns (uint256 proposalId) {
|
||||||
uint256 proposalId = super._propose(proposer, target, description);
|
// Implies all former predicates were valid
|
||||||
_proposalCodeHashes[proposalId] = target.codeHash;
|
proposalId = super._propose(proposer, target, description);
|
||||||
|
|
||||||
|
bytes32 proposalCodehash;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
proposalCodehash := extcodehash(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
proposalCodehashes[proposalId] = proposalCodehash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.12;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
import { LoopbackProxy } from "../v1/LoopbackProxy.sol";
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
|
||||||
|
|
||||||
|
import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol";
|
||||||
|
import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
|
||||||
|
|
||||||
|
contract RelayerRegistryProposal {
|
||||||
|
using SafeMath for uint256;
|
||||||
|
using Address for address;
|
||||||
|
|
||||||
|
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||||
|
|
||||||
|
address public immutable registry;
|
||||||
|
|
||||||
|
constructor(address _registry) public {
|
||||||
|
registry = _registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aight lets do this sirs
|
||||||
|
function executeProposal() external {
|
||||||
|
// address(this) has to be governance
|
||||||
|
address payable governance = payable(address(this));
|
||||||
|
|
||||||
|
// Get the two contracts gov depends on
|
||||||
|
address gasComp = address(GovernancePatchUpgrade(governance).gasCompensationVault());
|
||||||
|
address vault = address(GovernancePatchUpgrade(governance).userVault());
|
||||||
|
|
||||||
|
// Get the old staking contract
|
||||||
|
TornadoStakingRewards oldStaking = TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking()));
|
||||||
|
|
||||||
|
// Get all of the TORN out cuz broken
|
||||||
|
oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking)));
|
||||||
|
|
||||||
|
// And create a new staking contract
|
||||||
|
TornadoStakingRewards newStaking = new TornadoStakingRewards(governance, address(TORN), address(registry));
|
||||||
|
|
||||||
|
// Now upgrade the governance to the latest stuff
|
||||||
|
LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault)));
|
||||||
|
}
|
||||||
|
}
|
384
contracts/v4-patch/RelayerRegistry.sol
Normal file
384
contracts/v4-patch/RelayerRegistry.sol
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.12;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
|
||||||
|
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
import { EnsResolve } from "torn-token/contracts/ENS.sol";
|
||||||
|
import { TORN } from "torn-token/contracts/TORN.sol";
|
||||||
|
import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
|
||||||
|
|
||||||
|
interface ITornadoInstance {
|
||||||
|
function token() external view returns (address);
|
||||||
|
|
||||||
|
function denomination() external view returns (uint256);
|
||||||
|
|
||||||
|
function deposit(bytes32 commitment) external payable;
|
||||||
|
|
||||||
|
function withdraw(
|
||||||
|
bytes calldata proof,
|
||||||
|
bytes32 root,
|
||||||
|
bytes32 nullifierHash,
|
||||||
|
address payable recipient,
|
||||||
|
address payable relayer,
|
||||||
|
uint256 fee,
|
||||||
|
uint256 refund
|
||||||
|
) external payable;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IENS {
|
||||||
|
function owner(bytes32 node) external view returns (address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @dev Solidity implementation of the ENS namehash algorithm.
|
||||||
|
*
|
||||||
|
* Warning! Does not normalize or validate names before hashing.
|
||||||
|
* Original version can be found here https://github.com/JonahGroendal/ens-namehash/
|
||||||
|
*/
|
||||||
|
library ENSNamehash {
|
||||||
|
function namehash(bytes memory domain) internal pure returns (bytes32) {
|
||||||
|
return namehash(domain, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function namehash(bytes memory domain, uint256 i) internal pure returns (bytes32) {
|
||||||
|
if (domain.length <= i) return 0x0000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
|
||||||
|
uint256 len = labelLength(domain, i);
|
||||||
|
|
||||||
|
return keccak256(abi.encodePacked(namehash(domain, i + len + 1), keccak(domain, i, len)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function labelLength(bytes memory domain, uint256 i) private pure returns (uint256) {
|
||||||
|
uint256 len;
|
||||||
|
while (i + len != domain.length && domain[i + len] != 0x2e) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keccak(
|
||||||
|
bytes memory data,
|
||||||
|
uint256 offset,
|
||||||
|
uint256 len
|
||||||
|
) private pure returns (bytes32 ret) {
|
||||||
|
require(offset + len <= data.length);
|
||||||
|
assembly {
|
||||||
|
ret := keccak256(add(add(data, 32), offset), len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFeeManager {
|
||||||
|
function instanceFeeWithUpdate(ITornadoInstance _instance) external returns (uint160);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RelayerState {
|
||||||
|
uint256 balance;
|
||||||
|
bytes32 ensHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Registry contract, one of the main contracts of this protocol upgrade.
|
||||||
|
* The contract should store relayers' addresses and data attributed to the
|
||||||
|
* master address of the relayer. This data includes the relayers stake and
|
||||||
|
* his ensHash.
|
||||||
|
* A relayers master address has a number of subaddresses called "workers",
|
||||||
|
* these are all addresses which burn stake in communication with the proxy.
|
||||||
|
* If a relayer is not registered, he is not displayed on the frontend.
|
||||||
|
* @dev CONTRACT RISKS:
|
||||||
|
* - if setter functions are compromised, relayer metadata would be at risk, including the noted amount of his balance
|
||||||
|
* - if burn function is compromised, relayers run the risk of being unable to handle withdrawals
|
||||||
|
* - the above risk also applies to the nullify balance function
|
||||||
|
* */
|
||||||
|
contract RelayerRegistry is Initializable, EnsResolve {
|
||||||
|
using SafeMath for uint256;
|
||||||
|
using SafeERC20 for TORN;
|
||||||
|
using ENSNamehash for bytes;
|
||||||
|
|
||||||
|
TORN public immutable torn;
|
||||||
|
address public immutable governance;
|
||||||
|
IENS public immutable ens;
|
||||||
|
TornadoStakingRewards public immutable staking;
|
||||||
|
IFeeManager public immutable feeManager;
|
||||||
|
|
||||||
|
address public tornadoRouter;
|
||||||
|
uint256 public minStakeAmount;
|
||||||
|
|
||||||
|
mapping(address => RelayerState) public relayers;
|
||||||
|
mapping(address => address) public workers;
|
||||||
|
|
||||||
|
event RelayerBalanceNullified(address relayer);
|
||||||
|
event WorkerRegistered(address relayer, address worker);
|
||||||
|
event WorkerUnregistered(address relayer, address worker);
|
||||||
|
event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
|
||||||
|
event StakeBurned(address relayer, uint256 amountBurned);
|
||||||
|
event MinimumStakeAmount(uint256 minStakeAmount);
|
||||||
|
event RouterRegistered(address tornadoRouter);
|
||||||
|
event RelayerRegistered(bytes32 relayer, string ensName, address relayerAddress, uint256 stakedAmount);
|
||||||
|
|
||||||
|
modifier onlyGovernance() {
|
||||||
|
require(msg.sender == governance, "only governance");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyTornadoRouter() {
|
||||||
|
require(msg.sender == tornadoRouter, "only proxy");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyRelayer(address sender, address relayer) {
|
||||||
|
require(workers[sender] == relayer, "only relayer");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
address _torn,
|
||||||
|
address _governance,
|
||||||
|
address _ens,
|
||||||
|
bytes32 _staking,
|
||||||
|
bytes32 _feeManager
|
||||||
|
) public {
|
||||||
|
torn = TORN(_torn);
|
||||||
|
governance = _governance;
|
||||||
|
ens = IENS(_ens);
|
||||||
|
staking = TornadoStakingRewards(resolve(_staking));
|
||||||
|
feeManager = IFeeManager(resolve(_feeManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice initialize function for upgradeability
|
||||||
|
* @dev this contract will be deployed behind a proxy and should not assign values at logic address,
|
||||||
|
* params left out because self explainable
|
||||||
|
* */
|
||||||
|
function initialize(bytes32 _tornadoRouter) external initializer {
|
||||||
|
tornadoRouter = resolve(_tornadoRouter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should register a master address and optionally a set of workeres for a relayer + metadata
|
||||||
|
* @dev Relayer can't steal other relayers workers since they are registered, and a wallet (msg.sender check) can always unregister itself
|
||||||
|
* @param ensName ens name of the relayer
|
||||||
|
* @param stake the initial amount of stake in TORN the relayer is depositing
|
||||||
|
* */
|
||||||
|
function register(
|
||||||
|
string calldata ensName,
|
||||||
|
uint256 stake,
|
||||||
|
address[] calldata workersToRegister
|
||||||
|
) external {
|
||||||
|
_register(msg.sender, ensName, stake, workersToRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Register function equivalent with permit-approval instead of regular approve.
|
||||||
|
* */
|
||||||
|
function registerPermit(
|
||||||
|
string calldata ensName,
|
||||||
|
uint256 stake,
|
||||||
|
address[] calldata workersToRegister,
|
||||||
|
address relayer,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) external {
|
||||||
|
torn.permit(relayer, address(this), stake, deadline, v, r, s);
|
||||||
|
_register(relayer, ensName, stake, workersToRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _register(
|
||||||
|
address relayer,
|
||||||
|
string calldata ensName,
|
||||||
|
uint256 stake,
|
||||||
|
address[] calldata workersToRegister
|
||||||
|
) internal {
|
||||||
|
bytes32 ensHash = bytes(ensName).namehash();
|
||||||
|
require(relayer == ens.owner(ensHash), "only ens owner");
|
||||||
|
require(workers[relayer] == address(0), "cant register again");
|
||||||
|
RelayerState storage metadata = relayers[relayer];
|
||||||
|
|
||||||
|
require(metadata.ensHash == bytes32(0), "registered already");
|
||||||
|
require(stake >= minStakeAmount, "!min_stake");
|
||||||
|
|
||||||
|
torn.safeTransferFrom(relayer, address(staking), stake);
|
||||||
|
emit StakeAddedToRelayer(relayer, stake);
|
||||||
|
|
||||||
|
metadata.balance = stake;
|
||||||
|
metadata.ensHash = ensHash;
|
||||||
|
workers[relayer] = relayer;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < workersToRegister.length; i++) {
|
||||||
|
address worker = workersToRegister[i];
|
||||||
|
_registerWorker(relayer, worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit RelayerRegistered(ensHash, ensName, relayer, stake);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow relayers to register more workeres
|
||||||
|
* @param relayer Relayer which should send message from any worker which is already registered
|
||||||
|
* @param worker Address to register
|
||||||
|
* */
|
||||||
|
function registerWorker(address relayer, address worker) external onlyRelayer(msg.sender, relayer) {
|
||||||
|
_registerWorker(relayer, worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _registerWorker(address relayer, address worker) internal {
|
||||||
|
require(workers[worker] == address(0), "can't steal an address");
|
||||||
|
workers[worker] = relayer;
|
||||||
|
emit WorkerRegistered(relayer, worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow anybody to unregister an address they own
|
||||||
|
* @dev designed this way as to allow someone to unregister themselves in case a relayer misbehaves
|
||||||
|
* - this should be followed by an action like burning relayer stake
|
||||||
|
* - there was an option of allowing the sender to burn relayer stake in case of malicious behaviour, this feature was not included in the end
|
||||||
|
* - reverts if trying to unregister master, otherwise contract would break. in general, there should be no reason to unregister master at all
|
||||||
|
* */
|
||||||
|
function unregisterWorker(address worker) external {
|
||||||
|
if (worker != msg.sender) require(workers[worker] == msg.sender, "only owner of worker");
|
||||||
|
require(workers[worker] != worker, "cant unregister master");
|
||||||
|
emit WorkerUnregistered(workers[worker], worker);
|
||||||
|
workers[worker] = address(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow anybody to stake to a relayer more TORN
|
||||||
|
* @param relayer Relayer main address to stake to
|
||||||
|
* @param stake Stake to be added to relayer
|
||||||
|
* */
|
||||||
|
function stakeToRelayer(address relayer, uint256 stake) external {
|
||||||
|
_stakeToRelayer(msg.sender, relayer, stake);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev stakeToRelayer function equivalent with permit-approval instead of regular approve.
|
||||||
|
* @param staker address from that stake is paid
|
||||||
|
* */
|
||||||
|
function stakeToRelayerPermit(
|
||||||
|
address relayer,
|
||||||
|
uint256 stake,
|
||||||
|
address staker,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) external {
|
||||||
|
torn.permit(staker, address(this), stake, deadline, v, r, s);
|
||||||
|
_stakeToRelayer(staker, relayer, stake);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stakeToRelayer(
|
||||||
|
address staker,
|
||||||
|
address relayer,
|
||||||
|
uint256 stake
|
||||||
|
) internal {
|
||||||
|
require(workers[relayer] == relayer, "!registered");
|
||||||
|
torn.safeTransferFrom(staker, address(staking), stake);
|
||||||
|
relayers[relayer].balance = stake.add(relayers[relayer].balance);
|
||||||
|
emit StakeAddedToRelayer(relayer, stake);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should burn some relayer stake on withdraw and notify staking of this
|
||||||
|
* @dev IMPORTANT FUNCTION:
|
||||||
|
* - This should be only called by the tornado proxy
|
||||||
|
* - Should revert if relayer does not call proxy from valid worker
|
||||||
|
* - Should not overflow
|
||||||
|
* - Should underflow and revert (SafeMath) on not enough stake (balance)
|
||||||
|
* @param sender worker to check sender == relayer
|
||||||
|
* @param relayer address of relayer who's stake is being burned
|
||||||
|
* @param pool instance to get fee for
|
||||||
|
* */
|
||||||
|
function burn(
|
||||||
|
address sender,
|
||||||
|
address relayer,
|
||||||
|
ITornadoInstance pool
|
||||||
|
) external onlyTornadoRouter {
|
||||||
|
address masterAddress = workers[sender];
|
||||||
|
if (masterAddress == address(0)) {
|
||||||
|
require(workers[relayer] == address(0), "Only custom relayer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
require(masterAddress == relayer, "only relayer");
|
||||||
|
uint256 toBurn = feeManager.instanceFeeWithUpdate(pool);
|
||||||
|
relayers[relayer].balance = relayers[relayer].balance.sub(toBurn);
|
||||||
|
staking.addBurnRewards(toBurn);
|
||||||
|
emit StakeBurned(relayer, toBurn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance to set the minimum stake amount
|
||||||
|
* @param minAmount new minimum stake amount
|
||||||
|
* */
|
||||||
|
function setMinStakeAmount(uint256 minAmount) external onlyGovernance {
|
||||||
|
minStakeAmount = minAmount;
|
||||||
|
emit MinimumStakeAmount(minAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance to set a new tornado proxy address
|
||||||
|
* @param tornadoRouterAddress address of the new proxy
|
||||||
|
* */
|
||||||
|
function setTornadoRouter(address tornadoRouterAddress) external onlyGovernance {
|
||||||
|
tornadoRouter = tornadoRouterAddress;
|
||||||
|
emit RouterRegistered(tornadoRouterAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance to nullify a relayers balance
|
||||||
|
* @dev IMPORTANT FUNCTION:
|
||||||
|
* - Should nullify the balance
|
||||||
|
* - Adding nullified balance as rewards was refactored to allow for the flexibility of these funds (for gov to operate with them)
|
||||||
|
* @param relayer address of relayer who's balance is to nullify
|
||||||
|
* */
|
||||||
|
function nullifyBalance(address relayer) external onlyGovernance {
|
||||||
|
address masterAddress = workers[relayer];
|
||||||
|
require(relayer == masterAddress, "must be master");
|
||||||
|
relayers[masterAddress].balance = 0;
|
||||||
|
emit RelayerBalanceNullified(relayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should check if a worker is associated with a relayer
|
||||||
|
* @param toResolve address to check
|
||||||
|
* @return true if is associated
|
||||||
|
* */
|
||||||
|
function isRelayer(address toResolve) external view returns (bool) {
|
||||||
|
return workers[toResolve] != address(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should check if a worker is registered to the relayer stated
|
||||||
|
* @param relayer relayer to check
|
||||||
|
* @param toResolve address to check
|
||||||
|
* @return true if registered
|
||||||
|
* */
|
||||||
|
function isRelayerRegistered(address relayer, address toResolve) external view returns (bool) {
|
||||||
|
return workers[toResolve] == relayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should get a relayers ensHash
|
||||||
|
* @param relayer address to fetch for
|
||||||
|
* @return relayer's ensHash
|
||||||
|
* */
|
||||||
|
function getRelayerEnsHash(address relayer) external view returns (bytes32) {
|
||||||
|
return relayers[workers[relayer]].ensHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should get a relayers balance
|
||||||
|
* @param relayer relayer who's balance is to fetch
|
||||||
|
* @return relayer's balance
|
||||||
|
* */
|
||||||
|
function getRelayerBalance(address relayer) external view returns (uint256) {
|
||||||
|
return relayers[workers[relayer]].balance;
|
||||||
|
}
|
||||||
|
}
|
143
contracts/v4-patch/TornadoStakingRewards.sol
Normal file
143
contracts/v4-patch/TornadoStakingRewards.sol
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.12;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
|
||||||
|
import { EnsResolve } from "torn-token/contracts/ENS.sol";
|
||||||
|
|
||||||
|
interface ITornadoVault {
|
||||||
|
function withdrawTorn(address recipient, uint256 amount) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITornadoGovernance {
|
||||||
|
function lockedBalance(address account) external view returns (uint256);
|
||||||
|
|
||||||
|
function userVault() external view returns (ITornadoVault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This is the staking contract of the governance staking upgrade.
|
||||||
|
* This contract should hold the staked funds which are received upon relayer registration,
|
||||||
|
* and properly attribute rewards to addresses without security issues.
|
||||||
|
* @dev CONTRACT RISKS:
|
||||||
|
* - Relayer staked TORN at risk if contract is compromised.
|
||||||
|
* */
|
||||||
|
contract TornadoStakingRewards is Initializable, EnsResolve {
|
||||||
|
using SafeMath for uint256;
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
/// @notice 1e25
|
||||||
|
uint256 public immutable ratioConstant;
|
||||||
|
ITornadoGovernance public immutable Governance;
|
||||||
|
IERC20 public immutable torn;
|
||||||
|
address public immutable relayerRegistry;
|
||||||
|
|
||||||
|
/// @notice the sum torn_burned_i/locked_amount_i*coefficient where i is incremented at each burn
|
||||||
|
uint256 public accumulatedRewardPerTorn;
|
||||||
|
/// @notice notes down accumulatedRewardPerTorn for an address on a lock/unlock/claim
|
||||||
|
mapping(address => uint256) public accumulatedRewardRateOnLastUpdate;
|
||||||
|
/// @notice notes down how much an account may claim
|
||||||
|
mapping(address => uint256) public accumulatedRewards;
|
||||||
|
|
||||||
|
event RewardsUpdated(address indexed account, uint256 rewards);
|
||||||
|
event RewardsClaimed(address indexed account, uint256 rewardsClaimed);
|
||||||
|
|
||||||
|
modifier onlyGovernance() {
|
||||||
|
require(msg.sender == address(Governance), "only governance");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor code change here we won't resolve the registry by ENS
|
||||||
|
constructor(
|
||||||
|
address governanceAddress,
|
||||||
|
address tornAddress,
|
||||||
|
address _relayerRegistry
|
||||||
|
) public {
|
||||||
|
Governance = ITornadoGovernance(governanceAddress);
|
||||||
|
torn = IERC20(tornAddress);
|
||||||
|
relayerRegistry = _relayerRegistry;
|
||||||
|
ratioConstant = IERC20(tornAddress).totalSupply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should safely send a user his rewards.
|
||||||
|
* @dev IMPORTANT FUNCTION:
|
||||||
|
* We know that rewards are going to be updated every time someone locks or unlocks
|
||||||
|
* so we know that this function can't be used to falsely increase the amount of
|
||||||
|
* lockedTorn by locking in governance and subsequently calling it.
|
||||||
|
* - set rewards to 0 greedily
|
||||||
|
*/
|
||||||
|
function getReward() external {
|
||||||
|
uint256 rewards = _updateReward(msg.sender, Governance.lockedBalance(msg.sender));
|
||||||
|
rewards = rewards.add(accumulatedRewards[msg.sender]);
|
||||||
|
accumulatedRewards[msg.sender] = 0;
|
||||||
|
torn.safeTransfer(msg.sender, rewards);
|
||||||
|
emit RewardsClaimed(msg.sender, rewards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should increment the proper amount of rewards per torn for the contract
|
||||||
|
* @dev IMPORTANT FUNCTION:
|
||||||
|
* - calculation must not overflow with extreme values
|
||||||
|
* (amount <= 1e25) * 1e25 / (balance of vault <= 1e25) -> (extreme values)
|
||||||
|
* @param amount amount to add to the rewards
|
||||||
|
*/
|
||||||
|
function addBurnRewards(uint256 amount) external {
|
||||||
|
require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized");
|
||||||
|
accumulatedRewardPerTorn = accumulatedRewardPerTorn.add(
|
||||||
|
amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault())))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance to properly update the accumulated rewards rate for an account
|
||||||
|
* @param account address of account to update data for
|
||||||
|
* @param amountLockedBeforehand the balance locked beforehand in the governance contract
|
||||||
|
* */
|
||||||
|
function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand) external onlyGovernance {
|
||||||
|
uint256 claimed = _updateReward(account, amountLockedBeforehand);
|
||||||
|
accumulatedRewards[account] = accumulatedRewards[account].add(claimed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance rescue tokens from the staking rewards contract
|
||||||
|
* */
|
||||||
|
function withdrawTorn(uint256 amount) external onlyGovernance {
|
||||||
|
if (amount == type(uint256).max) amount = torn.balanceOf(address(this));
|
||||||
|
torn.safeTransfer(address(Governance), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should calculated the proper amount of rewards attributed to user since the last update
|
||||||
|
* @dev IMPORTANT FUNCTION:
|
||||||
|
* - calculation must not overflow with extreme values
|
||||||
|
* (accumulatedReward <= 1e25) * (lockedBeforehand <= 1e25) / 1e25
|
||||||
|
* - result may go to 0, since this implies on 1 TORN locked => accumulatedReward <= 1e7, meaning a very small reward
|
||||||
|
* @param account address of account to calculate rewards for
|
||||||
|
* @param amountLockedBeforehand the balance locked beforehand in the governance contract
|
||||||
|
* @return claimed the rewards attributed to user since the last update
|
||||||
|
*/
|
||||||
|
function _updateReward(address account, uint256 amountLockedBeforehand) private returns (uint256 claimed) {
|
||||||
|
if (amountLockedBeforehand != 0)
|
||||||
|
claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLockedBeforehand).div(
|
||||||
|
ratioConstant
|
||||||
|
);
|
||||||
|
accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn;
|
||||||
|
emit RewardsUpdated(account, claimed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should show a user his rewards.
|
||||||
|
* @param account address of account to calculate rewards for
|
||||||
|
*/
|
||||||
|
function checkReward(address account) external view returns (uint256 rewards) {
|
||||||
|
uint256 amountLocked = Governance.lockedBalance(account);
|
||||||
|
if (amountLocked != 0)
|
||||||
|
rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLocked).div(ratioConstant);
|
||||||
|
rewards = rewards.add(accumulatedRewards[account]);
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
version: '0.8.20',
|
||||||
|
settings: {
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
networks: {
|
networks: {
|
||||||
|
Loading…
Reference in New Issue
Block a user