Skip to content

Commit 45ae3c0

Browse files
authored
Merge pull request #71 from bgd-labs/fix/mixbytes
fix: mixbytes stata fixes
2 parents 077c99e + c2f6db7 commit 45ae3c0

File tree

8 files changed

+156
-92
lines changed

8 files changed

+156
-92
lines changed

src/contracts/extensions/static-a-token/ERC20AaveLMUpgradeable.sol

+8-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
2222
struct ERC20AaveLMStorage {
2323
address _referenceAsset; // a/v token to track rewards on INCENTIVES_CONTROLLER
2424
address[] _rewardTokens;
25-
mapping(address user => RewardIndexCache cache) _startIndex;
25+
mapping(address reward => RewardIndexCache cache) _startIndex;
2626
mapping(address user => mapping(address reward => UserRewardsData cache)) _userRewardsData;
2727
}
2828

@@ -39,6 +39,9 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
3939
IRewardsController public immutable INCENTIVES_CONTROLLER;
4040

4141
constructor(IRewardsController rewardsController) {
42+
if (address(rewardsController) == address(0)) {
43+
revert ZeroIncentivesControllerIsForbidden();
44+
}
4245
INCENTIVES_CONTROLLER = rewardsController;
4346
}
4447

@@ -195,11 +198,11 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
195198
}
196199

197200
/**
198-
* @notice Compute the pending in WAD. Pending is the amount to add (not yet unclaimed) rewards in WAD.
201+
* @notice Compute the pending in asset decimals. Pending is the amount to add (not yet unclaimed) rewards in asset decimals.
199202
* @param balance The balance of the user
200203
* @param rewardsIndexOnLastInteraction The index which was on the last interaction of the user
201204
* @param currentRewardsIndex The current rewards index in the system
202-
* @return The amount of pending rewards in WAD
205+
* @return The amount of pending rewards in asset decimals
203206
*/
204207
function _getPendingRewards(
205208
uint256 balance,
@@ -216,7 +219,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
216219
* @notice Compute the claimable rewards for a user
217220
* @param user The address of the user
218221
* @param reward The address of the reward
219-
* @param balance The balance of the user in WAD
222+
* @param balance The balance of the user in asset decimals
220223
* @param currentRewardsIndex The current rewards index
221224
* @return The total rewards that can be claimed by the user (if `fresh` flag true, after updating rewards)
222225
*/
@@ -299,7 +302,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM {
299302

300303
ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage();
301304
$._rewardTokens.push(reward);
302-
$._startIndex[reward] = RewardIndexCache(true, startIndex.toUint240());
305+
$._startIndex[reward] = RewardIndexCache(true, startIndex.toUint248());
303306

304307
emit RewardTokenRegistered(reward, startIndex);
305308
}

src/contracts/extensions/static-a-token/ERC4626StataTokenUpgradeable.sol

+24-5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St
7575

7676
///@inheritdoc IERC4626StataToken
7777
function depositATokens(uint256 assets, address receiver) external returns (uint256) {
78+
// because aToken is rebasable, we allow user to specify more then he has to compensate growth during the tx mining
79+
uint256 actualUserBalance = IERC20(aToken()).balanceOf(_msgSender());
80+
if (assets > actualUserBalance) {
81+
assets = actualUserBalance;
82+
}
83+
7884
uint256 shares = previewDeposit(assets);
7985
_deposit(_msgSender(), receiver, assets, shares, false);
8086

@@ -89,14 +95,27 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St
8995
SignatureParams memory sig,
9096
bool depositToAave
9197
) external returns (uint256) {
92-
IERC20Permit assetToDeposit = IERC20Permit(
93-
depositToAave ? asset() : address(_getERC4626StataTokenStorage()._aToken)
94-
);
98+
address assetToDeposit = depositToAave ? asset() : aToken();
9599

96100
try
97-
assetToDeposit.permit(_msgSender(), address(this), assets, deadline, sig.v, sig.r, sig.s)
101+
IERC20Permit(assetToDeposit).permit(
102+
_msgSender(),
103+
address(this),
104+
assets,
105+
deadline,
106+
sig.v,
107+
sig.r,
108+
sig.s
109+
)
98110
{} catch {}
99111

112+
// because aToken is rebasable, we allow user to specify more then he has to compensate growth during the tx mining
113+
// to make it consistent, we keep the same behaviour for the normal underlying too
114+
uint256 actualUserBalance = IERC20(assetToDeposit).balanceOf(_msgSender());
115+
if (assets > actualUserBalance) {
116+
assets = actualUserBalance;
117+
}
118+
100119
uint256 shares = previewDeposit(assets);
101120
_deposit(_msgSender(), receiver, assets, shares, depositToAave);
102121
return shares;
@@ -180,7 +199,7 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St
180199
// return remaining supply cap margin
181200
uint256 currentSupply = (IAToken(reserveData.aTokenAddress).scaledTotalSupply() +
182201
reserveData.accruedToTreasury).mulDiv(_rate(), RAY, Math.Rounding.Ceil);
183-
return currentSupply > supplyCap ? 0 : supplyCap - currentSupply;
202+
return currentSupply >= supplyCap ? 0 : supplyCap - currentSupply;
184203
}
185204

186205
///@inheritdoc IERC4626StataToken

src/contracts/extensions/static-a-token/README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ For this project, the security procedures applied/being finished are:
4545
The `StaticATokenLM`(v1) was based on solmate.
4646
To allow more flexibility the new `StataTokenV2`(v2) is based on [openzeppelin-contracts-upgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable) which relies on [`ERC-7201`](https://eips.ethereum.org/EIPS/eip-7201) which isolates storage per contract.
4747

48-
The implementation is seperated in two ERC20 extentions and one actual "merger" contract stitching functionality together.
48+
The implementation is separated in two ERC20 extensions and one actual "merger" contract stitching functionality together.
4949

5050
1. `ERC20AaveLM` is an abstract contract implementing the forwarding of liquidity mining from an underlying AaveERC20 - an ERC20 implementing `scaled` functions - to holders of a wrapper contract.
5151
The abstract contract is following `ERC-7201` and acts as erc20 extension.
@@ -70,13 +70,13 @@ To account for that specific use-case a dedicated `depositWithPermit` was added.
7070
### Direct AToken Interaction
7171

7272
In v1 deposit was overleaded to allow underlying & aToken deposits.
73-
While this appraoch was fine it seemed unclean and caused some confusion with integrators.
73+
While this approach was fine it seemed unclean and caused some confusion with integrators.
7474
Therefore v2 introduces dedicated `depositATokens` and `redeemATokens` methods.
7575

76-
#### PermissionlessRescuable
76+
#### Rescuable
7777

78-
[PermissionlessRescuable](https://github.com/bgd-labs/solidity-utils/blob/main/src/contracts/utils/PermissionlessRescuable.sol) has been applied to
79-
the `StataTokenV2` which will allow the anyone to rescue surplus tokens on the contract to the treasury.
78+
[Rescuable](https://github.com/bgd-labs/solidity-utils/blob/main/src/contracts/utils/Rescuable.sol) has been applied to
79+
the `StataTokenV2` which will allow the aclAdmin to rescue surplus tokens on the contract to the treasury.
8080

8181
#### Pausability
8282

src/contracts/extensions/static-a-token/interfaces/IERC20AaveLM.sol

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ pragma solidity ^0.8.10;
33

44
interface IERC20AaveLM {
55
struct UserRewardsData {
6-
uint128 rewardsIndexOnLastInteraction; // (in RAYs)
7-
uint128 unclaimedRewards; // (in RAYs)
6+
uint128 rewardsIndexOnLastInteraction;
7+
uint128 unclaimedRewards;
88
}
99

1010
struct RewardIndexCache {
1111
bool isRegistered;
1212
uint248 lastUpdatedIndex;
1313
}
1414

15+
error ZeroIncentivesControllerIsForbidden();
1516
error InvalidClaimer(address claimer);
1617
error RewardNotInitialized(address reward);
1718

@@ -58,18 +59,18 @@ interface IERC20AaveLM {
5859
function getTotalClaimableRewards(address reward) external view returns (uint256);
5960

6061
/**
61-
* @notice Get the total claimable rewards for a user in WAD
62+
* @notice Get the total claimable rewards for a user in asset decimals
6263
* @param user The address of the user
6364
* @param reward The reward to claim
64-
* @return uint256 The claimable amount of rewards in WAD
65+
* @return uint256 The claimable amount of rewards in asset decimals
6566
*/
6667
function getClaimableRewards(address user, address reward) external view returns (uint256);
6768

6869
/**
69-
* @notice The unclaimed rewards for a user in WAD
70+
* @notice The unclaimed rewards for a user in asset decimals
7071
* @param user The address of the user
7172
* @param reward The reward to claim
72-
* @return uint256 The unclaimed amount of rewards in WAD
73+
* @return uint256 The unclaimed amount of rewards in asset decimals
7374
*/
7475
function getUnclaimedRewards(address user, address reward) external view returns (uint256);
7576

tests/extensions/static-a-token/ERC20AaveLMUpgradable.t.sol

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ contract ERC20AaveLMUpgradableTest is TestnetProcedures {
9393
);
9494
}
9595

96+
function test_zeroIncentivesController() external {
97+
vm.expectRevert(IERC20AaveLM.ZeroIncentivesControllerIsForbidden.selector);
98+
new MockERC20AaveLMUpgradeable(IRewardsController(address(0)));
99+
}
100+
96101
function test_noRewardsInitialized() external {
97102
vm.expectRevert(
98103
abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, rewardToken)

0 commit comments

Comments
 (0)