Skip to content

Commit dffc3de

Browse files
DanielVFFranck
andauthored
Governance Fee Collection (#535)
* 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>
1 parent 58a9bd8 commit dffc3de

8 files changed

Lines changed: 104 additions & 27 deletions

File tree

contracts/contracts/interfaces/IVault.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ interface IVault {
4949

5050
function maxSupplyDiff() external view returns (uint256);
5151

52+
function setTrusteeAddress(address _address) external;
53+
54+
function trusteeAddress() external view returns (address);
55+
56+
function setTrusteeFeeBps(uint256 _basis) external;
57+
58+
function trusteeFeeBps() external view returns (uint256);
59+
5260
function supportAsset(address _asset) external;
5361

5462
function approveStrategy(address _addr) external;
@@ -117,7 +125,7 @@ interface IVault {
117125
uint256[] calldata _amounts
118126
) external;
119127

120-
function rebase() external returns (uint256);
128+
function rebase() external;
121129

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

contracts/contracts/vault/VaultAdmin.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,25 @@ contract VaultAdmin is VaultStorage {
235235
emit MaxSupplyDiffChanged(_maxSupplyDiff);
236236
}
237237

238+
/**
239+
* @dev Sets the trusteeAddress that can receive a portion of yield.
240+
* Setting to the zero address disables this feature.
241+
*/
242+
function setTrusteeAddress(address _address) external onlyGovernor {
243+
trusteeAddress = _address;
244+
emit TrusteeAddressChanged(_address);
245+
}
246+
247+
/**
248+
* @dev Sets the TrusteeFeeBps to the percentage of yield that should be
249+
* received in basis points.
250+
*/
251+
function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {
252+
require(_basis <= 5000, "basis cannot exceed 50%");
253+
trusteeFeeBps = _basis;
254+
emit TrusteeFeeBpsChanged(_basis);
255+
}
256+
238257
/***************************************
239258
Pause
240259
****************************************/

contracts/contracts/vault/VaultCore.sol

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -357,33 +357,39 @@ contract VaultCore is VaultStorage {
357357
/**
358358
* @dev Calculate the total value of assets held by the Vault and all
359359
* strategies and update the supply of OUSD.
360-
* @return uint256 New total supply of OUSD
361360
*/
362-
function rebase()
363-
public
364-
whenNotRebasePaused
365-
nonReentrant
366-
returns (uint256 newTotalSupply)
367-
{
368-
return _rebase();
361+
function rebase() public whenNotRebasePaused nonReentrant {
362+
_rebase();
369363
}
370364

371365
/**
372366
* @dev Calculate the total value of assets held by the Vault and all
373-
* strategies and update the supply of OUSD.
374-
* @return uint256 New total supply of OUSD
367+
* strategies and update the supply of OUSD, optionaly sending a
368+
* portion of the yield to the trustee.
375369
*/
376-
function _rebase()
377-
internal
378-
whenNotRebasePaused
379-
returns (uint256 newTotalSupply)
380-
{
381-
if (oUSD.totalSupply() == 0) return 0;
382-
uint256 oldTotalSupply = oUSD.totalSupply();
383-
newTotalSupply = _totalValue();
384-
// Only rachet upwards
385-
if (newTotalSupply > oldTotalSupply) {
386-
oUSD.changeSupply(newTotalSupply);
370+
function _rebase() internal whenNotRebasePaused {
371+
uint256 ousdSupply = oUSD.totalSupply();
372+
if (ousdSupply == 0) {
373+
return;
374+
}
375+
uint256 vaultValue = _totalValue();
376+
377+
// Yield fee collection
378+
address _trusteeAddress = trusteeAddress; // gas savings
379+
if (_trusteeAddress != address(0) && (vaultValue > ousdSupply)) {
380+
uint256 yield = vaultValue.sub(ousdSupply);
381+
uint256 fee = yield.mul(trusteeFeeBps).div(10000);
382+
require(yield > fee, "Fee must not be greater than yield");
383+
if (fee > 0) {
384+
oUSD.mint(_trusteeAddress, fee);
385+
}
386+
emit YieldDistribution(_trusteeAddress, yield, fee);
387+
}
388+
389+
// Only rachet OUSD supply upwards
390+
ousdSupply = oUSD.totalSupply(); // Final check should use latest value
391+
if (vaultValue > ousdSupply) {
392+
oUSD.changeSupply(vaultValue);
387393
}
388394
}
389395

contracts/contracts/vault/VaultStorage.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ contract VaultStorage is Initializable, Governable {
4444
event UniswapUpdated(address _address);
4545
event StrategistUpdated(address _address);
4646
event MaxSupplyDiffChanged(uint256 maxSupplyDiff);
47+
event YieldDistribution(address _to, uint256 _yield, uint256 _fee);
48+
event TrusteeFeeBpsChanged(uint256 _basis);
49+
event TrusteeAddressChanged(address _address);
4750

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

9598
uint256 public maxSupplyDiff;
9699

100+
// Trustee address that can collect a percentage of yield
101+
address public trusteeAddress;
102+
103+
// Amount of yield collected in basis points
104+
uint256 public trusteeFeeBps;
105+
97106
/**
98107
* @dev set the implementation for the admin, this needs to be in a base class else we cannot set it
99108
* @param newImpl address of the implementation

contracts/slither.db.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

contracts/test/helpers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const addresses = require("../utils/addresses");
88

99
chai.Assertion.addMethod("approxEqual", function (expected, message) {
1010
const actual = this._obj;
11-
chai.expect(actual, message).gte(expected.mul("9999").div("10000"));
12-
chai.expect(actual, message).lte(expected.mul("10001").div("10000"));
11+
chai.expect(actual, message).gte(expected.mul("99999").div("100000"));
12+
chai.expect(actual, message).lte(expected.mul("100001").div("100000"));
1313
});
1414

1515
chai.Assertion.addMethod("approxBalanceOf", async function (

contracts/test/vault/rebase.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
tusdUnits,
1010
getOracleAddress,
1111
setOracleTokenPriceUsd,
12+
expectApproxSupply,
1213
loadFixture,
1314
} = require("../helpers");
1415

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

177-
// All assets sould still remain in Vault
178+
// All assets should still remain in Vault
178179

179180
// Note defaultFixture sets up with 200 DAI already in the Strategy
180181
// 200 + 100 = 300
@@ -217,3 +218,37 @@ describe("Vault rebasing", async () => {
217218
await expect(await vault.priceProvider()).to.be.equal(oracle);
218219
});
219220
});
221+
222+
describe("Vault yield accrual to OGN", async () => {
223+
[
224+
{ yield: "1000", basis: 100, expectedFee: "10" },
225+
{ yield: "1000", basis: 5000, expectedFee: "500" },
226+
{ yield: "1523", basis: 900, expectedFee: "137.07" },
227+
{ yield: "0.000001", basis: 10, expectedFee: "0.00000001" },
228+
{ yield: "0", basis: 1000, expectedFee: "0" },
229+
].forEach((options) => {
230+
const { yield, basis, expectedFee } = options;
231+
it(`should collect on rebase a ${expectedFee} fee from ${yield} yield at ${basis}bp `, async function () {
232+
const fixture = await loadFixture(defaultFixture);
233+
const { matt, governor, ousd, usdt, vault, mockNonRebasing } = fixture;
234+
const trustee = mockNonRebasing;
235+
236+
// Setup trustee trustee on vault
237+
await vault.connect(governor).setTrusteeAddress(trustee.address);
238+
await vault.connect(governor).setTrusteeFeeBps(900);
239+
await expect(trustee).has.a.balanceOf("0", ousd);
240+
241+
// Create yield for the vault
242+
await usdt.connect(matt).mint(usdcUnits("1523"));
243+
await usdt.connect(matt).transfer(vault.address, usdcUnits("1523"));
244+
// Do rebase
245+
const supplyBefore = await ousd.totalSupply();
246+
await vault.rebase();
247+
// OUSD supply increases correctly
248+
await expectApproxSupply(ousd, supplyBefore.add(ousdUnits("1523")));
249+
// ogntrustee address increases correctly
250+
// 1523 * 0.09 = 137.07
251+
await expect(trustee).has.a.balanceOf("137.07", ousd);
252+
});
253+
});
254+
});

contracts/test/vault/redeem.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ describe("Vault Redeem", function () {
345345
// Manually call rebase because not triggered by mint
346346
await vault.rebase();
347347
// Rebase could have changed user balance
348-
// as there could have been yeild from different
348+
// as there could have been yield from different
349349
// oracle prices on redeems during a previous loop.
350350
let userBalance = await getUserOusdBalance(user);
351351
for (const amount of amounts) {

0 commit comments

Comments
 (0)