Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
418 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
...ds/InitializableRewardsDistributionRecipient.sol~6d935eb8c8797e240a7e9fde7603c90d730608ce
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = {} |
Oops, something went wrong.