Skip to content

Commit

Permalink
Merge 9c57056 into 9516051
Browse files Browse the repository at this point in the history
  • Loading branch information
naddison36 committed Jul 5, 2021
2 parents 9516051 + 9c57056 commit 51bbf2f
Show file tree
Hide file tree
Showing 30 changed files with 3,788 additions and 101 deletions.
6 changes: 6 additions & 0 deletions contracts/interfaces/IRewardsDistributionRecipient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ interface IRewardsDistributionRecipient {

function getRewardToken() external view returns (IERC20);
}

interface IRewardsRecipientWithPlatformToken {
function notifyRewardAmount(uint256 reward) external;
function getRewardToken() external view returns (IERC20);
function getPlatformToken() external view returns (IERC20);
}
28 changes: 28 additions & 0 deletions contracts/rewards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@


## Files

Why so many?

Actually only 3 are base contracts

### RewardsDistributor

Allows reward allocators ("FundManagers") to distribute rewards.

### StakingRewards

`StakingRewards` is `InitializableRewardsDistributionRecipient`
--------------> is `StakingTokenWrapper`

This preserves the code written, tested, audited and deployed by `Synthetix` (StakingRewards & StakingTokenWrapper).

Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/StakingRewards.sol)
Audit: https://github.com/sigp/public-audits/blob/master/synthetix/unipool/review.pdf`

### StakingRewardsWithPlatformToken

`StakingRewardsWithPlatformToken` is `InitializableRewardsDistributionRecipient`
-------------------------------> is `StakingTokenWrapper`

`StakingRewardsWithPlatformToken` deploys `PlatformTokenVendor` during its constructor
69 changes: 54 additions & 15 deletions contracts/rewards/RewardsDistributor.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.2;

import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol";

// import { InitializableGovernableWhitelist } from "../governance/InitializableGovernableWhitelist.sol";
import { IRewardsRecipientWithPlatformToken } from "../interfaces/IRewardsDistributionRecipient.sol";
import { ImmutableModule } from "../shared/ImmutableModule.sol";
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -19,9 +17,17 @@ contract RewardsDistributor is ImmutableModule {

mapping(address => bool) public fundManagers;

event AddFundManager(address indexed _address);
event AddedFundManager(address indexed _address);
event RemovedFundManager(address indexed _address);
event DistributedReward(address funder, address recipient, address rewardToken, uint256 amount);
event DistributedReward(
address funder,
address recipient,
address rewardToken,
uint256 amount,
address platformToken,
uint256 platformAmount
);


/**
* @dev Modifier to allow function calls only from a fundManager address.
Expand All @@ -37,7 +43,11 @@ contract RewardsDistributor is ImmutableModule {
address[] memory _fundManagers
)
ImmutableModule(_nexus)
{}
{
for(uint256 i = 0; i < _fundManagers.length; i++) {
_addFundManager(_fundManagers[i]);
}
}

/**
* @dev Allows the mStable governance to add a new FundManager
Expand All @@ -47,12 +57,20 @@ contract RewardsDistributor is ImmutableModule {
external
onlyGovernor
{
_addFundManager(_address);
}

/**
* @dev Adds a new whitelist address
* @param _address Address to add in whitelist
*/
function _addFundManager(address _address) internal {
require(_address != address(0), "Address is zero");
require(! fundManagers[_address], "Already fund manager");

fundManagers[_address] = true;

emit AddFundManager(_address);
emit AddedFundManager(_address);
}

/**
Expand All @@ -74,30 +92,51 @@ contract RewardsDistributor is ImmutableModule {
/**
* @dev Distributes reward tokens to list of recipients and notifies them
* of the transfer. Only callable by FundManagers
* @param _recipients Array of Reward recipients to credit
* @param _amounts Amounts of reward tokens to distribute
* @param _recipients Array of Reward recipients to credit
* @param _amounts Amounts of reward tokens to distribute
* @param _platformAmounts Amounts of platform tokens to distribute
*/
function distributeRewards(
IRewardsDistributionRecipient[] calldata _recipients,
uint256[] calldata _amounts
IRewardsRecipientWithPlatformToken[] calldata _recipients,
uint256[] calldata _amounts,
uint256[] calldata _platformAmounts
)
external
onlyFundManager
{
uint256 len = _recipients.length;
require(len > 0, "Must choose recipients");
require(len == _amounts.length, "Mismatching inputs");
require(len == _platformAmounts.length, "Mismatching inputs");

for(uint i = 0; i < len; i++){
for (uint256 i = 0; i < len; i++) {
uint256 amount = _amounts[i];
IRewardsDistributionRecipient recipient = _recipients[i];
IRewardsRecipientWithPlatformToken recipient = _recipients[i];

// Send the RewardToken to recipient
IERC20 rewardToken = recipient.getRewardToken();
rewardToken.safeTransferFrom(msg.sender, address(recipient), amount);
// Only after successfull tx - notify the contract of the new funds

// Send the PlatformToken to recipient
uint256 platformAmount = _platformAmounts[i];
address platformTokenAddress = address(0);
if(platformAmount > 0) {
IERC20 platformToken = recipient.getPlatformToken();
platformTokenAddress = address(platformToken);
platformToken.safeTransferFrom(msg.sender, address(recipient), platformAmount);
}

// Only after successful tx - notify the contract of the new funds
recipient.notifyRewardAmount(amount);

emit DistributedReward(msg.sender, address(recipient), address(rewardToken), amount);
emit DistributedReward(
msg.sender,
address(recipient),
address(rewardToken),
amount,
platformTokenAddress,
platformAmount
);
}
}
}
32 changes: 32 additions & 0 deletions contracts/rewards/staking/PlatformTokenVendor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.2;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { MassetHelpers } from "../../shared/MassetHelpers.sol";

/**
* @title PlatformTokenVendor
* @author Stability Labs Pty. Ltd.
* @notice Stores platform tokens for distributing to StakingReward participants
* @dev Only deploy this during the constructor of a given StakingReward contract
*/
contract PlatformTokenVendor {

IERC20 public immutable platformToken;
address public immutable parentStakingContract;

/** @dev Simple constructor that stores the parent address */
constructor(IERC20 _platformToken) public {
parentStakingContract = msg.sender;
platformToken = _platformToken;
MassetHelpers.safeInfiniteApprove(address(_platformToken), msg.sender);
}

/**
* @dev Re-approves the StakingReward contract to spend the platform token.
* Just incase for some reason approval has been reset.
*/
function reApproveOwner() external {
MassetHelpers.safeInfiniteApprove(address(platformToken), parentStakingContract);
}
}
Loading

0 comments on commit 51bbf2f

Please sign in to comment.