This repository has been archived by the owner on Sep 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
BoostRegistry.sol
176 lines (149 loc) · 8.06 KB
/
BoostRegistry.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {LibClone} from "@solady/utils/LibClone.sol";
import {ReentrancyGuard} from "@solady/utils/ReentrancyGuard.sol";
import {BoostLib} from "contracts/shared/BoostLib.sol";
import {ACloneable} from "contracts/shared/ACloneable.sol";
import {AAllowList} from "contracts/allowlists/AAllowList.sol";
/// @title Boost Registry
/// @notice A registry for base implementations and cloned instances
/// @dev This contract is used to register base implementations and deploy new instances of those implementations for use within the Boost protocol
contract BoostRegistry is ERC165, ReentrancyGuard {
using BoostLib for address;
/// @notice The types of bases that can be registered
enum RegistryType {
ACTION,
ALLOW_LIST,
BUDGET,
INCENTIVE,
VALIDATOR
}
/// @notice The data structure for a deployed clone
/// @param baseType The type of base implementation
/// @param name The display name for the clone
/// @param instance The address of the clone
/// @param deployer The address of the deployer
struct Clone {
RegistryType baseType;
ACloneable instance;
address deployer;
string name;
}
/// @notice Emitted when a new base implementation is registered
event Registered(RegistryType indexed registryType, bytes32 indexed identifier, address implementation);
/// @notice Emitted when a new instance of a base implementation is deployed
event Deployed(
RegistryType indexed registryType,
bytes32 indexed identifier,
address baseImplementation,
ACloneable deployedInstance
);
/// @notice Thrown when a base implementation is already registered
error AlreadyRegistered(RegistryType registryType, bytes32 identifier);
/// @notice Thrown when no match is found for the given identifier
error NotRegistered(bytes32 identifier);
/// @notice Thrown when the implementation is not a valid {ACloneable} base
error NotACloneable(address implementation);
/// @notice The registry of base implementations
mapping(bytes32 => ACloneable) private _bases;
/// @notice The registry of deployed clones
mapping(bytes32 => Clone) private _clones;
/// @notice The registry of clones created by a given deployer
mapping(address => bytes32[]) private _deployedClones;
/// @notice A modifier to ensure the given address holds a valid {ACloneable} base
/// @param implementation_ The address of the implementation to check
modifier onlyACloneables(address implementation_) {
if (!ACloneable(implementation_).supportsInterface(type(ACloneable).interfaceId)) {
revert NotACloneable(implementation_);
}
_;
}
/// @notice Register a new base implementation of a given type
/// @param type_ The base type for the implementation
/// @param name_ A name for the implementation (must be unique within the given type)
/// @param implementation_ The address of the implementation contract
/// @dev This function will either emit a `Registered` event or revert if the identifier has already been registered
/// @dev The given address must implement the given type interface (See {ERC165-supportsInterface})
function register(RegistryType type_, string calldata name_, address implementation_)
external
onlyACloneables(implementation_)
{
bytes32 identifier = getIdentifier(type_, name_);
if (address(_bases[identifier]) != address(0)) revert AlreadyRegistered(type_, identifier);
_bases[identifier] = ACloneable(implementation_);
emit Registered(type_, identifier, implementation_);
}
/// @notice Deploy a new instance of a registered base implementation
/// @param type_ The type of base implementation to be cloned
/// @param base_ The address of the base implementation to clone
/// @param name_ The display name for the clone
/// @param data_ The data payload for the cloned instance's initializer
/// @return instance The address of the deployed instance
/// @dev This function will either emit a `Deployed` event and return the clone or revert
function deployClone(RegistryType type_, address base_, string calldata name_, bytes calldata data_)
external
nonReentrant
returns (ACloneable instance)
{
// Deploy and initialize the clone
instance =
ACloneable(base_.cloneAndInitialize(keccak256(abi.encodePacked(type_, base_, name_, msg.sender)), data_));
// Ensure the clone's identifier is unique
bytes32 identifier = getCloneIdentifier(type_, base_, msg.sender, name_);
if (address(_clones[identifier].instance) != address(0)) revert AlreadyRegistered(type_, identifier);
// Register and report the newly deployed clone
_deployedClones[msg.sender].push(identifier);
_clones[identifier] = Clone({baseType: type_, instance: instance, deployer: msg.sender, name: name_});
emit Deployed(type_, identifier, base_, instance);
}
/// @notice Get the address of a registered base implementation
/// @param identifier_ The unique identifier for the implementation (see {getIdentifier})
/// @return implementation The address of the implementation
/// @dev This function will revert if the implementation is not registered
function getBaseImplementation(bytes32 identifier_) public view returns (ACloneable implementation) {
implementation = _bases[identifier_];
if (address(implementation) == address(0)) revert NotRegistered(identifier_);
}
/// @notice Get the address of a deployed clone by its identifier
/// @param identifier_ The unique identifier for the deployed clone (see {getCloneIdentifier})
/// @return clone The address of the deployed clone
function getClone(bytes32 identifier_) external view returns (Clone memory clone) {
clone = _clones[identifier_];
if (address(clone.instance) == address(0)) revert NotRegistered(identifier_);
}
/// @notice Get the list of identifiers of deployed clones for a given deployer
/// @param deployer_ The address of the deployer
/// @return clones The list of deployed clones for the given deployer
/// @dev WARNING: This function may return a large amount of data and is primarily intended for off-chain usage. It should be avoided in on-chain logic.
function getClones(address deployer_) external view returns (bytes32[] memory) {
return _deployedClones[deployer_];
}
/// @notice Build the identifier for a clone of a base implementation
/// @param type_ The base type for the implementation
/// @param base_ The address of the base implementation
/// @param deployer_ The address of the deployer
/// @param name_ The display name of the clone
/// @return identifier The unique identifier for the clone
function getCloneIdentifier(RegistryType type_, address base_, address deployer_, string calldata name_)
public
pure
returns (bytes32 identifier)
{
return _getIdentifier(type_, keccak256(abi.encodePacked(base_, deployer_, name_)));
}
/// @notice Build the identifier for a base implementation
/// @param type_ The base type for the implementation
/// @param name_ The name of the implementation
/// @return identifier The unique identifier for the implementation
function getIdentifier(RegistryType type_, string calldata name_) public pure returns (bytes32 identifier) {
return _getIdentifier(type_, keccak256(abi.encodePacked(name_)));
}
/// @notice Build a unique identifier for a given type and hash
/// @param type_ The base type for the implementation
/// @param hash_ The unique hash for the implementation
/// @return identifier The unique identifier for the implementation
function _getIdentifier(RegistryType type_, bytes32 hash_) internal pure returns (bytes32 identifier) {
return keccak256(abi.encodePacked(type_, hash_));
}
}