Skip to content

Commit 2c1063c

Browse files
authored
Merge pull request #19 from Gearbox-protocol/instance-deploy
feat: instance deploy and tests
2 parents 8541f1b + 5c0a47b commit 2c1063c

22 files changed

+973
-206
lines changed

contracts/global/AddressProvider.sol

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// (c) Gearbox Foundation, 2024.
44
pragma solidity ^0.8.23;
55

6-
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
76
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
87

98
import {LibString} from "@solady/utils/LibString.sol";
@@ -13,6 +12,7 @@ import {AddressNotFoundException} from "@gearbox-protocol/core-v3/contracts/inte
1312

1413
import {IAddressProvider, ContractValue} from "../interfaces/IAddressProvider.sol";
1514
import {AP_ADDRESS_PROVIDER, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol";
15+
import {ImmutableOwnableTrait} from "../traits/ImmutableOwnableTrait.sol";
1616

1717
struct ContractKey {
1818
string key;
@@ -21,7 +21,7 @@ struct ContractKey {
2121

2222
/// @title Address provider V3
2323
/// @notice Stores addresses of important contracts
24-
contract AddressProvider is Ownable2Step, IAddressProvider {
24+
contract AddressProvider is ImmutableOwnableTrait, IAddressProvider {
2525
using EnumerableSet for EnumerableSet.AddressSet;
2626
// using LibString for string;
2727
using LibString for bytes32;
@@ -37,15 +37,11 @@ contract AddressProvider is Ownable2Step, IAddressProvider {
3737

3838
ContractKey[] internal contractKeys;
3939

40-
constructor() {
40+
constructor(address _owner) ImmutableOwnableTrait(_owner) {
4141
// The first event is emitted for the address provider itself to aid in contract discovery
4242
emit SetAddress(AP_ADDRESS_PROVIDER.fromSmallString(), version, address(this));
4343
}
4444

45-
function owner() public view override(Ownable, IAddressProvider) returns (address) {
46-
return Ownable.owner();
47-
}
48-
4945
/// @notice Returns the address of a contract with a given key and version
5046
function getAddressOrRevert(string memory key, uint256 _version)
5147
public

contracts/global/BytecodeRepository.sol

Lines changed: 83 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
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.
44
pragma solidity ^0.8.17;
55

6-
import {Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
6+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
77
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
88
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
99
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
@@ -18,7 +18,7 @@ import "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol";
1818
import {Bytecode, AuditorSignature} from "../interfaces/Types.sol";
1919
import {EIP712Mainnet} from "../helpers/EIP712Mainnet.sol";
2020
import {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

Comments
 (0)