-
Notifications
You must be signed in to change notification settings - Fork 75
feat: add new contract to support merkl reward claiming #112
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
base: main
Are you sure you want to change the base?
Changes from all commits
34ae3cb
a2bdcc3
99a4131
75990a7
ffc8063
adabf30
7d5c2f0
558c9ef
510fc9c
4232f8c
4db183a
7fedcd1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| // All Rights Reserved © AaveCo | ||
|
|
||
| pragma solidity ^0.8.10; | ||
|
|
||
| import {IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPoolAddressesProvider.sol"; | ||
| import {IERC20Upgradeable} from "@openzeppelin-upgradeable/interfaces/IERC20Upgradeable.sol"; | ||
| import {SafeERC20Upgradeable} from "@openzeppelin-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; | ||
| import {IMerklDistributor} from "./dependencies/merkl/DistributorInterface.sol"; | ||
| import {ATokenVault} from "./ATokenVault.sol"; | ||
| import {IATokenVaultMerklRewardClaimer} from "./interfaces/IATokenVaultMerklRewardClaimer.sol"; | ||
|
|
||
| /** | ||
| * @title ATokenVaultMerklRewardClaimer | ||
| * @author Aave Protocol | ||
| * @notice ATokenVault, with Merkl reward claiming capability | ||
| */ | ||
| contract ATokenVaultMerklRewardClaimer is ATokenVault, IATokenVaultMerklRewardClaimer { | ||
| using SafeERC20Upgradeable for IERC20Upgradeable; | ||
|
|
||
| /** | ||
| * @dev Constructor. | ||
| * @param underlying The underlying ERC20 asset which can be supplied to Aave | ||
| * @param referralCode The Aave referral code to use for deposits from this vault | ||
| * @param poolAddressesProvider The address of the Aave v3 Pool Addresses Provider | ||
| */ | ||
| constructor(address underlying, uint16 referralCode, IPoolAddressesProvider poolAddressesProvider) | ||
| ATokenVault(underlying, referralCode, poolAddressesProvider) | ||
| {} | ||
|
|
||
| /// @inheritdoc IATokenVaultMerklRewardClaimer | ||
| function claimMerklRewards( | ||
| address[] calldata tokens, | ||
| uint256[] calldata amounts, | ||
| bytes32[][] calldata proofs, | ||
| address[] calldata rewardTokensToForward, | ||
| address destination | ||
| ) | ||
| public | ||
| override | ||
| onlyOwner | ||
| { | ||
| require(_s.merklDistributor != address(0), "MERKL_DISTRIBUTOR_NOT_SET"); | ||
| require(tokens.length == amounts.length && tokens.length == proofs.length, "ARRAY_LENGTH_MISMATCH"); | ||
|
|
||
| uint256[] memory currentBalancesOfRewardTokens = new uint256[](rewardTokensToForward.length); | ||
| for (uint256 i = 0; i < rewardTokensToForward.length; i++) { | ||
| currentBalancesOfRewardTokens[i] = IERC20Upgradeable(rewardTokensToForward[i]).balanceOf(address(this)); | ||
| } | ||
|
|
||
| address[] memory users = new address[](tokens.length); | ||
| for (uint256 i = 0; i < tokens.length; i++) { | ||
| users[i] = address(this); | ||
| } | ||
| IMerklDistributor(_s.merklDistributor).claim(users, tokens, amounts, proofs); | ||
| emit MerklRewardsClaimed(_s.merklDistributor, tokens, amounts); | ||
|
|
||
| for (uint256 i = 0; i < rewardTokensToForward.length; i++) { | ||
| uint256 newBalance = IERC20Upgradeable(rewardTokensToForward[i]).balanceOf(address(this)); | ||
| uint256 amountToForward = newBalance - currentBalancesOfRewardTokens[i]; | ||
| if (amountToForward > 0) { | ||
| IERC20Upgradeable(rewardTokensToForward[i]).safeTransfer(destination, amountToForward); | ||
| emit MerklRewardsTokenForwarded(rewardTokensToForward[i], destination, amountToForward); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// @inheritdoc IATokenVaultMerklRewardClaimer | ||
| /// @dev Allow setting address(0) to reset the Merkl distributor | ||
| function setMerklDistributor(address merklDistributor) external override onlyOwner { | ||
| address currentMerklDistributor = _s.merklDistributor; | ||
| _s.merklDistributor = merklDistributor; | ||
| emit MerklDistributorUpdated(currentMerklDistributor, merklDistributor); | ||
| } | ||
|
|
||
| /// @inheritdoc IATokenVaultMerklRewardClaimer | ||
| function getMerklDistributor() external view override returns (address) { | ||
| return _s.merklDistributor; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
| // Based on implementation from https://github.com/AngleProtocol/merkl-contracts/blob/b7bd0e65a3f366e4041bc83494cbd981f8852b16/contracts/Distributor.sol#L202 | ||
| pragma solidity ^0.8.10; | ||
|
|
||
| interface IMerklDistributor { | ||
| function claim( | ||
| address[] calldata users, | ||
| address[] calldata tokens, | ||
| uint256[] calldata amounts, | ||
| bytes32[][] calldata proofs | ||
| ) external; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| // All Rights Reserved © AaveCo | ||
|
|
||
| pragma solidity ^0.8.10; | ||
|
|
||
| /** | ||
| * @title IATokenVaultMerklRewardClaimer | ||
| * @author Aave Protocol | ||
| * @notice Defines the basic interface of the ATokenVaultMerklRewardClaimer | ||
| */ | ||
| interface IATokenVaultMerklRewardClaimer { | ||
| /** | ||
| * @dev Emitted when Merkl rewards are claimed by the vault contract | ||
| * @dev The token addresses do not always match the actual tokens received by the vault contract after rewards are claimed | ||
| * @dev The amounts do not always match the actual amounts received (the amounts may be the cumulative rewards earned by the user) | ||
| * @param distributor Address of the Merkl distributor contract | ||
| * @param tokens Addresses of the ERC-20 reward tokens claimed (the tokens passed as params to the Merkl distributor contract) | ||
| * @param amounts Amounts of the reward tokens claimed for each token (the amounts passed as params to the Merkl distributor contract) | ||
| */ | ||
| event MerklRewardsClaimed(address indexed distributor, address[] tokens, uint256[] amounts); | ||
|
|
||
| /** | ||
| * @dev Emitted when a reward token is forwarded to a destination | ||
| * @param token Address of the ERC-20 reward token | ||
| * @param destination Address of the destination receiving the reward token | ||
| * @param amount Amount of the reward token transferred to the destination | ||
| */ | ||
| event MerklRewardsTokenForwarded(address indexed token, address indexed destination, uint256 indexed amount); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could do smt similar to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no strong preference, but I wanted to avoid emitting the event if the Imagine it would look like this: We can also pass in an array of destinations to |
||
|
|
||
| /** | ||
| * @dev Emitted when the Merkl distributor address is updated | ||
| * @param oldMerklDistributor The old address of the Merkl distributor contract | ||
| * @param newMerklDistributor The new address of the Merkl distributor contract | ||
| */ | ||
| event MerklDistributorUpdated(address indexed oldMerklDistributor, address indexed newMerklDistributor); | ||
|
|
||
| /** | ||
| * @notice Claims Merkl protocol rewards accrued from vault deposits. | ||
| * @dev Only callable by the owner | ||
| * @dev Merkl distributor address must be set | ||
| * @dev The IMerklDistributor.claim(...) function does not return a list of tokens and amounts the users actually receive | ||
| * @dev The order of the tokens, amounts, and proofs must align with eachother | ||
| * @param tokens Addresses of the ERC-20 reward tokens to claim (the tokens passed as params to the Merkl distributor contract) | ||
| * @param amounts Amounts of the reward tokens to claim for each token | ||
| * @param proofs Merkl proof passed to the Merkl distributor contract | ||
aa-eyup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @param rewardTokensToForward Addresses of the ERC-20 tokens received by the vault contract from reward claiming (to be set if rewards should be forwarded to the `destination`) | ||
| * @param destination Address to send the received tokens to | ||
| */ | ||
| function claimMerklRewards( | ||
| address[] calldata tokens, | ||
| uint256[] calldata amounts, | ||
| bytes32[][] calldata proofs, | ||
| address[] calldata rewardTokensToForward, | ||
| address destination | ||
| ) external; | ||
|
|
||
| /** | ||
| * @notice Sets the Merkl distributor address for the vault uses to claim Merkl rewards. | ||
| * @dev Only callable by the owner | ||
| * @param merklDistributor Address of the new Merkl distributor contract | ||
| */ | ||
| function setMerklDistributor(address merklDistributor) external; | ||
|
|
||
| /** | ||
| * @notice Returns the address of the Merkl distributor contract. | ||
| * @return The address of the Merkl distributor contract | ||
| */ | ||
| function getMerklDistributor() external view returns (address); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.