Skip to content

Commit 3851611

Browse files
committed
feat: aToken vault factory
1 parent dc25b5a commit 3851611

File tree

2 files changed

+1172
-0
lines changed

2 files changed

+1172
-0
lines changed

src/ATokenVaultFactory.sol

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
// All Rights Reserved © AaveCo
3+
4+
pragma solidity ^0.8.10;
5+
6+
import {IERC20} from "@openzeppelin/interfaces/IERC20.sol";
7+
import {IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPoolAddressesProvider.sol";
8+
import {TransparentUpgradeableProxy} from "@openzeppelin/proxy/transparent/TransparentUpgradeableProxy.sol";
9+
import {ATokenVault} from "./ATokenVault.sol";
10+
11+
/**
12+
* @title ATokenVaultFactory
13+
* @author Aave Protocol
14+
* @notice Factory contract for deploying ATokenVault instances using the proxy pattern
15+
*/
16+
contract ATokenVaultFactory {
17+
/*//////////////////////////////////////////////////////////////
18+
EVENTS
19+
//////////////////////////////////////////////////////////////*/
20+
21+
/**
22+
* @dev Emitted when a new vault is deployed
23+
* @param vault The address of the deployed vault proxy
24+
* @param implementation The address of the vault implementation
25+
* @param underlying The underlying asset address
26+
* @param deployer The address that deployed the vault
27+
* @param owner The owner of the vault
28+
*/
29+
event VaultDeployed(
30+
address indexed vault,
31+
address indexed implementation,
32+
address indexed underlying,
33+
address deployer,
34+
address owner,
35+
uint16 referralCode,
36+
address poolAddressesProvider
37+
);
38+
39+
/*//////////////////////////////////////////////////////////////
40+
STATE VARIABLES
41+
//////////////////////////////////////////////////////////////*/
42+
43+
/// @notice Array of all deployed vaults
44+
address[] public allVaults;
45+
46+
/// @notice Mapping from underlying asset to deployed vaults
47+
mapping(address => address[]) public vaultsByUnderlying;
48+
49+
/// @notice Mapping from deployer to deployed vaults
50+
mapping(address => address[]) public vaultsByDeployer;
51+
52+
/// @notice Mapping to check if a vault was deployed by this factory
53+
mapping(address => bool) public isVaultDeployed;
54+
55+
/// @notice Counter for unique salt generation
56+
uint256 private deploymentCounter;
57+
58+
/// @notice Proxy admin address for all deployed vaults
59+
address public immutable PROXY_ADMIN;
60+
61+
/*//////////////////////////////////////////////////////////////
62+
STRUCTS
63+
//////////////////////////////////////////////////////////////*/
64+
65+
/**
66+
* @dev Struct containing constructor parameters for vault deployment
67+
*/
68+
struct VaultParams {
69+
address underlying;
70+
uint16 referralCode;
71+
IPoolAddressesProvider poolAddressesProvider;
72+
address owner;
73+
uint256 initialFee;
74+
string shareName;
75+
string shareSymbol;
76+
uint256 initialLockDeposit;
77+
}
78+
79+
/*//////////////////////////////////////////////////////////////
80+
CONSTRUCTOR
81+
//////////////////////////////////////////////////////////////*/
82+
83+
/**
84+
* @dev Constructor
85+
* @param proxyAdmin The address that will be the admin of all deployed proxies
86+
*/
87+
constructor(address proxyAdmin) {
88+
require(proxyAdmin != address(0), "ZERO_ADDRESS_NOT_VALID");
89+
PROXY_ADMIN = proxyAdmin;
90+
}
91+
92+
/*//////////////////////////////////////////////////////////////
93+
EXTERNAL FUNCTIONS
94+
//////////////////////////////////////////////////////////////*/
95+
96+
/**
97+
* @notice Deploy a new ATokenVault with the given parameters
98+
* @param params All parameters needed for vault deployment and initialization
99+
* @return vault The address of the deployed vault proxy
100+
*/
101+
function deployVault(VaultParams memory params) public returns (address vault) {
102+
require(params.underlying != address(0), "ZERO_ADDRESS_NOT_VALID");
103+
require(address(params.poolAddressesProvider) != address(0), "ZERO_ADDRESS_NOT_VALID");
104+
require(params.owner != address(0), "ZERO_ADDRESS_NOT_VALID");
105+
require(params.initialLockDeposit > 0, "ZERO_INITIAL_LOCK_DEPOSIT");
106+
require(bytes(params.shareName).length > 0, "EMPTY_SHARE_NAME");
107+
require(bytes(params.shareSymbol).length > 0, "EMPTY_SHARE_SYMBOL");
108+
109+
// Transfer the initial lock deposit from caller to this contract
110+
IERC20(params.underlying).transferFrom(
111+
msg.sender,
112+
address(this),
113+
params.initialLockDeposit
114+
);
115+
116+
uint256 currentCounter = deploymentCounter++;
117+
118+
bytes32 salt = keccak256(abi.encodePacked(
119+
params.underlying,
120+
params.referralCode,
121+
address(params.poolAddressesProvider),
122+
msg.sender,
123+
block.timestamp,
124+
currentCounter
125+
));
126+
127+
address implementation = address(new ATokenVault{salt: salt}(
128+
params.underlying,
129+
params.referralCode,
130+
params.poolAddressesProvider
131+
));
132+
133+
vault = address(new TransparentUpgradeableProxy{salt: salt}(
134+
implementation,
135+
PROXY_ADMIN,
136+
""
137+
));
138+
139+
IERC20(params.underlying).approve(vault, params.initialLockDeposit);
140+
141+
// Initialize the proxy (this will trigger the token transfer)
142+
ATokenVault(vault).initialize(
143+
params.owner,
144+
params.initialFee,
145+
params.shareName,
146+
params.shareSymbol,
147+
params.initialLockDeposit
148+
);
149+
150+
allVaults.push(vault);
151+
vaultsByUnderlying[params.underlying].push(vault);
152+
vaultsByDeployer[msg.sender].push(vault);
153+
isVaultDeployed[vault] = true;
154+
155+
emit VaultDeployed(
156+
vault,
157+
implementation,
158+
params.underlying,
159+
msg.sender,
160+
params.owner,
161+
params.referralCode,
162+
address(params.poolAddressesProvider)
163+
);
164+
}
165+
166+
/*//////////////////////////////////////////////////////////////
167+
VIEW FUNCTIONS
168+
//////////////////////////////////////////////////////////////*/
169+
170+
/**
171+
* @notice Get the total number of deployed vaults
172+
* @return The total number of vaults
173+
*/
174+
function getAllVaultsLength() external view returns (uint256) {
175+
return allVaults.length;
176+
}
177+
178+
/**
179+
* @notice Get all deployed vaults
180+
* @return Array of all vault addresses
181+
*/
182+
function getAllVaults() external view returns (address[] memory) {
183+
return allVaults;
184+
}
185+
186+
/**
187+
* @notice Get vaults by underlying asset
188+
* @param underlying The underlying asset address
189+
* @return Array of vault addresses for the underlying asset
190+
*/
191+
function getVaultsByUnderlying(address underlying) external view returns (address[] memory) {
192+
return vaultsByUnderlying[underlying];
193+
}
194+
195+
/**
196+
* @notice Get vaults by deployer
197+
* @param deployer The deployer address
198+
* @return Array of vault addresses deployed by the deployer
199+
*/
200+
function getVaultsByDeployer(address deployer) external view returns (address[] memory) {
201+
return vaultsByDeployer[deployer];
202+
}
203+
204+
/**
205+
* @notice Get vault deployment info
206+
* @param vaultIndex The index of the vault in allVaults array
207+
* @return vault The vault address
208+
* @return underlying The underlying asset
209+
*/
210+
function getVaultInfo(uint256 vaultIndex) external view returns (
211+
address vault,
212+
address underlying
213+
) {
214+
require(vaultIndex < allVaults.length, "INVALID_VAULT_INDEX");
215+
vault = allVaults[vaultIndex];
216+
ATokenVault vaultContract = ATokenVault(vault);
217+
underlying = address(vaultContract.UNDERLYING());
218+
}
219+
220+
/**
221+
* @notice Check if a vault was deployed by this factory
222+
* @param vault The vault address to check
223+
* @return True if the vault was deployed by this factory
224+
*/
225+
function isDeployedVault(address vault) external view returns (bool) {
226+
return isVaultDeployed[vault];
227+
}
228+
}

0 commit comments

Comments
 (0)