-
Notifications
You must be signed in to change notification settings - Fork 2
/
KeeperGauge.sol
164 lines (141 loc) · 5.63 KB
/
KeeperGauge.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IController.sol";
import "../../interfaces/tokenomics/IKeeperGauge.sol";
import "../../libraries/ScaledMath.sol";
import "../../libraries/Errors.sol";
import "../../libraries/AddressProviderHelpers.sol";
import "../../libraries/UncheckedMath.sol";
import "../access/Authorization.sol";
contract KeeperGauge is IKeeperGauge, Authorization {
using AddressProviderHelpers for IAddressProvider;
using ScaledMath for uint256;
using UncheckedMath for uint256;
struct KeeperRecord {
mapping(uint256 => uint256) feesInPeriod;
uint256 nextEpochToClaim;
bool firstEpochSet;
}
mapping(address => KeeperRecord) public keeperRecords;
mapping(uint256 => uint256) public perPeriodTotalFees;
IController public immutable controller;
address public immutable pool;
uint256 public epoch;
uint48 public lastUpdated;
mapping(uint256 => uint256) public perPeriodTotalInflation;
bool public override killed;
modifier onlyInflationManager() {
require(msg.sender == address(controller.inflationManager()), Error.UNAUTHORIZED_ACCESS);
_;
}
constructor(IController _controller, address _pool)
Authorization(_controller.addressProvider().getRoleManager())
{
controller = _controller;
pool = _pool;
lastUpdated = uint48(block.timestamp);
}
/**
* @notice Shut down the gauge.
* @dev Accrued inflation can still be claimed from the gauge after shutdown.
* @return `true` if successful.
*/
function kill() external override onlyInflationManager returns (bool) {
poolCheckpoint();
epoch++;
killed = true;
return true;
}
/**
* @notice Report fees generated by a keeper (this is the basis for inflation accrual).
* @dev lpTokenAddress is included for forward compatibility with a single gauge solution.
* @param beneficiary Address of the keeper who earned the fees.
* @param amount Amount of fees (in lp tokens) earned.
* @param lpTokenAddress Address of the lpToken in which the fees are paid.
* @return `true` if successful.
*/
function reportFees(
address beneficiary,
uint256 amount,
address lpTokenAddress
) external override returns (bool) {
lpTokenAddress; // silencing compiler warning
require(
IController(controller).addressProvider().isWhiteListedFeeHandler(msg.sender),
Error.ADDRESS_NOT_WHITELISTED
);
require(!killed, Error.CONTRACT_PAUSED);
if (!keeperRecords[beneficiary].firstEpochSet) {
keeperRecords[beneficiary].firstEpochSet = true;
keeperRecords[beneficiary].nextEpochToClaim = epoch;
}
keeperRecords[beneficiary].feesInPeriod[epoch] += amount;
perPeriodTotalFees[epoch] += amount;
return true;
}
/**
* @notice Advance the inflation accrual epoch.
* @return `true` if successful.
*/
function advanceEpoch() external virtual override onlyInflationManager returns (bool) {
poolCheckpoint();
epoch++;
return true;
}
function claimRewards(address beneficiary) external override returns (uint256) {
return claimRewards(beneficiary, epoch);
}
function claimableRewards(address beneficiary) external view override returns (uint256) {
return _calcTotalClaimable(beneficiary, keeperRecords[beneficiary].nextEpochToClaim, epoch);
}
function poolCheckpoint() public override returns (bool) {
if (killed) return false;
uint256 timeElapsed = block.timestamp - uint256(lastUpdated);
uint256 currentRate = IController(controller).inflationManager().getKeeperRateForPool(pool);
perPeriodTotalInflation[epoch] += currentRate * timeElapsed;
lastUpdated = uint48(block.timestamp);
return true;
}
/**
* @notice Claim rewards with an epoch up to which they should be claimed specified.
* @param beneficiary Address to claim rewards for.
* @param endEpoch Epoch up to which to claim rewards.
* @return The amount of rewards claimed.
*/
function claimRewards(address beneficiary, uint256 endEpoch) public override returns (uint256) {
require(
msg.sender == beneficiary || _roleManager().hasRole(Roles.GAUGE_ZAP, msg.sender),
Error.UNAUTHORIZED_ACCESS
);
if (endEpoch > epoch) {
endEpoch = epoch;
}
uint256 totalClaimable = _calcTotalClaimable(
beneficiary,
keeperRecords[beneficiary].nextEpochToClaim,
endEpoch
);
keeperRecords[beneficiary].nextEpochToClaim = endEpoch;
require(totalClaimable > 0, Error.ZERO_TRANSFER_NOT_ALLOWED);
_mintRewards(beneficiary, totalClaimable);
return totalClaimable;
}
function _mintRewards(address beneficiary, uint256 amount) internal returns (bool) {
IController(controller).inflationManager().mintRewards(beneficiary, amount);
return true;
}
function _calcTotalClaimable(
address beneficiary,
uint256 startEpoch,
uint256 endEpoch
) internal view returns (uint256) {
uint256 totalClaimable;
for (uint256 i = startEpoch; i < endEpoch; i = i.uncheckedInc()) {
totalClaimable += (
keeperRecords[beneficiary].feesInPeriod[i].scaledDiv(perPeriodTotalFees[i])
).scaledMul(perPeriodTotalInflation[i]);
}
return totalClaimable;
}
}