Skip to content

Commit

Permalink
feat: Add WIP GaugeBriber (mstable#256)
Browse files Browse the repository at this point in the history
* feat: Add GaugeBriber
  • Loading branch information
superduck35 authored Oct 26, 2021
1 parent a2c98d5 commit 66f3646
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 0 deletions.
107 changes: 107 additions & 0 deletions contracts/buy-and-make/GaugeBriber.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;

import { IRevenueRecipient } from "../interfaces/IRevenueRecipient.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 GaugeBriber
* @author mStable
* @notice Collect system revenue in mUSD, converts to MTA, funds bribe on Votium
* @dev VERSION: 1.0
* DATE: 2021-10-19
*/
contract GaugeBriber is IRevenueRecipient, ImmutableModule {
using SafeERC20 for IERC20;

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

IERC20 public immutable musd;

address public immutable keeper;
address public briber;

IRevenueRecipient public childRecipient;
uint256 public feeSplit;

uint256[2] public available;

constructor(
address _nexus,
address _musd,
address _keeper,
address _briber,
address _childRecipient
) ImmutableModule(_nexus) {
musd = IERC20(_musd);
keeper = _keeper;
briber = _briber;
childRecipient = IRevenueRecipient(_childRecipient);
}

modifier keeperOrGovernor() {
require(msg.sender == keeper || msg.sender == _governor(), "Only keeper or governor");
_;
}

/**
* @dev Simply transfers the mAsset from the sender to here
* @param _mAsset Address of mAsset
* @param _amount Units of mAsset collected
*/
function notifyRedistributionAmount(address _mAsset, uint256 _amount) external override {
require(_mAsset == address(musd), "This Recipient is only for mUSD");
// Transfer from sender to here
IERC20(_mAsset).safeTransferFrom(msg.sender, address(this), _amount);

available[0] += ((_amount * (1e18 - feeSplit)) / 1e18);
available[1] += ((_amount * feeSplit) / 1e18);

emit RevenueReceived(_mAsset, _amount);
}

/**
* @dev Withdraws to bribing capacity
*/
function forward() external keeperOrGovernor {
uint256 amt = available[0];
available[0] = 0;
musd.safeTransfer(briber, amt);

uint256 amtChild = available[1];
if (amtChild > 0) {
available[1] = 0;
childRecipient.notifyRedistributionAmount(address(musd), amtChild);
}
emit Withdrawn(amt, amtChild);
}

/**
* @dev Sets fee split details for child revenue recipient
* @param _briber new briber
* @param _newRecipient Address of child RevenueRecipient
* @param _feeSplit Percentage of total received that goes to child
*/
function setConfig(
address _briber,
address _newRecipient,
uint256 _feeSplit
) external onlyGovernor {
require(_feeSplit <= 5e17, "Must be less than 50%");
require(_briber != address(0), "Invalid briber");
briber = _briber;
childRecipient = IRevenueRecipient(_newRecipient);
feeSplit = _feeSplit;
}

/**
* @dev Abstract override
*/
function depositToPool(
address[] calldata, /* _mAssets */
uint256[] calldata /* _percentages */
) external override {}
}
1 change: 1 addition & 0 deletions tasks.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./tasks/deployMbtc"
import "./tasks/deployFeeders"
import "./tasks/deployMV3"
import "./tasks/deployPolygon"
import "./tasks/deployBriber"
import "./tasks/deploySavingsManager"
import "./tasks/feeder"
import "./tasks/mBTC"
Expand Down
36 changes: 36 additions & 0 deletions tasks/deployBriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import "ts-node/register"
import "tsconfig-paths/register"
import { task, types } from "hardhat/config"
import { GaugeBriber__factory } from "types/generated"
import { deployContract } from "./utils/deploy-utils"
import { getSigner } from "./utils/signerFactory"
import { verifyEtherscan } from "./utils/etherscan"
import { getChain, resolveAddress } from "./utils/networkAddressFactory"

task("deploy-GaugeBriber")
.addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", types.string)
.setAction(async (taskArgs, hre) => {
const signer = await getSigner(hre, taskArgs.speed)
const chain = getChain(hre)

const nexus = resolveAddress("Nexus", chain)
const musd = resolveAddress("mUSD", chain, "address")
const keeper = "0xb81473f20818225302b8fffb905b53d58a793d84"
const briber = "0xd0f0F590585384AF7AB420bE1CFB3A3F8a82D775"
const childRecipient = resolveAddress("RevenueRecipient", chain)

const gaugeBriber = await deployContract(new GaugeBriber__factory(signer), "GaugeBriber", [
nexus,
musd,
keeper,
briber,
childRecipient,
])

await verifyEtherscan(hre, {
address: gaugeBriber.address,
contract: "contracts/buy-and-make/GaugeBriber.sol:GaugeBriber",
})
})

module.exports = {}
133 changes: 133 additions & 0 deletions test-fork/buy-and-make/gauge-briber.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { DEAD_ADDRESS, ZERO_ADDRESS, ONE_WEEK } from "@utils/constants"
import { impersonate, impersonateAccount } from "@utils/fork"
import { simpleToExactAmount } from "@utils/math"
import { increaseTime } from "@utils/time"
import { assertBNClose } from "@utils/assertions"
import { expect } from "chai"
import { Signer } from "ethers"
import * as hre from "hardhat"
import {
SavingsManager,
SavingsManager__factory,
GaugeBriber,
GaugeBriber__factory,
ERC20__factory,
Collector,
Collector__factory,
} from "types/generated"
import { Account } from "types"
import { Chain } from "tasks/utils/tokens"
import { resolveAddress } from "../../tasks/utils/networkAddressFactory"
import { deployContract } from "../../tasks/utils/deploy-utils"

const musdWhaleAddress = "0x136d841d4bece3fc0e4debb94356d8b6b4b93209"
const governorAddress = resolveAddress("Governor")
const deployerAddress = resolveAddress("OperationsSigner")
const ethWhaleAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"

// 1. Deploy && change Recipient
// - check config
// 2. Beta testing
// - collector.distributeInterest
// - forward
// - setConfig
// - collector.distributeInterest
// - check split
context("Recipient deployment and upgrade", () => {
let deployer: Account
let governor: Account
let ethWhale: Signer
let musdWhale: Signer
let savingsManager: SavingsManager
let musdAddr: string
let gaugeBriber: GaugeBriber
let collector: Collector

const { network } = hre

before("reset block number", async () => {
await network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: process.env.NODE_URL,
blockNumber: 13467671,
},
},
],
})
deployer = await impersonateAccount(deployerAddress)
governor = await impersonateAccount(governorAddress)
ethWhale = await impersonate(ethWhaleAddress)
musdWhale = await impersonate(musdWhaleAddress)

// send some Ether to the impersonated multisig contract as it doesn't have Ether
await ethWhale.sendTransaction({
to: governorAddress,
value: simpleToExactAmount(1),
})
})
context("1. Deploying", () => {
it("deploys new contract", async () => {
const nexus = resolveAddress("Nexus", Chain.mainnet)
musdAddr = resolveAddress("mUSD", Chain.mainnet, "address")
const keeper = "0xb81473f20818225302b8fffb905b53d58a793d84"
const briber = "0xd0f0F590585384AF7AB420bE1CFB3A3F8a82D775"
const childRecipient = resolveAddress("RevenueRecipient", Chain.mainnet)

gaugeBriber = await deployContract(new GaugeBriber__factory(deployer.signer), "GaugeBriber", [
nexus,
musdAddr,
keeper,
briber,
childRecipient,
])
})
it("execs upgrade", async () => {
const savingsManagerAddress = resolveAddress("SavingsManager", Chain.mainnet)
savingsManager = SavingsManager__factory.connect(savingsManagerAddress, governor.signer)
await savingsManager.setRevenueRecipient(musdAddr, gaugeBriber.address)

const collectorAddress = resolveAddress("Collector", Chain.mainnet)
collector = Collector__factory.connect(collectorAddress, governor.signer)
})
})
// 2. Beta testing
// - collector.distributeInterest
// - forward
// - setConfig
// - collector.distributeInterest
// - check split
context("2. Beta tests", () => {
let bal
it("collects & distributes to revenueRecipient", async () => {
await collector.distributeInterest([musdAddr], true)
bal = await ERC20__factory.connect(musdAddr, deployer.signer).balanceOf(gaugeBriber.address)
expect(bal).gt(0)
expect(await gaugeBriber.available(0)).eq(bal)
})
it("forwards to briber", async () => {
await gaugeBriber.forward()
const briberBal = await ERC20__factory.connect(musdAddr, deployer.signer).balanceOf(
"0xd0f0F590585384AF7AB420bE1CFB3A3F8a82D775",
)
expect(briberBal).eq(bal)
})
it("sets config", async () => {
await gaugeBriber.connect(governor.signer).setConfig(DEAD_ADDRESS, ZERO_ADDRESS, simpleToExactAmount(1, 17))
expect(await gaugeBriber.briber()).eq(DEAD_ADDRESS)
expect(await gaugeBriber.childRecipient()).eq(ZERO_ADDRESS)
expect(await gaugeBriber.feeSplit()).eq(simpleToExactAmount(1, 17))
})
it("collects & distributes to revenueRecipient", async () => {
await increaseTime(ONE_WEEK)
await collector.distributeInterest([musdAddr], true)
bal = await ERC20__factory.connect(musdAddr, deployer.signer).balanceOf(gaugeBriber.address)
expect(bal).gt(0)
const available0 = await gaugeBriber.available(0)
const available1 = await gaugeBriber.available(1)
assertBNClose(bal, available0.add(available1), 1)
})
})
})

0 comments on commit 66f3646

Please sign in to comment.