Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes + Merge into single checker #2

Open
wants to merge 10 commits into
base: nv/fixes
Choose a base branch
from
Open
50 changes: 18 additions & 32 deletions contracts/ArbitrumResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
pragma solidity 0.8.4;

import "./Resolver.sol";
import "./interfaces/ITenderizer.sol";

import "./interfaces/ILivepeer.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol";
Expand All @@ -13,51 +12,38 @@ contract ArbitrumResolver is Resolver {
IQuoterV2 public uniswapQuoter;
uint256 MAX_ROUND = 2**256 - 1;

bytes32 constant LIVEPEER = 0xd205d14d3a0d2f58a4c37465ba21641a9c0ee2069cf49bd9f35c0cc161a04dd7; // "Livepeer"

function rebaseChecker(address _tenderizer)
external
override
view
returns (bool canExec, bytes memory execPayload){
execPayload = abi.encode();
execPayload = abi.encodeWithSelector(IResolver.claimRewardsExecutor.selector, _tenderizer);
Protocol storage protocol = protocols[_tenderizer];
ITenderizer tenderizer = ITenderizer(_tenderizer);

// Return true if pending deposits to stake
canExec = _depositChecker(_tenderizer);
if(canExec){
return (canExec, execPayload);
}

if(protocol.lastClaim + protocol.rebaseInterval > block.timestamp) {
return (canExec, execPayload);
}

ITenderizer tenderizer = ITenderizer(_tenderizer);
uint256 currentPrinciple = tenderizer.totalStakedTokens();
uint256 stake;

if (keccak256(bytes(protocol.name)) == keccak256(bytes("Livepeer"))) {
if (keccak256(bytes(protocol.name)) == LIVEPEER) {
// Livepeer
ILivepeer livepeer = ILivepeer(protocol.stakingContract);
stake = livepeer.pendingStake(address(this), MAX_ROUND);

// Check for ETH Rewards
uint256 ethFees = livepeer.pendingFees(address(this), MAX_ROUND);
if(ethFees > 0){
IQuoterV2.QuoteExactInputSingleParams memory params = IQuoterV2.QuoteExactInputSingleParams({
tokenIn: address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2),
tokenOut: address(protocol.steak),
amountIn: ethFees,
fee: 10000,
sqrtPriceLimitX96: 0
});
(uint256 amountOut,,,)= uniswapQuoter.quoteExactInputSingle(params);
stake += amountOut;
}
stake = livepeer.pendingStake(_tenderizer, MAX_ROUND);
}

uint256 blockTimestamp = block.timestamp;

if (stake > currentPrinciple + protocol.rebaseThreshold
&& (protocol.lastRebase == 0
|| protocol.lastRebase + protocol.rebaseInternval < blockTimestamp)){
protocol.lastRebase = blockTimestamp;
if (stake > currentPrinciple + protocol.rebaseThreshold){
canExec = true;
} else {
canExec = false;
}
}

// Livepeer specific functions
function setUniswapQuoter(address _uniswapQuoter) external onlyGov {
uniswapQuoter = IQuoterV2(_uniswapQuoter);
}
}
7 changes: 3 additions & 4 deletions contracts/IResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ pragma solidity 0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IResolver {
function depositChecker(address _tenderizer)
external
returns (bool canExec, bytes memory execPayload);

function rebaseChecker (address _tenderizer)
external
view
returns (bool canExec, bytes memory execPayload);

function register(
Expand All @@ -26,4 +23,6 @@ interface IResolver {
) external;

function setGov(address _gov) external;

function claimRewardsExecutor(address _tenderizer) external ;
}
41 changes: 24 additions & 17 deletions contracts/MainnetResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
pragma solidity 0.8.4;

import "./Resolver.sol";
import "./interfaces/ITenderizer.sol";

import "./interfaces/IGraph.sol";
import "./interfaces/IMatic.sol";
Expand All @@ -15,49 +14,57 @@ contract MainnetResolver is Resolver {
uint256 constant EXCHANGE_RATE_PRECISION = 100; // For Validator ID < 8
uint256 constant EXCHANGE_RATE_PRECISION_HIGH = 10**29; // For Validator ID >= 8

bytes32 constant GRAPH = 0xf33f789e3939d11e1b15e7342d3161b39f98259904e8ebdc1da58ce84a17f509; // "Graph"
bytes32 constant AUDIUS = 0xbf92ffa8d618cd090d960a5b3cb58c78332d37eedf59819530a17714aa2dc74c; // "Audius"
bytes32 constant MATIC = 0xe0323cd44c3bff8ae1a6f6bb89d41ecaa34bcb9eab6e20fe02a77f37f7344b83; // "Matic"

function rebaseChecker(address _tenderizer)
external
override
view
returns (bool canExec, bytes memory execPayload){
execPayload = abi.encode();
execPayload = abi.encodeWithSelector(IResolver.claimRewardsExecutor.selector, _tenderizer);
Protocol storage protocol = protocols[_tenderizer];
ITenderizer tenderizer = ITenderizer(_tenderizer);

// Return true if pending deposits to stake
canExec = _depositChecker(_tenderizer);
if(canExec){
return (canExec, execPayload);
}

if(protocol.lastClaim + protocol.rebaseInterval > block.timestamp) {
return (canExec, execPayload);
}

ITenderizer tenderizer = ITenderizer(_tenderizer);
uint256 currentPrinciple = tenderizer.totalStakedTokens();
uint256 stake;

if (keccak256(bytes(protocol.name)) == keccak256(bytes("Graph"))) {
if (keccak256(bytes(protocol.name)) == GRAPH) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from a code readability point of view, having a function with 2 string params would be the best, is this a better trade-off?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yea considered this, although with variable names I think the readability is still okay, and save some gas by not having to compute two hashes.

// Graph
address node = tenderizer.node();
IGraph graph = IGraph(protocol.stakingContract);
IGraph.Delegation memory delegation = graph.getDelegation(node, address(this));
IGraph.Delegation memory delegation = graph.getDelegation(node, _tenderizer);
IGraph.DelegationPool memory delPool = graph.delegationPools(node);

uint256 delShares = delegation.shares;
uint256 totalShares = delPool.shares;
uint256 totalTokens = delPool.tokens;

stake = (delShares * totalTokens) / totalShares;
} else if (keccak256(bytes(protocol.name)) == keccak256(bytes("Audius"))) {
} else if (keccak256(bytes(protocol.name)) == AUDIUS) {
// Audius
IAudius audius = IAudius(protocol.stakingContract);
stake = audius.getTotalDelegatorStake(address(this));
} else if (keccak256(bytes(protocol.name)) == keccak256(bytes("Matic"))) {
stake = audius.getTotalDelegatorStake(_tenderizer);
} else if (keccak256(bytes(protocol.name)) == MATIC) {
// Matic
IMatic matic = IMatic(protocol.stakingContract);
uint256 shares = matic.balanceOf(address(this));
uint256 shares = matic.balanceOf(_tenderizer);
stake = (shares * _getExchangeRate(matic)) / _getExchangeRatePrecision(matic);
}

uint256 blockTimestamp = block.timestamp;

if (stake > currentPrinciple + protocol.rebaseThreshold
&& (protocol.lastRebase == 0
|| protocol.lastRebase + protocol.rebaseInternval < blockTimestamp)){
protocol.lastRebase = blockTimestamp;
if (stake > currentPrinciple + protocol.rebaseThreshold){
canExec = true;
} else {
canExec = false;
}
}

Expand Down
65 changes: 35 additions & 30 deletions contracts/Resolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
pragma solidity 0.8.4;

import "./IResolver.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";

abstract contract Resolver is IResolver, Initializable {
import "./interfaces/ITenderizer.sol";

abstract contract Resolver is IResolver, ContextUpgradeable {

struct Protocol {
string name;
IERC20 steak;
address stakingContract;
uint256 depositInterval;
uint256 depositThreshold;
uint256 lastDeposit;
uint256 rebaseInternval;
uint256 rebaseInterval;
uint256 rebaseThreshold;
uint256 lastRebase;
uint256 lastClaim;
}

mapping(address => Protocol) protocols;
Expand All @@ -29,34 +30,39 @@ abstract contract Resolver is IResolver, Initializable {
}

function initialize() external initializer {
__Context_init_unchained();
gov = msg.sender;
}

function depositChecker(address _tenderizer)
external
override
returns (bool canExec, bytes memory execPayload){
function _depositChecker(address _tenderizer)
view
internal
returns (bool canExec){
Protocol storage protocol = protocols[_tenderizer];

if (protocol.lastClaim + protocol.depositInterval > block.timestamp) {
return false;
}

uint256 tenderizerSteakBal = protocol.steak.balanceOf(_tenderizer);
uint256 blockTimestamp = block.timestamp;

if(tenderizerSteakBal > protocol.depositThreshold
&& (protocol.lastDeposit == 0
|| protocol.lastDeposit + protocol.depositInterval < blockTimestamp)) {
protocol.lastDeposit = blockTimestamp;
if (tenderizerSteakBal >= protocol.depositThreshold) {
canExec = true;
execPayload = abi.encode(tenderizerSteakBal);
} else {
canExec = false;
execPayload = abi.encode();
}
}

function rebaseChecker(address _tenderizer)
external
override
view
virtual
returns (bool canExec, bytes memory execPayload);

function claimRewardsExecutor(address _tenderizer) external override {
ITenderizer tenderizer = ITenderizer(_tenderizer);
protocols[_tenderizer].lastClaim = block.timestamp;
tenderizer.claimRewards();
}

// Governance functions
function register(
Expand All @@ -66,20 +72,19 @@ abstract contract Resolver is IResolver, Initializable {
address _stakingContract,
uint256 _depositInterval,
uint256 _depositThreshold,
uint256 _rebaseInternval,
uint256 _rebaseInterval,
uint256 _rebaseThreshold
) onlyGov external override {
protocols[_tenderizer] = Protocol(
_name,
_steak,
_stakingContract,
_depositInterval,
_depositThreshold,
0,
_rebaseInternval,
_rebaseThreshold,
0
);
protocols[_tenderizer] = Protocol({
name: _name,
steak: _steak,
stakingContract: _stakingContract,
depositInterval: _depositInterval,
depositThreshold: _depositThreshold,
rebaseInterval: _rebaseInterval,
rebaseThreshold: _rebaseThreshold,
lastClaim: block.timestamp - _rebaseInterval // initialize checkpoint
});
}

function setGov(address _gov) onlyGov external override {
Expand Down
16 changes: 2 additions & 14 deletions contracts/interfaces/ITenderizer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,9 @@ pragma solidity 0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// TODO: Remove unwatned functions

/**
* @title Tenderizer is the base contract to be implemented.
* @notice Tenderizer is responsible for all Protocol interactions (staking, unstaking, claiming rewards)
* while also keeping track of user depsotis/withdrawals and protocol fees.
* @dev New implementations are required to inherit this contract and override any required internal functions.
*/
interface ITenderizer {

function deposit(uint256 _amount) external;
function claimRewards() external;
function node() external view returns (address);

/**
* @notice Total Staked Tokens returns the total amount of underlying tokens staked by this Tenderizer.
* @return totalStaked total amount staked by this Tenderizer
*/
function totalStakedTokens() external view returns (uint256 totalStaked);
}
1 change: 1 addition & 0 deletions deployments/mainnet/.chainId
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
Loading