-
Notifications
You must be signed in to change notification settings - Fork 8
/
BasicActions.sol
422 lines (358 loc) · 16.2 KB
/
BasicActions.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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {ODSafeManager} from '@contracts/proxies/ODSafeManager.sol';
import {ISAFEEngine} from '@interfaces/ISAFEEngine.sol';
import {SafeCast} from '@openzeppelin/utils/math/SafeCast.sol';
import {IBasicActions} from '@interfaces/proxies/actions/IBasicActions.sol';
import {ITaxCollector} from '@interfaces/ITaxCollector.sol';
import {Math, RAY} from '@libraries/Math.sol';
import {CommonActions} from '@contracts/proxies/actions/CommonActions.sol';
/**
* @title BasicActions
* @notice This contract defines the actions that can be executed to manage a SAFE
*/
contract BasicActions is CommonActions, IBasicActions {
using Math for uint256;
using SafeCast for int256;
// --- Internal functions ---
/**
* @notice Gets delta debt generated for delta wad (always positive)
* @dev Total SAFE debt minus available safeHandler COIN balance
*/
function _getGeneratedDeltaDebt(
address _safeEngine,
bytes32 _cType,
address _safeHandler,
uint256 _deltaWad
) internal view returns (int256 _deltaDebt) {
uint256 _rate = ISAFEEngine(_safeEngine).cData(_cType).accumulatedRate;
uint256 _coinAmount = ISAFEEngine(_safeEngine).coinBalance(_safeHandler);
// If there was already enough COIN in the safeEngine balance, just exits it without adding more debt
if (_coinAmount < _deltaWad * RAY) {
// Calculates the needed deltaDebt so together with the existing coins in the safeEngine is enough to exit wad amount of COIN tokens
_deltaDebt = ((_deltaWad * RAY - _coinAmount) / _rate).toInt();
// This is neeeded due lack of precision. It might need to sum an extra deltaDebt wei (for the given COIN wad amount)
_deltaDebt = uint256(_deltaDebt) * _rate < _deltaWad * RAY ? _deltaDebt + 1 : _deltaDebt;
}
}
/**
* @notice Gets repaid delta debt generated
* @dev The rate adjusted debt of the SAFE
*/
function _getRepaidDeltaDebt(
address _safeEngine,
bytes32 _cType,
address _safeHandler
) internal view returns (int256 _deltaDebt) {
uint256 _rate = ISAFEEngine(_safeEngine).cData(_cType).accumulatedRate;
uint256 _generatedDebt = ISAFEEngine(_safeEngine).safes(_cType, _safeHandler).generatedDebt;
uint256 _coinAmount = ISAFEEngine(_safeEngine).coinBalance(_safeHandler);
// Uses the whole coin balance in the safeEngine to reduce the debt
_deltaDebt = (_coinAmount / _rate).toInt();
// Checks the calculated deltaDebt is not higher than safe.generatedDebt (total debt), otherwise uses its value
_deltaDebt = uint256(_deltaDebt) <= _generatedDebt ? -_deltaDebt : -_generatedDebt.toInt();
}
/**
* @notice Gets repaid debt
* @dev The rate adjusted SAFE's debt minus COIN balance available in usr's address
*/
function _getRepaidDebt(
address _safeEngine,
address _usr,
bytes32 _cType,
address _safeHandler
) internal view returns (uint256 _deltaWad) {
uint256 _rate = ISAFEEngine(_safeEngine).cData(_cType).accumulatedRate;
uint256 _generatedDebt = ISAFEEngine(_safeEngine).safes(_cType, _safeHandler).generatedDebt;
uint256 _coinAmount = ISAFEEngine(_safeEngine).coinBalance(_usr);
// Uses the whole coin balance in the safeEngine to reduce the debt
uint256 _rad = _generatedDebt * _rate - _coinAmount;
// Calculates the equivalent COIN amount
_deltaWad = _rad / RAY;
// If the rad precision has some dust, it will need to request for 1 extra wad wei
_deltaWad = _deltaWad * RAY < _rad ? _deltaWad + 1 : _deltaWad;
}
/**
* @notice Generates debt
* @dev Modifies the SAFE collateralization ratio, increasing the debt and sends the COIN amount to the user's address
*/
function _generateDebt(address _manager, address _coinJoin, uint256 _safeId, uint256 _deltaWad) internal {
address _safeEngine = ODSafeManager(_manager).safeEngine();
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
int256 deltaDebt = _getGeneratedDeltaDebt(_safeEngine, _safeInfo.collateralType, _safeInfo.safeHandler, _deltaWad);
// Generates debt in the SAFE
_modifySAFECollateralization(_manager, _safeId, 0, deltaDebt, false);
// Moves the COIN amount to user's address
// deltaDebt should always be positive, but we use SafeCast as an extra guard
_collectAndExitCoins(_manager, _coinJoin, _safeId, deltaDebt.toUint256());
}
/**
* @notice Repays debt
* @dev Joins COIN amount into the safeEngine and modifies the SAFE collateralization reducing the debt
*/
function _repayDebt(address _manager, address _coinJoin, uint256 _safeId, uint256 _deltaWad) internal {
address _safeEngine = ODSafeManager(_manager).safeEngine();
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
_taxSingle(_manager, _safeId);
// Joins COIN amount into the safeEngine
_joinSystemCoins(_coinJoin, _safeInfo.safeHandler, _deltaWad);
// Paybacks debt to the SAFE
_modifySAFECollateralization(
_manager, _safeId, 0, _getRepaidDeltaDebt(_safeEngine, _safeInfo.collateralType, _safeInfo.safeHandler), false
);
}
/// @notice Routes the openSAFE call to the ODSafeManager contract
function _openSAFE(address _manager, bytes32 _cType, address _usr) internal returns (uint256 _safeId) {
_safeId = ODSafeManager(_manager).openSAFE(_cType, _usr);
}
/// @notice Routes the transferCollateral call to the ODSafeManager contract
function _transferCollateral(address _manager, uint256 _safeId, address _dst, uint256 _deltaWad) internal {
if (_deltaWad == 0) return;
ODSafeManager(_manager).transferCollateral(_safeId, _dst, _deltaWad);
}
/// @notice Routes the transferInternalCoins call to the ODSafeManager contract
function _transferInternalCoins(address _manager, uint256 _safeId, address _dst, uint256 _rad) internal {
ODSafeManager(_manager).transferInternalCoins(_safeId, _dst, _rad);
}
/// @notice Routes the modifySAFECollateralization call to the ODSafeManager contract
function _modifySAFECollateralization(
address _manager,
uint256 _safeId,
int256 _deltaCollateral,
int256 _deltaDebt,
bool _nonSafeHandlerAddress
) internal {
ODSafeManager(_manager).modifySAFECollateralization(_safeId, _deltaCollateral, _deltaDebt, _nonSafeHandlerAddress);
}
/**
* @notice Joins collateral and exits an amount of COIN
*/
function _lockTokenCollateralAndGenerateDebt(
address _manager,
address _collateralJoin,
address _coinJoin,
uint256 _safeId,
uint256 _collateralAmount,
uint256 _deltaWad
) internal {
address _safeEngine = ODSafeManager(_manager).safeEngine();
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
// Takes token amount from user's wallet and joins into the safeEngine
_joinCollateral(_collateralJoin, _safeInfo.safeHandler, _collateralAmount);
int256 deltaDebt = _getGeneratedDeltaDebt(_safeEngine, _safeInfo.collateralType, _safeInfo.safeHandler, _deltaWad);
// Locks token amount into the SAFE and generates debt
_modifySAFECollateralization(_manager, _safeId, _collateralAmount.toInt(), deltaDebt, false);
// Exits and transfers COIN amount to the user's address
// deltaDebt should always be positive, but we use SafeCast as an extra guard
_collectAndExitCoins(_manager, _coinJoin, _safeId, deltaDebt.toUint256());
}
/**
* @notice Transfers an amount of COIN to the proxy address and exits to the user's address
*/
function _collectAndExitCoins(address _manager, address _coinJoin, uint256 _safeId, uint256 _deltaWad) internal {
// Moves the COIN amount to proxy's address
_transferInternalCoins(_manager, _safeId, address(this), _deltaWad * RAY);
// Exits the COIN amount to the user's address
_exitSystemCoins(_coinJoin, _deltaWad * RAY);
}
/**
* @notice Transfers an amount of collateral to the proxy address and exits collateral tokens to the user
*/
function _collectAndExitCollateral(
address _manager,
address _collateralJoin,
uint256 _safeId,
uint256 _deltaWad
) internal {
// Moves the amount from the SAFE handler to proxy's address
_transferCollateral(_manager, _safeId, address(this), _deltaWad);
// _transferCollateral(_manager, _safeId, _safeData.safeHandler, _deltaWad);
// Exits a rounded down amount of collateral
_exitCollateral(_collateralJoin, _deltaWad);
}
// --- Methods ---
/// @inheritdoc IBasicActions
function openSAFE(address _manager, bytes32 _cType, address _usr) external delegateCall returns (uint256 _safeId) {
return _openSAFE(_manager, _cType, _usr);
}
/// @inheritdoc IBasicActions
function generateDebt(address _manager, address _coinJoin, uint256 _safeId, uint256 _deltaWad) external delegateCall {
_generateDebt(_manager, _coinJoin, _safeId, _deltaWad);
}
/// @inheritdoc IBasicActions
function allowSAFE(address _manager, uint256 _safeId, address _usr, bool _ok) external delegateCall {
ODSafeManager(_manager).allowSAFE(_safeId, _usr, _ok);
}
/// @inheritdoc IBasicActions
function modifySAFECollateralization(
address _manager,
uint256 _safeId,
int256 _deltaCollateral,
int256 _deltaDebt
) external delegateCall {
_modifySAFECollateralization(_manager, _safeId, _deltaCollateral, _deltaDebt, false);
}
/// @inheritdoc IBasicActions
function transferCollateral(address _manager, uint256 _safeId, address _dst, uint256 _deltaWad) external delegateCall {
_transferCollateral(_manager, _safeId, _dst, _deltaWad);
}
/// @inheritdoc IBasicActions
function transferInternalCoins(address _manager, uint256 _safeId, address _dst, uint256 _rad) external delegateCall {
_transferInternalCoins(_manager, _safeId, _dst, _rad);
}
/// @inheritdoc IBasicActions
function quitSystem(address _manager, uint256 _safeId) external delegateCall {
ODSafeManager(_manager).quitSystem(_safeId);
}
/// @inheritdoc IBasicActions
function moveSAFE(address _manager, uint256 _safeSrc, uint256 _safeDst) external delegateCall {
ODSafeManager(_manager).moveSAFE(_safeSrc, _safeDst);
}
/// @inheritdoc IBasicActions
function addSAFE(address _manager, uint256 _safe) external delegateCall {
ODSafeManager(_manager).addSAFE(_safe);
}
/// @inheritdoc IBasicActions
function removeSAFE(address _manager, uint256 _safe) external delegateCall {
ODSafeManager(_manager).removeSAFE(_safe);
}
/// @inheritdoc IBasicActions
function protectSAFE(address _manager, uint256 _safe, address _saviour) external delegateCall {
ODSafeManager(_manager).protectSAFE(_safe, _saviour);
}
/// @inheritdoc IBasicActions
function repayDebt(address _manager, address _coinJoin, uint256 _safeId, uint256 _deltaWad) external delegateCall {
_repayDebt(_manager, _coinJoin, _safeId, _deltaWad);
}
/// @inheritdoc IBasicActions
function lockTokenCollateral(
address _manager,
address _collateralJoin,
uint256 _safeId,
uint256 _deltaWad
) external delegateCall {
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
// Takes token amount from user's wallet and joins into the safeEngine
_joinCollateral(_collateralJoin, _safeInfo.safeHandler, _deltaWad);
// Locks token amount in the safe
_modifySAFECollateralization(_manager, _safeId, _deltaWad.toInt(), 0, false);
}
/// @inheritdoc IBasicActions
function freeTokenCollateral(
address _manager,
address _collateralJoin,
uint256 _safeId,
uint256 _deltaWad
) external delegateCall {
_taxSingle(_manager, _safeId);
// Unlocks token amount from the SAFE
_modifySAFECollateralization(_manager, _safeId, -_deltaWad.toInt(), 0, false);
// Transfers token amount to the user's address
_collectAndExitCollateral(_manager, _collateralJoin, _safeId, _deltaWad);
}
/// @inheritdoc IBasicActions
function repayAllDebt(address _manager, address _coinJoin, uint256 _safeId) external delegateCall {
address _safeEngine = ODSafeManager(_manager).safeEngine();
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
_taxSingle(_manager, _safeId);
ISAFEEngine.SAFE memory _safeData = ISAFEEngine(_safeEngine).safes(_safeInfo.collateralType, _safeInfo.safeHandler);
// Joins COIN amount into the safeEngine
_joinSystemCoins(
_coinJoin,
address(this),
_getRepaidDebt(_safeEngine, address(this), _safeInfo.collateralType, _safeInfo.safeHandler)
);
// Paybacks debt to the SAFE (allowed because reducing debt of the SAFE)
_modifySAFECollateralization(_manager, _safeId, 0, -_safeData.generatedDebt.toInt(), true);
}
/// @inheritdoc IBasicActions
function lockTokenCollateralAndGenerateDebt(
address _manager,
address _collateralJoin,
address _coinJoin,
uint256 _safe,
uint256 _collateralAmount,
uint256 _deltaWad
) external delegateCall {
_lockTokenCollateralAndGenerateDebt(_manager, _collateralJoin, _coinJoin, _safe, _collateralAmount, _deltaWad);
}
/// @inheritdoc IBasicActions
function openLockTokenCollateralAndGenerateDebt(
address _manager,
address _collateralJoin,
address _coinJoin,
bytes32 _cType,
uint256 _collateralAmount,
uint256 _deltaWad
) external delegateCall returns (uint256 _safe) {
_safe = _openSAFE(_manager, _cType, address(this));
_lockTokenCollateralAndGenerateDebt(_manager, _collateralJoin, _coinJoin, _safe, _collateralAmount, _deltaWad);
}
/// @inheritdoc IBasicActions
function repayDebtAndFreeTokenCollateral(
address _manager,
address _collateralJoin,
address _coinJoin,
uint256 _safeId,
uint256 _collateralWad,
uint256 _debtWad
) external delegateCall {
address _safeEngine = ODSafeManager(_manager).safeEngine();
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
_taxSingle(_manager, _safeId);
// Joins COIN amount into the safeEngine
_joinSystemCoins(_coinJoin, _safeInfo.safeHandler, _debtWad);
// Paybacks debt to the SAFE and unlocks token amount from it
_modifySAFECollateralization(
_manager,
_safeId,
-_collateralWad.toInt(),
_getRepaidDeltaDebt(_safeEngine, _safeInfo.collateralType, _safeInfo.safeHandler),
false
);
// Transfers token amount to the user's address
_collectAndExitCollateral(_manager, _collateralJoin, _safeId, _collateralWad);
}
/// @inheritdoc IBasicActions
function repayAllDebtAndFreeTokenCollateral(
address _manager,
address _collateralJoin,
address _coinJoin,
uint256 _safeId,
uint256 _collateralWad
) external delegateCall {
address _safeEngine = ODSafeManager(_manager).safeEngine();
//collecting tax before getting safe data to avoid overflow in collection
_taxSingle(_manager, _safeId);
ODSafeManager.SAFEData memory _safeInfo = ODSafeManager(_manager).safeData(_safeId);
ISAFEEngine.SAFE memory _safeData = ISAFEEngine(_safeEngine).safes(_safeInfo.collateralType, _safeInfo.safeHandler);
_taxSingle(_manager, _safeId);
// Joins COIN amount into the safeEngine
_joinSystemCoins(
_coinJoin,
_safeInfo.safeHandler,
_getRepaidDebt(_safeEngine, _safeInfo.safeHandler, _safeInfo.collateralType, _safeInfo.safeHandler)
);
// Paybacks debt to the SAFE and unlocks token amount from it
_modifySAFECollateralization(_manager, _safeId, -_collateralWad.toInt(), -_safeData.generatedDebt.toInt(), false);
// Transfers token amount to the user's address
_collectAndExitCollateral(_manager, _collateralJoin, _safeId, _collateralWad);
}
/// @inheritdoc IBasicActions
function collectTokenCollateral(
address _manager,
address _collateralJoin,
uint256 _safeId,
uint256 _deltaWad
) external delegateCall {
// Transfers token amount to the user's address
_collectAndExitCollateral(_manager, _collateralJoin, _safeId, _deltaWad);
}
/**
* @dev calls Tax single on taxCollector. do this before making any calls to safeManager modifySafeCollateralization
*/
function _taxSingle(address _manager, uint256 _safeId) internal {
ODSafeManager.SAFEData memory _safeData = ODSafeManager(_manager).safeData(_safeId);
ITaxCollector(ODSafeManager(_manager).taxCollector()).taxSingle(_safeData.collateralType);
}
}