From ff606d9d3f6621a785e9d28033c954de40ea7069 Mon Sep 17 00:00:00 2001
From: Shahul Hameed <10547529+shahthepro@users.noreply.github.com>
Date: Thu, 26 Sep 2024 15:53:01 +0400
Subject: [PATCH] Trim down code
---
contracts/contracts/harvest/Dripper.sol | 20 +--
.../contracts/harvest/FixedRateDripper.sol | 136 ++++++------------
.../deploy/base/013_fixed_rate_dripper.js | 30 ++++
contracts/docs/DripperStorage.svg | 2 +-
contracts/docs/OETHDripperStorage.svg | 2 +-
5 files changed, 85 insertions(+), 105 deletions(-)
create mode 100644 contracts/deploy/base/013_fixed_rate_dripper.js
diff --git a/contracts/contracts/harvest/Dripper.sol b/contracts/contracts/harvest/Dripper.sol
index 02301a7e53..060cd27893 100644
--- a/contracts/contracts/harvest/Dripper.sol
+++ b/contracts/contracts/harvest/Dripper.sol
@@ -19,10 +19,10 @@ import { IVault } from "../interfaces/IVault.sol";
*
* Design notes
* - USDT has a smaller resolution than the number of seconds
- * in a week, which can make per block payouts have a rounding error. However
+ * in a week, which can make per second payouts have a rounding error. However
* the total effect is not large - cents per day, and this money is
* not lost, just distributed in the future. While we could use a higher
- * decimal precision for the drip perBlock, we chose simpler code.
+ * decimal precision for the drip perSecond, we chose simpler code.
* - By calculating the changing drip rates on collects only, harvests and yield
* events don't have to call anything on this contract or pay any extra gas.
* Collect() is already be paying for a single write, since it has to reset
@@ -49,7 +49,7 @@ contract Dripper is Governable {
struct Drip {
uint64 lastCollect; // overflows 262 billion years after the sun dies
- uint192 perBlock; // drip rate per block
+ uint192 perSecond; // drip rate per second
}
address immutable vault; // OUSD vault
@@ -86,7 +86,11 @@ contract Dripper is Governable {
/// @dev Change the drip duration. Governor only.
/// @param _durationSeconds the number of seconds to drip out the entire
/// balance over if no collects were called during that time.
- function setDripDuration(uint256 _durationSeconds) external onlyGovernor {
+ function setDripDuration(uint256 _durationSeconds)
+ external
+ virtual
+ onlyGovernor
+ {
require(_durationSeconds > 0, "duration must be non-zero");
dripDuration = _durationSeconds;
_collect(); // duration change take immediate effect
@@ -113,21 +117,21 @@ contract Dripper is Governable {
returns (uint256)
{
uint256 elapsed = block.timestamp - _drip.lastCollect;
- uint256 allowed = (elapsed * _drip.perBlock);
+ uint256 allowed = (elapsed * _drip.perSecond);
return (allowed > _balance) ? _balance : allowed;
}
/// @dev Sends the currently dripped funds to be vault, and sets
/// the new drip rate based on the new balance.
- function _collect() internal {
+ function _collect() internal virtual {
// Calculate send
uint256 balance = IERC20(token).balanceOf(address(this));
uint256 amountToSend = _availableFunds(balance, drip);
uint256 remaining = balance - amountToSend;
- // Calculate new drip perBlock
+ // Calculate new drip perSecond
// Gas savings by setting entire struct at one time
drip = Drip({
- perBlock: uint192(remaining / dripDuration),
+ perSecond: uint192(remaining / dripDuration),
lastCollect: uint64(block.timestamp)
});
// Send funds
diff --git a/contracts/contracts/harvest/FixedRateDripper.sol b/contracts/contracts/harvest/FixedRateDripper.sol
index 7178c3ae49..06636e9c29 100644
--- a/contracts/contracts/harvest/FixedRateDripper.sol
+++ b/contracts/contracts/harvest/FixedRateDripper.sol
@@ -3,121 +3,67 @@ pragma solidity ^0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
-import { Governable } from "../governance/Governable.sol";
import { IVault } from "../interfaces/IVault.sol";
+import { Dripper } from "./Dripper.sol";
/**
- * @title OUSD Dripper
+ * @title Fixed Rate Dripper
*
- * The dripper contract smooths out the yield from point-in-time yield events
- * and spreads the yield out over a configurable time period. This ensures a
- * continuous per block yield to makes users happy as their next rebase
- * amount is always moving up. Also, this makes historical day to day yields
- * smooth, rather than going from a near zero day, to a large APY day, then
- * back to a near zero day again.
- *
- *
- * Design notes
- * - USDT has a smaller resolution than the number of seconds
- * in a week, which can make per block payouts have a rounding error. However
- * the total effect is not large - cents per day, and this money is
- * not lost, just distributed in the future. While we could use a higher
- * decimal precision for the drip perBlock, we chose simpler code.
- * - By calculating the changing drip rates on collects only, harvests and yield
- * events don't have to call anything on this contract or pay any extra gas.
- * Collect() is already be paying for a single write, since it has to reset
- * the lastCollect time.
- * - By having a collectAndRebase method, and having our external systems call
- * that, the OUSD vault does not need any changes, not even to know the address
- * of the dripper.
- * - A rejected design was to retro-calculate the drip rate on each collect,
- * based on the balance at the time of the collect. While this would have
- * required less state, and would also have made the contract respond more quickly
- * to new income, it would break the predictability that is this contract's entire
- * purpose. If we did this, the amount of fundsAvailable() would make sharp increases
- * when funds were deposited.
- * - When the dripper recalculates the rate, it targets spending the balance over
- * the duration. This means that every time that collect is called, if no
- * new funds have been deposited the duration is being pushed back and the
- * rate decreases. This is expected, and ends up following a smoother but
- * longer curve the more collect() is called without incoming yield.
+ * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.
+ * However the Strategist decides the rate and it doesn't change after
+ * a drip.
*
*/
-contract FixedRateDripper is Governable {
+contract FixedRateDripper is Dripper {
using SafeERC20 for IERC20;
- address immutable vault; // OUSD vault
- address immutable token; // token to drip out
- uint256 public dripRate; // WETH per second
- uint256 public lastCollect; // Last collect timestamp
+ event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);
- constructor(address _vault, address _token) {
- vault = _vault;
- token = _token;
+ /**
+ * @dev Verifies that the caller is the Governor or Strategist.
+ */
+ modifier onlyGovernorOrStrategist() {
+ require(
+ isGovernor() || msg.sender == IVault(vault).strategistAddr(),
+ "Caller is not the Strategist or Governor"
+ );
+ _;
}
- /// @notice How much funds have dripped out already and are currently
- // available to be sent to the vault.
- /// @return The amount that would be sent if a collect was called
- function availableFunds() external view returns (uint256) {
- uint256 balance = IERC20(token).balanceOf(address(this));
- return _availableFunds(balance);
- }
+ constructor(address _vault, address _token) Dripper(_vault, _token) {}
- /// @notice Collect all dripped funds and send to vault.
- /// Recalculate new drip rate.
- function collect() external {
- _collect();
+ /// @inheritdoc Dripper
+ function setDripDuration(uint256) external virtual override {
+ // Not used in FixedRateDripper
+ revert("Drip duration disabled");
}
- /// @notice Collect all dripped funds, send to vault, recalculate new drip
- /// rate, and rebase OUSD.
- function collectAndRebase() external {
- _collect();
- IVault(vault).rebase();
- }
+ /// @inheritdoc Dripper
+ function _collect() internal virtual override {
+ // Calculate amount to send
+ uint256 balance = IERC20(token).balanceOf(address(this));
+ uint256 amountToSend = _availableFunds(balance, drip);
- /// @dev Change the drip rate. Governor only.
- /// @param _rate Amount of token to drip per second
- /// balance over if no collects were called during that time.
- function setDripRate(uint256 _rate) external onlyGovernor {
- require(_rate > 0, "rate must be non-zero");
- // Collect at current rate
- _collect();
- // Update the rate
- dripRate = _rate;
- }
+ // Update timestamp
+ drip.lastCollect = uint64(block.timestamp);
- /// @dev Transfer out ERC20 tokens held by the contract. Governor only.
- /// @param _asset ERC20 token address
- /// @param _amount amount to transfer
- function transferToken(address _asset, uint256 _amount)
- external
- onlyGovernor
- {
- IERC20(_asset).safeTransfer(governor(), _amount);
+ // Send funds
+ IERC20(token).safeTransfer(vault, amountToSend);
}
- /// @dev Calculate available funds by taking the lower of either the
- /// currently dripped out funds or the balance available.
- /// Uses passed in parameters to calculate with for gas savings.
- /// @param _balance current balance in contract
- function _availableFunds(uint256 _balance) internal view returns (uint256) {
- uint256 elapsed = block.timestamp - lastCollect;
- uint256 allowed = (elapsed * dripRate);
- return (allowed > _balance) ? _balance : allowed;
- }
+ /**
+ * @dev Sets the drip rate. Callable by Strategist or Governor.
+ * Can be set to zero to stop dripper.
+ * @param _perSecond Rate of WETH to drip per second
+ */
+ function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {
+ emit DripRateUpdated(_perSecond, drip.perSecond);
- /// @dev Sends the currently dripped funds to be vault, and sets
- /// the new drip rate based on the new balance.
- function _collect() internal {
- // Calculate send
- uint256 balance = IERC20(token).balanceOf(address(this));
- uint256 amountToSend = _availableFunds(balance);
- lastCollect = block.timestamp;
+ // Collect at existing rate
+ _collect();
- // Send funds
- IERC20(token).safeTransfer(vault, amountToSend);
+ // Update rate
+ drip.perSecond = _perSecond;
}
}
diff --git a/contracts/deploy/base/013_fixed_rate_dripper.js b/contracts/deploy/base/013_fixed_rate_dripper.js
new file mode 100644
index 0000000000..ca2e4aabc3
--- /dev/null
+++ b/contracts/deploy/base/013_fixed_rate_dripper.js
@@ -0,0 +1,30 @@
+const { deployOnBaseWithGuardian } = require("../../utils/deploy-l2");
+const { deployWithConfirmation } = require("../../utils/deploy");
+const addresses = require("../../utils/addresses");
+
+module.exports = deployOnBaseWithGuardian(
+ {
+ deployName: "013_fixed_rate_dripper",
+ },
+ async ({ ethers }) => {
+ const cOETHbDripperProxy = await ethers.getContract("OETHBaseDripperProxy");
+ const cOETHbVaultProxy = await ethers.getContract("OETHBaseVaultProxy");
+
+ // Deploy new implementation
+ const dOETHbDripper = await deployWithConfirmation("FixedRateDripper", [
+ cOETHbVaultProxy.address,
+ addresses.base.WETH,
+ ]);
+
+ return {
+ actions: [
+ {
+ // 1. Upgrade Dripper
+ contract: cOETHbDripperProxy,
+ signature: "upgradeTo(address)",
+ args: [dOETHbDripper.address],
+ },
+ ],
+ };
+ }
+);
diff --git a/contracts/docs/DripperStorage.svg b/contracts/docs/DripperStorage.svg
index 9ee1fef514..de6ac6b239 100644
--- a/contracts/docs/DripperStorage.svg
+++ b/contracts/docs/DripperStorage.svg
@@ -39,7 +39,7 @@
type: variable (bytes)
-uint192: perBlock (24)
+uint192: perSecond (24)
uint64: lastCollect (8)
diff --git a/contracts/docs/OETHDripperStorage.svg b/contracts/docs/OETHDripperStorage.svg
index a6b80c2e8b..e2da1f7190 100644
--- a/contracts/docs/OETHDripperStorage.svg
+++ b/contracts/docs/OETHDripperStorage.svg
@@ -39,7 +39,7 @@
type: variable (bytes)
-uint192: perBlock (24)
+uint192: perSecond (24)
uint64: lastCollect (8)