Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate Stata4626 #8

Merged
merged 43 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
c225aa5
Separate Stata4626
kyzia551 Aug 13, 2024
4475a18
change to erc7201
kyzia551 Aug 13, 2024
863485f
regenerated storage location
kyzia551 Aug 13, 2024
6034a94
change latestAnswer calculation logic
kyzia551 Aug 13, 2024
0ecfd37
DRAFT: Refactoring in extensions style
kyzia551 Aug 14, 2024
b651f7f
add initializer
kyzia551 Aug 14, 2024
8e2a8a8
remove unused params at __Stata4626_init
kyzia551 Aug 14, 2024
87b5390
remove RayMathExplicitRounding
kyzia551 Aug 14, 2024
7c0e5b6
regenerated ERC20AaveLMStorageLocation
kyzia551 Aug 14, 2024
721b437
add RAY constant
kyzia551 Aug 14, 2024
9b6691f
remove IInitializableStata4626LM
kyzia551 Aug 14, 2024
482f8ca
depositWithPermit
kyzia551 Aug 14, 2024
4434e4f
disclamer on _update overload
kyzia551 Aug 14, 2024
b596ccc
some descriptions cleanup
kyzia551 Aug 14, 2024
09d6ec2
change require to revert
kyzia551 Aug 14, 2024
1ad079d
add comment to latestAnswer calc
kyzia551 Aug 14, 2024
3021817
add comment to latestAnswer calc -1
kyzia551 Aug 14, 2024
2fffe82
make ERC20AaveLMUpgradable abstract
kyzia551 Aug 14, 2024
08c95d1
update license
kyzia551 Aug 14, 2024
cef08a2
rename merger and 4626 contracts
kyzia551 Aug 14, 2024
915282d
change Upgradable to Upgradeable
kyzia551 Aug 14, 2024
7fbb149
move _disableInitializers into StataTokenV2
kyzia551 Aug 14, 2024
89d258d
rename IStata4626 to IERC4626StataToken
kyzia551 Aug 14, 2024
24ede0b
rename init on ERC4626StataToken
kyzia551 Aug 14, 2024
66b8fec
Changes on stata initializations, to follow more strict guidelines
eboadom Aug 14, 2024
4035b6f
Changes to make stata more consistent with using ERC20 extensions
eboadom Aug 14, 2024
425c7db
Fix on function called on initialize of stata
eboadom Aug 14, 2024
589abf9
Merge pull request #11 from bgd-labs/feat/stata-oz-extensions-standards
kyzia551 Aug 14, 2024
114be98
feat: improved tests
sakulstra Aug 15, 2024
87cee0e
fix: update test
sakulstra Aug 15, 2024
fbaa45f
feat: add erc4626 tests
sakulstra Aug 15, 2024
3fe5ae2
fix: migrate some more tests
sakulstra Aug 15, 2024
1906609
fix: improve tests
sakulstra Aug 15, 2024
588e1fc
refactor: move to dedicated files
sakulstra Aug 15, 2024
bd5b9a9
feat: improve tests
sakulstra Aug 15, 2024
0698a73
fix typo
kyzia551 Aug 16, 2024
37b55db
feat: add permit tests
sakulstra Aug 16, 2024
174cd79
fix: linting
sakulstra Aug 16, 2024
617be16
feat: improved docs
sakulstra Aug 16, 2024
9cf1cc0
fix: typos
sakulstra Aug 16, 2024
c6befb0
Merge pull request #12 from bgd-labs/feat/improved-tests
kyzia551 Aug 16, 2024
c193fa8
fix: use internal function
sakulstra Aug 16, 2024
dca9d8c
Merge pull request #9 from bgd-labs/ref/to-oz-extensions
sakulstra Aug 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ aave-v3-core/=src/core/
aave-v3-periphery/=src/periphery/
solidity-utils/=lib/solidity-utils/src/
forge-std/=lib/forge-std/src/
ds-test/=lib/forge-std/lib/ds-test/src/
ds-test/=lib/forge-std/lib/ds-test/src/
openzeppelin-contracts-upgradeable/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/
openzeppelin-contracts/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/
2 changes: 1 addition & 1 deletion scripts/misc/DeployAaveV3MarketBatchedBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ abstract contract DeployAaveV3MarketBatchedBase is DeployUtils, MarketInput, Scr
metadataReporter.writeJsonReportMarket(report);
}

function _loadWarnings(MarketConfig memory config, DeployFlags memory flags) internal view {
function _loadWarnings(MarketConfig memory config, DeployFlags memory flags) internal pure {
if (config.paraswapAugustusRegistry == address(0)) {
console.log(
'Warning: Paraswap Adapters will be skipped at deployment due missing config.paraswapAugustusRegistry'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import '../../interfaces/IMarketReportTypes.sol';
import {TransparentProxyFactory, ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol';
import {StaticATokenLM} from 'aave-v3-periphery/contracts/static-a-token/StaticATokenLM.sol';
import {StataTokenV2} from 'aave-v3-periphery/contracts/static-a-token/StataTokenV2.sol';
import {StaticATokenFactory} from 'aave-v3-periphery/contracts/static-a-token/StaticATokenFactory.sol';
import {IErrors} from '../../interfaces/IErrors.sol';

Expand All @@ -17,7 +17,7 @@ contract AaveV3HelpersProcedureTwo is IErrors {

staticATokenReport.transparentProxyFactory = address(new TransparentProxyFactory());
staticATokenReport.staticATokenImplementation = address(
new StaticATokenLM(IPool(pool), IRewardsController(rewardsController))
new StataTokenV2(IPool(pool), IRewardsController(rewardsController))
);
staticATokenReport.staticATokenFactoryImplementation = address(
new StaticATokenFactory(
Expand Down
42 changes: 0 additions & 42 deletions src/periphery/contracts/libraries/RayMathExplicitRounding.sol

This file was deleted.

305 changes: 305 additions & 0 deletions src/periphery/contracts/static-a-token/ERC20AaveLMUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import {ERC20Upgradeable} from 'openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol';
import {IERC20} from 'openzeppelin-contracts/contracts/interfaces/IERC20.sol';
import {SafeERC20} from 'openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol';
import {SafeCast} from 'solidity-utils/contracts/oz-common/SafeCast.sol';

import {IRewardsController} from '../rewards/interfaces/IRewardsController.sol';
import {IERC20AaveLM} from './interfaces/IERC20AaveLM.sol';

/**
* @title ERC20AaveLMUpgradeable.sol
* @notice Wrapper smart contract that supports tracking and claiming liquidity mining rewards from the Aave system
* @dev ERC20 extension, so ERC20 initialization should be done by the children contract/s
* @author BGD labs
*/
abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
using SafeCast for uint256;

/// @custom:storage-location erc7201:aave-dao.storage.ERC20AaveLM
struct ERC20AaveLMStorage {
address _referenceAsset; // a/v token to track rewards on INCENTIVES_CONTROLLER
address[] _rewardTokens;
mapping(address user => RewardIndexCache cache) _startIndex;
mapping(address user => mapping(address reward => UserRewardsData cache)) _userRewardsData;
}

// keccak256(abi.encode(uint256(keccak256("aave-dao.storage.ERC20AaveLM")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20AaveLMStorageLocation =
0x4fad66563f105be0bff96185c9058c4934b504d3ba15ca31e86294f0b01fd200;

function _getERC20AaveLMStorage() private pure returns (ERC20AaveLMStorage storage $) {
assembly {
$.slot := ERC20AaveLMStorageLocation
}
}

IRewardsController public immutable INCENTIVES_CONTROLLER;

constructor(IRewardsController rewardsController) {
INCENTIVES_CONTROLLER = rewardsController;
}

function __ERC20AaveLM_init(address referenceAsset_) internal onlyInitializing {
__ERC20AaveLM_init_unchained(referenceAsset_);
}
function __ERC20AaveLM_init_unchained(address referenceAsset_) internal onlyInitializing {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
$._referenceAsset = referenceAsset_;

if (INCENTIVES_CONTROLLER != IRewardsController(address(0))) {
refreshRewardTokens();
}
}

///@inheritdoc IERC20AaveLM
function claimRewardsOnBehalf(
address onBehalfOf,
address receiver,
address[] memory rewards
) external {
address msgSender = _msgSender();
if (msgSender != onBehalfOf && msgSender != INCENTIVES_CONTROLLER.getClaimer(onBehalfOf)) {
revert InvalidClaimer(msgSender);
}

_claimRewardsOnBehalf(onBehalfOf, receiver, rewards);
}

///@inheritdoc IERC20AaveLM
function claimRewards(address receiver, address[] memory rewards) external {
_claimRewardsOnBehalf(_msgSender(), receiver, rewards);
}

///@inheritdoc IERC20AaveLM
function claimRewardsToSelf(address[] memory rewards) external {
_claimRewardsOnBehalf(_msgSender(), _msgSender(), rewards);
}

///@inheritdoc IERC20AaveLM
function refreshRewardTokens() public override {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
address[] memory rewards = INCENTIVES_CONTROLLER.getRewardsByAsset($._referenceAsset);
for (uint256 i = 0; i < rewards.length; i++) {
_registerRewardToken(rewards[i]);
}
}

///@inheritdoc IERC20AaveLM
function collectAndUpdateRewards(address reward) public returns (uint256) {
if (reward == address(0)) {
return 0;
}

ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
address[] memory assets = new address[](1);
assets[0] = address($._referenceAsset);

return INCENTIVES_CONTROLLER.claimRewards(assets, type(uint256).max, address(this), reward);
}

///@inheritdoc IERC20AaveLM
function isRegisteredRewardToken(address reward) public view override returns (bool) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
return $._startIndex[reward].isRegistered;
}

///@inheritdoc IERC20AaveLM
function getCurrentRewardsIndex(address reward) public view returns (uint256) {
if (address(reward) == address(0)) {
return 0;
}
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
(, uint256 nextIndex) = INCENTIVES_CONTROLLER.getAssetIndex($._referenceAsset, reward);
return nextIndex;
}

///@inheritdoc IERC20AaveLM
function getTotalClaimableRewards(address reward) external view returns (uint256) {
if (reward == address(0)) {
return 0;
}

ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
address[] memory assets = new address[](1);
assets[0] = $._referenceAsset;
uint256 freshRewards = INCENTIVES_CONTROLLER.getUserRewards(assets, address(this), reward);
return IERC20(reward).balanceOf(address(this)) + freshRewards;
}

///@inheritdoc IERC20AaveLM
function getClaimableRewards(address user, address reward) external view returns (uint256) {
return _getClaimableRewards(user, reward, balanceOf(user), getCurrentRewardsIndex(reward));
}

///@inheritdoc IERC20AaveLM
function getUnclaimedRewards(address user, address reward) external view returns (uint256) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
return $._userRewardsData[user][reward].unclaimedRewards;
}

///@inheritdoc IERC20AaveLM
function getReferenceAsset() external view returns (address) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
return $._referenceAsset;
}

///@inheritdoc IERC20AaveLM
function rewardTokens() external view returns (address[] memory) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
return $._rewardTokens;
}

/**
* @notice Updates rewards for senders and receiver in a transfer (not updating rewards for address(0))
* @param from The address of the sender of tokens
* @param to The address of the receiver of tokens
*/
function _update(address from, address to, uint256 amount) internal virtual override {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
for (uint256 i = 0; i < $._rewardTokens.length; i++) {
address rewardToken = address($._rewardTokens[i]);
uint256 rewardsIndex = getCurrentRewardsIndex(rewardToken);
if (from != address(0)) {
_updateUser(from, rewardsIndex, rewardToken);
}
if (to != address(0) && from != to) {
_updateUser(to, rewardsIndex, rewardToken);
}
}
super._update(from, to, amount);
}

/**
* @notice Adding the pending rewards to the unclaimed for specific user and updating user index
* @param user The address of the user to update
* @param currentRewardsIndex The current rewardIndex
* @param rewardToken The address of the reward token
*/
function _updateUser(address user, uint256 currentRewardsIndex, address rewardToken) internal {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
uint256 balance = balanceOf(user);
if (balance > 0) {
$._userRewardsData[user][rewardToken].unclaimedRewards = _getClaimableRewards(
user,
rewardToken,
balance,
currentRewardsIndex
).toUint128();
}
$._userRewardsData[user][rewardToken].rewardsIndexOnLastInteraction = currentRewardsIndex
.toUint128();
}

/**
* @notice Compute the pending in WAD. Pending is the amount to add (not yet unclaimed) rewards in WAD.
* @param balance The balance of the user
* @param rewardsIndexOnLastInteraction The index which was on the last interaction of the user
* @param currentRewardsIndex The current rewards index in the system
* @return The amount of pending rewards in WAD
*/
function _getPendingRewards(
uint256 balance,
uint256 rewardsIndexOnLastInteraction,
uint256 currentRewardsIndex
) internal view returns (uint256) {
if (balance == 0) {
return 0;
}
return (balance * (currentRewardsIndex - rewardsIndexOnLastInteraction)) / 10 ** decimals();
}

/**
* @notice Compute the claimable rewards for a user
* @param user The address of the user
* @param reward The address of the reward
* @param balance The balance of the user in WAD
* @param currentRewardsIndex The current rewards index
* @return The total rewards that can be claimed by the user (if `fresh` flag true, after updating rewards)
*/
function _getClaimableRewards(
address user,
address reward,
uint256 balance,
uint256 currentRewardsIndex
) internal view returns (uint256) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
RewardIndexCache memory rewardsIndexCache = $._startIndex[reward];
if (!rewardsIndexCache.isRegistered) {
revert RewardNotInitialized(reward);
}

UserRewardsData memory currentUserRewardsData = $._userRewardsData[user][reward];
return
currentUserRewardsData.unclaimedRewards +
_getPendingRewards(
balance,
currentUserRewardsData.rewardsIndexOnLastInteraction == 0
? rewardsIndexCache.lastUpdatedIndex
: currentUserRewardsData.rewardsIndexOnLastInteraction,
currentRewardsIndex
);
}

/**
* @notice Claim rewards on behalf of a user and send them to a receiver
* @param onBehalfOf The address to claim on behalf of
* @param rewards The addresses of the rewards
* @param receiver The address to receive the rewards
*/
function _claimRewardsOnBehalf(
address onBehalfOf,
address receiver,
address[] memory rewards
) internal virtual {
for (uint256 i = 0; i < rewards.length; i++) {
if (address(rewards[i]) == address(0)) {
continue;
}
uint256 currentRewardsIndex = getCurrentRewardsIndex(rewards[i]);
uint256 balance = balanceOf(onBehalfOf);
uint256 userReward = _getClaimableRewards(
onBehalfOf,
rewards[i],
balance,
currentRewardsIndex
);
uint256 totalRewardTokenBalance = IERC20(rewards[i]).balanceOf(address(this));
uint256 unclaimedReward = 0;

if (userReward > totalRewardTokenBalance) {
totalRewardTokenBalance += collectAndUpdateRewards(address(rewards[i]));
}

if (userReward > totalRewardTokenBalance) {
unclaimedReward = userReward - totalRewardTokenBalance;
userReward = totalRewardTokenBalance;
}
if (userReward > 0) {
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
$._userRewardsData[onBehalfOf][rewards[i]].unclaimedRewards = unclaimedReward.toUint128();
$
._userRewardsData[onBehalfOf][rewards[i]]
.rewardsIndexOnLastInteraction = currentRewardsIndex.toUint128();
SafeERC20.safeTransfer(IERC20(rewards[i]), receiver, userReward);
}
}
}

/**
* @notice Initializes a new rewardToken
* @param reward The reward token to be registered
*/
function _registerRewardToken(address reward) internal {
if (isRegisteredRewardToken(reward)) return;
uint256 startIndex = getCurrentRewardsIndex(reward);

ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
$._rewardTokens.push(reward);
$._startIndex[reward] = RewardIndexCache(true, startIndex.toUint240());

emit RewardTokenRegistered(reward, startIndex);
}
}
Loading