Skip to content

Commit 422ca9b

Browse files
committed
test: add unit tests for ATokenVaultMerklRewardClaimer
1 parent ba801e1 commit 422ca9b

File tree

4 files changed

+230
-7
lines changed

4 files changed

+230
-7
lines changed

src/ATokenVaultMerklRewardClaimer.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ contract ATokenVaultMerklRewardClaimer is ATokenVault, IATokenVaultMerklRewardCl
5353
}
5454

5555
/// @inheritdoc IATokenVaultMerklRewardClaimer
56+
/// @dev Allow setting address(0) to reset the Merkl distributor
5657
function setMerklDistributor(address merklDistributor) external override onlyOwner {
57-
require(merklDistributor != address(0), "ZERO_ADDRESS_NOT_VALID");
5858
address currentMerklDistributor = _s.merklDistributor;
5959
_s.merklDistributor = merklDistributor;
6060
emit MerklDistributorUpdated(currentMerklDistributor, merklDistributor);

test/ATokenVaultMerklRewardClaimer.t.sol renamed to test/ATokenVaultMerklRewardClaimerFork.t.sol

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ import {IATokenVaultMerklRewardClaimer} from "../src/interfaces/IATokenVaultMerk
1515
import {IATokenVault} from "../src/interfaces/IATokenVault.sol";
1616

1717
/**
18-
* @title ATokenVaultMerklRewardClaimerTest
19-
* @notice Test suite for claiming Merkl rewards from the ATokenVault
20-
* @dev Forks Ethereum mainnet to etch the ATokenVault onto an address that has claimable rewards as of the forked block
18+
* @title ATokenVaultMerklRewardClaimerForkTest
19+
* @notice Test suite for claiming Merkl rewards from the ATokenVault on a forked Ethereum mainnet
20+
* @dev Etches the ATokenVault onto an address that has claimable rewards as of the forked block
2121
* @dev foundry.toml must use evm_version = 'cancun' to run this test
2222
*/
23-
contract ATokenVaultMerklRewardClaimerTest is ATokenVaultBaseTest {
23+
contract ATokenVaultMerklRewardClaimerForkTest is ATokenVaultBaseTest {
2424
using stdStorage for StdStorage;
2525
uint256 ethereumFork;
2626
// The block before rewards claim in tx: https://etherscan.io/tx/0x42ef6b499d1b6e96a4250f2d5a005b60173386e2ac3a3e424aa407db3da802ea
@@ -134,10 +134,13 @@ contract ATokenVaultMerklRewardClaimerTest is ATokenVaultBaseTest {
134134
IATokenVaultMerklRewardClaimer(address(vault)).setMerklDistributor(MERKL_DISTRIBUTOR);
135135
}
136136

137-
function testSetMerklDistributorRevertsIfZeroAddress() public {
137+
function testSetMerklDistributorAllowsZeroAddress() public {
138+
vm.prank(OWNER);
139+
IATokenVaultMerklRewardClaimer(address(vault)).setMerklDistributor(MERKL_DISTRIBUTOR);
140+
assertEq(IATokenVaultMerklRewardClaimer(address(vault)).getMerklDistributor(), MERKL_DISTRIBUTOR);
138141
vm.prank(OWNER);
139-
vm.expectRevert(bytes("ZERO_ADDRESS_NOT_VALID"));
140142
IATokenVaultMerklRewardClaimer(address(vault)).setMerklDistributor(address(0));
143+
assertEq(IATokenVaultMerklRewardClaimer(address(vault)).getMerklDistributor(), address(0));
141144
}
142145

143146
function testSetMerklDistributorRevertsIfNotOwner() public {
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.10;
4+
5+
import "forge-std/Test.sol";
6+
7+
import {IAToken} from "@aave-v3-core/interfaces/IAToken.sol";
8+
9+
import {IATokenVaultMerklRewardClaimer} from "../src/interfaces/IATokenVaultMerklRewardClaimer.sol";
10+
11+
import {MockAavePoolAddressesProvider} from "./mocks/MockAavePoolAddressesProvider.sol";
12+
import {MockAavePool} from "./mocks/MockAavePool.sol";
13+
import {MockAToken} from "./mocks/MockAToken.sol";
14+
import {MockDAI} from "./mocks/MockDAI.sol";
15+
import {MockMerklDistributor} from "./mocks/MockMerklDistributor.sol";
16+
import {ATokenVaultBaseTest} from "./ATokenVaultBaseTest.t.sol";
17+
import "./utils/Constants.sol";
18+
19+
/**
20+
* @title ATokenVaultMerklRewardsClaimerTest
21+
* @notice Unit test suite for claiming Merkl rewards from the ATokenVault
22+
*/
23+
contract ATokenVaultMerklRewardsClaimerTest is ATokenVaultBaseTest {
24+
MockMerklDistributor merklDistributor;
25+
MockAavePoolAddressesProvider poolAddrProvider;
26+
MockAavePool pool;
27+
MockAToken aDai;
28+
MockDAI dai;
29+
IATokenVaultMerklRewardClaimer vaultMerklRewardClaimer;
30+
31+
function setUp() public override {
32+
// NOTE: Real DAI has non-standard permit. These tests assume tokens with standard permit
33+
dai = new MockDAI();
34+
35+
aDai = new MockAToken(address(dai));
36+
pool = new MockAavePool();
37+
pool.mockReserve(address(dai), aDai);
38+
poolAddrProvider = new MockAavePoolAddressesProvider(address(pool));
39+
40+
vaultAssetAddress = address(aDai);
41+
42+
pool.setReserveConfigMap(RESERVE_CONFIG_MAP_UNCAPPED_ACTIVE);
43+
44+
merklDistributor = new MockMerklDistributor();
45+
46+
// Sets the `vault`, but we will not use the vault deployment
47+
_deployATokenVaultMerklRewardClaimer(address(dai), address(poolAddrProvider));
48+
vaultMerklRewardClaimer = IATokenVaultMerklRewardClaimer(address(vault));
49+
}
50+
51+
function testClaimMerklRewards() public {
52+
_setMerklDistributor();
53+
54+
bytes32 proof = keccak256("proof1");
55+
(address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) = _buildMerklRewardsClaimData(address(dai), 1000, proof);
56+
57+
vm.prank(OWNER);
58+
vaultMerklRewardClaimer.claimMerklRewards(rewardTokens, amounts, proofs);
59+
60+
assertEq(merklDistributor.getLastUsers().length, 1);
61+
assertEq(merklDistributor.getLastUsers()[0], address(vaultMerklRewardClaimer));
62+
assertEq(merklDistributor.getLastTokens().length, 1);
63+
assertEq(merklDistributor.getLastTokens()[0], address(dai));
64+
assertEq(merklDistributor.getLastAmounts().length, 1);
65+
assertEq(merklDistributor.getLastAmounts()[0], 1000);
66+
assertEq(merklDistributor.getLastProofs().length, 1);
67+
assertEq(merklDistributor.getLastProofs()[0][0], proof);
68+
}
69+
70+
function testClaimMerklRewardsEmitsEvent() public {
71+
_setMerklDistributor();
72+
73+
bytes32 proof = keccak256("proof1");
74+
(address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) = _buildMerklRewardsClaimData(address(dai), 1000, proof);
75+
vm.prank(OWNER);
76+
vm.expectEmit(true, false, false, true, address(vaultMerklRewardClaimer));
77+
emit IATokenVaultMerklRewardClaimer.MerklRewardsClaimed(rewardTokens, amounts);
78+
vaultMerklRewardClaimer.claimMerklRewards(rewardTokens, amounts, proofs);
79+
}
80+
81+
function testClaimMerklRewardsRevertsIfMerklDistributorNotSet() public {
82+
bytes32 proof = keccak256("proof1");
83+
(address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) = _buildMerklRewardsClaimData(address(dai), 1000, proof);
84+
vm.prank(OWNER);
85+
vm.expectRevert(bytes("MERKL_DISTRIBUTOR_NOT_SET"));
86+
vaultMerklRewardClaimer.claimMerklRewards(rewardTokens, amounts, proofs);
87+
}
88+
89+
function testClaimMerklRewardsRevertsIfNotOwner() public {
90+
bytes32 proof = keccak256("proof1");
91+
(address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) = _buildMerklRewardsClaimData(address(dai), 1000, proof);
92+
vm.expectRevert(bytes("Ownable: caller is not the owner"));
93+
vaultMerklRewardClaimer.claimMerklRewards(rewardTokens, amounts, proofs);
94+
}
95+
96+
function testClaimMerklRewardsRevertsIfMerklDistributorReverts() public {
97+
_setMerklDistributor();
98+
99+
bytes32 proof = keccak256("proof1");
100+
(address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) = _buildMerklRewardsClaimData(address(dai), 1000, proof);
101+
string memory revertReason = "revert because of insufficient balance";
102+
merklDistributor.setShouldRevert(true, revertReason);
103+
vm.expectRevert(bytes(revertReason));
104+
vm.prank(OWNER);
105+
vaultMerklRewardClaimer.claimMerklRewards(rewardTokens, amounts, proofs);
106+
}
107+
108+
function testSetMerklDistributor() public {
109+
vm.prank(OWNER);
110+
vaultMerklRewardClaimer.setMerklDistributor(address(merklDistributor));
111+
assertEq(vaultMerklRewardClaimer.getMerklDistributor(), address(merklDistributor));
112+
}
113+
114+
function testSetMerklDistributorEmitsEvent() public {
115+
vm.prank(OWNER);
116+
vm.expectEmit(true, false, false, true, address(vaultMerklRewardClaimer));
117+
emit IATokenVaultMerklRewardClaimer.MerklDistributorUpdated(address(0), address(merklDistributor));
118+
vaultMerklRewardClaimer.setMerklDistributor(address(merklDistributor));
119+
120+
address newMerklDistributor = makeAddr("newMerklDistributor");
121+
vm.prank(OWNER);
122+
vm.expectEmit(true, false, false, true, address(vaultMerklRewardClaimer));
123+
emit IATokenVaultMerklRewardClaimer.MerklDistributorUpdated(address(merklDistributor), newMerklDistributor);
124+
vaultMerklRewardClaimer.setMerklDistributor(newMerklDistributor);
125+
}
126+
127+
function testSetMerklDistributorAllowsZeroAddress() public {
128+
vm.prank(OWNER);
129+
vaultMerklRewardClaimer.setMerklDistributor(address(merklDistributor));
130+
assertEq(vaultMerklRewardClaimer.getMerklDistributor(), address(merklDistributor));
131+
vm.prank(OWNER);
132+
vaultMerklRewardClaimer.setMerklDistributor(address(0));
133+
assertEq(vaultMerklRewardClaimer.getMerklDistributor(), address(0));
134+
}
135+
136+
function testSetMerklDistributorRevertsIfNotOwner() public {
137+
vm.expectRevert(bytes("Ownable: caller is not the owner"));
138+
vaultMerklRewardClaimer.setMerklDistributor(address(merklDistributor));
139+
}
140+
141+
function _setMerklDistributor() internal {
142+
vm.prank(OWNER);
143+
vaultMerklRewardClaimer.setMerklDistributor(address(merklDistributor));
144+
}
145+
146+
function _buildMerklRewardsClaimData(address token, uint256 amount, bytes32 proof) internal returns (address[] memory rewardTokens, uint256[] memory amounts, bytes32[][] memory proofs) {
147+
rewardTokens = new address[](1);
148+
rewardTokens[0] = token;
149+
amounts = new uint256[](1);
150+
amounts[0] = amount;
151+
proofs = new bytes32[][](1);
152+
proofs[0] = new bytes32[](1);
153+
proofs[0][0] = proof;
154+
}
155+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.10;
4+
5+
contract MockMerklDistributor {
6+
bool public claimCalled = false;
7+
address[] public lastUsers;
8+
address[] public lastTokens;
9+
uint256[] public lastAmounts;
10+
bytes32[][] public lastProofs;
11+
bool public shouldRevert = false;
12+
string public revertReason = "";
13+
14+
function claim(
15+
address[] calldata users,
16+
address[] calldata tokens,
17+
uint256[] calldata amounts,
18+
bytes32[][] calldata proofs
19+
) external {
20+
if (shouldRevert) {
21+
revert(revertReason);
22+
}
23+
24+
claimCalled = true;
25+
26+
// Store the call data to be checked in the test
27+
delete lastUsers;
28+
delete lastTokens;
29+
delete lastAmounts;
30+
delete lastProofs;
31+
for (uint256 i = 0; i < users.length; i++) {
32+
lastUsers.push(users[i]);
33+
}
34+
for (uint256 i = 0; i < tokens.length; i++) {
35+
lastTokens.push(tokens[i]);
36+
}
37+
for (uint256 i = 0; i < amounts.length; i++) {
38+
lastAmounts.push(amounts[i]);
39+
}
40+
for (uint256 i = 0; i < proofs.length; i++) {
41+
lastProofs.push(proofs[i]);
42+
}
43+
}
44+
45+
function setShouldRevert(bool _shouldRevert, string memory _reason) external {
46+
shouldRevert = _shouldRevert;
47+
revertReason = _reason;
48+
}
49+
50+
function getLastUsers() external view returns (address[] memory) {
51+
return lastUsers;
52+
}
53+
54+
function getLastTokens() external view returns (address[] memory) {
55+
return lastTokens;
56+
}
57+
58+
function getLastAmounts() external view returns (uint256[] memory) {
59+
return lastAmounts;
60+
}
61+
62+
function getLastProofs() external view returns (bytes32[][] memory) {
63+
return lastProofs;
64+
}
65+
}

0 commit comments

Comments
 (0)