-
Notifications
You must be signed in to change notification settings - Fork 1
/
GlobalImpliedCollateralService.sol
251 lines (215 loc) · 7.92 KB
/
GlobalImpliedCollateralService.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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
import "openzeppelin/token/ERC20/ERC20.sol";
import "./Permissions.sol";
import "./StabilityPod/PoolCollateral.sol";
/// @title Global Implied Collateral Service
/// @author 0xScotch <scotch@malt.money>
/// @notice A contract that provides an abstraction above all implied collateral sources
contract GlobalImpliedCollateralService is Permissions {
bytes32 public immutable UPDATER_MANAGER_ROLE;
// Note that the values in CoreCollateral are all denominated in malt.decimals
CoreCollateral public collateral;
address internal immutable deployer;
address public updaterManager;
address public proposedManager;
ERC20 public malt;
mapping(address => address) public poolUpdaters;
mapping(address => PoolCollateral) public poolCollateral;
mapping(address => address) public poolUpdatersLookup;
event SetPoolUpdater(address pool, address updater);
event Sync(
address indexed pool,
uint256 total,
uint256 overflow,
uint256 liquidityExtension,
uint256 swingTrader,
uint256 swingTraderMalt,
uint256 arbTokens
);
event SyncTotals(
uint256 totalCollateral,
uint256 totalRewardOverflow,
uint256 totalLiquidityExtension,
uint256 swingTraderCollateral,
uint256 swingTraderMalt,
uint256 arbTokens
);
event ChangeUpdaterManager(address manager);
event ProposeUpdaterManager(address manager);
constructor(
address _repository,
address initialAdmin,
address _malt,
address _deployer
) {
require(_repository != address(0), "GlobImpCol: Timelock addr(0)");
require(initialAdmin != address(0), "GlobImpCol: Admin addr(0)");
require(_malt != address(0), "GlobImpCol: Malt addr(0)");
_initialSetup(_repository);
UPDATER_MANAGER_ROLE = 0x46a4238f90cacb5750da6fcead9da5df56c925da47001c1e8d3e05c0b5a42012;
_roleSetup(
0x46a4238f90cacb5750da6fcead9da5df56c925da47001c1e8d3e05c0b5a42012,
initialAdmin
);
malt = ERC20(_malt);
deployer = _deployer;
}
function totalPhantomMalt() external view returns (uint256) {
// Represents the amount of Malt held by the protocol in an effectively burned state
// This could potentially be more complex in the future
return collateral.swingTraderMalt;
}
function collateralRatio() public view returns (uint256) {
uint256 decimals = malt.decimals();
uint256 totalSupply = malt.totalSupply();
if (totalSupply == 0) {
return 0;
}
return (collateral.total * (10**decimals)) / totalSupply;
}
function swingTraderCollateralRatio() public view returns (uint256) {
uint256 decimals = malt.decimals();
uint256 totalSupply = malt.totalSupply();
if (totalSupply == 0) {
return 0;
}
return (collateral.swingTrader * (10**decimals)) / malt.totalSupply();
}
function swingTraderCollateralDeficit() public view returns (uint256) {
// Note that collateral.swingTrader is already denominated in malt.decimals()
uint256 maltSupply = malt.totalSupply();
uint256 collateral = collateral.swingTrader; // gas
if (collateral >= maltSupply) {
return 0;
}
return maltSupply - collateral;
}
function setPoolUpdater(address _pool, address _updater)
external
onlyRoleMalt(UPDATER_MANAGER_ROLE, "Must have updater manager role")
{
require(_updater != address(0), "GlobImpCol: No addr(0)");
poolUpdaters[_updater] = _pool;
address oldUpdater = poolUpdatersLookup[_pool];
emit SetPoolUpdater(_pool, _updater);
poolUpdaters[oldUpdater] = address(0);
poolUpdatersLookup[_pool] = _updater;
}
function sync(PoolCollateral memory _pool) external {
require(
poolUpdaters[msg.sender] == _pool.lpPool,
"GlobImpCol: Unknown pool"
);
PoolCollateral storage existingPool = poolCollateral[_pool.lpPool];
uint256 existingCollateral = existingPool.total;
uint256 total = collateral.total; // gas
if (existingCollateral <= total) {
total -= existingCollateral; // subtract existing value
} else {
total = 0;
}
uint256 swingTraderMalt = collateral.swingTraderMalt; // gas
if (existingPool.swingTraderMalt <= swingTraderMalt) {
swingTraderMalt -= existingPool.swingTraderMalt;
} else {
swingTraderMalt = 0;
}
uint256 swingTraderCollat = collateral.swingTrader; // gas
if (existingPool.swingTrader <= swingTraderCollat) {
swingTraderCollat -= existingPool.swingTrader;
} else {
swingTraderCollat = 0;
}
uint256 arb = collateral.arbTokens; // gas
if (existingPool.arbTokens <= arb) {
arb -= existingPool.arbTokens;
} else {
arb = 0;
}
uint256 overflow = collateral.rewardOverflow; // gas
if (existingPool.rewardOverflow <= overflow) {
overflow -= existingPool.rewardOverflow;
} else {
overflow = 0;
}
uint256 liquidityExtension = collateral.liquidityExtension; // gas
if (existingPool.liquidityExtension <= liquidityExtension) {
liquidityExtension -= existingPool.liquidityExtension;
} else {
liquidityExtension = 0;
}
total += _pool.total;
swingTraderMalt += _pool.swingTraderMalt;
swingTraderCollat += _pool.swingTrader;
arb += _pool.arbTokens;
overflow += _pool.rewardOverflow;
liquidityExtension += _pool.liquidityExtension;
// Update global collateral
collateral.total = total;
// Update global swing trader malt
collateral.swingTraderMalt = swingTraderMalt;
// Update global ST collateral
collateral.swingTrader = swingTraderCollat;
// Update global arb tokens
collateral.arbTokens = arb;
// Update global overflow
collateral.rewardOverflow = overflow;
// Update global liquidityExtension
collateral.liquidityExtension = liquidityExtension;
// Update PoolCollateral for this pool
existingPool.lpPool = _pool.lpPool;
existingPool.total = _pool.total;
existingPool.rewardOverflow = _pool.rewardOverflow;
existingPool.liquidityExtension = _pool.liquidityExtension;
existingPool.swingTrader = _pool.swingTrader;
existingPool.swingTraderMalt = _pool.swingTraderMalt;
existingPool.arbTokens = _pool.arbTokens;
emit Sync(
existingPool.lpPool,
existingPool.total,
existingPool.rewardOverflow,
existingPool.liquidityExtension,
existingPool.swingTrader,
existingPool.swingTraderMalt,
existingPool.arbTokens
);
emit SyncTotals(
total,
overflow,
liquidityExtension,
swingTraderCollat,
swingTraderMalt,
arb
);
}
/// @notice Privileged method for setting the initial updater manager
/// @param _updaterManager The address of the new manager
function setUpdaterManager(address _updaterManager) external {
require(msg.sender == deployer, "Only deployer");
require(_updaterManager != address(0), "Cannot use addr(0)");
require(updaterManager == address(0), "Initial updater already set");
updaterManager = _updaterManager;
_grantRole(UPDATER_MANAGER_ROLE, _updaterManager);
}
/// @notice Privileged method for proposing a new updater manager
/// @param _updaterManager The address of the newly proposed manager contract
/// @dev Only callable via the existing updater manager contract
function proposeNewUpdaterManager(address _updaterManager)
external
onlyRoleMalt(UPDATER_MANAGER_ROLE, "Must have updater manager role")
{
require(_updaterManager != address(0), "Cannot use addr(0)");
proposedManager = _updaterManager;
emit ProposeUpdaterManager(_updaterManager);
}
/// @notice Method for a proposed updater manager contract to accept the role
/// @dev Only callable via the proposedManager
function acceptUpdaterManagerRole() external {
require(msg.sender == proposedManager, "Must be proposedManager");
_transferRole(proposedManager, updaterManager, UPDATER_MANAGER_ROLE);
proposedManager = address(0);
updaterManager = msg.sender;
emit ChangeUpdaterManager(msg.sender);
}
}