Skip to content

Commit

Permalink
Merge pull request #10 from euler-xyz/feat/reward_streams-integration
Browse files Browse the repository at this point in the history
[WIP]Feat: reward streams integration
  • Loading branch information
haythemsellami authored May 27, 2024
2 parents de89aa8 + 7b8f703 commit db94cb9
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
path = lib/euler-vault-kit
url = https://github.com/euler-xyz/euler-vault-kit

[submodule "lib/reward-streams"]
path = lib/reward-streams
url = https://github.com/euler-xyz/reward-streams
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ libs = ["lib"]
test = 'test'
optimizer = true
optimizer_runs = 20_000
solc = "0.8.23"
# solc = "0.8.0"
gas_reports = ["*"]
fs_permissions = [{ access = "read", path = "./"}]

Expand Down
1 change: 1 addition & 0 deletions lib/reward-streams
Submodule reward-streams added at 66aafc
5 changes: 3 additions & 2 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ ds-test/=lib/ethereum-vault-connector/lib/forge-std/lib/ds-test/src/
erc4626-tests/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/lib/erc4626-tests/
ethereum-vault-connector/=lib/ethereum-vault-connector/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/
openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/
evk/=lib/euler-vault-kit/
reward-streams=lib/reward-streams/src
openzeppelin-contracts/=lib/reward-streams/lib/openzeppelin-contracts/contracts
@openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/
68 changes: 68 additions & 0 deletions src/BalanceForwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IBalanceForwarder} from "./interface/IBalanceForwarder.sol";
import {IBalanceTracker} from "./interface/IBalanceTracker.sol";

/// @title BalanceForwarderModule
/// @custom:security-contact [email protected]
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams
abstract contract BalanceForwarder is IBalanceForwarder {
error NotSupported();

IBalanceTracker public immutable balanceTracker;

mapping(address => bool) internal isBalanceForwarderEnabled;

event EnableBalanceForwarder(address indexed _user);
event DisableBalanceForwarder(address indexed _user);

constructor(address _balanceTracker) {
balanceTracker = IBalanceTracker(_balanceTracker);
}

/// @notice Enables balance forwarding for the authenticated account
/// @dev Only the authenticated account can enable balance forwarding for itself
/// @dev Should call the IBalanceTracker hook with the current account's balance
function enableBalanceForwarder() external virtual;

/// @notice Disables balance forwarding for the authenticated account
/// @dev Only the authenticated account can disable balance forwarding for itself
/// @dev Should call the IBalanceTracker hook with the account's balance of 0
function disableBalanceForwarder() external virtual;

/// @notice Retrieve the address of rewards contract, tracking changes in account's balances
/// @return The balance tracker address
function balanceTrackerAddress() external view returns (address) {
return address(balanceTracker);
}

/// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract
/// @param _account Address to query
/// @return True if balance forwarder is enabled
function balanceForwarderEnabled(address _account) external view returns (bool) {
return isBalanceForwarderEnabled[_account];
}

function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal {
if (address(balanceTracker) == address(0)) revert NotSupported();

isBalanceForwarderEnabled[_sender] = true;
IBalanceTracker(balanceTracker).balanceTrackerHook(_sender, _senderBalance, false);

emit EnableBalanceForwarder(_sender);
}

/// @notice Disables balance forwarding for the authenticated account
/// @dev Only the authenticated account can disable balance forwarding for itself
/// @dev Should call the IBalanceTracker hook with the account's balance of 0
function _disableBalanceForwarder(address _sender) internal {
if (address(balanceTracker) == address(0)) revert NotSupported();

isBalanceForwarderEnabled[_sender] = false;
IBalanceTracker(balanceTracker).balanceTrackerHook(_sender, 0, false);

emit DisableBalanceForwarder(_sender);
}
}
46 changes: 39 additions & 7 deletions src/FourSixTwoSixAgg.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Context} from "openzeppelin-contracts/utils/Context.sol";
import {ERC20, IERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC4626, IERC4626} from "openzeppelin-contracts/token/ERC20/extensions/ERC4626.sol";
import {Context} from "@openzeppelin/utils/Context.sol";
import {ERC20, IERC20} from "@openzeppelin/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {ERC4626, IERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol";
import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol";
import {EVCUtil, IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol";
import {AccessControlEnumerable} from "openzeppelin-contracts/access/AccessControlEnumerable.sol";
import {BalanceForwarder} from "./BalanceForwarder.sol";

// @note Do NOT use with fee on transfer tokens
// @note Do NOT use with rebasing tokens
// @note Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol
// @note expired by Yearn v3 ❤️
// TODO addons for reward stream support
// TODO custom withdraw queue support
contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable {
contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEnumerable {
using SafeERC20 for IERC20;

error Reentrancy();
Expand Down Expand Up @@ -106,13 +107,14 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable {
/// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points
constructor(
IEVC _evc,
address _balanceTracker,
address _asset,
string memory _name,
string memory _symbol,
uint256 _initialCashAllocationPoints,
address[] memory _initialStrategies,
uint256[] memory _initialStrategiesAllocationPoints
) EVCUtil(address(_evc)) ERC4626(IERC20(_asset)) ERC20(_name, _symbol) {
) BalanceForwarder(_balanceTracker) EVCUtil(address(_evc)) ERC4626(IERC20(_asset)) ERC20(_name, _symbol) {
esrSlot.locked = REENTRANCYLOCK__UNLOCKED;

if (_initialStrategies.length != _initialStrategiesAllocationPoints.length) revert ArrayLengthMismatch();
Expand All @@ -139,6 +141,21 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable {
_setRoleAdmin(STRATEGY_REMOVER_ROLE, STRATEGY_REMOVER_ROLE_ADMIN_ROLE);
}

/// @notice Enables balance forwarding for sender
/// @dev Should call the IBalanceTracker hook with the current user's balance
function enableBalanceForwarder() external override nonReentrant {
address user = _msgSender();
uint256 userBalance = this.balanceOf(user);

_enableBalanceForwarder(user, userBalance);
}

/// @notice Disables balance forwarding for the sender
/// @dev Should call the IBalanceTracker hook with the account's balance of 0
function disableBalanceForwarder() external override nonReentrant {
_disableBalanceForwarder(_msgSender());
}

/// @notice Rebalance strategy allocation.
/// @dev This function will first harvest yield, gulps and update interest.
/// @dev If current allocation is greater than target allocation, the aggregator will withdraw the excess assets.
Expand Down Expand Up @@ -391,6 +408,7 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable {
/// @dev See {IERC4626-_deposit}.
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override {
totalAssetsDeposited += assets;

super._deposit(caller, receiver, assets, shares);
}

Expand Down Expand Up @@ -537,6 +555,20 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable {
}
}

/// @dev Override _afterTokenTransfer hook to call IBalanceTracker.balanceTrackerHook()
/// @dev Calling .balanceTrackerHook() passing the address total balance
/// @param from Address sending the amount
/// @param to Address receiving the amount
function _afterTokenTransfer(address from, address to, uint256 /*amount*/ ) internal override {
if ((from != address(0)) && (isBalanceForwarderEnabled[from])) {
balanceTracker.balanceTrackerHook(from, super.balanceOf(from), false);
}

if ((to != address(0)) && (isBalanceForwarderEnabled[to])) {
balanceTracker.balanceTrackerHook(to, super.balanceOf(to), false);
}
}

/// @dev Get accrued interest without updating it.
/// @param esrSlotCache Cached esrSlot
/// @return uint256 accrued interest
Expand Down
12 changes: 12 additions & 0 deletions src/interface/IBalanceForwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IBalanceForwarder {
function balanceTrackerAddress() external view returns (address);

function balanceForwarderEnabled(address account) external view returns (bool);

function enableBalanceForwarder() external;

function disableBalanceForwarder() external;
}
19 changes: 19 additions & 0 deletions src/interface/IBalanceTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.0;

/// @title IBalanceTracker
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Provides an interface for tracking the balance of accounts.
interface IBalanceTracker {
/// @notice Executes the balance tracking hook for an account.
/// @dev This function is called by the Balance Forwarder contract which was enabled for the account. This function
/// must be called with the current balance of the account when enabling the balance forwarding for it. This
/// function must be called with 0 balance of the account when disabling the balance forwarding for it. This
/// function allows to be called on zero balance transfers, when the newAccountBalance is the same as the previous
/// one. To prevent DOS attacks, forfeitRecentReward should be used appropriately.
/// @param account The account address to execute the hook for.
/// @param newAccountBalance The new balance of the account.
/// @param forfeitRecentReward Whether to forfeit the most recent reward and not update the accumulator.
function balanceTrackerHook(address account, uint256 newAccountBalance, bool forfeitRecentReward) external;
}
1 change: 1 addition & 0 deletions test/common/FourSixTwoSixAggBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase {
vm.startPrank(deployer);
fourSixTwoSixAgg = new FourSixTwoSixAgg(
evc,
address(0),
address(assetTST),
"assetTST_Agg",
"assetTST_Agg",
Expand Down
Loading

0 comments on commit db94cb9

Please sign in to comment.