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