1- // SPDX-License-Identifier: BUSL-1.1
1+ // SPDX-License-Identifier: BUSL-1.1
22// Gearbox Protocol. Generalized leverage for DeFi protocols
33// (c) Gearbox Foundation, 2024.
44pragma solidity ^ 0.8.17 ;
55
6- import {Ownable} from "@openzeppelin/contracts/access/Ownable2Step .sol " ;
6+ import {Ownable} from "@openzeppelin/contracts/access/Ownable .sol " ;
77import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol " ;
88import {Create2} from "@openzeppelin/contracts/utils/Create2.sol " ;
99import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol " ;
@@ -18,7 +18,7 @@ import "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol";
1818import {Bytecode, AuditorSignature} from "../interfaces/Types.sol " ;
1919import {EIP712Mainnet} from "../helpers/EIP712Mainnet.sol " ;
2020import {Domain} from "../libraries/Domain.sol " ;
21-
21+ import {ImmutableOwnableTrait} from " ../traits/ImmutableOwnableTrait.sol " ;
2222/**
2323 * @title BytecodeRepository
2424 *
@@ -43,7 +43,8 @@ import {Domain} from "../libraries/Domain.sol";
4343 *
4444 * This structure ensures consistency and clarity when deploying and managing contracts within the system.
4545 */
46- contract BytecodeRepository is Ownable , SanityCheckTrait , IBytecodeRepository , EIP712Mainnet {
46+
47+ contract BytecodeRepository is ImmutableOwnableTrait , SanityCheckTrait , IBytecodeRepository , EIP712Mainnet {
4748 using EnumerableSet for EnumerableSet.AddressSet;
4849 using EnumerableSet for EnumerableSet.Bytes32Set;
4950 using ECDSA for bytes32 ;
@@ -59,17 +60,18 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
5960 uint256 public constant override version = 3_10 ;
6061 bytes32 public constant override contractType = AP_BYTECODE_REPOSITORY;
6162
62- bytes32 public constant BYTECODE_META_TYPEHASH =
63- keccak256 ("BytecodeMeta (bytes32 contractType,uint256 version,bytes initCode,address author,string source) " );
63+ bytes32 public constant BYTECODE_TYPEHASH =
64+ keccak256 ("Bytecode (bytes32 contractType,uint256 version,bytes initCode,address author,string source) " );
6465
65- bytes32 public constant _SIGNATURE_TYPEHASH = keccak256 ("SignBytecodeHash(bytes32 bytecodeHash,string reportUrl) " );
66+ bytes32 public constant AUDITOR_SIGNATURE_TYPEHASH =
67+ keccak256 ("SignBytecodeHash(bytes32 bytecodeHash,string reportUrl) " );
6668
6769 //
6870 // STORAGE
6971 //
7072
7173 // bytecodeHash => Bytecode
72- mapping (bytes32 => Bytecode) public bytecodeByHash ;
74+ mapping (bytes32 => Bytecode) internal _bytecodeByHash ;
7375
7476 // bytecodeHash => array of AuditorSignature
7577 mapping (bytes32 => AuditorSignature[]) internal _auditorSignaturesByHash;
@@ -108,22 +110,23 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
108110 mapping (bytes32 => mapping (uint256 => uint256 )) public latestMinorVersion;
109111 mapping (bytes32 => mapping (uint256 => uint256 )) public latestPatchVersion;
110112
111- constructor (address _owner ) EIP712Mainnet (contractType.fromSmallString (), version.toString ()) Ownable () {
112- _transferOwnership (_owner);
113- }
113+ constructor (address _owner )
114+ EIP712Mainnet (contractType.fromSmallString (), version.toString ())
115+ ImmutableOwnableTrait (_owner)
116+ {}
114117
115118 /// @notice Computes a unique hash for _bytecode metadata
116119 /// @param _bytecode Bytecode metadata including contract type, version, _bytecode, author and source
117120 /// @return bytes32 Hash of the metadata
118121 function computeBytecodeHash (Bytecode calldata _bytecode ) public pure returns (bytes32 ) {
119122 return keccak256 (
120123 abi.encode (
121- BYTECODE_META_TYPEHASH ,
124+ BYTECODE_TYPEHASH ,
122125 _bytecode.contractType,
123126 _bytecode.version,
124127 keccak256 (_bytecode.initCode),
125128 _bytecode.author,
126- _bytecode.source
129+ keccak256 ( bytes ( _bytecode.source))
127130 )
128131 );
129132 }
@@ -156,7 +159,7 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
156159 revert ContractNameVersionAlreadyExistsException ();
157160 }
158161
159- bytecodeByHash [bytecodeHash] = _bytecode;
162+ _bytecodeByHash [bytecodeHash] = _bytecode;
160163
161164 emit UploadBytecode (
162165 bytecodeHash,
@@ -187,7 +190,7 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
187190 revert BytecodeIsNotAuditedException ();
188191 }
189192
190- Bytecode storage _bytecode = bytecodeByHash [bytecodeHash];
193+ Bytecode storage _bytecode = _bytecodeByHash [bytecodeHash];
191194
192195 bytes memory initCode = _bytecode.initCode;
193196
@@ -197,16 +200,18 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
197200 // Combine code + constructor params
198201 bytes memory bytecodeWithParams = abi.encodePacked (initCode, constructorParams);
199202
203+ bytes32 saltUnique = keccak256 (abi.encode (salt, msg .sender ));
204+
200205 // Compute CREATE2 address
201- newContract = Create2.computeAddress (salt , keccak256 (bytecodeWithParams), address ( this ));
206+ newContract = Create2.computeAddress (saltUnique , keccak256 (bytecodeWithParams));
202207
203208 // Check if the contract already deployed
204209 if (newContract.code.length != 0 ) {
205210 revert BytecodeAlreadyExistsAtAddressException (newContract);
206211 }
207212
208213 // Deploy
209- Create2.deploy (0 , salt , bytecodeWithParams);
214+ Create2.deploy (0 , saltUnique , bytecodeWithParams);
210215
211216 // Verify IVersion
212217 if (IVersion (newContract).contractType () != _contractType || IVersion (newContract).version () != _version) {
@@ -228,23 +233,27 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
228233 /// @param constructorParams Constructor parameters
229234 /// @param salt Unique salt for CREATE2 deployment
230235 /// @return Address where the contract would be deployed
231- function computeAddress (bytes32 _contractType , uint256 _version , bytes memory constructorParams , bytes32 salt )
232- external
233- view
234- returns (address )
235- {
236+ function computeAddress (
237+ bytes32 _contractType ,
238+ uint256 _version ,
239+ bytes memory constructorParams ,
240+ bytes32 salt ,
241+ address deployer
242+ ) external view returns (address ) {
236243 // Retrieve bytecodeHash
237244 bytes32 bytecodeHash = approvedBytecodeHash[_contractType][_version];
238245 if (bytecodeHash == 0 ) {
239246 revert BytecodeIsNotApprovedException (_contractType, _version);
240247 }
241- Bytecode storage _bytecode = bytecodeByHash [bytecodeHash];
248+ Bytecode storage _bytecode = _bytecodeByHash [bytecodeHash];
242249
243250 // Combine code + constructor params
244251 bytes memory bytecodeWithParams = abi.encodePacked (_bytecode.initCode, constructorParams);
245252
253+ bytes32 saltUnique = keccak256 (abi.encode (salt, deployer));
254+
246255 // Return CREATE2 address
247- return Create2.computeAddress (salt , keccak256 (bytecodeWithParams), address ( this ));
256+ return Create2.computeAddress (saltUnique , keccak256 (bytecodeWithParams));
248257 }
249258
250259 // Auditing
@@ -261,7 +270,8 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
261270 }
262271
263272 // Re-create typed data
264- bytes32 structHash = keccak256 (abi.encode (_SIGNATURE_TYPEHASH, bytecodeHash, keccak256 (bytes (reportUrl))));
273+ bytes32 structHash =
274+ keccak256 (abi.encode (AUDITOR_SIGNATURE_TYPEHASH, bytecodeHash, keccak256 (bytes (reportUrl))));
265275 // Hash with our pinned domain
266276 address signer = ECDSA.recover (_hashTypedDataV4 (structHash), signature);
267277
@@ -283,68 +293,60 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
283293
284294 emit BytecodeSigned (bytecodeHash, signer, reportUrl, signature);
285295
286- _approveContract (bytecodeHash);
296+ Bytecode storage _bytecode = _bytecodeByHash[bytecodeHash];
297+
298+ bytes32 _contractType = _bytecode.contractType;
299+ address author = _bytecode.author;
300+
301+ bool isSystemContract = allowedSystemContracts[bytecodeHash];
302+
303+ address currentOwner = contractTypeOwner[_contractType];
304+ bool isPublicDomain = isContractNameInPublicDomain (_contractType);
305+
306+ if (currentOwner == address (0 ) || isSystemContract) {
307+ contractTypeOwner[_contractType] = author;
308+ } else if (isPublicDomain && (currentOwner != author)) {
309+ revert NotDomainOwnerException ();
310+ }
311+
312+ if (isSystemContract || isPublicDomain) {
313+ _approveContract (_contractType, _bytecode.version, bytecodeHash, author);
314+ }
287315 }
288316
289317 /// @notice Allows owner to mark contracts as system contracts
290318 /// @param bytecodeHash Hash of the _bytecode metadata to allow
291319 function allowSystemContract (bytes32 bytecodeHash ) external onlyOwner {
292320 allowedSystemContracts[bytecodeHash] = true ;
293- _approveContract (bytecodeHash);
321+
322+ if (isBytecodeUploaded (bytecodeHash) && isBytecodeAudited (bytecodeHash)) {
323+ Bytecode storage _bytecode = _bytecodeByHash[bytecodeHash];
324+ contractTypeOwner[_bytecode.contractType] = _bytecode.author;
325+ _approveContract (_bytecode.contractType, _bytecode.version, bytecodeHash, _bytecode.author);
326+ }
294327 }
295328
296329 /// @notice Internal function to approve contract _bytecode
297330 /// @param bytecodeHash Hash of the _bytecode metadata to approve
298- function _approveContract (bytes32 bytecodeHash ) internal {
299- if (! isBytecodeUploaded (bytecodeHash)) {
300- return ;
301- }
302-
303- Bytecode storage _bytecode = bytecodeByHash[bytecodeHash];
331+ function _approveContract (bytes32 _contractType , uint256 _version , bytes32 bytecodeHash , address author ) internal {
332+ if (approvedBytecodeHash[_contractType][_version] == 0 ) {
333+ approvedBytecodeHash[_contractType][_version] = bytecodeHash;
304334
305- bytes32 _contractType = _bytecode.contractType;
335+ uint256 majorVersion = (_version / 100 ) * 100 ;
336+ uint256 minorVersion = ((_version / 10 ) % 10 ) * 10 + majorVersion;
306337
307- if (approvedBytecodeHash[_contractType][_bytecode.version] != 0 ) {
308- return ;
309- }
310-
311- address author = _bytecode.author;
312- if (allowedSystemContracts[bytecodeHash]) {
313- // System contracts could have any author if it signed by DAO
314- contractTypeOwner[_contractType] = author;
315- } else if (isContractNameInPublicDomain (_contractType)) {
316- // public domain => (domain, postfix) ownership
317- address currentOwner = contractTypeOwner[_contractType];
318-
319- if (currentOwner == address (0 )) {
320- contractTypeOwner[_contractType] = author;
321- } else if (currentOwner != author) {
322- revert NotDomainOwnerException ();
338+ if (latestVersion[_contractType] < _version) {
339+ latestVersion[_contractType] = _version;
323340 }
324- } else {
325- revert NotAllowedSystemContractException (bytecodeHash);
326- }
327-
328- uint256 bytecodeVersion = _bytecode.version;
329-
330- if (approvedBytecodeHash[_bytecode.contractType][bytecodeVersion] == 0 ) {
331- approvedBytecodeHash[_bytecode.contractType][bytecodeVersion] = bytecodeHash;
332-
333- uint256 majorVersion = (bytecodeVersion / 100 ) * 100 ;
334- uint256 minorVersion = ((bytecodeVersion / 10 ) % 10 ) * 10 + majorVersion;
335-
336- if (latestVersion[_bytecode.contractType] < bytecodeVersion) {
337- latestVersion[_bytecode.contractType] = bytecodeVersion;
341+ if (latestMinorVersion[_contractType][majorVersion] < _version) {
342+ latestMinorVersion[_contractType][majorVersion] = _version;
338343 }
339- if (latestMinorVersion[_bytecode.contractType][majorVersion ] < bytecodeVersion ) {
340- latestMinorVersion[_bytecode.contractType][majorVersion ] = bytecodeVersion ;
344+ if (latestPatchVersion[_contractType][minorVersion ] < _version ) {
345+ latestPatchVersion[_contractType][minorVersion ] = _version ;
341346 }
342- if (latestPatchVersion[_bytecode.contractType][minorVersion] < bytecodeVersion) {
343- latestPatchVersion[_bytecode.contractType][minorVersion] = bytecodeVersion;
344- }
345- }
346347
347- emit ApproveContract (bytecodeHash, _contractType, _bytecode.version);
348+ emit ApproveContract (bytecodeHash, _contractType, _version);
349+ }
348350 }
349351
350352 //
@@ -395,7 +397,7 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
395397 return ;
396398 }
397399
398- if (LibString.fromSmallString (domain).contains ("_ " )) {
400+ if (LibString.fromSmallString (domain).contains (":: " )) {
399401 return ;
400402 }
401403
@@ -513,7 +515,7 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
513515 // HELPERS
514516 //
515517 function isBytecodeUploaded (bytes32 bytecodeHash ) public view returns (bool ) {
516- return bytecodeByHash [bytecodeHash].author != address (0 );
518+ return _bytecodeByHash [bytecodeHash].author != address (0 );
517519 }
518520
519521 function revertIfInitCodeForbidden (bytes memory initCode ) public view {
@@ -535,4 +537,12 @@ contract BytecodeRepository is Ownable, SanityCheckTrait, IBytecodeRepository, E
535537
536538 return false ;
537539 }
540+
541+ function bytecodeByHash (bytes32 bytecodeHash ) external view returns (Bytecode memory ) {
542+ return _bytecodeByHash[bytecodeHash];
543+ }
544+
545+ function domainSeparatorV4 () external view returns (bytes32 ) {
546+ return _domainSeparatorV4 ();
547+ }
538548}
0 commit comments