-
Notifications
You must be signed in to change notification settings - Fork 233
/
SuperfluidToken.sol
397 lines (353 loc) · 13.2 KB
/
SuperfluidToken.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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// SPDX-License-Identifier: AGPLv3
pragma solidity 0.8.23;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {
ISuperfluid,
ISuperAgreement,
ISuperfluidGovernance,
ISuperfluidToken
} from "../interfaces/superfluid/ISuperfluid.sol";
import { FixedSizeData } from "../libs/FixedSizeData.sol";
/**
* @title Superfluid's token implementation
*
* @author Superfluid
*/
abstract contract SuperfluidToken is ISuperfluidToken
{
bytes32 private constant _REWARD_ADDRESS_CONFIG_KEY =
keccak256("org.superfluid-finance.superfluid.rewardAddress");
using SafeCast for uint256;
using SafeCast for int256;
/// @dev Superfluid contract
ISuperfluid immutable internal _host;
/// @dev Active agreement bitmap
mapping(address => uint256) internal _inactiveAgreementBitmap;
/// @dev Shared Settled balance for the account
mapping(address => int256) internal _sharedSettledBalances;
/// @dev Total supply
uint256 internal _totalSupply;
// NOTE: for future compatibility, these are reserved solidity slots
// The sub-class of SuperfluidToken solidity slot will start after _reserve13
uint256 internal _reserve4;
uint256 private _reserve5;
uint256 private _reserve6;
uint256 private _reserve7;
uint256 private _reserve8;
uint256 private _reserve9;
uint256 private _reserve10;
uint256 private _reserve11;
uint256 private _reserve12;
uint256 internal _reserve13;
constructor(
ISuperfluid host
) {
_host = host;
}
/// @dev ISuperfluidToken.getHost implementation
function getHost()
external view
override(ISuperfluidToken)
returns(address host)
{
return address(_host);
}
/**************************************************************************
* Real-time balance functions
*************************************************************************/
/// @dev ISuperfluidToken.realtimeBalanceOf implementation
function realtimeBalanceOf(
address account,
uint256 timestamp
)
public view virtual override
returns (
int256 availableBalance,
uint256 deposit,
uint256 owedDeposit)
{
availableBalance = _sharedSettledBalances[account];
ISuperAgreement[] memory activeAgreements = getAccountActiveAgreements(account);
for (uint256 i = 0; i < activeAgreements.length; ++i) {
(
int256 agreementDynamicBalance,
uint256 agreementDeposit,
uint256 agreementOwedDeposit) = activeAgreements[i]
.realtimeBalanceOf(
this,
account,
timestamp
);
deposit = deposit + agreementDeposit;
owedDeposit = owedDeposit + agreementOwedDeposit;
// 1. Available Balance = Dynamic Balance - Max(0, Deposit - OwedDeposit)
// 2. Deposit should not be shared between agreements
availableBalance = availableBalance
+ agreementDynamicBalance
- (
agreementDeposit > agreementOwedDeposit ?
(agreementDeposit - agreementOwedDeposit) : 0
).toInt256();
}
}
/// @dev ISuperfluidToken.realtimeBalanceOfNow implementation
function realtimeBalanceOfNow(
address account
)
public view virtual override
returns (
int256 availableBalance,
uint256 deposit,
uint256 owedDeposit,
uint256 timestamp)
{
timestamp = _host.getNow();
(
availableBalance,
deposit,
owedDeposit
) = realtimeBalanceOf(account, timestamp);
}
function isAccountCritical(
address account,
uint256 timestamp
)
public view virtual override
returns(bool isCritical)
{
(int256 availableBalance,,) = realtimeBalanceOf(account, timestamp);
return availableBalance < 0;
}
function isAccountCriticalNow(
address account
)
external view virtual override
returns(bool isCritical)
{
return isAccountCritical(account, _host.getNow());
}
function isAccountSolvent(
address account,
uint256 timestamp
)
public view virtual override
returns(bool isSolvent)
{
(int256 availableBalance, uint256 deposit, uint256 owedDeposit) =
realtimeBalanceOf(account, timestamp);
// Available Balance = Realtime Balance - Max(0, Deposit - OwedDeposit)
int realtimeBalance = availableBalance
+ (deposit > owedDeposit ? (deposit - owedDeposit) : 0).toInt256();
return realtimeBalance >= 0;
}
function isAccountSolventNow(
address account
)
external view virtual override
returns(bool isSolvent)
{
return isAccountSolvent(account, _host.getNow());
}
/// @dev ISuperfluidToken.getAccountActiveAgreements implementation
function getAccountActiveAgreements(address account)
public view virtual override
returns(ISuperAgreement[] memory)
{
return _host.mapAgreementClasses(~_inactiveAgreementBitmap[account]);
}
/**************************************************************************
* Token implementation helpers
*************************************************************************/
function _mint(
address account,
uint256 amount
)
internal
{
_sharedSettledBalances[account] = _sharedSettledBalances[account] + amount.toInt256();
_totalSupply = _totalSupply + amount;
}
function _burn(
address account,
uint256 amount
)
internal
{
(int256 availableBalance,,) = realtimeBalanceOf(account, _host.getNow());
if (availableBalance < amount.toInt256()) {
revert SF_TOKEN_BURN_INSUFFICIENT_BALANCE();
}
_sharedSettledBalances[account] = _sharedSettledBalances[account] - amount.toInt256();
_totalSupply = _totalSupply - amount;
}
function _move(
address from,
address to,
int256 amount
)
internal
{
(int256 availableBalance,,) = realtimeBalanceOf(from, _host.getNow());
if (availableBalance < amount) {
revert SF_TOKEN_MOVE_INSUFFICIENT_BALANCE();
}
_sharedSettledBalances[from] = _sharedSettledBalances[from] - amount;
_sharedSettledBalances[to] = _sharedSettledBalances[to] + amount;
}
function _getRewardAccount() internal view returns (address rewardAccount) {
ISuperfluidGovernance gov = _host.getGovernance();
rewardAccount = gov.getConfigAsAddress(_host, this, _REWARD_ADDRESS_CONFIG_KEY);
}
/**************************************************************************
* Super Agreement hosting functions
*************************************************************************/
/// @dev ISuperfluidToken.createAgreement implementation
function createAgreement(
bytes32 id,
bytes32[] calldata data
)
external virtual override
{
address agreementClass = msg.sender;
bytes32 slot = keccak256(abi.encode("AgreementData", agreementClass, id));
if (FixedSizeData.hasData(slot, data.length)) {
revert SF_TOKEN_AGREEMENT_ALREADY_EXISTS();
}
FixedSizeData.storeData(slot, data);
emit AgreementCreated(agreementClass, id, data);
}
/// @dev ISuperfluidToken.getAgreementData implementation
function getAgreementData(
address agreementClass,
bytes32 id,
uint dataLength
)
external view virtual override
returns(bytes32[] memory data)
{
bytes32 slot = keccak256(abi.encode("AgreementData", agreementClass, id));
data = FixedSizeData.loadData(slot, dataLength);
}
/// @dev ISuperfluidToken.updateAgreementData implementation
function updateAgreementData(
bytes32 id,
bytes32[] calldata data
)
external virtual override
{
address agreementClass = msg.sender;
bytes32 slot = keccak256(abi.encode("AgreementData", agreementClass, id));
FixedSizeData.storeData(slot, data);
emit AgreementUpdated(msg.sender, id, data);
}
/// @dev ISuperfluidToken.terminateAgreement implementation
function terminateAgreement(
bytes32 id,
uint dataLength
)
external virtual override
{
address agreementClass = msg.sender;
bytes32 slot = keccak256(abi.encode("AgreementData", agreementClass, id));
if (!FixedSizeData.hasData(slot,dataLength)) {
revert SF_TOKEN_AGREEMENT_DOES_NOT_EXIST();
}
FixedSizeData.eraseData(slot, dataLength);
emit AgreementTerminated(msg.sender, id);
}
/// @dev ISuperfluidToken.updateAgreementState implementation
function updateAgreementStateSlot(
address account,
uint256 slotId,
bytes32[] calldata slotData
)
external virtual override
{
bytes32 slot = keccak256(abi.encode("AgreementState", msg.sender, account, slotId));
FixedSizeData.storeData(slot, slotData);
emit AgreementStateUpdated(msg.sender, account, slotId);
}
/// @dev ISuperfluidToken.getAgreementState implementation
function getAgreementStateSlot(
address agreementClass,
address account,
uint256 slotId,
uint dataLength
)
external view virtual override
returns (bytes32[] memory slotData) {
bytes32 slot = keccak256(abi.encode("AgreementState", agreementClass, account, slotId));
slotData = FixedSizeData.loadData(slot, dataLength);
}
/// @dev ISuperfluidToken.settleBalance implementation
function settleBalance(
address account,
int256 delta
)
external virtual override
onlyAgreement
{
_sharedSettledBalances[account] = _sharedSettledBalances[account] + delta;
}
/// @dev ISuperfluidToken.makeLiquidationPayoutsV2 implementation
function makeLiquidationPayoutsV2(
bytes32 id,
bytes memory liquidationTypeData,
address liquidatorAccount, // the address executing the liquidation
bool useDefaultRewardAccount, // Whether or not the default reward account receives the rewardAmount
address targetAccount, // Account to be liquidated
uint256 rewardAmount, // The amount the rewarded account will receive
int256 targetAccountBalanceDelta // The delta amount the target account balance should change by
) external virtual override onlyAgreement {
address rewardAccount = _getRewardAccount();
// we set the rewardAccount to the user who executed the liquidation if
// no rewardAccount is set (aka. ANARCHY MODE - should not occur in reality, for testing purposes)
if (rewardAccount == address(0)) {
rewardAccount = liquidatorAccount;
}
address rewardAmountReceiver = useDefaultRewardAccount ? rewardAccount : liquidatorAccount;
if (targetAccountBalanceDelta <= 0) {
// LIKELY BRANCH: target account pays penalty to rewarded account
assert(rewardAmount.toInt256() == -targetAccountBalanceDelta);
_sharedSettledBalances[rewardAmountReceiver] += rewardAmount.toInt256();
_sharedSettledBalances[targetAccount] += targetAccountBalanceDelta;
emit IERC20.Transfer(targetAccount, rewardAmountReceiver, rewardAmount);
} else {
// LESS LIKELY BRANCH: target account is bailed out
// NOTE: useDefaultRewardAccount being true is undefined behavior
// because the default reward account isn't receiving the rewardAmount by default
assert(!useDefaultRewardAccount);
_sharedSettledBalances[rewardAccount] -= (rewardAmount.toInt256() + targetAccountBalanceDelta);
_sharedSettledBalances[liquidatorAccount] += rewardAmount.toInt256();
_sharedSettledBalances[targetAccount] += targetAccountBalanceDelta;
emit IERC20.Transfer(rewardAccount, liquidatorAccount, rewardAmount);
emit IERC20.Transfer(rewardAccount, targetAccount, uint256(targetAccountBalanceDelta));
}
emit AgreementLiquidatedV2(
msg.sender,
id,
liquidatorAccount,
targetAccount,
rewardAmountReceiver,
rewardAmount,
targetAccountBalanceDelta,
liquidationTypeData
);
}
/**************************************************************************
* Modifiers
*************************************************************************/
modifier onlyAgreement() {
if (!_host.isAgreementClassListed(ISuperAgreement(msg.sender))) {
revert SF_TOKEN_ONLY_LISTED_AGREEMENT();
}
_;
}
modifier onlyHost() {
if (address(_host) != msg.sender) {
revert SF_TOKEN_ONLY_HOST();
}
_;
}
}