@@ -13,6 +13,8 @@ import "./interfaces/ISystemStatus.sol";
13
13
import "./interfaces/IExchanger.sol " ;
14
14
import "./interfaces/IIssuer.sol " ;
15
15
import "./interfaces/IRewardsDistribution.sol " ;
16
+ import "./interfaces/ILiquidator.sol " ;
17
+ import "./interfaces/ILiquidatorRewards.sol " ;
16
18
import "./interfaces/IVirtualSynth.sol " ;
17
19
18
20
contract BaseSynthetix is IERC20 , ExternStateToken , MixinResolver , ISynthetix {
@@ -29,6 +31,8 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
29
31
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger " ;
30
32
bytes32 private constant CONTRACT_ISSUER = "Issuer " ;
31
33
bytes32 private constant CONTRACT_REWARDSDISTRIBUTION = "RewardsDistribution " ;
34
+ bytes32 private constant CONTRACT_LIQUIDATORREWARDS = "LiquidatorRewards " ;
35
+ bytes32 private constant CONTRACT_LIQUIDATOR = "Liquidator " ;
32
36
33
37
// ========== CONSTRUCTOR ==========
34
38
@@ -48,11 +52,13 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
48
52
49
53
// Note: use public visibility so that it can be invoked in a subclass
50
54
function resolverAddressesRequired () public view returns (bytes32 [] memory addresses ) {
51
- addresses = new bytes32 [](4 );
55
+ addresses = new bytes32 [](6 );
52
56
addresses[0 ] = CONTRACT_SYSTEMSTATUS;
53
57
addresses[1 ] = CONTRACT_EXCHANGER;
54
58
addresses[2 ] = CONTRACT_ISSUER;
55
59
addresses[3 ] = CONTRACT_REWARDSDISTRIBUTION;
60
+ addresses[4 ] = CONTRACT_LIQUIDATORREWARDS;
61
+ addresses[5 ] = CONTRACT_LIQUIDATOR;
56
62
}
57
63
58
64
function systemStatus () internal view returns (ISystemStatus) {
@@ -71,6 +77,14 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
71
77
return IRewardsDistribution (requireAndGetAddress (CONTRACT_REWARDSDISTRIBUTION));
72
78
}
73
79
80
+ function liquidatorRewards () internal view returns (ILiquidatorRewards) {
81
+ return ILiquidatorRewards (requireAndGetAddress (CONTRACT_LIQUIDATORREWARDS));
82
+ }
83
+
84
+ function liquidator () internal view returns (ILiquidator) {
85
+ return ILiquidator (requireAndGetAddress (CONTRACT_LIQUIDATOR));
86
+ }
87
+
74
88
function debtBalanceOf (address account , bytes32 currencyKey ) external view returns (uint ) {
75
89
return issuer ().debtBalanceOf (account, currencyKey);
76
90
}
@@ -297,20 +311,66 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
297
311
return issuer ().burnSynthsToTargetOnBehalf (burnForAddress, messageSender);
298
312
}
299
313
300
- function liquidateDelinquentAccount (address account , uint susdAmount )
301
- external
302
- systemActive
303
- optionalProxy
304
- returns (bool )
305
- {
306
- (uint totalRedeemed , uint amountLiquidated ) =
307
- issuer ().liquidateDelinquentAccount (account, susdAmount, messageSender);
314
+ /// @notice Force liquidate a delinquent account and distribute the redeemed SNX rewards amongst the appropriate recipients.
315
+ /// @dev The SNX transfers will revert if the amount to send is more than balanceOf account (i.e. due to escrowed balance).
316
+ function liquidateDelinquentAccount (address account ) external systemActive optionalProxy returns (bool ) {
317
+ (uint totalRedeemed , uint amountLiquidated ) = issuer ().liquidateAccount (account, false );
308
318
309
319
emitAccountLiquidated (account, totalRedeemed, amountLiquidated, messageSender);
310
320
311
- // Transfer SNX redeemed to messageSender
312
- // Reverts if amount to redeem is more than balanceOf account, ie due to escrowed balance
313
- return _transferByProxy (account, messageSender, totalRedeemed);
321
+ if (totalRedeemed > 0 ) {
322
+ uint stakerRewards; // The amount of rewards to be sent to the LiquidatorRewards contract.
323
+ uint flagReward = liquidator ().flagReward ();
324
+ uint liquidateReward = liquidator ().liquidateReward ();
325
+ // Check if the total amount of redeemed SNX is enough to payout the liquidation rewards.
326
+ if (totalRedeemed > flagReward.add (liquidateReward)) {
327
+ // Transfer the flagReward to the account who flagged this account for liquidation.
328
+ address flagger = liquidator ().getLiquidationCallerForAccount (account);
329
+ bool flagRewardTransferSucceeded = _transferByProxy (account, flagger, flagReward);
330
+ require (flagRewardTransferSucceeded, "Flag reward transfer did not succeed " );
331
+
332
+ // Transfer the liquidateReward to liquidator (the account who invoked this liquidation).
333
+ bool liquidateRewardTransferSucceeded = _transferByProxy (account, messageSender, liquidateReward);
334
+ require (liquidateRewardTransferSucceeded, "Liquidate reward transfer did not succeed " );
335
+
336
+ // The remaining SNX to be sent to the LiquidatorRewards contract.
337
+ stakerRewards = totalRedeemed.sub (flagReward.add (liquidateReward));
338
+ } else {
339
+ /* If the total amount of redeemed SNX is greater than zero
340
+ but is less than the sum of the flag & liquidate rewards,
341
+ then just send all of the SNX to the LiquidatorRewards contract. */
342
+ stakerRewards = totalRedeemed;
343
+ }
344
+
345
+ bool liquidatorRewardTransferSucceeded = _transferByProxy (account, address (liquidatorRewards ()), stakerRewards);
346
+ require (liquidatorRewardTransferSucceeded, "Transfer to LiquidatorRewards failed " );
347
+
348
+ // Inform the LiquidatorRewards contract about the incoming SNX rewards.
349
+ liquidatorRewards ().notifyRewardAmount (stakerRewards);
350
+
351
+ return true ;
352
+ } else {
353
+ // In this unlikely case, the total redeemed SNX is not greater than zero so don't perform any transfers.
354
+ return false ;
355
+ }
356
+ }
357
+
358
+ /// @notice Allows an account to self-liquidate anytime its c-ratio is below the target issuance ratio.
359
+ function liquidateSelf () external systemActive optionalProxy returns (bool ) {
360
+ // Self liquidate the account (`isSelfLiquidation` flag must be set to `true`).
361
+ (uint totalRedeemed , uint amountLiquidated ) = issuer ().liquidateAccount (messageSender, true );
362
+
363
+ emitAccountLiquidated (messageSender, totalRedeemed, amountLiquidated, messageSender);
364
+
365
+ // Transfer the redeemed SNX to the LiquidatorRewards contract.
366
+ // Reverts if amount to redeem is more than balanceOf account (i.e. due to escrowed balance).
367
+ bool success = _transferByProxy (messageSender, address (liquidatorRewards ()), totalRedeemed);
368
+ require (success, "Transfer to LiquidatorRewards failed " );
369
+
370
+ // Inform the LiquidatorRewards contract about the incoming SNX rewards.
371
+ liquidatorRewards ().notifyRewardAmount (totalRedeemed);
372
+
373
+ return success;
314
374
}
315
375
316
376
function exchangeWithTrackingForInitiator (
@@ -369,7 +429,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
369
429
_;
370
430
}
371
431
372
- function _systemActive () private {
432
+ function _systemActive () private view {
373
433
systemStatus ().requireSystemActive ();
374
434
}
375
435
@@ -378,7 +438,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
378
438
_;
379
439
}
380
440
381
- function _issuanceActive () private {
441
+ function _issuanceActive () private view {
382
442
systemStatus ().requireIssuanceActive ();
383
443
}
384
444
@@ -387,7 +447,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
387
447
_;
388
448
}
389
449
390
- function _exchangeActive (bytes32 src , bytes32 dest ) private {
450
+ function _exchangeActive (bytes32 src , bytes32 dest ) private view {
391
451
systemStatus ().requireExchangeBetweenSynthsAllowed (src, dest);
392
452
}
393
453
@@ -396,7 +456,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
396
456
_;
397
457
}
398
458
399
- function _onlyExchanger () private {
459
+ function _onlyExchanger () private view {
400
460
require (msg .sender == address (exchanger ()), "Only Exchanger can invoke this " );
401
461
}
402
462
0 commit comments