Skip to content

Commit

Permalink
treasury mints bounty on rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
Zayen-X committed Dec 13, 2021
1 parent 796c1bb commit f65a711
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 54 deletions.
2 changes: 1 addition & 1 deletion contracts/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ contract OlympusStaking is OlympusAccessControlled {

if (address(distributor) != address(0)) {
distributor.distribute();
bounty = distributor.bounty();
bounty = distributor.retrieveBounty(); // Will mint ohm for this contract if there exists a bounty
}
uint256 balance = OHM.balanceOf(address(this));
uint256 staked = sOHM.circulatingSupply();
Expand Down
28 changes: 22 additions & 6 deletions contracts/StakingDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import "./interfaces/IDistributor.sol";

import "./types/OlympusAccessControlled.sol";


contract Distributor is IDistributor, OlympusAccessControlled {
/* ========== DEPENDENCIES ========== */

Expand Down Expand Up @@ -47,7 +46,7 @@ contract Distributor is IDistributor, OlympusAccessControlled {
constructor(
address _treasury,
address _ohm,
address _staking,
address _staking,
address _authority
) OlympusAccessControlled(IOlympusAuthority(_authority)) {
require(_treasury != address(0), "Zero address: Treasury");
Expand Down Expand Up @@ -78,6 +77,16 @@ contract Distributor is IDistributor, OlympusAccessControlled {
}
}

function retrieveBounty() external override returns (uint256) {
require(msg.sender == staking, "Only staking");
// If the distributor bounty is > 0, mint it for the staking contract.
if (bounty > 0) {
treasury.mint(address(staking), bounty);
}

return bounty;
}

/* ====== INTERNAL FUNCTIONS ====== */

/**
Expand All @@ -96,12 +105,13 @@ contract Distributor is IDistributor, OlympusAccessControlled {
}
} else {
// if rate should decrease
if (info[_index].rate > adjustment.rate) { // protect from underflow
if (info[_index].rate > adjustment.rate) {
// protect from underflow
info[_index].rate = info[_index].rate.sub(adjustment.rate); // lower rate
} else {
info[_index].rate = 0;
}

if (info[_index].rate <= adjustment.target) {
// if target met
adjustments[_index].rate = 0; // turn off adjustment
Expand Down Expand Up @@ -164,7 +174,10 @@ contract Distributor is IDistributor, OlympusAccessControlled {
@param _index uint
*/
function removeRecipient(uint256 _index) external override {
require(msg.sender == authority.governor() || msg.sender == authority.guardian(), "Caller is not governor or guardian");
require(
msg.sender == authority.governor() || msg.sender == authority.guardian(),
"Caller is not governor or guardian"
);
require(info[_index].recipient != address(0), "Recipient does not exist");
info[_index].recipient = address(0);
info[_index].rate = 0;
Expand All @@ -183,7 +196,10 @@ contract Distributor is IDistributor, OlympusAccessControlled {
uint256 _rate,
uint256 _target
) external override {
require(msg.sender == authority.governor() || msg.sender == authority.guardian(), "Caller is not governor or guardian");
require(
msg.sender == authority.governor() || msg.sender == authority.guardian(),
"Caller is not governor or guardian"
);
require(info[_index].recipient != address(0), "Recipient does not exist");

if (msg.sender == authority.guardian()) {
Expand Down
11 changes: 10 additions & 1 deletion contracts/interfaces/IDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@ pragma solidity >=0.7.5;

interface IDistributor {
function distribute() external;

function bounty() external view returns (uint256);

function retrieveBounty() external returns (uint256);

function nextRewardAt(uint256 _rate) external view returns (uint256);

function nextRewardFor(address _recipient) external view returns (uint256);

function setBounty(uint256 _bounty) external;

function addRecipient(address _recipient, uint256 _rewardRate) external;

function removeRecipient(uint256 _index) external;

function setAdjustment(
uint256 _index,
bool _add,
uint256 _rate,
uint256 _target
) external;
}
}
104 changes: 58 additions & 46 deletions test/dapp_test/src/Staking.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import "../../../contracts/StakingDistributor.sol";
import "../../../contracts/OlympusAuthority.sol";

import "./util/Hevm.sol";
import "./util/MockContract.sol";

contract StakingTest is DSTest {
using FixedPoint for *;
Expand All @@ -31,18 +32,27 @@ contract StakingTest is DSTest {
sOlympus internal sohm;
gOHM internal gohm;

MockContract internal mockToken;

/// @dev Hevm setup
Hevm internal constant hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
uint256 internal constant AMOUNT = 1000;
uint256 internal constant EPOCH_LENGTH = 8; // In Seconds
uint256 internal constant START_TIME = 0; // Starting at this epoch
uint256 internal constant NEXT_REBASE_TIME = 1; // Next epoch is here
uint256 internal constant BOUNTY = 42000; // TODO whats bounty?
uint256 internal constant BOUNTY = 42;

function setUp() public {
// Start at timestamp
hevm.warp(START_TIME);

// Setup mockToken to deposit into treasury (for excess reserves)
mockToken = new MockContract();
mockToken.givenMethodReturn(abi.encodeWithSelector(ERC20.name.selector), abi.encode("mock DAO"));
mockToken.givenMethodReturn(abi.encodeWithSelector(ERC20.symbol.selector), abi.encode("MOCK"));
mockToken.givenMethodReturnUint(abi.encodeWithSelector(ERC20.decimals.selector), 18);
mockToken.givenMethodReturnBool(abi.encodeWithSelector(IERC20.transferFrom.selector), true);

authority = new OlympusAuthority(address(this), address(this), address(this), address(this));

ohm = new OlympusERC20Token(address(authority));
Expand All @@ -52,8 +62,7 @@ contract StakingTest is DSTest {
sohm.setgOHM(address(gohm));

treasury = new OlympusTreasury(address(ohm), 1, address(authority));
// If you want to test the Treasury permissions
// authority.pushVault(address(treasury), true);

staking = new OlympusStaking(
address(ohm),
address(sohm),
Expand All @@ -66,13 +75,20 @@ contract StakingTest is DSTest {

distributor = new Distributor(address(treasury), address(ohm), address(staking), address(authority));
distributor.setBounty(BOUNTY);

staking.setDistributor(address(distributor));
treasury.enable(OlympusTreasury.STATUS.REWARDMANAGER, address(distributor), address(0)); // Allows distributor to mint ohm.
treasury.enable(OlympusTreasury.STATUS.RESERVETOKEN, address(mockToken), address(0)); // Allow mock token to be deposited into treasury
treasury.enable(OlympusTreasury.STATUS.RESERVEDEPOSITOR, address(this), address(0)); // Allow this contract to deposit token into treeasury

sohm.initialize(address(staking), address(treasury));
gohm.migrate(address(staking), address(sohm));

ohm.mint(address(this), AMOUNT);
// Give the treasury permissions to mint
authority.pushVault(address(treasury), true);

// Deposit a token who's profit (3rd param) determines how much ohm the treasury can mint
uint256 depositAmount = 20e18;
treasury.deposit(depositAmount, address(mockToken), BOUNTY.mul(2)); // Mints (depositAmount- 2xBounty) for this contract
}

function testStakeNoBalance() public {
Expand Down Expand Up @@ -130,23 +146,25 @@ contract StakingTest is DSTest {
bool claim = true;

// Stake the ohm
ohm.approve(address(staking), AMOUNT);
uint256 amountStaked = staking.stake(address(this), AMOUNT, isSohm, claim);
assertEq(amountStaked, AMOUNT);
uint256 initialOhmBalance = ohm.balanceOf(address(this));
ohm.approve(address(staking), initialOhmBalance);
uint256 amountStaked = staking.stake(address(this), initialOhmBalance, isSohm, claim);
assertEq(amountStaked, initialOhmBalance);

// Validate balances post stake
uint256 ohmBalance = ohm.balanceOf(address(this));
uint256 sOhmBalance = sohm.balanceOf(address(this));

assertEq(ohmBalance, 0);
assertEq(sOhmBalance, AMOUNT);
assertEq(sOhmBalance, initialOhmBalance);

// test the unstake
// Unstake sOHM
sohm.approve(address(staking), sOhmBalance);
staking.unstake(address(this), sOhmBalance, triggerRebase, isSohm);

// Validate Balances post unstake
ohmBalance = ohm.balanceOf(address(this));
sOhmBalance = sohm.balanceOf(address(this));

assertEq(ohmBalance, AMOUNT);
assertEq(ohmBalance, initialOhmBalance);
assertEq(sOhmBalance, 0);
}

Expand All @@ -156,34 +174,31 @@ contract StakingTest is DSTest {
bool claim = true;

// Stake the ohm
ohm.approve(address(staking), AMOUNT);
uint256 amountStaked = staking.stake(address(this), AMOUNT, isSohm, claim);
assertEq(amountStaked, AMOUNT);
uint256 initialOhmBalance = ohm.balanceOf(address(this));
ohm.approve(address(staking), initialOhmBalance);
uint256 amountStaked = staking.stake(address(this), initialOhmBalance, isSohm, claim);
assertEq(amountStaked, initialOhmBalance);

// test the unstake
// Move into next rebase window
hevm.warp(EPOCH_LENGTH);

// Validate balances post stake
// Post initial rebase, distribution amount is 0, so sOHM balance doens't change.
uint256 ohmBalance = ohm.balanceOf(address(this));
uint256 sOhmBalance = sohm.balanceOf(address(this));
assertEq(ohmBalance, 0);
assertEq(sOhmBalance, AMOUNT);
assertEq(sOhmBalance, initialOhmBalance);

// Unstake sOHM
sohm.approve(address(staking), sOhmBalance);
try staking.unstake(address(this), sOhmBalance, triggerRebase, isSohm) {
fail();
} catch Error(string memory error) {
assertEq(error, "Insufficient OHM balance in contract");
}

// TODO: wire up staking to treasury so it can mint OHM
// ohmBalance = ohm.balanceOf(address(this));
// sOhmBalance = sohm.balanceOf(address(this));
staking.unstake(address(this), sOhmBalance, triggerRebase, isSohm);

// uint256 expectedAmount = AMOUNT.add(BOUNTY);
// assertEq(ohmBalance, expectedAmount);
// assertEq(sOhmBalance, 0);
// Validate balances post unstake
ohmBalance = ohm.balanceOf(address(this));
sOhmBalance = sohm.balanceOf(address(this));
uint256 expectedAmount = initialOhmBalance.add(BOUNTY); // Rebase earns a bounty
assertEq(ohmBalance, expectedAmount);
assertEq(sOhmBalance, 0);
}

function testUnstakeAtRebaseFromGohm() public {
Expand All @@ -192,34 +207,31 @@ contract StakingTest is DSTest {
bool claim = true;

// Stake the ohm
ohm.approve(address(staking), AMOUNT);
uint256 amountStaked = staking.stake(address(this), AMOUNT, isSohm, claim);

uint256 gohmAmount = gohm.balanceTo(AMOUNT);
uint256 initialOhmBalance = ohm.balanceOf(address(this));
ohm.approve(address(staking), initialOhmBalance);
uint256 amountStaked = staking.stake(address(this), initialOhmBalance, isSohm, claim);
uint256 gohmAmount = gohm.balanceTo(initialOhmBalance);
assertEq(amountStaked, gohmAmount);

// test the unstake
// Move into next rebase window
hevm.warp(EPOCH_LENGTH);

// Validate balances post-stake
uint256 ohmBalance = ohm.balanceOf(address(this));
uint256 gohmBalance = gohm.balanceOf(address(this));
assertEq(ohmBalance, 0);
assertEq(gohmBalance, gohmAmount);

// Unstake gOHM
gohm.approve(address(staking), gohmBalance);
try staking.unstake(address(this), gohmBalance, triggerRebase, isSohm) {
fail();
} catch Error(string memory error) {
assertEq(error, "Insufficient OHM balance in contract");
}
staking.unstake(address(this), gohmBalance, triggerRebase, isSohm);

// TODO: wire up staking to treasury so it can mint OHM
// ohmBalance = ohm.balanceOf(address(this));
// gohmBalance = gohm.balanceOf(address(this));

// uint256 expectedOhm = AMOUNT.add(BOUNTY);
// assertEq(ohmBalance, expectedOhm);
// assertEq(gohmBalance, 0);
// Validate balances post unstake
ohmBalance = ohm.balanceOf(address(this));
gohmBalance = gohm.balanceOf(address(this));
uint256 expectedOhm = initialOhmBalance.add(BOUNTY); // Rebase earns a bounty
assertEq(ohmBalance, expectedOhm);
assertEq(gohmBalance, 0);
}
}

0 comments on commit f65a711

Please sign in to comment.