// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 8: EUSDMiningIncentives.sol
// SPDX-License-Identifier: GPL-3.0pragmasolidity ^0.8.17;/**
* @title EUSDMiningIncentives is a stripped down version of Synthetix StakingRewards.sol, to reward esLBR to eUSD&peUSD minters.
* Differences from the original contract,
* - totalStaked and stakedOf(user) are different from the original version.
* - When a user's borrowing changes in any of the Lst vaults, the `refreshReward()` function needs to be called to update the data.
*/import"@openzeppelin/contracts/access/Ownable.sol";
import"../interfaces/IesLBR.sol";
import"../interfaces/IEUSD.sol";
import"../interfaces/ILybra.sol";
import"../interfaces/Iconfigurator.sol";
import"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
interfaceIesLBRBoost{
functiongetUserBoost(address user,
uint256 userUpdatedAt,
uint256 finishAt
) externalviewreturns (uint256);
}
contractEUSDMiningIncentivesisOwnable{
Iconfigurator publicimmutable configurator;
IesLBRBoost public esLBRBoost;
IEUSD publicimmutable EUSD;
addresspublic esLBR;
addresspublic LBR;
addresspublic wETH;
address[] public vaults;
// Duration of rewards to be paid out (in seconds)uint256public duration =604_800;
// Timestamp of when the rewards finishuint256public finishAt;
// Minimum of last updated time and reward finish timeuint256public updatedAt;
// Reward to be paid out per seconduint256public rewardRatio;
// Sum of (reward ratio * dt * 1e18 / total supply)uint256public rewardPerTokenStored;
// User address => rewardPerTokenStoredmapping(address=>uint256) public userRewardPerTokenPaid;
// User address => rewards to be claimedmapping(address=>uint256) public rewards;
mapping(address=>uint256) public userUpdatedAt;
uint256public extraRatio =10*1e18;
uint256public biddingFeeRatio =3000;
addresspublic ethlbrStakePool;
uint256public minDlpRatio =500;
AggregatorV3Interface internal lpPriceFeed;
AggregatorV3Interface internal lbrPriceFeed;
boolpublic isEUSDBuyoutAllowed =true;
boolpublic v1Supported =true;
addressimmutable oldLybra;
eventVaultsChanged(address[] vaults, uint256 time);
eventLBROracleChanged(address newOracle, uint256 time);
eventLpOracleChanged(address newOracle, uint256 time);
eventTokenChanged(address newLBR, address newEsLBR, uint256 time);
eventClaimReward(addressindexed user, uint256 amount, uint256 time);
eventClaimedOtherEarnings(addressindexed user, addressindexed Victim, uint256 buyAmount, uint256 biddingFee, bool useEUSD, uint256 time);
eventNotifyRewardChanged(uint256 addAmount, uint256 time);
//wETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2constructor(address _config, address _lpOracle, address _lbrOracle, address _weth, address _oldEUSD) {
configurator = Iconfigurator(_config);
EUSD = IEUSD(configurator.getEUSDAddress());
lpPriceFeed = AggregatorV3Interface(_lpOracle);
lbrPriceFeed = AggregatorV3Interface(_lbrOracle);
wETH = _weth;
oldLybra = _oldEUSD;
}
modifierupdateReward(address _account) {
rewardPerTokenStored = rewardPerToken();
updatedAt = lastTimeRewardApplicable();
if (_account !=address(0)) {
rewards[_account] = earned(_account);
userRewardPerTokenPaid[_account] = rewardPerTokenStored;
userUpdatedAt[_account] =block.timestamp;
}
_;
}
functionsetToken(address _lbr, address _eslbr) externalonlyOwner{
LBR = _lbr;
esLBR = _eslbr;
emit TokenChanged(_lbr, _eslbr, block.timestamp);
}
functionsetLBROracle(address _lbrOracle) externalonlyOwner{
lbrPriceFeed = AggregatorV3Interface(_lbrOracle);
emit LBROracleChanged(_lbrOracle, block.timestamp);
}
functionsetLpOracle(address _lpOracle) externalonlyOwner{
lpPriceFeed = AggregatorV3Interface(_lpOracle);
emit LpOracleChanged(_lpOracle, block.timestamp);
}
functionsetPools(address[] memory _vaults) externalonlyOwner{
require(_vaults.length<=10, "EL");
for (uint i =0; i < _vaults.length; i++) {
require(configurator.mintVault(_vaults[i]), "NOT_VAULT");
}
vaults = _vaults;
emit VaultsChanged(_vaults, block.timestamp);
}
functionsetBiddingCost(uint256 _biddingRatio) externalonlyOwner{
require(_biddingRatio <=8000, "BCE");
biddingFeeRatio = _biddingRatio;
}
functionsetExtraRatio(uint256 ratio) externalonlyOwner{
require(ratio <=1e20, "BCE");
extraRatio = ratio;
}
functionsetMinDlpRatio(uint256 ratio) externalonlyOwner{
require(ratio <=1_000, "BCE");
minDlpRatio = ratio;
}
functionsetBoost(address _boost) externalonlyOwner{
esLBRBoost = IesLBRBoost(_boost);
}
functionsetV1Supported(bool _bool) externalonlyOwner{
v1Supported = _bool;
}
functionsetRewardsDuration(uint256 _duration) externalonlyOwner{
require(finishAt <block.timestamp, "reward duration not finished");
duration = _duration;
}
functionsetEthlbrStakeInfo(address _pool) externalonlyOwner{
ethlbrStakePool = _pool;
}
functionsetEUSDBuyoutAllowed(bool _bool) externalonlyOwner{
isEUSDBuyoutAllowed = _bool;
}
/**
* @notice Returns the total amount of minted eUSD&peUSD in the asset pools.
* @return The total amount of minted eUSD&peUSD.
* @dev It iterates through the vaults array and retrieves the total circulation of each asset pool using the getPoolTotalCirculation()
* function from the ILybra interface. The total staked amount is calculated by multiplying the total circulation by the vault's
* weight (obtained from configurator.getVaultWeight()).
*/functiontotalStaked() publicviewreturns (uint256) {
uint256 amount;
for (uint i =0; i < vaults.length; i++) {
ILybra vault = ILybra(vaults[i]);
amount += vault.getPoolTotalCirculation() * configurator.getVaultWeight(vaults[i]) /1e20;
}
if(v1Supported) {
amount += IEUSD(oldLybra).totalSupply() * configurator.getVaultWeight(oldLybra) /1e20;
}
return amount;
}
/**
* @notice Returns the total amount of borrowed eUSD and peUSD by the user.
*/functionstakedOf(address user) publicviewreturns (uint256) {
uint256 amount;
for (uint i =0; i < vaults.length; i++) {
ILybra vault = ILybra(vaults[i]);
amount += vault.getBorrowedOf(user) * configurator.getVaultWeight(vaults[i]) /1e20;
}
if(v1Supported) {
amount += ILybra(oldLybra).getBorrowedOf(user) * configurator.getVaultWeight(oldLybra) /1e20;
}
return amount;
}
/**
* @notice Returns the value of the user's staked LP tokens in the ETH-LBR liquidity pool.
* @param user The user's address.
* @return The value of the user's staked LP tokens.
*/functionstakedLBRLpValue(address user) publicviewreturns (uint256) {
(, int lpPrice, , , ) = lpPriceFeed.latestRoundData();
return IEUSD(ethlbrStakePool).balanceOf(user) *uint256(lpPrice) /1e8;
}
functionlastTimeRewardApplicable() publicviewreturns (uint256) {
return _min(finishAt, block.timestamp);
}
functionrewardPerToken() publicviewreturns (uint256) {
if (totalStaked() ==0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored + (rewardRatio * (lastTimeRewardApplicable() - updatedAt) *1e18) / totalStaked();
}
/**
* @notice Update user's claimable reward data and record the timestamp.
*/functionrefreshReward(address _account) externalupdateReward(_account) {}
functiongetBoost(address _account) publicviewreturns (uint256) {
uint256 redemptionBoost;
if (configurator.isRedemptionProvider(_account)) {
redemptionBoost = extraRatio;
}
return100*1e18+ redemptionBoost + esLBRBoost.getUserBoost(_account, userUpdatedAt[_account], finishAt);
}
functionearned(address _account) publicviewreturns (uint256) {
return ((stakedOf(_account) * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) /1e38) + rewards[_account];
}
/**
* @notice Checks if the user's earnings can be claimed by others.
* @param user The user's address.
* @return A boolean indicating if the user's earnings can be claimed by others.
*/functionisOtherEarningsClaimable(address user) publicviewreturns (bool) {
uint256 staked = stakedOf(user);
if(staked ==0) returntrue;
return (stakedLBRLpValue(user) *10_000) / staked < minDlpRatio;
}
functiongetReward() externalupdateReward(msg.sender) {
require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");
uint256 reward = rewards[msg.sender];
if (reward >0) {
rewards[msg.sender] =0;
IesLBR(esLBR).mint(msg.sender, reward);
emit ClaimReward(msg.sender, reward, block.timestamp);
}
}
/**
* @notice Purchasing the esLBR earnings from users who have insufficient DLP.
* @param user The address of the user whose earnings will be purchased.
* @param useEUSD Boolean indicating if the purchase will be made using eUSD.
* Requirements:
* The user's earnings must be claimable by others.
* If using eUSD, the purchase must be permitted.
* The user must have non-zero rewards.
* If using eUSD, the caller must have sufficient eUSD balance and allowance.
*/function_buyOtherEarnings(address user, bool useEUSD) internalupdateReward(user) {
require(isOtherEarningsClaimable(user), "The rewards of the user cannot be bought out");
require(rewards[user] !=0, "ZA");
if(useEUSD) {
require(isEUSDBuyoutAllowed, "The purchase using eUSD is not permitted.");
}
uint256 reward = rewards[user];
rewards[user] =0;
uint256 biddingFee = (reward * biddingFeeRatio) /10_000;
if(useEUSD) {
(, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
biddingFee = biddingFee *uint256(lbrPrice) /1e8;
bool success = EUSD.transferFrom(msg.sender, address(owner()), biddingFee);
require(success, "TF");
} else {
IesLBR(LBR).burn(msg.sender, biddingFee);
}
IesLBR(esLBR).mint(msg.sender, reward);
emit ClaimedOtherEarnings(msg.sender, user, reward, biddingFee, useEUSD, block.timestamp);
}
functionbuyOthersEarnings(address[] memory users, bool useEUSD) external{
for(uint256 i; i < users.length; i++) {
_buyOtherEarnings(users[i], useEUSD);
}
}
functionnotifyRewardAmount(uint256 amount
) externalonlyOwnerupdateReward(address(0)) {
require(amount !=0, "amount = 0");
if (block.timestamp>= finishAt) {
rewardRatio = amount / duration;
} else {
uint256 remainingRewards = (finishAt -block.timestamp) * rewardRatio;
rewardRatio = (amount + remainingRewards) / duration;
}
require(rewardRatio !=0, "reward ratio = 0");
finishAt =block.timestamp+ duration;
updatedAt =block.timestamp;
emit NotifyRewardChanged(amount, block.timestamp);
}
function_min(uint256 x, uint256 y) privatepurereturns (uint256) {
return x <= y ? x : y;
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}