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 exchange( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) { return exchanger().exchange(messageSender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, messageSender); } function exchangeOnBehalf( address exchangeForAddress, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) { return exchanger().exchangeOnBehalf( exchangeForAddress, messageSender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey ); } function exchangeWithTracking( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) { return exchanger().exchangeWithTracking( messageSender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, messageSender, originator, trackingCode ); } function exchangeOnBehalfWithTracking( address exchangeForAddress, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) { return exchanger().exchangeOnBehalfWithTracking( exchangeForAddress, messageSender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, originator, trackingCode ); } 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 SNX 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 SNX 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 SNX 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 SynthExchange( address indexed account, bytes32 fromCurrencyKey, uint256 fromAmount, bytes32 toCurrencyKey, uint256 toAmount, address toAddress ); bytes32 internal constant SYNTHEXCHANGE_SIG = keccak256( "SynthExchange(address,bytes32,uint256,bytes32,uint256,address)" ); function emitSynthExchange( address account, bytes32 fromCurrencyKey, uint256 fromAmount, bytes32 toCurrencyKey, uint256 toAmount, address toAddress ) external onlyExchanger { proxy._emit( abi.encode(fromCurrencyKey, fromAmount, toCurrencyKey, toAmount, toAddress), 2, SYNTHEXCHANGE_SIG, addressToBytes32(account), 0, 0 ); } event ExchangeTracking(bytes32 indexed trackingCode, bytes32 toCurrencyKey, uint256 toAmount); bytes32 internal constant EXCHANGE_TRACKING_SIG = keccak256("ExchangeTracking(bytes32,bytes32,uint256)"); function emitExchangeTracking( bytes32 trackingCode, bytes32 toCurrencyKey, uint256 toAmount ) external onlyExchanger { proxy._emit(abi.encode(toCurrencyKey, toAmount), 2, EXCHANGE_TRACKING_SIG, trackingCode, 0, 0); } event ExchangeReclaim(address indexed account, bytes32 currencyKey, uint amount); bytes32 internal constant EXCHANGERECLAIM_SIG = keccak256("ExchangeReclaim(address,bytes32,uint256)"); function emitExchangeReclaim( address account, bytes32 currencyKey, uint256 amount ) external onlyExchanger { proxy._emit(abi.encode(currencyKey, amount), 2, EXCHANGERECLAIM_SIG, addressToBytes32(account), 0, 0); } event ExchangeRebate(address indexed account, bytes32 currencyKey, uint amount); bytes32 internal constant EXCHANGEREBATE_SIG = keccak256("ExchangeRebate(address,bytes32,uint256)"); function emitExchangeRebate( address account, bytes32 currencyKey, uint256 amount ) external onlyExchanger { proxy._emit(abi.encode(currencyKey, amount), 2, EXCHANGEREBATE_SIG, addressToBytes32(account), 0, 0); } 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 ); } // ========== MODIFIERS ========== modifier onlyExchanger() { _onlyExchanger(); _; } function _onlyExchanger() private { require(msg.sender == address(exchanger()), "Only Exchanger can invoke this"); } modifier exchangeActive(bytes32 src, bytes32 dest) { _exchangeActive(src, dest); _; } function _exchangeActive(bytes32 src, bytes32 dest) private { systemStatus().requireExchangeActive(); systemStatus().requireSynthsActive(src, dest); } }