Skip to content

Commit c33f294

Browse files
feat: aToken Vault Revenut-Splitter Owner - Initial impl
1 parent dc25b5a commit c33f294

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
// All Rights Reserved © AaveCo
3+
4+
pragma solidity ^0.8.10;
5+
6+
import {Ownable} from "@openzeppelin/access/Ownable.sol";
7+
import {IATokenVault} from "./interfaces/IATokenVault.sol";
8+
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";
9+
import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
10+
11+
contract ATokenVaultOwner is Ownable {
12+
using SafeERC20 for IERC20;
13+
14+
event RecipientSet(address indexed recipient, uint16 shareInBps);
15+
16+
event RevenueSplit(address indexed recipient, address indexed asset, uint256 amount);
17+
18+
uint256 public constant TOTAL_SHARE_IN_BPS = 10_000; // 100.00%
19+
20+
IATokenVault public immutable VAULT;
21+
22+
struct Recipient {
23+
address addr;
24+
uint16 shareInBps;
25+
}
26+
27+
Recipient[] internal _recipients;
28+
29+
constructor(address vault, address owner, Recipient[] memory recipients) {
30+
VAULT = IATokenVault(vault);
31+
_setRecipients(recipients);
32+
_transferOwnership(owner);
33+
}
34+
35+
function transferVaultOwnership(address newOwner) public onlyOwner {
36+
_claimRewards();
37+
_withdrawFees();
38+
Ownable(address(VAULT)).transferOwnership(newOwner);
39+
}
40+
41+
// Fees - Percentage of the yield earned by the vault (aToken)
42+
// TODO: Does it make sense to allow partial withdrawal? I got rid of the `amount` param by doing a full one always
43+
// TODO: Any reason to make it onlyOwner?
44+
function withdrawFees() public {
45+
_withdrawFees();
46+
}
47+
48+
// Rewards - Percentage of the token incentives granted to the vault
49+
// TODO: Any reason to make it onlyOwner?
50+
function claimRewards() external {
51+
_claimRewards();
52+
}
53+
54+
// TODO: Any reason to make it onlyOwner?
55+
function splitRevenue(address[] calldata assets) public {
56+
Recipient[] memory recipients = _recipients;
57+
for (uint256 i = 0; i < assets.length; i++) {
58+
uint256 amountToSplit = IERC20(assets[i]).balanceOf(address(this));
59+
for (uint256 j = 0; j < recipients.length; j++) {
60+
uint256 amountForRecipient = amountToSplit * recipients[j].shareInBps / TOTAL_SHARE_IN_BPS;
61+
if (amountForRecipient > 0) {
62+
IERC20(assets[i]).safeTransfer(recipients[j].addr, amountForRecipient);
63+
}
64+
emit RevenueSplit(recipients[j].addr, assets[i], amountForRecipient);
65+
}
66+
}
67+
}
68+
69+
// TODO: address(0) used in the event instead of ad-hoc event for native revenue?
70+
// TODO: Should we assume recipients will succeed at receiving native? If one fails, the whole call fails.
71+
function splitRevenue() public {
72+
uint256 amountToSplit = address(this).balance;
73+
for (uint256 j = 0; j < _recipients.length; j++) {
74+
uint256 amountForRecipient = amountToSplit * _recipients[j].shareInBps / TOTAL_SHARE_IN_BPS;
75+
if (amountForRecipient > 0) {
76+
(bool transferSucceeded, ) = _recipients[j].addr.call{value: amountForRecipient}("");
77+
require(transferSucceeded, "NATIVE_TRANSFER_FAILED");
78+
}
79+
emit RevenueSplit(_recipients[j].addr, address(0), amountForRecipient);
80+
}
81+
}
82+
83+
function emergencyRescue(address asset, address to, uint256 amount) public onlyOwner {
84+
VAULT.emergencyRescue(asset, to, amount);
85+
}
86+
87+
function setFee(uint256 newFee) public onlyOwner {
88+
VAULT.setFee(newFee);
89+
}
90+
91+
function getRecipients() public view returns (Recipient[] memory) {
92+
return _recipients;
93+
}
94+
95+
function _claimRewards() internal {
96+
VAULT.claimRewards(address(this));
97+
}
98+
99+
function _withdrawFees() internal {
100+
uint256 feesToWithdraw = VAULT.getClaimableFees();
101+
VAULT.withdrawFees(address(this), feesToWithdraw);
102+
}
103+
104+
// Assumes no duplicates
105+
function _setRecipients(Recipient[] memory recipients) internal {
106+
uint256 accumulatedShareInBps = 0;
107+
for (uint256 i = 0; i < recipients.length; i++) {
108+
require(recipients[i].shareInBps > 0, "BPS_SHARE_CANNOT_BE_ZERO");
109+
accumulatedShareInBps += recipients[i].shareInBps;
110+
_recipients.push(recipients[i]);
111+
emit RecipientSet(recipients[i].addr, recipients[i].shareInBps);
112+
}
113+
require(accumulatedShareInBps == TOTAL_SHARE_IN_BPS, "WRONG_BPS_SUM");
114+
}
115+
}

0 commit comments

Comments
 (0)