-
Notifications
You must be signed in to change notification settings - Fork 6
/
Core.sol
221 lines (192 loc) · 6.45 KB
/
Core.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// SPDX-License-Identifier: MIT
pragma solidity 0.6.11;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20, SafeMath} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/math/Math.sol";
import {IPeak} from "./interfaces/IPeak.sol";
import {IbBTC} from "./interfaces/IbBTC.sol";
import {ICore} from "./interfaces/ICore.sol";
import {GovernableProxy} from "./common/proxy/GovernableProxy.sol";
contract Core is GovernableProxy, ICore {
using SafeERC20 for IERC20;
using SafeMath for uint;
using Math for uint;
uint constant PRECISION = 1e4;
IbBTC public immutable bBTC;
BadgerGuestListAPI public guestList;
enum PeakState { Extinct, Active, Dormant }
mapping(address => PeakState) public peaks;
address[] public peakAddresses;
address public feeSink;
uint public mintFee;
uint public redeemFee;
uint public accumulatedFee;
uint256[50] private __gap;
// END OF STORAGE VARIABLES
event PeakWhitelisted(address indexed peak);
event FeeCollected(uint amount);
/**
* @param _bBTC bBTC token address
*/
constructor(address _bBTC) public {
require(_bBTC != address(0), "NULL_ADDRESS");
bBTC = IbBTC(_bBTC);
}
/**
* @notice Mint bBTC
* @dev Only whitelisted peaks can call this function
* @param btc BTC amount supplied, scaled by 1e18
* @return bBtc Badger BTC that was minted
*/
function mint(uint btc, address account, bytes32[] calldata merkleProof)
override
external
returns(uint)
{
require(peaks[msg.sender] == PeakState.Active, "PEAK_INACTIVE");
if (address(guestList) != address(0)) {
require(
guestList.authorized(account, btc, merkleProof),
"guest-list-authorization"
);
}
(uint bBtc, uint fee) = btcToBbtc(btc);
require(bBtc > 0, "MINTING_0_bBTC");
accumulatedFee = accumulatedFee.add(fee);
bBTC.mint(account, bBtc);
return bBtc;
}
/**
* @param btc BTC amount supplied
*/
function btcToBbtc(uint btc) override public view returns (uint bBtc, uint fee) {
uint _totalSupply = IERC20(address(bBTC)).totalSupply().add(accumulatedFee);
if (_totalSupply > 0) {
bBtc = btc.mul(_totalSupply).div(totalSystemAssets());
} else {
bBtc = btc;
}
fee = bBtc.mul(mintFee).div(PRECISION);
bBtc = bBtc.sub(fee);
}
/**
* @notice Redeem bBTC
* @dev Only whitelisted peaks can call this function
* @param bBtc bBTC amount to redeem
* @return btc amount redeemed, scaled by 1e36
*/
function redeem(uint bBtc, address account) override external returns (uint) {
require(bBtc > 0, "REDEEMING_0_bBTC");
require(peaks[msg.sender] != PeakState.Extinct, "PEAK_EXTINCT");
(uint btc, uint fee) = bBtcToBtc(bBtc);
accumulatedFee = accumulatedFee.add(fee);
bBTC.burn(account, bBtc);
return btc;
}
/**
* @return btc amount redeemed, scaled by 1e36
*/
function bBtcToBtc(uint bBtc) override public view returns (uint btc, uint fee) {
fee = bBtc.mul(redeemFee).div(PRECISION);
btc = bBtc.sub(fee).mul(pricePerShare());
}
function pricePerShare() override public view returns (uint) {
uint _totalSupply = IERC20(address(bBTC)).totalSupply().add(accumulatedFee);
if (_totalSupply > 0) {
return totalSystemAssets().mul(1e18).div(_totalSupply);
}
return 1e18;
}
/**
* @notice Collect all the accumulated fee (denominated in bBTC)
*/
function collectFee() external {
require(feeSink != address(0), "NULL_ADDRESS");
uint _fee = accumulatedFee;
require(_fee > 0, "NO_FEE");
accumulatedFee = 0;
bBTC.mint(feeSink, _fee);
emit FeeCollected(_fee);
}
function totalSystemAssets() public view returns (uint totalAssets) {
address[] memory _peakAddresses = peakAddresses;
uint numPeaks = _peakAddresses.length;
for (uint i = 0; i < numPeaks; i++) {
if (peaks[_peakAddresses[i]] == PeakState.Extinct) {
continue;
}
totalAssets = totalAssets.add(
IPeak(_peakAddresses[i]).portfolioValue()
);
}
}
/* ##### Governance ##### */
/**
* @notice Whitelist a new peak
* @param peak Address of the contract that interfaces with the 3rd-party protocol
*/
function whitelistPeak(address peak)
external
onlyGovernance
{
require(
peaks[peak] == PeakState.Extinct,
"DUPLICATE_PEAK"
);
address[] memory _peakAddresses = peakAddresses;
uint numPeaks = _peakAddresses.length;
for (uint i = 0; i < numPeaks; i++) {
require(_peakAddresses[i] != peak, "USE_setPeakStatus");
}
IPeak(peak).portfolioValue(); // sanity check
peakAddresses.push(peak);
peaks[peak] = PeakState.Active;
emit PeakWhitelisted(peak);
}
/**
* @notice Change a peaks status
*/
function setPeakStatus(address peak, PeakState state)
external
onlyGovernance
{
require(
peaks[peak] != PeakState.Extinct,
"Peak is extinct"
);
if (state == PeakState.Extinct) {
require(IPeak(peak).portfolioValue() <= 1e15, "NON_TRIVIAL_FUNDS_IN_PEAK");
}
peaks[peak] = state;
}
/**
* @notice Set config
* @param _mintFee Mint Fee
* @param _redeemFee Redeem Fee
* @param _feeSink Address of the EOA/contract where accumulated fee will be transferred
*/
function setConfig(
uint _mintFee,
uint _redeemFee,
address _feeSink
)
external
onlyGovernance
{
require(
_mintFee <= PRECISION
&& _redeemFee <= PRECISION,
"INVALID_PARAMETERS"
);
require(_feeSink != address(0), "NULL_ADDRESS");
mintFee = _mintFee;
redeemFee = _redeemFee;
feeSink = _feeSink;
}
function setGuestList(address _guestList) external onlyGovernance {
guestList = BadgerGuestListAPI(_guestList);
}
}
interface BadgerGuestListAPI {
function authorized(address guest, uint256 amount, bytes32[] calldata merkleProof) external view returns (bool);
}