From b58cf7624b7b7160157d08eb5a068e9083e80a95 Mon Sep 17 00:00:00 2001 From: antisaa Date: Thu, 18 Jul 2024 19:42:40 +0200 Subject: [PATCH] remove flywheel-v2 library, add missing contracts and fix tests --- .../strategies/flywheel/FlywheelCore.sol | 272 ++++++++++++++++++ .../strategies/flywheel/IFlywheelBooster.sol | 34 +++ .../strategies/flywheel/IonicFlywheelCore.sol | 4 +- .../flywheel/rewards/BaseFlywheelRewards.sol | 37 +++ .../rewards/FlywheelDynamicRewards.sol | 69 +++++ .../rewards/FlywheelStaticRewards.sol | 65 +++++ .../flywheel/rewards/IFlywheelRewards.sol | 40 +++ .../rewards/IonicFlywheelDynamicRewards.sol | 6 +- contracts/test/ComptrollerTest.t.sol | 4 +- contracts/test/DeployMarkets.t.sol | 6 +- contracts/test/DevTesting.t.sol | 25 ++ contracts/test/FLRTest.t.sol | 6 +- contracts/test/LiquidityMining.t.sol | 8 +- lib/flywheel-v2 | 1 - remappings.txt | 3 +- 15 files changed, 560 insertions(+), 20 deletions(-) create mode 100644 contracts/ionic/strategies/flywheel/FlywheelCore.sol create mode 100644 contracts/ionic/strategies/flywheel/IFlywheelBooster.sol create mode 100644 contracts/ionic/strategies/flywheel/rewards/BaseFlywheelRewards.sol create mode 100644 contracts/ionic/strategies/flywheel/rewards/FlywheelDynamicRewards.sol create mode 100644 contracts/ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol create mode 100644 contracts/ionic/strategies/flywheel/rewards/IFlywheelRewards.sol delete mode 160000 lib/flywheel-v2 diff --git a/contracts/ionic/strategies/flywheel/FlywheelCore.sol b/contracts/ionic/strategies/flywheel/FlywheelCore.sol new file mode 100644 index 00000000..fc87f08a --- /dev/null +++ b/contracts/ionic/strategies/flywheel/FlywheelCore.sol @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {Auth, Authority} from "solmate/auth/Auth.sol"; +import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; +import {SafeCastLib} from "solmate/utils/SafeCastLib.sol"; + +import {IFlywheelRewards} from "./rewards/IFlywheelRewards.sol"; +import {IFlywheelBooster} from "./IFlywheelBooster.sol"; + +/** + @title Flywheel Core Incentives Manager + @notice Flywheel is a general framework for managing token incentives. + It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies. + + The Core contract maintaings three important pieces of state: + * the rewards index which determines how many rewards are owed per token per strategy. User indexes track how far behind the strategy they are to lazily calculate all catch-up rewards. + * the accrued (unclaimed) rewards per user. + * references to the booster and rewards module described below. + + Core does not manage any tokens directly. The rewards module maintains token balances, and approves core to pull transfer them to users when they claim. + + SECURITY NOTE: For maximum accuracy and to avoid exploits, rewards accrual should be notified atomically through the accrue hook. + Accrue should be called any time tokens are transferred, minted, or burned. + */ +contract FlywheelCore is Auth { + using SafeTransferLib for ERC20; + using SafeCastLib for uint256; + + /// @notice The token to reward + ERC20 public immutable rewardToken; + + /// @notice append-only list of strategies added + ERC20[] public allStrategies; + + /// @notice the rewards contract for managing streams + IFlywheelRewards public flywheelRewards; + + /// @notice optional booster module for calculating virtual balances on strategies + IFlywheelBooster public flywheelBooster; + + constructor( + ERC20 _rewardToken, + IFlywheelRewards _flywheelRewards, + IFlywheelBooster _flywheelBooster, + address _owner, + Authority _authority + ) Auth(_owner, _authority) { + rewardToken = _rewardToken; + flywheelRewards = _flywheelRewards; + flywheelBooster = _flywheelBooster; + } + + /*/////////////////////////////////////////////////////////////// + ACCRUE/CLAIM LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + @notice Emitted when a user's rewards accrue to a given strategy. + @param strategy the updated rewards strategy + @param user the user of the rewards + @param rewardsDelta how many new rewards accrued to the user + @param rewardsIndex the market index for rewards per token accrued + */ + event AccrueRewards(ERC20 indexed strategy, address indexed user, uint256 rewardsDelta, uint256 rewardsIndex); + + /** + @notice Emitted when a user claims accrued rewards. + @param user the user of the rewards + @param amount the amount of rewards claimed + */ + event ClaimRewards(address indexed user, uint256 amount); + + /// @notice The accrued but not yet transferred rewards for each user + mapping(address => uint256) public rewardsAccrued; + + /** + @notice accrue rewards for a single user on a strategy + @param strategy the strategy to accrue a user's rewards on + @param user the user to be accrued + @return the cumulative amount of rewards accrued to user (including prior) + */ + function accrue(ERC20 strategy, address user) public returns (uint256) { + RewardsState memory state = strategyState[strategy]; + + if (state.index == 0) return 0; + + state = accrueStrategy(strategy, state); + return accrueUser(strategy, user, state); + } + + /** + @notice accrue rewards for a two users on a strategy + @param strategy the strategy to accrue a user's rewards on + @param user the first user to be accrued + @param user the second user to be accrued + @return the cumulative amount of rewards accrued to the first user (including prior) + @return the cumulative amount of rewards accrued to the second user (including prior) + */ + function accrue( + ERC20 strategy, + address user, + address secondUser + ) public returns (uint256, uint256) { + RewardsState memory state = strategyState[strategy]; + + if (state.index == 0) return (0, 0); + + state = accrueStrategy(strategy, state); + return (accrueUser(strategy, user, state), accrueUser(strategy, secondUser, state)); + } + + /** + @notice claim rewards for a given user + @param user the user claiming rewards + @dev this function is public, and all rewards transfer to the user + */ + function claimRewards(address user) external { + uint256 accrued = rewardsAccrued[user]; + + if (accrued != 0) { + rewardsAccrued[user] = 0; + + rewardToken.safeTransferFrom(address(flywheelRewards), user, accrued); + + emit ClaimRewards(user, accrued); + } + } + + /*/////////////////////////////////////////////////////////////// + ADMIN LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + @notice Emitted when a new strategy is added to flywheel by the admin + @param newStrategy the new added strategy + */ + event AddStrategy(address indexed newStrategy); + + /// @notice initialize a new strategy + function addStrategyForRewards(ERC20 strategy) external requiresAuth { + _addStrategyForRewards(strategy); + } + + function _addStrategyForRewards(ERC20 strategy) internal { + require(strategyState[strategy].index == 0, "strategy"); + strategyState[strategy] = RewardsState({index: ONE, lastUpdatedTimestamp: block.timestamp.safeCastTo32()}); + + allStrategies.push(strategy); + emit AddStrategy(address(strategy)); + } + + function getAllStrategies() external view returns (ERC20[] memory) { + return allStrategies; + } + + /** + @notice Emitted when the rewards module changes + @param newFlywheelRewards the new rewards module + */ + event FlywheelRewardsUpdate(address indexed newFlywheelRewards); + + /// @notice swap out the flywheel rewards contract + function setFlywheelRewards(IFlywheelRewards newFlywheelRewards) external requiresAuth { + uint256 oldRewardBalance = rewardToken.balanceOf(address(flywheelRewards)); + if (oldRewardBalance > 0) { + rewardToken.safeTransferFrom(address(flywheelRewards), address(newFlywheelRewards), oldRewardBalance); + } + + flywheelRewards = newFlywheelRewards; + + emit FlywheelRewardsUpdate(address(newFlywheelRewards)); + } + + /** + @notice Emitted when the booster module changes + @param newBooster the new booster module + */ + event FlywheelBoosterUpdate(address indexed newBooster); + + /// @notice swap out the flywheel booster contract + function setBooster(IFlywheelBooster newBooster) external requiresAuth { + flywheelBooster = newBooster; + + emit FlywheelBoosterUpdate(address(newBooster)); + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL ACCOUNTING LOGIC + //////////////////////////////////////////////////////////////*/ + + struct RewardsState { + /// @notice The strategy's last updated index + uint224 index; + /// @notice The timestamp the index was last updated at + uint32 lastUpdatedTimestamp; + } + + /// @notice the fixed point factor of flywheel + uint224 public constant ONE = 1e18; + + /// @notice The strategy index and last updated per strategy + mapping(ERC20 => RewardsState) public strategyState; + + /// @notice user index per strategy + mapping(ERC20 => mapping(address => uint224)) public userIndex; + + /// @notice accumulate global rewards on a strategy + function accrueStrategy(ERC20 strategy, RewardsState memory state) + private + returns (RewardsState memory rewardsState) + { + // calculate accrued rewards through module + uint256 strategyRewardsAccrued = flywheelRewards.getAccruedRewards(strategy, state.lastUpdatedTimestamp); + + rewardsState = state; + if (strategyRewardsAccrued > 0) { + // use the booster or token supply to calculate reward index denominator + uint256 supplyTokens = address(flywheelBooster) != address(0) + ? flywheelBooster.boostedTotalSupply(strategy) + : strategy.totalSupply(); + + uint224 deltaIndex; + + if (supplyTokens != 0) deltaIndex = ((strategyRewardsAccrued * ONE) / supplyTokens).safeCastTo224(); + + // accumulate rewards per token onto the index, multiplied by fixed-point factor + rewardsState = RewardsState({ + index: state.index + deltaIndex, + lastUpdatedTimestamp: block.timestamp.safeCastTo32() + }); + strategyState[strategy] = rewardsState; + } + } + + /// @notice accumulate rewards on a strategy for a specific user + function accrueUser( + ERC20 strategy, + address user, + RewardsState memory state + ) private returns (uint256) { + // load indices + uint224 strategyIndex = state.index; + uint224 supplierIndex = userIndex[strategy][user]; + + // sync user index to global + userIndex[strategy][user] = strategyIndex; + + // if user hasn't yet accrued rewards, grant them interest from the strategy beginning if they have a balance + // zero balances will have no effect other than syncing to global index + if (supplierIndex == 0) { + supplierIndex = ONE; + } + + uint224 deltaIndex = strategyIndex - supplierIndex; + // use the booster or token balance to calculate reward balance multiplier + uint256 supplierTokens = address(flywheelBooster) != address(0) + ? flywheelBooster.boostedBalanceOf(strategy, user) + : strategy.balanceOf(user); + + // accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed + uint256 supplierDelta = (supplierTokens * deltaIndex) / ONE; + uint256 supplierAccrued = rewardsAccrued[user] + supplierDelta; + + rewardsAccrued[user] = supplierAccrued; + + emit AccrueRewards(strategy, user, supplierDelta, strategyIndex); + + return supplierAccrued; + } +} diff --git a/contracts/ionic/strategies/flywheel/IFlywheelBooster.sol b/contracts/ionic/strategies/flywheel/IFlywheelBooster.sol new file mode 100644 index 00000000..eb430e99 --- /dev/null +++ b/contracts/ionic/strategies/flywheel/IFlywheelBooster.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {ERC20} from "solmate/tokens/ERC20.sol"; + +/** + @title Balance Booster Module for Flywheel + @notice Flywheel is a general framework for managing token incentives. + It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies. + + The Booster module is an optional module for virtually boosting or otherwise transforming user balances. + If a booster is not configured, the strategies ERC-20 balanceOf/totalSupply will be used instead. + + Boosting logic can be associated with referrals, vote-escrow, or other strategies. + + SECURITY NOTE: similar to how Core needs to be notified any time the strategy user composition changes, the booster would need to be notified of any conditions which change the boosted balances atomically. + This prevents gaming of the reward calculation function by using manipulated balances when accruing. +*/ +interface IFlywheelBooster { + /** + @notice calculate the boosted supply of a strategy. + @param strategy the strategy to calculate boosted supply of + @return the boosted supply + */ + function boostedTotalSupply(ERC20 strategy) external view returns (uint256); + + /** + @notice calculate the boosted balance of a user in a given strategy. + @param strategy the strategy to calculate boosted balance of + @param user the user to calculate boosted balance of + @return the boosted balance + */ + function boostedBalanceOf(ERC20 strategy, address user) external view returns (uint256); +} diff --git a/contracts/ionic/strategies/flywheel/IonicFlywheelCore.sol b/contracts/ionic/strategies/flywheel/IonicFlywheelCore.sol index 3bd90c3f..8ad8b33f 100644 --- a/contracts/ionic/strategies/flywheel/IonicFlywheelCore.sol +++ b/contracts/ionic/strategies/flywheel/IonicFlywheelCore.sol @@ -5,8 +5,8 @@ import { ERC20 } from "solmate/tokens/ERC20.sol"; import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol"; import { SafeCastLib } from "solmate/utils/SafeCastLib.sol"; -import { IFlywheelRewards } from "flywheel-v2/interfaces/IFlywheelRewards.sol"; -import { IFlywheelBooster } from "flywheel-v2/interfaces/IFlywheelBooster.sol"; +import { IFlywheelRewards } from "./rewards/IFlywheelRewards.sol"; +import { IFlywheelBooster } from "./IFlywheelBooster.sol"; import { SafeOwnableUpgradeable } from "../../../ionic/SafeOwnableUpgradeable.sol"; diff --git a/contracts/ionic/strategies/flywheel/rewards/BaseFlywheelRewards.sol b/contracts/ionic/strategies/flywheel/rewards/BaseFlywheelRewards.sol new file mode 100644 index 00000000..83ffe7e9 --- /dev/null +++ b/contracts/ionic/strategies/flywheel/rewards/BaseFlywheelRewards.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {SafeTransferLib, ERC20} from "solmate/utils/SafeTransferLib.sol"; +import {IFlywheelRewards} from "./IFlywheelRewards.sol"; +import {IonicFlywheelCore} from "../IonicFlywheelCore.sol"; + +/** + @title Flywheel Reward Module + @notice Determines how many rewards accrue to each strategy globally over a given time period. + @dev approves the flywheel core for the reward token to allow balances to be managed by the module but claimed from core. +*/ +abstract contract BaseFlywheelRewards is IFlywheelRewards { + using SafeTransferLib for ERC20; + + /// @notice thrown when caller is not the flywheel + error FlywheelError(); + + /// @notice the reward token paid + ERC20 public immutable override rewardToken; + + /// @notice the flywheel core contract + IonicFlywheelCore public immutable override flywheel; + + constructor(IonicFlywheelCore _flywheel) { + flywheel = _flywheel; + ERC20 _rewardToken = _flywheel.rewardToken(); + rewardToken = _rewardToken; + + _rewardToken.safeApprove(address(_flywheel), type(uint256).max); + } + + modifier onlyFlywheel() { + if (msg.sender != address(flywheel)) revert FlywheelError(); + _; + } +} diff --git a/contracts/ionic/strategies/flywheel/rewards/FlywheelDynamicRewards.sol b/contracts/ionic/strategies/flywheel/rewards/FlywheelDynamicRewards.sol new file mode 100644 index 00000000..de6a1142 --- /dev/null +++ b/contracts/ionic/strategies/flywheel/rewards/FlywheelDynamicRewards.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import "./BaseFlywheelRewards.sol"; +import {SafeCastLib} from "solmate/utils/SafeCastLib.sol"; + +/** + @title Flywheel Dynamic Reward Stream + @notice Determines rewards based on a dynamic reward stream. + Rewards are transferred linearly over a "rewards cycle" to prevent gaming the reward distribution. + The reward source can be arbitrary logic, but most common is to "pass through" rewards from some other source. + The getNextCycleRewards() hook should also transfer the next cycle's rewards to this contract to ensure proper accounting. +*/ +abstract contract FlywheelDynamicRewards is BaseFlywheelRewards { + using SafeTransferLib for ERC20; + using SafeCastLib for uint256; + + event NewRewardsCycle(uint32 indexed start, uint32 indexed end, uint192 reward); + + /// @notice the length of a rewards cycle + uint32 public immutable rewardsCycleLength; + + struct RewardsCycle { + uint32 start; + uint32 end; + uint192 reward; + } + + mapping(ERC20 => RewardsCycle) public rewardsCycle; + + constructor(IonicFlywheelCore _flywheel, uint32 _rewardsCycleLength) BaseFlywheelRewards(_flywheel) { + rewardsCycleLength = _rewardsCycleLength; + } + + /** + @notice calculate and transfer accrued rewards to flywheel core + @param strategy the strategy to accrue rewards for + @return amount the amount of tokens accrued and transferred + */ + function getAccruedRewards(ERC20 strategy, uint32 lastUpdatedTimestamp) + external + override + onlyFlywheel + returns (uint256 amount) + { + RewardsCycle memory cycle = rewardsCycle[strategy]; + + uint32 timestamp = block.timestamp.safeCastTo32(); + + uint32 latest = timestamp >= cycle.end ? cycle.end : timestamp; + uint32 earliest = lastUpdatedTimestamp <= cycle.start ? cycle.start : lastUpdatedTimestamp; + if (cycle.end != 0) { + amount = (cycle.reward * (latest - earliest)) / (cycle.end - cycle.start); + assert(amount <= cycle.reward); // should never happen because latest <= cycle.end and earliest >= cycle.start + } + // if cycle has ended, reset cycle and transfer all available + if (timestamp >= cycle.end) { + uint32 end = ((timestamp + rewardsCycleLength) / rewardsCycleLength) * rewardsCycleLength; + uint192 rewards = getNextCycleRewards(strategy); + + // reset for next cycle + rewardsCycle[strategy] = RewardsCycle({start: timestamp, end: end, reward: rewards}); + + emit NewRewardsCycle(timestamp, end, rewards); + } + } + + function getNextCycleRewards(ERC20 strategy) internal virtual returns (uint192); +} diff --git a/contracts/ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol b/contracts/ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol new file mode 100644 index 00000000..b81e7411 --- /dev/null +++ b/contracts/ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {Auth, Authority} from "solmate/auth/Auth.sol"; +import "./BaseFlywheelRewards.sol"; + +/** + @title Flywheel Static Reward Stream + @notice Determines rewards per strategy based on a fixed reward rate per second +*/ +contract FlywheelStaticRewards is Auth, BaseFlywheelRewards { + event RewardsInfoUpdate(ERC20 indexed strategy, uint224 rewardsPerSecond, uint32 rewardsEndTimestamp); + + struct RewardsInfo { + /// @notice Rewards per second + uint224 rewardsPerSecond; + /// @notice The timestamp the rewards end at + /// @dev use 0 to specify no end + uint32 rewardsEndTimestamp; + } + + /// @notice rewards info per strategy + mapping(ERC20 => RewardsInfo) public rewardsInfo; + + constructor( + IonicFlywheelCore _flywheel, + address _owner, + Authority _authority + ) Auth(_owner, _authority) BaseFlywheelRewards(_flywheel) {} + + /** + @notice set rewards per second and rewards end time for Fei Rewards + @param strategy the strategy to accrue rewards for + @param rewards the rewards info for the strategy + */ + function setRewardsInfo(ERC20 strategy, RewardsInfo calldata rewards) external requiresAuth { + rewardsInfo[strategy] = rewards; + emit RewardsInfoUpdate(strategy, rewards.rewardsPerSecond, rewards.rewardsEndTimestamp); + } + + /** + @notice calculate and transfer accrued rewards to flywheel core + @param strategy the strategy to accrue rewards for + @param lastUpdatedTimestamp the last updated time for strategy + @return amount the amount of tokens accrued and transferred + */ + function getAccruedRewards(ERC20 strategy, uint32 lastUpdatedTimestamp) + external + view + override + onlyFlywheel + returns (uint256 amount) + { + RewardsInfo memory rewards = rewardsInfo[strategy]; + + uint256 elapsed; + if (rewards.rewardsEndTimestamp == 0 || rewards.rewardsEndTimestamp > block.timestamp) { + elapsed = block.timestamp - lastUpdatedTimestamp; + } else if (rewards.rewardsEndTimestamp > lastUpdatedTimestamp) { + elapsed = rewards.rewardsEndTimestamp - lastUpdatedTimestamp; + } + + amount = rewards.rewardsPerSecond * elapsed; + } +} diff --git a/contracts/ionic/strategies/flywheel/rewards/IFlywheelRewards.sol b/contracts/ionic/strategies/flywheel/rewards/IFlywheelRewards.sol new file mode 100644 index 00000000..9dfcf506 --- /dev/null +++ b/contracts/ionic/strategies/flywheel/rewards/IFlywheelRewards.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {IonicFlywheelCore} from "../IonicFlywheelCore.sol"; + +/** + @title Rewards Module for Flywheel + @notice Flywheel is a general framework for managing token incentives. + It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies. + + The Rewards module is responsible for: + * determining the ongoing reward amounts to entire strategies (core handles the logic for dividing among users) + * actually holding rewards that are yet to be claimed + + The reward stream can follow arbitrary logic as long as the amount of rewards passed to flywheel core has been sent to this contract. + + Different module strategies include: + * a static reward rate per second + * a decaying reward rate + * a dynamic just-in-time reward stream + * liquid governance reward delegation (Curve Gauge style) + + SECURITY NOTE: The rewards strategy should be smooth and continuous, to prevent gaming the reward distribution by frontrunning. + */ +interface IFlywheelRewards { + /** + @notice calculate the rewards amount accrued to a strategy since the last update. + @param strategy the strategy to accrue rewards for. + @param lastUpdatedTimestamp the last time rewards were accrued for the strategy. + @return rewards the amount of rewards accrued to the market + */ + function getAccruedRewards(ERC20 strategy, uint32 lastUpdatedTimestamp) external returns (uint256 rewards); + + /// @notice return the flywheel core address + function flywheel() external view returns (IonicFlywheelCore); + + /// @notice return the reward token associated with flywheel core. + function rewardToken() external view returns (ERC20); +} diff --git a/contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewards.sol b/contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewards.sol index c2f767ae..893804c7 100644 --- a/contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewards.sol +++ b/contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewards.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.10; -import { FlywheelDynamicRewards } from "flywheel-v2/rewards/FlywheelDynamicRewards.sol"; -import { FlywheelCore } from "flywheel-v2/FlywheelCore.sol"; +import { FlywheelDynamicRewards } from "./FlywheelDynamicRewards.sol"; +import { IonicFlywheelCore } from "../IonicFlywheelCore.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; interface ICERC20 { @@ -16,7 +16,7 @@ interface IPlugin { contract IonicFlywheelDynamicRewards is FlywheelDynamicRewards { using SafeTransferLib for ERC20; - constructor(FlywheelCore _flywheel, uint32 _cycleLength) FlywheelDynamicRewards(_flywheel, _cycleLength) {} + constructor(IonicFlywheelCore _flywheel, uint32 _cycleLength) FlywheelDynamicRewards(_flywheel, _cycleLength) {} function getNextCycleRewards(ERC20 strategy) internal override returns (uint192) { // make it work for both pulled (claimed) and pushed (transferred some other way) rewards diff --git a/contracts/test/ComptrollerTest.t.sol b/contracts/test/ComptrollerTest.t.sol index c4a63bc0..8c87da29 100644 --- a/contracts/test/ComptrollerTest.t.sol +++ b/contracts/test/ComptrollerTest.t.sol @@ -13,8 +13,8 @@ import { ICErc20 } from "../compound/CTokenInterfaces.sol"; import { ComptrollerErrorReporter } from "../compound/ErrorReporter.sol"; import { DiamondExtension } from "../ionic/DiamondExtension.sol"; -import { IFlywheelBooster } from "flywheel-v2/interfaces/IFlywheelBooster.sol"; -import { IFlywheelRewards } from "flywheel-v2/interfaces/IFlywheelRewards.sol"; +import { IFlywheelBooster } from "../ionic/strategies/flywheel/IFlywheelBooster.sol"; +import { IFlywheelRewards } from "../ionic/strategies/flywheel/rewards/IFlywheelRewards.sol"; import { ERC20 } from "solmate/tokens/ERC20.sol"; import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol"; import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/contracts/test/DeployMarkets.t.sol b/contracts/test/DeployMarkets.t.sol index c707f91f..d2f5b8be 100644 --- a/contracts/test/DeployMarkets.t.sol +++ b/contracts/test/DeployMarkets.t.sol @@ -7,9 +7,9 @@ import { ERC20 } from "solmate/tokens/ERC20.sol"; import { Auth, Authority } from "solmate/auth/Auth.sol"; import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol"; import { FuseFlywheelDynamicRewardsPlugin } from "fuse-flywheel/rewards/FuseFlywheelDynamicRewardsPlugin.sol"; -import { FlywheelCore } from "flywheel-v2/FlywheelCore.sol"; -import { IFlywheelBooster } from "flywheel-v2/interfaces/IFlywheelBooster.sol"; -import { IFlywheelRewards } from "flywheel-v2/interfaces/IFlywheelRewards.sol"; +import { FlywheelCore } from "../ionic/strategies/flywheel/FlywheelCore.sol"; +import { IFlywheelBooster } from "../ionic/strategies/flywheel/IFlywheelBooster.sol"; +import { IFlywheelRewards } from "../ionic/strategies/flywheel/rewards/IFlywheelRewards.sol"; import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import { ICErc20, ICErc20Plugin, ICErc20PluginRewards } from "../compound/CTokenInterfaces.sol"; diff --git a/contracts/test/DevTesting.t.sol b/contracts/test/DevTesting.t.sol index 0229e49c..69cab81b 100644 --- a/contracts/test/DevTesting.t.sol +++ b/contracts/test/DevTesting.t.sol @@ -7,6 +7,7 @@ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/trans import "./config/BaseTest.t.sol"; import { IonicComptroller } from "../compound/ComptrollerInterface.sol"; import { ComptrollerFirstExtension } from "../compound/ComptrollerFirstExtension.sol"; +import { CErc20PluginRewardsDelegate } from "../compound/CErc20PluginRewardsDelegate.sol"; import { Unitroller } from "../compound/Unitroller.sol"; import { DiamondExtension } from "../ionic/DiamondExtension.sol"; import { ICErc20 } from "../compound/CTokenInterfaces.sol"; @@ -635,6 +636,30 @@ contract DevTesting is BaseTest { _functionCall(0xa12c1E460c06B1745EFcbfC9A1f666a8749B0e3A, hex"20b72325000000000000000000000000f28570694a6c9cd0494955966ae75af61abf5a0700000000000000000000000000000000000000000000000001bc1214ed792fbb0000000000000000000000004341620757bee7eb4553912fafc963e59c949147000000000000000000000000c53edeafb6d502daec5a7015d67936cea0cd0f520000000000000000000000000000000000000000000000000000000000000000", "error in call"); } + function testCtokenUpgrade() public debuggingOnly forkAtBlock(MODE_MAINNET, 10255413) { + CErc20PluginRewardsDelegate newImpl = new CErc20PluginRewardsDelegate(); + TransparentUpgradeableProxy proxy = TransparentUpgradeableProxy(payable(address(wethMarket))); + + + (uint256[] memory poolIds, PoolDirectory.Pool[] memory pools) = PoolDirectory(0x39C353Cf9041CcF467A04d0e78B63d961E81458a).getActivePools(); + + emit log_named_uint("First Pool ID", poolIds[0]); + emit log_named_uint("First Pool ID", poolIds[1]); + emit log_named_string("First Pool Address", pools[0].name); + emit log_named_string("First Pool Address", pools[0].name); + emit log_named_address("First Pool Address", pools[0].creator); + emit log_named_address("First Pool Address", pools[1].creator); + emit log_named_address("First Pool Address", pools[0].comptroller); + emit log_named_address("First Pool Address", pools[1].comptroller); + //bytes32 bytesAtSlot = vm.load(address(proxy), 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103); + //address admin = address(uint160(uint256(bytesAtSlot))); + //vm.prank(admin); + //proxy.upgradeTo(address(newImpl)); + + //vm.prank(dpa.owner()); + //proxy.upgradeTo(address(newImpl)); + } + function _functionCall( address target, bytes memory data, diff --git a/contracts/test/FLRTest.t.sol b/contracts/test/FLRTest.t.sol index 8f0d2521..97f5f201 100644 --- a/contracts/test/FLRTest.t.sol +++ b/contracts/test/FLRTest.t.sol @@ -10,8 +10,8 @@ import { Authority } from "solmate/auth/Auth.sol"; import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol"; import { IERC20MetadataUpgradeable, IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; -import { IFlywheelBooster } from "flywheel-v2/interfaces/IFlywheelBooster.sol"; -import { FlywheelStaticRewards } from "flywheel-v2/rewards/FlywheelStaticRewards.sol"; +import { IFlywheelBooster } from "../ionic/strategies/flywheel/IFlywheelBooster.sol"; +import { FlywheelStaticRewards } from "../ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol"; import { FuseFlywheelCore } from "fuse-flywheel/FuseFlywheelCore.sol"; import { CErc20 } from "../compound/CToken.sol"; @@ -48,7 +48,7 @@ contract FLRTest is BaseTest { address(this) ); - rewards = new FlywheelStaticRewards(FuseFlywheelCore(address(flywheel)), address(this), Authority(address(0))); + rewards = new FlywheelStaticRewards(IonicFlywheelCore(address(flywheel)), address(this), Authority(address(0))); flywheel.setFlywheelRewards(rewards); flywheel.addStrategyForRewards(ERC20(mkt)); diff --git a/contracts/test/LiquidityMining.t.sol b/contracts/test/LiquidityMining.t.sol index d3cf5e6a..11d8f61c 100644 --- a/contracts/test/LiquidityMining.t.sol +++ b/contracts/test/LiquidityMining.t.sol @@ -6,9 +6,9 @@ import "forge-std/Vm.sol"; import { ERC20 } from "solmate/tokens/ERC20.sol"; import { Auth, Authority } from "solmate/auth/Auth.sol"; import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol"; -import { FlywheelStaticRewards } from "flywheel-v2/rewards/FlywheelStaticRewards.sol"; -import { FlywheelCore } from "flywheel-v2/FlywheelCore.sol"; -import { IFlywheelBooster } from "flywheel-v2/interfaces/IFlywheelBooster.sol"; +import { FlywheelStaticRewards } from "../ionic/strategies/flywheel/rewards/FlywheelStaticRewards.sol"; +import { IFlywheelBooster } from "../ionic/strategies/flywheel/IFlywheelBooster.sol"; +import { IFlywheelRewards } from "../ionic/strategies/flywheel/rewards/IFlywheelRewards.sol"; import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import { ICErc20 } from "../compound/CTokenInterfaces.sol"; @@ -131,7 +131,7 @@ contract LiquidityMiningTest is BaseTest { TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(impl), address(dpa), ""); flywheel = IonicFlywheel(address(proxy)); flywheel.initialize(rewardToken, FlywheelStaticRewards(address(0)), IFlywheelBooster(address(0)), address(this)); - rewards = new FlywheelStaticRewards(FlywheelCore(address(flywheel)), address(this), Authority(address(0))); + rewards = new FlywheelStaticRewards(IonicFlywheelCore(address(flywheel)), address(this), Authority(address(0))); flywheel.setFlywheelRewards(rewards); flywheelClaimer = new IonicFlywheelLensRouter(poolDirectory); diff --git a/lib/flywheel-v2 b/lib/flywheel-v2 deleted file mode 160000 index 5ac5d314..00000000 --- a/lib/flywheel-v2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ac5d314362188e3ece31a9db7deba529f4e8cc7 diff --git a/remappings.txt b/remappings.txt index 7d02f86f..4582b00c 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,5 @@ -flywheel/=lib/flywheel-v2/src/ +flywheel/=lib/fuse-flywheel/lib/flywheel-v2/src/ solidity-bytes-utils/=lib/solidity-bytes-utils/ @openzeppelin=lib/openzeppelin-contracts/ @pythnetwork/pyth-sdk-solidity/=lib/pyth-sdk-solidity/ @pythnetwork/express-relay-sdk-solidity/=node_modules/@pythnetwork/express-relay-sdk-solidity -