Skip to content

Commit

Permalink
Merge 8ad91ce into 158816d
Browse files Browse the repository at this point in the history
  • Loading branch information
alsco77 committed Mar 8, 2021
2 parents 158816d + 8ad91ce commit 818717e
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 30 deletions.
10 changes: 10 additions & 0 deletions contracts/buy-and-make/IConfigurableRightsPool.sol
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.0;

interface IConfigurableRightsPool {
function joinswapExternAmountIn(
address tokenIn,
uint256 tokenAmountIn,
uint256 minPoolAmountOut
) external returns (uint256 poolAmountOut);
}
93 changes: 93 additions & 0 deletions contracts/buy-and-make/RevenueRecipient.sol
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.0;

import { IRevenueRecipient } from "../interfaces/IRevenueRecipient.sol";
import { IConfigurableRightsPool } from "./IConfigurableRightsPool.sol";
import { ImmutableModule } from "../shared/ImmutableModule.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
* @title RevenueRecipient
* @author mStable
* @notice Simply receives mAssets and then deposits to a pre-defined Balancer
* ConfigurableRightsPool.
* @dev VERSION: 1.0
* DATE: 2021-03-08
*/
contract RevenueRecipient is IRevenueRecipient, ImmutableModule {
using SafeERC20 for IERC20;

event RevenueReceived(address indexed mAsset, uint256 amountIn, uint256 amountOut);

// BPT To which all revenue should be deposited
IConfigurableRightsPool public immutable mBPT;

// Minimum output units per 1e18 input units
mapping(address => uint256) public minOut;

/**
* @dev Creates the RevenueRecipient contract
* @param _nexus mStable system Nexus address
* @param _targetPool Balancer pool to which all revenue should be deposited
* @param _assets Initial list of supported mAssets
* @param _minOut Minimum BPT out per mAsset unit
*/
constructor(
address _nexus,
address _targetPool,
address[] memory _assets,
uint256[] memory _minOut
) ImmutableModule(_nexus) {
mBPT = IConfigurableRightsPool(_targetPool);

uint256 len = _assets.length;
for (uint256 i = 0; i < len; i++) {
minOut[_assets[i]] = _minOut[i];
IERC20(_assets[i]).safeApprove(_targetPool, 2**256 - 1);
}
}

/**
* @dev Called by SavingsManager after revenue has accrued
* @param _mAsset Address of mAsset
* @param _amount Units of mAsset collected
*/
function notifyRedistributionAmount(address _mAsset, uint256 _amount) external override {
// Transfer from sender to here
IERC20(_mAsset).safeTransferFrom(msg.sender, address(this), _amount);

// Deposit into pool
uint256 minBPT = (_amount * minOut[_mAsset]) / 1e18;
uint256 poolAmountOut = mBPT.joinswapExternAmountIn(_mAsset, _amount, minBPT);

emit RevenueReceived(_mAsset, _amount, poolAmountOut);
}

/**
* @dev Simply approves spending of a given mAsset by BPT
* @param _mAsset Address of mAsset to approve
*/
function approveAsset(address _mAsset) external onlyGovernor {
IERC20(_mAsset).safeApprove(address(mBPT), 0);
IERC20(_mAsset).safeApprove(address(mBPT), 2**256 - 1);
}

/**
* @dev Sets the minimum amount of BPT to receive for a given mAsset
* @param _mAsset Address of mAsset
* @param _minOut Scaled amount to receive per 1e18 mAsset units
*/
function updateAmountOut(address _mAsset, uint256 _minOut) external onlyGovernor {
minOut[_mAsset] = _minOut;
}

/**
* @dev Migrates BPT to a new revenue recipient
* @param _recipient Address of recipient
*/
function migrateBPT(address _recipient) external onlyGovernor {
IERC20 mBPT_ = IERC20(address(mBPT));
mBPT_.safeTransfer(_recipient, mBPT_.balanceOf(address(this)));
}
}
7 changes: 7 additions & 0 deletions contracts/interfaces/IRevenueRecipient.sol
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.0;

interface IRevenueRecipient {
/** @dev Recipient */
function notifyRedistributionAmount(address _mAsset, uint256 _amount) external;
}
5 changes: 0 additions & 5 deletions contracts/interfaces/ISavingsManager.sol
Expand Up @@ -17,8 +17,3 @@ interface ISavingsManager {
/** @dev Public privs */
function collectAndDistributeInterest(address _mAsset) external;
}

interface IRevenueRecipient {
/** @dev Recipient */
function notifyRedistributionAmount(address _mAsset, uint256 _amount) external;
}
@@ -0,0 +1,47 @@
pragma solidity 0.5.16;

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

/**
* @title RewardsDistributionRecipient
* @author Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/RewardsDistributionRecipient.sol)
* Changes by: Stability Labs Pty. Ltd.
* @notice RewardsDistributionRecipient gets notified of additional rewards by the rewardsDistributor
* @dev Changes: Addition of Module and abstract `getRewardToken` func + cosmetic
*/
contract InitializableRewardsDistributionRecipient is IRewardsDistributionRecipient, InitializableModule2 {

// @abstract
function notifyRewardAmount(uint256 reward) external;
function getRewardToken() external view returns (IERC20);

// This address has the ability to distribute the rewards
address public rewardsDistributor;

/** @dev Recipient is a module, governed by mStable governance */
function _initialize(address _nexus, address _rewardsDistributor) internal {
InitializableModule2._initialize(_nexus);
rewardsDistributor = _rewardsDistributor;
}

/**
* @dev Only the rewards distributor can notify about rewards
*/
modifier onlyRewardsDistributor() {
require(msg.sender == rewardsDistributor, "Caller is not reward distributor");
_;
}

/**
* @dev Change the rewardsDistributor - only called by mStable governor
* @param _rewardsDistributor Address of the new distributor
*/
function setRewardsDistribution(address _rewardsDistributor)
external
onlyGovernor
{
rewardsDistributor = _rewardsDistributor;
}
}
38 changes: 38 additions & 0 deletions contracts/z_mocks/buy-and-make/MockBPool.sol
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.0;
import { IConfigurableRightsPool } from "../../buy-and-make/IConfigurableRightsPool.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";


contract MockBPool is ERC20, IConfigurableRightsPool {

// output = input * ratio / 1e18
uint256 inputToOutputRatio;
mapping(address => bool) private _tokenIsValid;

constructor(uint256 _inputToOutputRatio, address[] memory _tokens, string memory _name, string memory _symbol) ERC20(_name, _symbol) {
inputToOutputRatio = _inputToOutputRatio;
uint len = _tokens.length;
for(uint i = 0; i < len; i++){
_tokenIsValid[_tokens[i]] = true;
}
}

function joinswapExternAmountIn(
address tokenIn,
uint tokenAmountIn,
uint minPoolAmountOut
)
external
override
returns (uint poolAmountOut)
{
require(_tokenIsValid[tokenIn], "Invalid token");
IERC20(tokenIn).transferFrom(msg.sender, address(this), tokenAmountIn);
poolAmountOut = tokenAmountIn * inputToOutputRatio / 1e18;
require(poolAmountOut > minPoolAmountOut, "Invalid output amount");
_mint(msg.sender, poolAmountOut);
}

}
53 changes: 53 additions & 0 deletions tasks/deployRevenueRecipient.ts
@@ -0,0 +1,53 @@
/* eslint-disable no-console */
import "ts-node/register"
import "tsconfig-paths/register"

import { DEAD_ADDRESS } from "@utils/constants"
import { task } from "hardhat/config"
import { RevenueRecipient__factory } from "types/generated"
import { simpleToExactAmount, BN } from "@utils/math"

interface CommonAddresses {
mAssets: string[]
minOuts: BN[]
nexus: string
bPool: string
}

task("deployRevenueRecipient", "Deploys an instance of revenue recipient contract").setAction(async (_, hre) => {
const { ethers, network } = hre
const [deployer] = await ethers.getSigners()

if (network.name !== "mainnet" && network.name !== "ropsten") throw Error("Invalid network")

const addresses: CommonAddresses =
network.name === "ropsten"
? {
mAssets: ["0x4E1000616990D83e56f4b5fC6CC8602DcfD20459", "0x4A677A48A790f26eac4c97f495E537558Abf6A79"],
minOuts: [],
nexus: "0xeD04Cd19f50F893792357eA53A549E23Baf3F6cB",
bPool: DEAD_ADDRESS,
}
: {
mAssets: ["0xe2f2a5C287993345a840Db3B0845fbC70f5935a5", "0x945Facb997494CC2570096c74b5F66A3507330a1"],
minOuts: [simpleToExactAmount(1, 18), simpleToExactAmount(1, 18)], // TODO - add this
nexus: "0xAFcE80b19A8cE13DEc0739a1aaB7A028d6845Eb3",
bPool: DEAD_ADDRESS, // TODO - add this
}

// Deploy
const recipient = await new RevenueRecipient__factory(deployer).deploy(
addresses.nexus,
addresses.bPool,
addresses.mAssets,
addresses.minOuts,
)
const reciept = await recipient.deployTransaction.wait()
console.log(`Deployed Recipient to ${recipient.address}. gas used ${reciept.gasUsed}`)

// Complete setup
// - Update SavingsRate in SavingsManager
// - Add RevenueRecipient address in SavingsManager
})

module.exports = {}

0 comments on commit 818717e

Please sign in to comment.