Skip to content

Commit

Permalink
Governance Fee Collection (#535)
Browse files Browse the repository at this point in the history
* Increase precision on approxEquals helper to just a single +1 or -1 of a 1e6 fixed point.

* Prettier on previous code.

* Collect a percentage of yield during rebasing.

* More tests.

* Spelling fix in old code.

* Rename to trustee. Thanks Micah!

* Fix safe math. Thanks Tom!

* Contract designed for the these values to initialize at zero.

* Prettier

* Rename vaultSupply to vaultValue.

* Remove old return comments.

* Rename basis to bps in new functions and variables.

* Add view functions to ivault for parameters

* Update variable name for slither

Co-authored-by: Franck <franck@originprotocol.com>
  • Loading branch information
DanielVF and Franck authored Feb 2, 2021
1 parent 58a9bd8 commit dffc3de
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 27 deletions.
10 changes: 9 additions & 1 deletion contracts/contracts/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ interface IVault {

function maxSupplyDiff() external view returns (uint256);

function setTrusteeAddress(address _address) external;

function trusteeAddress() external view returns (address);

function setTrusteeFeeBps(uint256 _basis) external;

function trusteeFeeBps() external view returns (uint256);

function supportAsset(address _asset) external;

function approveStrategy(address _addr) external;
Expand Down Expand Up @@ -117,7 +125,7 @@ interface IVault {
uint256[] calldata _amounts
) external;

function rebase() external returns (uint256);
function rebase() external;

function totalValue() external view returns (uint256 value);

Expand Down
19 changes: 19 additions & 0 deletions contracts/contracts/vault/VaultAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ contract VaultAdmin is VaultStorage {
emit MaxSupplyDiffChanged(_maxSupplyDiff);
}

/**
* @dev Sets the trusteeAddress that can receive a portion of yield.
* Setting to the zero address disables this feature.
*/
function setTrusteeAddress(address _address) external onlyGovernor {
trusteeAddress = _address;
emit TrusteeAddressChanged(_address);
}

/**
* @dev Sets the TrusteeFeeBps to the percentage of yield that should be
* received in basis points.
*/
function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {
require(_basis <= 5000, "basis cannot exceed 50%");
trusteeFeeBps = _basis;
emit TrusteeFeeBpsChanged(_basis);
}

/***************************************
Pause
****************************************/
Expand Down
48 changes: 27 additions & 21 deletions contracts/contracts/vault/VaultCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -357,33 +357,39 @@ contract VaultCore is VaultStorage {
/**
* @dev Calculate the total value of assets held by the Vault and all
* strategies and update the supply of OUSD.
* @return uint256 New total supply of OUSD
*/
function rebase()
public
whenNotRebasePaused
nonReentrant
returns (uint256 newTotalSupply)
{
return _rebase();
function rebase() public whenNotRebasePaused nonReentrant {
_rebase();
}

/**
* @dev Calculate the total value of assets held by the Vault and all
* strategies and update the supply of OUSD.
* @return uint256 New total supply of OUSD
* strategies and update the supply of OUSD, optionaly sending a
* portion of the yield to the trustee.
*/
function _rebase()
internal
whenNotRebasePaused
returns (uint256 newTotalSupply)
{
if (oUSD.totalSupply() == 0) return 0;
uint256 oldTotalSupply = oUSD.totalSupply();
newTotalSupply = _totalValue();
// Only rachet upwards
if (newTotalSupply > oldTotalSupply) {
oUSD.changeSupply(newTotalSupply);
function _rebase() internal whenNotRebasePaused {
uint256 ousdSupply = oUSD.totalSupply();
if (ousdSupply == 0) {
return;
}
uint256 vaultValue = _totalValue();

// Yield fee collection
address _trusteeAddress = trusteeAddress; // gas savings
if (_trusteeAddress != address(0) && (vaultValue > ousdSupply)) {
uint256 yield = vaultValue.sub(ousdSupply);
uint256 fee = yield.mul(trusteeFeeBps).div(10000);
require(yield > fee, "Fee must not be greater than yield");
if (fee > 0) {
oUSD.mint(_trusteeAddress, fee);
}
emit YieldDistribution(_trusteeAddress, yield, fee);
}

// Only rachet OUSD supply upwards
ousdSupply = oUSD.totalSupply(); // Final check should use latest value
if (vaultValue > ousdSupply) {
oUSD.changeSupply(vaultValue);
}
}

Expand Down
9 changes: 9 additions & 0 deletions contracts/contracts/vault/VaultStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ contract VaultStorage is Initializable, Governable {
event UniswapUpdated(address _address);
event StrategistUpdated(address _address);
event MaxSupplyDiffChanged(uint256 maxSupplyDiff);
event YieldDistribution(address _to, uint256 _yield, uint256 _fee);
event TrusteeFeeBpsChanged(uint256 _basis);
event TrusteeAddressChanged(address _address);

// Assets supported by the Vault, i.e. Stablecoins
struct Asset {
Expand Down Expand Up @@ -94,6 +97,12 @@ contract VaultStorage is Initializable, Governable {

uint256 public maxSupplyDiff;

// Trustee address that can collect a percentage of yield
address public trusteeAddress;

// Amount of yield collected in basis points
uint256 public trusteeFeeBps;

/**
* @dev set the implementation for the admin, this needs to be in a base class else we cannot set it
* @param newImpl address of the implementation
Expand Down
2 changes: 1 addition & 1 deletion contracts/slither.db.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions contracts/test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const addresses = require("../utils/addresses");

chai.Assertion.addMethod("approxEqual", function (expected, message) {
const actual = this._obj;
chai.expect(actual, message).gte(expected.mul("9999").div("10000"));
chai.expect(actual, message).lte(expected.mul("10001").div("10000"));
chai.expect(actual, message).gte(expected.mul("99999").div("100000"));
chai.expect(actual, message).lte(expected.mul("100001").div("100000"));
});

chai.Assertion.addMethod("approxBalanceOf", async function (
Expand Down
37 changes: 36 additions & 1 deletion contracts/test/vault/rebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
tusdUnits,
getOracleAddress,
setOracleTokenPriceUsd,
expectApproxSupply,
loadFixture,
} = require("../helpers");

Expand Down Expand Up @@ -174,7 +175,7 @@ describe("Vault rebasing", async () => {
await expect(await vault.getStrategyCount()).to.equal(0);
await vault.connect(governor).allocate();

// All assets sould still remain in Vault
// All assets should still remain in Vault

// Note defaultFixture sets up with 200 DAI already in the Strategy
// 200 + 100 = 300
Expand Down Expand Up @@ -217,3 +218,37 @@ describe("Vault rebasing", async () => {
await expect(await vault.priceProvider()).to.be.equal(oracle);
});
});

describe("Vault yield accrual to OGN", async () => {
[
{ yield: "1000", basis: 100, expectedFee: "10" },
{ yield: "1000", basis: 5000, expectedFee: "500" },
{ yield: "1523", basis: 900, expectedFee: "137.07" },
{ yield: "0.000001", basis: 10, expectedFee: "0.00000001" },
{ yield: "0", basis: 1000, expectedFee: "0" },
].forEach((options) => {
const { yield, basis, expectedFee } = options;
it(`should collect on rebase a ${expectedFee} fee from ${yield} yield at ${basis}bp `, async function () {
const fixture = await loadFixture(defaultFixture);
const { matt, governor, ousd, usdt, vault, mockNonRebasing } = fixture;
const trustee = mockNonRebasing;

// Setup trustee trustee on vault
await vault.connect(governor).setTrusteeAddress(trustee.address);
await vault.connect(governor).setTrusteeFeeBps(900);
await expect(trustee).has.a.balanceOf("0", ousd);

// Create yield for the vault
await usdt.connect(matt).mint(usdcUnits("1523"));
await usdt.connect(matt).transfer(vault.address, usdcUnits("1523"));
// Do rebase
const supplyBefore = await ousd.totalSupply();
await vault.rebase();
// OUSD supply increases correctly
await expectApproxSupply(ousd, supplyBefore.add(ousdUnits("1523")));
// ogntrustee address increases correctly
// 1523 * 0.09 = 137.07
await expect(trustee).has.a.balanceOf("137.07", ousd);
});
});
});
2 changes: 1 addition & 1 deletion contracts/test/vault/redeem.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ describe("Vault Redeem", function () {
// Manually call rebase because not triggered by mint
await vault.rebase();
// Rebase could have changed user balance
// as there could have been yeild from different
// as there could have been yield from different
// oracle prices on redeems during a previous loop.
let userBalance = await getUserOusdBalance(user);
for (const amount of amounts) {
Expand Down

0 comments on commit dffc3de

Please sign in to comment.