Skip to content

Commit 5028099

Browse files
authored
Alpheratz release 2.69.0 (Synthetixio#1783)
1 parent d5a28a1 commit 5028099

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+7643
-1052
lines changed

contracts/BaseSynthetix.sol

+76-16
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import "./interfaces/ISystemStatus.sol";
1313
import "./interfaces/IExchanger.sol";
1414
import "./interfaces/IIssuer.sol";
1515
import "./interfaces/IRewardsDistribution.sol";
16+
import "./interfaces/ILiquidator.sol";
17+
import "./interfaces/ILiquidatorRewards.sol";
1618
import "./interfaces/IVirtualSynth.sol";
1719

1820
contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
@@ -29,6 +31,8 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
2931
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
3032
bytes32 private constant CONTRACT_ISSUER = "Issuer";
3133
bytes32 private constant CONTRACT_REWARDSDISTRIBUTION = "RewardsDistribution";
34+
bytes32 private constant CONTRACT_LIQUIDATORREWARDS = "LiquidatorRewards";
35+
bytes32 private constant CONTRACT_LIQUIDATOR = "Liquidator";
3236

3337
// ========== CONSTRUCTOR ==========
3438

@@ -48,11 +52,13 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
4852

4953
// Note: use public visibility so that it can be invoked in a subclass
5054
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
51-
addresses = new bytes32[](4);
55+
addresses = new bytes32[](6);
5256
addresses[0] = CONTRACT_SYSTEMSTATUS;
5357
addresses[1] = CONTRACT_EXCHANGER;
5458
addresses[2] = CONTRACT_ISSUER;
5559
addresses[3] = CONTRACT_REWARDSDISTRIBUTION;
60+
addresses[4] = CONTRACT_LIQUIDATORREWARDS;
61+
addresses[5] = CONTRACT_LIQUIDATOR;
5662
}
5763

5864
function systemStatus() internal view returns (ISystemStatus) {
@@ -71,6 +77,14 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
7177
return IRewardsDistribution(requireAndGetAddress(CONTRACT_REWARDSDISTRIBUTION));
7278
}
7379

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+
7488
function debtBalanceOf(address account, bytes32 currencyKey) external view returns (uint) {
7589
return issuer().debtBalanceOf(account, currencyKey);
7690
}
@@ -297,20 +311,66 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
297311
return issuer().burnSynthsToTargetOnBehalf(burnForAddress, messageSender);
298312
}
299313

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);
308318

309319
emitAccountLiquidated(account, totalRedeemed, amountLiquidated, messageSender);
310320

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;
314374
}
315375

316376
function exchangeWithTrackingForInitiator(
@@ -369,7 +429,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
369429
_;
370430
}
371431

372-
function _systemActive() private {
432+
function _systemActive() private view {
373433
systemStatus().requireSystemActive();
374434
}
375435

@@ -378,7 +438,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
378438
_;
379439
}
380440

381-
function _issuanceActive() private {
441+
function _issuanceActive() private view {
382442
systemStatus().requireIssuanceActive();
383443
}
384444

@@ -387,7 +447,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
387447
_;
388448
}
389449

390-
function _exchangeActive(bytes32 src, bytes32 dest) private {
450+
function _exchangeActive(bytes32 src, bytes32 dest) private view {
391451
systemStatus().requireExchangeBetweenSynthsAllowed(src, dest);
392452
}
393453

@@ -396,7 +456,7 @@ contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
396456
_;
397457
}
398458

399-
function _onlyExchanger() private {
459+
function _onlyExchanger() private view {
400460
require(msg.sender == address(exchanger()), "Only Exchanger can invoke this");
401461
}
402462

contracts/Exchanger.sol

+12-18
Original file line numberDiff line numberDiff line change
@@ -433,42 +433,38 @@ contract Exchanger is Owned, MixinSystemSettings, IExchanger {
433433
IVirtualSynth vSynth
434434
)
435435
{
436+
require(sourceAmount > 0, "Zero amount");
437+
436438
// Using struct to resolve stack too deep error
437439
IExchanger.ExchangeEntry memory entry;
438440

439441
entry.roundIdForSrc = exchangeRates().getCurrentRoundId(sourceCurrencyKey);
440442
entry.roundIdForDest = exchangeRates().getCurrentRoundId(destinationCurrencyKey);
441443

444+
uint sourceAmountAfterSettlement = _settleAndCalcSourceAmountRemaining(sourceAmount, from, sourceCurrencyKey);
445+
446+
// If, after settlement the user has no balance left (highly unlikely), then return to prevent
447+
// emitting events of 0 and don't revert so as to ensure the settlement queue is emptied
448+
if (sourceAmountAfterSettlement == 0) {
449+
return (0, 0, IVirtualSynth(0));
450+
}
451+
442452
(entry.destinationAmount, entry.sourceRate, entry.destinationRate) = exchangeRates().effectiveValueAndRatesAtRound(
443453
sourceCurrencyKey,
444-
sourceAmount,
454+
sourceAmountAfterSettlement,
445455
destinationCurrencyKey,
446456
entry.roundIdForSrc,
447457
entry.roundIdForDest
448458
);
449459

450-
_ensureCanExchangeAtRound(
451-
sourceCurrencyKey,
452-
sourceAmount,
453-
destinationCurrencyKey,
454-
entry.roundIdForSrc,
455-
entry.roundIdForDest
456-
);
460+
_ensureCanExchangeAtRound(sourceCurrencyKey, destinationCurrencyKey, entry.roundIdForSrc, entry.roundIdForDest);
457461

458462
// SIP-65: Decentralized Circuit Breaker
459463
// mutative call to suspend system if the rate is invalid
460464
if (_exchangeRatesCircuitBroken(sourceCurrencyKey, destinationCurrencyKey)) {
461465
return (0, 0, IVirtualSynth(0));
462466
}
463467

464-
uint sourceAmountAfterSettlement = _settleAndCalcSourceAmountRemaining(sourceAmount, from, sourceCurrencyKey);
465-
466-
// If, after settlement the user has no balance left (highly unlikely), then return to prevent
467-
// emitting events of 0 and don't revert so as to ensure the settlement queue is emptied
468-
if (sourceAmountAfterSettlement == 0) {
469-
return (0, 0, IVirtualSynth(0));
470-
}
471-
472468
bool tooVolatile;
473469
(entry.exchangeFeeRate, tooVolatile) = _feeRateForExchangeAtRounds(
474470
sourceCurrencyKey,
@@ -642,13 +638,11 @@ contract Exchanger is Owned, MixinSystemSettings, IExchanger {
642638

643639
function _ensureCanExchangeAtRound(
644640
bytes32 sourceCurrencyKey,
645-
uint sourceAmount,
646641
bytes32 destinationCurrencyKey,
647642
uint roundIdForSrc,
648643
uint roundIdForDest
649644
) internal view {
650645
require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");
651-
require(sourceAmount > 0, "Zero amount");
652646

653647
bytes32[] memory synthKeys = new bytes32[](2);
654648
synthKeys[0] = sourceCurrencyKey;

contracts/ExchangerWithFeeRecAlternatives.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ contract ExchangerWithFeeRecAlternatives is MinimalProxyFactory, Exchanger {
186186
} else {
187187
// Otherwise, convert source to sUSD value
188188
(uint amountReceivedInUSD, uint sUsdFee, , , , ) =
189-
_getAmountsForAtomicExchangeMinusFees(sourceAmount, sourceCurrencyKey, sUSD);
189+
_getAmountsForAtomicExchangeMinusFees(sourceAmountAfterSettlement, sourceCurrencyKey, sUSD);
190190
sourceSusdValue = amountReceivedInUSD.add(sUsdFee);
191191
}
192192

0 commit comments

Comments
 (0)