-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathDbrDistributor.sol
128 lines (108 loc) · 4.56 KB
/
DbrDistributor.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "src/interfaces/IERC20.sol";
interface IDBR {
function markets(address) external view returns (bool);
function mint(address, uint) external;
}
interface IINVEscrow {
function market() external view returns (address);
function beneficiary() external view returns (address);
}
interface IMarket {
function escrows(address) external view returns (address);
function collateral() external view returns (IERC20);
}
contract DbrDistributor {
IDBR public immutable dbr;
address public gov;
address public operator;
address public constant INV = 0x41D5D79431A913C4aE7d69a668ecdfE5fF9DFB68;
uint public constant mantissa = 10**18;
uint public minRewardRate; // starts at 0
uint public maxRewardRate = type(uint).max / 3652500 days; // 10,000 years
uint public rewardRate; // starts at 0
uint public lastUpdate;
uint public rewardIndexMantissa;
uint public totalSupply;
mapping (address => uint) public balanceOf;
mapping (address => uint) public stakerIndexMantissa;
mapping (address => uint) public accruedRewards;
modifier updateIndex() {
uint deltaT = block.timestamp - lastUpdate;
if(deltaT > 0) {
if(rewardRate > 0 && totalSupply > 0) {
uint rewardsAccrued = deltaT * rewardRate * mantissa;
rewardIndexMantissa += rewardsAccrued / totalSupply;
}
lastUpdate = block.timestamp;
}
uint deltaIndex = rewardIndexMantissa - stakerIndexMantissa[msg.sender];
uint bal = balanceOf[msg.sender];
uint stakerDelta = bal * deltaIndex;
stakerIndexMantissa[msg.sender] = rewardIndexMantissa;
accruedRewards[msg.sender] += stakerDelta / mantissa;
_;
}
modifier onlyGov() {
require(msg.sender == gov, "ONLY GOV");
_;
}
modifier onlyOperator() {
require(msg.sender == operator, "ONLY OPERATOR");
_;
}
modifier onlyINVEscrow() { // We don't check if escrow's market is INV and assume all calling markets to be INV markets
_; // we break checks-effects-interactions to guard against re-entrancy below
IMarket market = IMarket(IINVEscrow(msg.sender).market());
address beneficiary = IINVEscrow(msg.sender).beneficiary();
require(dbr.markets(address(market)), "UNSUPPORTED MARKET");
require(address(market.collateral()) == address(INV), "UNSUPPORTED TOKEN");
require(market.escrows(beneficiary) == msg.sender, "MSG SENDER NOT A VALID ESCROW");
}
constructor (IDBR _dbr, address _gov, address _operator) {
dbr = _dbr;
gov = _gov;
operator = _operator;
lastUpdate = block.timestamp;
}
function setOperator(address _operator) public onlyGov { operator = _operator; }
function setGov(address _gov) public onlyGov { gov = _gov; }
function setRewardRateConstraints(uint _min, uint _max) public onlyGov updateIndex {
require(_max < type(uint).max / 3652500 days); // cannot overflow and revert within 10,000 years
require(_max >= _min);
minRewardRate = _min;
maxRewardRate = _max;
if(rewardRate > _max) {
rewardRate = _max;
} else if(rewardRate < _min) {
rewardRate = _min;
}
}
function setRewardRate(uint _rewardRate) public onlyOperator updateIndex {
require(_rewardRate >= minRewardRate, "REWARD RATE BELOW MIN");
require(_rewardRate <= maxRewardRate, "REWARD RATE ABOVE MIN");
rewardRate = _rewardRate;
}
function stake(uint amount) public updateIndex onlyINVEscrow {
balanceOf[msg.sender] += amount;
totalSupply += amount;
}
function unstake(uint amount) public updateIndex onlyINVEscrow {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
}
function claimable(address user) public view returns(uint) {
uint deltaT = block.timestamp - lastUpdate;
uint rewardsAccrued = deltaT * rewardRate * mantissa;
uint _rewardIndexMantissa = totalSupply > 0 ? rewardIndexMantissa + (rewardsAccrued / totalSupply) : rewardIndexMantissa;
uint deltaIndex = _rewardIndexMantissa - stakerIndexMantissa[user];
uint bal = balanceOf[user];
uint stakerDelta = bal * deltaIndex / mantissa;
return (accruedRewards[user] + stakerDelta);
}
function claim(address to) public updateIndex onlyINVEscrow {
dbr.mint(to, accruedRewards[msg.sender]);
accruedRewards[msg.sender] = 0;
}
}