pragma solidity ^0.5.16; // Inheritance import "./BaseSynthetix.sol"; // Internal references import "./interfaces/IRewardEscrow.sol"; import "./interfaces/IRewardEscrowV2.sol"; import "./interfaces/ISupplySchedule.sol"; // https://docs.synthetix.io/contracts/source/contracts/synthetix contract Synthetix is BaseSynthetix { // ========== ADDRESS RESOLVER CONFIGURATION ========== bytes32 private constant CONTRACT_REWARD_ESCROW = "RewardEscrow"; bytes32 private constant CONTRACT_REWARDESCROW_V2 = "RewardEscrowV2"; bytes32 private constant CONTRACT_SUPPLYSCHEDULE = "SupplySchedule"; // ========== CONSTRUCTOR ========== constructor( address payable _proxy, TokenState _tokenState, address _owner, uint _totalSupply, address _resolver ) public BaseSynthetix(_proxy, _tokenState, _owner, _totalSupply, _resolver) {} function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = BaseSynthetix.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](3); newAddresses[0] = CONTRACT_REWARD_ESCROW; newAddresses[1] = CONTRACT_REWARDESCROW_V2; newAddresses[2] = CONTRACT_SUPPLYSCHEDULE; return combineArrays(existingAddresses, newAddresses); } // ========== VIEWS ========== function rewardEscrow() internal view returns (IRewardEscrow) { return IRewardEscrow(requireAndGetAddress(CONTRACT_REWARD_ESCROW)); } function rewardEscrowV2() internal view returns (IRewardEscrowV2) { return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW_V2)); } function supplySchedule() internal view returns (ISupplySchedule) { return ISupplySchedule(requireAndGetAddress(CONTRACT_SUPPLYSCHEDULE)); } // ========== OVERRIDDEN FUNCTIONS ========== function exchangeWithVirtual( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, bytes32 trackingCode ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived, IVirtualSynth vSynth) { return exchanger().exchangeWithVirtual( messageSender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, messageSender, trackingCode ); } function settle(bytes32 currencyKey) external optionalProxy returns ( uint reclaimed, uint refunded, uint numEntriesSettled ) { return exchanger().settle(messageSender, currencyKey); } function mint() external issuanceActive returns (bool) { require(address(rewardsDistribution()) != address(0), "RewardsDistribution not set"); ISupplySchedule _supplySchedule = supplySchedule(); IRewardsDistribution _rewardsDistribution = rewardsDistribution(); uint supplyToMint = _supplySchedule.mintableSupply(); require(supplyToMint > 0, "No supply is mintable"); // record minting event before mutation to token supply _supplySchedule.recordMintEvent(supplyToMint); // Set minted DEM balance to RewardEscrow's balance // Minus the minterReward and set balance of minter to add reward uint minterReward = _supplySchedule.minterReward(); // Get the remainder uint amountToDistribute = supplyToMint.sub(minterReward); // Set the token balance to the RewardsDistribution contract tokenState.setBalanceOf( address(_rewardsDistribution), tokenState.balanceOf(address(_rewardsDistribution)).add(amountToDistribute) ); emitTransfer(address(this), address(_rewardsDistribution), amountToDistribute); // Kick off the distribution of rewards _rewardsDistribution.distributeRewards(amountToDistribute); // Assign the minters reward. tokenState.setBalanceOf(msg.sender, tokenState.balanceOf(msg.sender).add(minterReward)); emitTransfer(address(this), msg.sender, minterReward); totalSupply = totalSupply.add(supplyToMint); return true; } function liquidateDelinquentAccount(address account, uint susdAmount) external systemActive optionalProxy returns (bool) { (uint totalRedeemed, uint amountLiquidated) = issuer().liquidateDelinquentAccount(account, susdAmount, messageSender); emitAccountLiquidated(account, totalRedeemed, amountLiquidated, messageSender); // Transfer DEM redeemed to messageSender // Reverts if amount to redeem is more than balanceOf account, ie due to escrowed balance return _transferByProxy(account, messageSender, totalRedeemed); } /* Once off function for SIP-60 to migrate DEM balances in the RewardEscrow contract * To the new RewardEscrowV2 contract */ function migrateEscrowBalanceToRewardEscrowV2() external onlyOwner { // Record balanceOf(RewardEscrow) contract uint rewardEscrowBalance = tokenState.balanceOf(address(rewardEscrow())); // transfer all of RewardEscrow's balance to RewardEscrowV2 // _internalTransfer emits the transfer event _internalTransfer(address(rewardEscrow()), address(rewardEscrowV2()), rewardEscrowBalance); } // ========== EVENTS ========== event AccountLiquidated(address indexed account, uint snxRedeemed, uint amountLiquidated, address liquidator); bytes32 internal constant ACCOUNTLIQUIDATED_SIG = keccak256("AccountLiquidated(address,uint256,uint256,address)"); function emitAccountLiquidated( address account, uint256 snxRedeemed, uint256 amountLiquidated, address liquidator ) internal { proxy._emit( abi.encode(snxRedeemed, amountLiquidated, liquidator), 2, ACCOUNTLIQUIDATED_SIG, addressToBytes32(account), 0, 0 ); } }