From 21b35a70d65f1a190073744dbba69b51e1008088 Mon Sep 17 00:00:00 2001 From: gas-limit Date: Mon, 18 Nov 2024 19:40:55 -0500 Subject: [PATCH] made folders --- README.md | 4 +- script/Counter.s.sol | 19 -- .../ChefIncentivesMultiRewarder.sol | 302 ++++++++++++++++++ src/Counter.sol | 14 - src/Standalone/StandaloneRewarder.sol | 302 ++++++++++++++++++ test/Counter.t.sol | 24 -- 6 files changed, 606 insertions(+), 59 deletions(-) delete mode 100644 script/Counter.s.sol create mode 100644 src/ChefIncentivesController/ChefIncentivesMultiRewarder.sol delete mode 100644 src/Counter.sol create mode 100644 src/Standalone/StandaloneRewarder.sol delete mode 100644 test/Counter.t.sol diff --git a/README.md b/README.md index 6445c66..2b247d6 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Ensure Foundry is installed. If not, follow the instructions at [Foundry's offic 1. Clone the repository: ```bash - git clone https://github.com/your-repo-name.git - cd your-repo-name + git clone https://github.com/gas-limit/MultiRewardsChef.git + cd MultiRewardsChef ``` 2. Install dependencies: diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/src/ChefIncentivesController/ChefIncentivesMultiRewarder.sol b/src/ChefIncentivesController/ChefIncentivesMultiRewarder.sol new file mode 100644 index 0000000..bf72997 --- /dev/null +++ b/src/ChefIncentivesController/ChefIncentivesMultiRewarder.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; + +import "../dependencies/openzeppelin/contracts/IERC20.sol"; +import "../dependencies/openzeppelin/contracts/SafeERC20.sol"; +import "../dependencies/openzeppelin/contracts/SafeMath.sol"; + +contract MultiIncentiveModule { + + using SafeMath for uint256; + + address multiIncentiveAdmin; + + // aToken address => total staked amount + mapping(address => uint256) public multiTotalStaked; + // aToken address => staked amount + mapping(address => mapping(address => uint256)) public multiUserStaked; + // aToken address => rewardToken array + mapping(address => address[]) public multiRewardTokens; + // rewardToken address => isMultiRewardToken + mapping(address => bool) public isMultiRewardToken; + // aTokenAddress => rewardTokenAddress => multiRewardPerToken + mapping(address => mapping(address => uint256)) public multiRewardPerToken; + // user address => aToken address => reward address => multiRewardPerTokenOffsetScaled + mapping(address => mapping(address => mapping(address => uint256))) + public multiUserRewardOffset; + // user address => rewardToken address => accumulated rewards + mapping(address => mapping(address => uint256)) public multiUserPendingRewards; + // aToken address => rewardToken address => rewardPerSecond + mapping(address => mapping(address => uint256)) public multiRewardPerSecond; + // aToken address => rewardToken address => lastMultiUpdateTime + mapping(address => mapping(address => uint256)) public lastMultiUpdateTime; + + uint256 internal constant SCALE = 1e24; + + event multiStakeRecorded(address user, address aToken, uint256 amount); + event multiUnstakeRecorded(address user, address aToken, uint256 amount); + event multiRewardHarvested( + address user, + address aToken, + address rewardToken, + uint256 amount + ); + event multiRewardAdded( + address aToken, + address rewardToken, + uint256 rewardsPerSecond + ); + event multiRewardRemoved(address aToken, address rewardToken); + event multiRewardUpdated( + address aToken, + address rewardToken, + uint256 rewardsPerSecond + ); + event multiRewardDeposited(address rewardToken, uint256 amount); + event multiRewardWithdrawn(address rewardToken, uint256 amount); + + modifier onlyIncentiveAdmin() { + require(msg.sender == multiIncentiveAdmin, "caller is not the multiIncentiveAdmin"); + _; + } + + constructor() { + multiIncentiveAdmin = msg.sender; + } + + // ╒═════════════════════✰° + // USER FUNCTIONS + // °✰════════════════════╛ + + // Stake aToken + function _stakeIncentiveTokens(address _aToken, uint256 _amount, address _user) internal { + require(_amount > 0, "amount must be greater than 0"); + calculateUserPending(_user, _aToken); + // Update rewards before modifying stakes to ensure proper accounting + updateMultiRewardAccounting(_aToken); + + multiTotalStaked[_aToken] = multiTotalStaked[_aToken].add(_amount); + multiUserStaked[_user][_aToken] = multiUserStaked[_user][_aToken].add(_amount); + + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + // Set the offset to current rewardPerToken to start counting from this point + multiUserRewardOffset[_user][_aToken][rewardToken] = + _simulateRewardPerToken(_aToken, rewardToken, multiTotalStaked[_aToken].sub(_amount)); + } + + emit multiStakeRecorded(_user, _aToken, _amount); + } + + // Withdraw aToken + function _unstakeIncentiveTokens(address _aToken, uint256 _amount, address _user) internal { + require(_amount > 0, "amount must be greater than 0"); + // assume check for sufficient staked amount is done in parent contract + _claimMultiRewards(_aToken, _user); + uint256 stakedAmount = multiUserStaked[_user][_aToken]; + require(stakedAmount >= _amount, "insufficient staked amount"); + multiUserStaked[_user][_aToken] = stakedAmount.sub(_amount); + multiTotalStaked[_aToken] = multiTotalStaked[_aToken].sub(_amount); + + emit multiUnstakeRecorded(_user, _aToken, _amount); + } + + // Harvest rewards + function _claimMultiRewards(address _aToken, address _user) internal { + updateMultiRewardAccounting(_aToken); + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + uint256 earnedAmountScaled = (multiRewardPerToken[_aToken][rewardToken] - + multiUserRewardOffset[_user][_aToken][ + rewardToken + ]) * multiUserStaked[_user][_aToken]; + uint256 earnedAmountWithoutPending = earnedAmountScaled.div(SCALE); + uint256 earnedAmountActual = earnedAmountWithoutPending + multiUserPendingRewards[_user][rewardToken]; + if (earnedAmountActual == 0) { + continue; + } + multiUserRewardOffset[_user][_aToken][ + rewardToken + ] = multiRewardPerToken[_aToken][rewardToken]; + // IERC20(rewardToken).transfer(_user, earnedAmountActual); + emit multiRewardHarvested(_user, _aToken, rewardToken, earnedAmountActual); + } + } + + // ╒═════════════════════════✰° + // INTERNAL ACCOUNTING + // °✰════════════════════════╛ + + // Update reward accounting + function updateMultiRewardAccounting(address _aToken) internal { + if(multiTotalStaked[_aToken] == 0) { + return; + } + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + address rewardToken; + uint256 rewardTokenCount = rewardTokenList.length; + for (uint256 i = 0; i < rewardTokenCount; i++) { + rewardToken = rewardTokenList[i]; + uint256 timeElapsed = block.timestamp - + lastMultiUpdateTime[_aToken][rewardToken]; + uint256 rewardToAdd = timeElapsed.mul( + multiRewardPerSecond[_aToken][rewardToken]); + multiRewardPerToken[_aToken][rewardToken] += rewardToAdd + .mul(SCALE) + .div(multiTotalStaked[_aToken]); + + lastMultiUpdateTime[_aToken][rewardToken] = block.timestamp; + } + } + + function calculateUserPending(address _user, address _aToken) internal { + (address[] memory rewardTokenList, uint256[] memory earnedAmounts) = previewEarned( + _user, + _aToken + ); + for(uint256 i = 0; i < rewardTokenList.length; i++) { + address rewardToken = rewardTokenList[i]; + multiUserPendingRewards[_user][rewardToken] += earnedAmounts[i]; + } + + } + + // ╒═════════════════════════✰° + // ADMIN FUNCTIONS + // °✰════════════════════════╛ + + // Add a new reward to a specific aToken + function addIncentiveReward( + address _aToken, + address _rewardToken, + uint256 _rewardsPerSecond + ) external onlyIncentiveAdmin { + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + // check if reward token already exists + for (uint256 i = 0; i < rewardTokenList.length; i++) { + require( + rewardTokenList[i] != _rewardToken, + "reward token already exists" + ); + } + + multiRewardTokens[_aToken].push(_rewardToken); + multiRewardPerSecond[_aToken][_rewardToken] = _rewardsPerSecond; + lastMultiUpdateTime[_aToken][_rewardToken] = block.timestamp; + isMultiRewardToken[_rewardToken] = true; + + emit multiRewardAdded(_aToken, _rewardToken, _rewardsPerSecond); + } + + // Remove a reward from a specific aToken + function removeIncentiveReward( + address _aToken, + address _rewardToken + ) external onlyIncentiveAdmin { + updateMultiRewardAccounting(_aToken); + require(multiRewardPerSecond[_aToken][_rewardToken] != 0, "reward token does not exist"); + + // Stop accumulating new rewards + multiRewardPerSecond[_aToken][_rewardToken] = 0; + + emit multiRewardRemoved(_aToken, _rewardToken); + } + + + // Update reward per second for a specific aToken + function adjustRewardRate(address _aToken, address _rewardToken, uint256 _rewardsPerSecond) external onlyIncentiveAdmin { + require(multiRewardPerSecond[_aToken][_rewardToken] != 0, "reward token does not exist"); + updateMultiRewardAccounting(_aToken); + multiRewardPerSecond[_aToken][_rewardToken] = _rewardsPerSecond; + emit multiRewardUpdated(_aToken, _rewardToken, _rewardsPerSecond); + } + + function depositReward(address _rewardAddress, uint256 _amount) external onlyIncentiveAdmin { + IERC20(_rewardAddress).transferFrom(msg.sender, address(this), _amount); + emit multiRewardDeposited(_rewardAddress, _amount); + } + + function withdrawReward(address _rewardAddress, uint256 _amount) external onlyIncentiveAdmin { + IERC20(_rewardAddress).transfer(msg.sender, _amount); + emit multiRewardWithdrawn(_rewardAddress, _amount); + } + + // ╒═════════════════════════✰° + // USER PREVIEW REWARDS + // °✰════════════════════════╛ + + function previewEarned(address _user, address _aToken) + public + view + returns (address[] memory multiRewardTokens_, uint256[] memory earnedAmounts_) + { + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + multiRewardTokens_ = new address[](rewardTokenCount); + earnedAmounts_ = new uint256[](rewardTokenCount); + uint256 totalStakedAmount = multiTotalStaked[_aToken]; + uint256 userStakedAmount = multiUserStaked[_user][_aToken]; + + if (userStakedAmount == 0 || totalStakedAmount == 0) { + // No staked amount or no total staked amount, earned is zero + return (multiRewardTokens_, earnedAmounts_); + } + + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + uint256 simulatedRewardPerToken = _simulateRewardPerToken( + _aToken, + rewardToken, + totalStakedAmount + ); + uint256 earnedAmountActual = _calculateEarnedAmount( + _user, + _aToken, + rewardToken, + simulatedRewardPerToken, + userStakedAmount + ); + earnedAmounts_[i] = earnedAmountActual + multiUserPendingRewards[_user][rewardToken]; + multiRewardTokens_[i] = rewardToken; + } + } + + function _simulateRewardPerToken( + address _aToken, + address _rewardToken, + uint256 totalStakedAmount + ) internal view returns (uint256) { + if (totalStakedAmount == 0) { + return multiRewardPerToken[_aToken][_rewardToken]; + } + + uint256 timeElapsed = block.timestamp - lastMultiUpdateTime[_aToken][_rewardToken]; + uint256 rewardToAdd = timeElapsed.mul(multiRewardPerSecond[_aToken][_rewardToken]); + uint256 simulatedRewardPerToken = multiRewardPerToken[_aToken][_rewardToken].add( + rewardToAdd.mul(SCALE).div(totalStakedAmount) + ); + return simulatedRewardPerToken; + } + + function _calculateEarnedAmount( + address _user, + address _aToken, + address _rewardToken, + uint256 simulatedRewardPerToken, + uint256 userStakedAmount + ) internal view returns (uint256) { + uint256 userRewardPerTokenOffset = multiUserRewardOffset[_user][_aToken][_rewardToken]; + uint256 earnedAmountScaled = simulatedRewardPerToken.sub(userRewardPerTokenOffset) + .mul(userStakedAmount); + uint256 earnedAmountActual = earnedAmountScaled.div(SCALE); + return earnedAmountActual; + } + + + +} \ No newline at end of file diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/Standalone/StandaloneRewarder.sol b/src/Standalone/StandaloneRewarder.sol new file mode 100644 index 0000000..bf72997 --- /dev/null +++ b/src/Standalone/StandaloneRewarder.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; + +import "../dependencies/openzeppelin/contracts/IERC20.sol"; +import "../dependencies/openzeppelin/contracts/SafeERC20.sol"; +import "../dependencies/openzeppelin/contracts/SafeMath.sol"; + +contract MultiIncentiveModule { + + using SafeMath for uint256; + + address multiIncentiveAdmin; + + // aToken address => total staked amount + mapping(address => uint256) public multiTotalStaked; + // aToken address => staked amount + mapping(address => mapping(address => uint256)) public multiUserStaked; + // aToken address => rewardToken array + mapping(address => address[]) public multiRewardTokens; + // rewardToken address => isMultiRewardToken + mapping(address => bool) public isMultiRewardToken; + // aTokenAddress => rewardTokenAddress => multiRewardPerToken + mapping(address => mapping(address => uint256)) public multiRewardPerToken; + // user address => aToken address => reward address => multiRewardPerTokenOffsetScaled + mapping(address => mapping(address => mapping(address => uint256))) + public multiUserRewardOffset; + // user address => rewardToken address => accumulated rewards + mapping(address => mapping(address => uint256)) public multiUserPendingRewards; + // aToken address => rewardToken address => rewardPerSecond + mapping(address => mapping(address => uint256)) public multiRewardPerSecond; + // aToken address => rewardToken address => lastMultiUpdateTime + mapping(address => mapping(address => uint256)) public lastMultiUpdateTime; + + uint256 internal constant SCALE = 1e24; + + event multiStakeRecorded(address user, address aToken, uint256 amount); + event multiUnstakeRecorded(address user, address aToken, uint256 amount); + event multiRewardHarvested( + address user, + address aToken, + address rewardToken, + uint256 amount + ); + event multiRewardAdded( + address aToken, + address rewardToken, + uint256 rewardsPerSecond + ); + event multiRewardRemoved(address aToken, address rewardToken); + event multiRewardUpdated( + address aToken, + address rewardToken, + uint256 rewardsPerSecond + ); + event multiRewardDeposited(address rewardToken, uint256 amount); + event multiRewardWithdrawn(address rewardToken, uint256 amount); + + modifier onlyIncentiveAdmin() { + require(msg.sender == multiIncentiveAdmin, "caller is not the multiIncentiveAdmin"); + _; + } + + constructor() { + multiIncentiveAdmin = msg.sender; + } + + // ╒═════════════════════✰° + // USER FUNCTIONS + // °✰════════════════════╛ + + // Stake aToken + function _stakeIncentiveTokens(address _aToken, uint256 _amount, address _user) internal { + require(_amount > 0, "amount must be greater than 0"); + calculateUserPending(_user, _aToken); + // Update rewards before modifying stakes to ensure proper accounting + updateMultiRewardAccounting(_aToken); + + multiTotalStaked[_aToken] = multiTotalStaked[_aToken].add(_amount); + multiUserStaked[_user][_aToken] = multiUserStaked[_user][_aToken].add(_amount); + + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + // Set the offset to current rewardPerToken to start counting from this point + multiUserRewardOffset[_user][_aToken][rewardToken] = + _simulateRewardPerToken(_aToken, rewardToken, multiTotalStaked[_aToken].sub(_amount)); + } + + emit multiStakeRecorded(_user, _aToken, _amount); + } + + // Withdraw aToken + function _unstakeIncentiveTokens(address _aToken, uint256 _amount, address _user) internal { + require(_amount > 0, "amount must be greater than 0"); + // assume check for sufficient staked amount is done in parent contract + _claimMultiRewards(_aToken, _user); + uint256 stakedAmount = multiUserStaked[_user][_aToken]; + require(stakedAmount >= _amount, "insufficient staked amount"); + multiUserStaked[_user][_aToken] = stakedAmount.sub(_amount); + multiTotalStaked[_aToken] = multiTotalStaked[_aToken].sub(_amount); + + emit multiUnstakeRecorded(_user, _aToken, _amount); + } + + // Harvest rewards + function _claimMultiRewards(address _aToken, address _user) internal { + updateMultiRewardAccounting(_aToken); + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + uint256 earnedAmountScaled = (multiRewardPerToken[_aToken][rewardToken] - + multiUserRewardOffset[_user][_aToken][ + rewardToken + ]) * multiUserStaked[_user][_aToken]; + uint256 earnedAmountWithoutPending = earnedAmountScaled.div(SCALE); + uint256 earnedAmountActual = earnedAmountWithoutPending + multiUserPendingRewards[_user][rewardToken]; + if (earnedAmountActual == 0) { + continue; + } + multiUserRewardOffset[_user][_aToken][ + rewardToken + ] = multiRewardPerToken[_aToken][rewardToken]; + // IERC20(rewardToken).transfer(_user, earnedAmountActual); + emit multiRewardHarvested(_user, _aToken, rewardToken, earnedAmountActual); + } + } + + // ╒═════════════════════════✰° + // INTERNAL ACCOUNTING + // °✰════════════════════════╛ + + // Update reward accounting + function updateMultiRewardAccounting(address _aToken) internal { + if(multiTotalStaked[_aToken] == 0) { + return; + } + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + address rewardToken; + uint256 rewardTokenCount = rewardTokenList.length; + for (uint256 i = 0; i < rewardTokenCount; i++) { + rewardToken = rewardTokenList[i]; + uint256 timeElapsed = block.timestamp - + lastMultiUpdateTime[_aToken][rewardToken]; + uint256 rewardToAdd = timeElapsed.mul( + multiRewardPerSecond[_aToken][rewardToken]); + multiRewardPerToken[_aToken][rewardToken] += rewardToAdd + .mul(SCALE) + .div(multiTotalStaked[_aToken]); + + lastMultiUpdateTime[_aToken][rewardToken] = block.timestamp; + } + } + + function calculateUserPending(address _user, address _aToken) internal { + (address[] memory rewardTokenList, uint256[] memory earnedAmounts) = previewEarned( + _user, + _aToken + ); + for(uint256 i = 0; i < rewardTokenList.length; i++) { + address rewardToken = rewardTokenList[i]; + multiUserPendingRewards[_user][rewardToken] += earnedAmounts[i]; + } + + } + + // ╒═════════════════════════✰° + // ADMIN FUNCTIONS + // °✰════════════════════════╛ + + // Add a new reward to a specific aToken + function addIncentiveReward( + address _aToken, + address _rewardToken, + uint256 _rewardsPerSecond + ) external onlyIncentiveAdmin { + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + // check if reward token already exists + for (uint256 i = 0; i < rewardTokenList.length; i++) { + require( + rewardTokenList[i] != _rewardToken, + "reward token already exists" + ); + } + + multiRewardTokens[_aToken].push(_rewardToken); + multiRewardPerSecond[_aToken][_rewardToken] = _rewardsPerSecond; + lastMultiUpdateTime[_aToken][_rewardToken] = block.timestamp; + isMultiRewardToken[_rewardToken] = true; + + emit multiRewardAdded(_aToken, _rewardToken, _rewardsPerSecond); + } + + // Remove a reward from a specific aToken + function removeIncentiveReward( + address _aToken, + address _rewardToken + ) external onlyIncentiveAdmin { + updateMultiRewardAccounting(_aToken); + require(multiRewardPerSecond[_aToken][_rewardToken] != 0, "reward token does not exist"); + + // Stop accumulating new rewards + multiRewardPerSecond[_aToken][_rewardToken] = 0; + + emit multiRewardRemoved(_aToken, _rewardToken); + } + + + // Update reward per second for a specific aToken + function adjustRewardRate(address _aToken, address _rewardToken, uint256 _rewardsPerSecond) external onlyIncentiveAdmin { + require(multiRewardPerSecond[_aToken][_rewardToken] != 0, "reward token does not exist"); + updateMultiRewardAccounting(_aToken); + multiRewardPerSecond[_aToken][_rewardToken] = _rewardsPerSecond; + emit multiRewardUpdated(_aToken, _rewardToken, _rewardsPerSecond); + } + + function depositReward(address _rewardAddress, uint256 _amount) external onlyIncentiveAdmin { + IERC20(_rewardAddress).transferFrom(msg.sender, address(this), _amount); + emit multiRewardDeposited(_rewardAddress, _amount); + } + + function withdrawReward(address _rewardAddress, uint256 _amount) external onlyIncentiveAdmin { + IERC20(_rewardAddress).transfer(msg.sender, _amount); + emit multiRewardWithdrawn(_rewardAddress, _amount); + } + + // ╒═════════════════════════✰° + // USER PREVIEW REWARDS + // °✰════════════════════════╛ + + function previewEarned(address _user, address _aToken) + public + view + returns (address[] memory multiRewardTokens_, uint256[] memory earnedAmounts_) + { + address[] memory rewardTokenList = multiRewardTokens[_aToken]; + uint256 rewardTokenCount = rewardTokenList.length; + multiRewardTokens_ = new address[](rewardTokenCount); + earnedAmounts_ = new uint256[](rewardTokenCount); + uint256 totalStakedAmount = multiTotalStaked[_aToken]; + uint256 userStakedAmount = multiUserStaked[_user][_aToken]; + + if (userStakedAmount == 0 || totalStakedAmount == 0) { + // No staked amount or no total staked amount, earned is zero + return (multiRewardTokens_, earnedAmounts_); + } + + for (uint256 i = 0; i < rewardTokenCount; i++) { + address rewardToken = rewardTokenList[i]; + uint256 simulatedRewardPerToken = _simulateRewardPerToken( + _aToken, + rewardToken, + totalStakedAmount + ); + uint256 earnedAmountActual = _calculateEarnedAmount( + _user, + _aToken, + rewardToken, + simulatedRewardPerToken, + userStakedAmount + ); + earnedAmounts_[i] = earnedAmountActual + multiUserPendingRewards[_user][rewardToken]; + multiRewardTokens_[i] = rewardToken; + } + } + + function _simulateRewardPerToken( + address _aToken, + address _rewardToken, + uint256 totalStakedAmount + ) internal view returns (uint256) { + if (totalStakedAmount == 0) { + return multiRewardPerToken[_aToken][_rewardToken]; + } + + uint256 timeElapsed = block.timestamp - lastMultiUpdateTime[_aToken][_rewardToken]; + uint256 rewardToAdd = timeElapsed.mul(multiRewardPerSecond[_aToken][_rewardToken]); + uint256 simulatedRewardPerToken = multiRewardPerToken[_aToken][_rewardToken].add( + rewardToAdd.mul(SCALE).div(totalStakedAmount) + ); + return simulatedRewardPerToken; + } + + function _calculateEarnedAmount( + address _user, + address _aToken, + address _rewardToken, + uint256 simulatedRewardPerToken, + uint256 userStakedAmount + ) internal view returns (uint256) { + uint256 userRewardPerTokenOffset = multiUserRewardOffset[_user][_aToken][_rewardToken]; + uint256 earnedAmountScaled = simulatedRewardPerToken.sub(userRewardPerTokenOffset) + .mul(userStakedAmount); + uint256 earnedAmountActual = earnedAmountScaled.div(SCALE); + return earnedAmountActual; + } + + + +} \ No newline at end of file diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -}