Skip to content
This repository was archived by the owner on Apr 2, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions contracts/global/BytecodeRepository.sol
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,14 @@ contract BytecodeRepository is ImmutableOwnableTrait, SanityCheckTrait, IBytecod
source: bytecode.source,
authorSignature: bytecode.authorSignature
});
emit UploadBytecode(bytecodeHash, bytecode.contractType, bytecode.version, bytecode.author, bytecode.source);
emit UploadBytecode(
bytecodeHash,
bytecode.contractType,
bytecode.version,
bytecode.author,
bytecode.source,
bytecode.authorSignature
);
}

// ----------------- //
Expand Down Expand Up @@ -299,7 +306,7 @@ contract BytecodeRepository is ImmutableOwnableTrait, SanityCheckTrait, IBytecod
}
}
reports.push(auditReport);
emit AuditBytecode(bytecodeHash, auditor, auditReport.reportUrl);
emit AuditBytecode(bytecodeHash, auditor, auditReport.reportUrl, auditReport.signature);
}

// ----------------- //
Expand Down
18 changes: 8 additions & 10 deletions contracts/instance/InstanceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ contract InstanceManager is Ownable, IInstanceManager {
? _getLegacyGearStakingAddress()
: _deploySystemContract(contractType_, version_);

if (newSystemContract != address(0)) _setAddress(contractType_, newSystemContract, saveVersion);
_setAddress(contractType_, newSystemContract, saveVersion);
}

/// @notice Allows cross-chain governance to set a global address in the address provider
Expand Down Expand Up @@ -203,15 +203,12 @@ contract InstanceManager is Ownable, IInstanceManager {
}

/// @dev Deploys a system contract and returns its address
function _deploySystemContract(bytes32 _contractType, uint256 _version) internal returns (address) {
try ProxyCall(crossChainGovernanceProxy).proxyCall(
function _deploySystemContract(bytes32 contractType_, uint256 version_) internal returns (address) {
bytes memory result = ProxyCall(crossChainGovernanceProxy).proxyCall(
address(bytecodeRepository),
abi.encodeCall(BytecodeRepository.deploy, (_contractType, _version, abi.encode(addressProvider), 0))
) returns (bytes memory result) {
return abi.decode(result, (address));
} catch {
return address(0);
}
abi.encodeCall(BytecodeRepository.deploy, (contractType_, version_, abi.encode(addressProvider), 0))
);
return abi.decode(result, (address));
}

/// @dev Whether there is a legacy instance on this chain
Expand All @@ -229,7 +226,8 @@ contract InstanceManager is Ownable, IInstanceManager {
return 0xe88846b6C85AA67688e453c7eaeeeb40F51e1F0a;
} else if (block.chainid == 42161) {
return 0xf3599BEfe8E79169Afd5f0b7eb0A1aA322F193D9;
} else {
revert();
}
return address(0);
}
}
10 changes: 4 additions & 6 deletions contracts/instance/PriceFeedStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,13 @@ contract PriceFeedStore is
/// @notice Executes price feed configuration `calls` with owner privileges
/// @dev Reverts if caller is not owner
/// @dev Reverts if any of call targets is not a known price feed
/// @dev Reverts if any of calls transfers or renounces ownership over price feed
/// @dev Reverts if any of calls renounces ownership over price feed
function configurePriceFeeds(Call[] calldata calls) external override onlyOwner {
uint256 numCalls = calls.length;
for (uint256 i; i < numCalls; ++i) {
if (!_knownPriceFeeds.contains(calls[i].target)) revert PriceFeedIsNotKnownException(calls[i].target);
bytes4 selector = bytes4(calls[i].callData);
if (selector == Ownable.transferOwnership.selector || selector == Ownable.renounceOwnership.selector) {
if (selector == Ownable.renounceOwnership.selector) {
revert ForbiddenConfigurationMethodException(selector);
}
calls[i].target.functionCall(calls[i].callData);
Expand Down Expand Up @@ -292,14 +292,12 @@ contract PriceFeedStore is

/// @dev Returns whether `priceFeed` is deployed externally or via BCR.
/// For latter case, also ensures that price feed is owned by the store.
function _validatePriceFeedDeployment(address priceFeed) internal view returns (bool) {
function _validatePriceFeedDeployment(address priceFeed) internal returns (bool) {
if (!IBytecodeRepository(bytecodeRepository).isDeployedFromRepository(priceFeed)) return true;

try Ownable2Step(priceFeed).acceptOwnership() {} catch {}
try Ownable(priceFeed).owner() returns (address owner_) {
if (owner_ != address(this)) revert PriceFeedIsNotOwnedByStore(priceFeed);
try Ownable2Step(priceFeed).pendingOwner() returns (address pendingOwner_) {
if (pendingOwner_ != address(0)) revert PriceFeedIsNotOwnedByStore(priceFeed);
} catch {}
} catch {}

return false;
Expand Down
53 changes: 29 additions & 24 deletions contracts/interfaces/IBytecodeRepository.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,50 @@ interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
event AddAuditor(address indexed auditor, string name);
event AddPublicDomain(bytes32 indexed domain);
event AddSystemDomain(bytes32 indexed domain);
event AllowContract(bytes32 indexed bytecodeHash, bytes32 indexed cType, uint256 indexed ver);
event AuditBytecode(bytes32 indexed bytecodeHash, address indexed auditor, string reportUrl);
event AllowContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event AuditBytecode(bytes32 indexed bytecodeHash, address indexed auditor, string reportUrl, bytes signature);
event DeployContract(
bytes32 indexed bytecodeHash, bytes32 indexed cType, uint256 indexed ver, address contractAddress
bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version, address contractAddress
);
event ForbidContract(bytes32 indexed bytecodeHash, bytes32 indexed cType, uint256 indexed ver);
event ForbidContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event ForbidInitCode(bytes32 indexed initCodeHash);
event RemoveAuditor(address indexed auditor);
event RemoveContractTypeOwner(bytes32 indexed cType);
event SetContractTypeOwner(bytes32 indexed cType, address indexed owner);
event RemoveContractTypeOwner(bytes32 indexed contractType);
event SetContractTypeOwner(bytes32 indexed contractType, address indexed owner);
event SetTokenSpecificPostfix(address indexed token, bytes32 indexed postfix);
event UploadBytecode(
bytes32 indexed bytecodeHash, bytes32 indexed cType, uint256 indexed ver, address author, string source
bytes32 indexed bytecodeHash,
bytes32 indexed contractType,
uint256 indexed version,
address author,
string source,
bytes signature
);

// ------ //
// ERRORS //
// ------ //

error AuditorIsNotApprovedException(address auditor);
error AuthorIsNotContractTypeOwnerException(bytes32 cType, address author);
error BytecodeIsAlreadyAllowedException(bytes32 cType, uint256 ver);
error AuthorIsNotContractTypeOwnerException(bytes32 contractType, address author);
error BytecodeIsAlreadyAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsAlreadySignedByAuditorException(bytes32 bytecodeHash, address auditor);
error BytecodeIsNotAllowedException(bytes32 cType, uint256 ver);
error BytecodeIsNotAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsNotAuditedException(bytes32 bytecodeHash);
error BytecodeIsNotUploadedException(bytes32 bytecodeHash);
error CallerIsNotBytecodeAuthorException(address caller);
error ContractIsAlreadyDeployedException(address deployedContract);
error ContractTypeIsNotInPublicDomainException(bytes32 cType);
error ContractTypeIsNotInPublicDomainException(bytes32 contractType);
error DomainIsAlreadyMarketAsPublicException(bytes32 domain);
error DomainIsAlreadyMarketAsSystemException(bytes32 domain);
error InitCodeIsForbiddenException(bytes32 initCodeHash);
error InvalidAuditorSignatureException(address auditor);
error InvalidAuthorSignatureException(address author);
error InvalidBytecodeException(bytes32 bytecodeHash);
error InvalidContractTypeException(bytes32 cType);
error InvalidContractTypeException(bytes32 contractType);
error InvalidDomainException(bytes32 domain);
error InvalidVersionException(bytes32 cType, uint256 ver);
error VersionNotFoundException(bytes32 cType);
error InvalidVersionException(bytes32 contractType, uint256 version);
error VersionNotFoundException(bytes32 contractType);

// --------------- //
// EIP-712 GETTERS //
Expand All @@ -76,13 +81,13 @@ interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
function isDeployedFromRepository(address deployedContract) external view returns (bool);
function getDeployedContractBytecodeHash(address deployedContract) external view returns (bytes32);
function computeAddress(
bytes32 cType,
uint256 ver,
bytes32 contractType,
uint256 version,
bytes calldata constructorParams,
bytes32 salt,
address deployer
) external view returns (address);
function deploy(bytes32 cType, uint256 ver, bytes calldata constructorParams, bytes32 salt)
function deploy(bytes32 contractType, uint256 version, bytes calldata constructorParams, bytes32 salt)
external
returns (address);

Expand All @@ -108,11 +113,11 @@ interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
// ALLOWING BYTECODE //
// ----------------- //

function getAllowedBytecodeHash(bytes32 cType, uint256 ver) external view returns (bytes32);
function getContractTypeOwner(bytes32 cType) external view returns (address);
function getAllowedBytecodeHash(bytes32 contractType, uint256 version) external view returns (bytes32);
function getContractTypeOwner(bytes32 contractType) external view returns (address);
function allowSystemContract(bytes32 bytecodeHash) external;
function allowPublicContract(bytes32 bytecodeHash) external;
function removePublicContractType(bytes32 cType) external;
function removePublicContractType(bytes32 contractType) external;

// ------------------ //
// DOMAINS MANAGEMENT //
Expand Down Expand Up @@ -152,8 +157,8 @@ interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
// VERSION CONTROL //
// --------------- //

function getVersions(bytes32 cType) external view returns (uint256[] memory);
function getLatestVersion(bytes32 cType) external view returns (uint256);
function getLatestMinorVersion(bytes32 cType, uint256 majorVersion) external view returns (uint256);
function getLatestPatchVersion(bytes32 cType, uint256 minorVersion) external view returns (uint256);
function getVersions(bytes32 contractType) external view returns (uint256[] memory);
function getLatestVersion(bytes32 contractType) external view returns (uint256);
function getLatestMinorVersion(bytes32 contractType, uint256 majorVersion) external view returns (uint256);
function getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) external view returns (uint256);
}
2 changes: 1 addition & 1 deletion contracts/market/MarketConfigurator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ contract MarketConfigurator is DeployerTrait, IMarketConfigurator {
}
}

/// @dev Returns latest patch in the address provider for given contract type and minor version
/// @dev Returns latest patch in the address provider for given contract type with matching minor version
function _getLatestPatch(bytes32 key, uint256 minorVersion) internal view returns (address) {
return _getAddressOrRevert(key, IAddressProvider(addressProvider).getLatestPatchVersion(key, minorVersion));
}
Expand Down
5 changes: 2 additions & 3 deletions contracts/market/legacy/MarketConfiguratorLegacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,10 @@ contract MarketConfiguratorLegacy is MarketConfigurator {
address rateKeeper = _rateKeeper(quotaKeeper);
address lossPolicy = IContractsRegister(contractsRegister).getLossPolicy(pool);

// NOTE: authorize factories for contracts that might be used after the migration;
// legacy price oracle is left unauthorized since it's not gonna be used after the migration
// NOTE: authorize factories for contracts that might still be used after the migration; legacy price oracle
// is left unauthorized since it's not gonna be used, IRM is unauthorized since it's not configurable
_authorizeFactory(factories.poolFactory, pool, pool);
_authorizeFactory(factories.poolFactory, pool, quotaKeeper);
_authorizeFactory(factories.interestRateModelFactory, pool, interestRateModel);
_authorizeFactory(factories.rateKeeperFactory, pool, rateKeeper);
_authorizeFactory(factories.lossPolicyFactory, pool, lossPolicy);

Expand Down
4 changes: 2 additions & 2 deletions contracts/test/configuration/ConfigurationTestHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ contract ConfigurationTestHelper is Test, GlobalSetup {

bytes32 bytecodeHash = _uploadByteCodeAndSign(type(MockLossPolicy).creationCode, "LOSS_POLICY::MOCK", 3_10);

calls[0] = _generateAllowSystemContractCall(bytecodeHash);
calls[0] = _generateAllowPublicContractCall(bytecodeHash);

_submitBatchAndSign("Allow system contracts", calls);
_submitBatchAndSign("Allow public contracts", calls);
}

function _deployTestPool() internal returns (address) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ contract InterestRateModelConfigurationUnitTest is ConfigurationTestHelper {
function test_IRM_01_configure() public {
CrossChainCall[] memory calls = new CrossChainCall[](1);
bytes32 bytecodeHash = _uploadByteCodeAndSign(type(MockIRM).creationCode, "IRM::MOCK", 3_10);
calls[0] = _generateAllowSystemContractCall(bytecodeHash);
_submitBatchAndSign("Allow system contracts", calls);
calls[0] = _generateAllowPublicContractCall(bytecodeHash);
_submitBatchAndSign("Allow public contracts", calls);

vm.prank(admin);
address newIRM = marketConfigurator.updateInterestRateModel(
Expand Down
3 changes: 1 addition & 2 deletions contracts/test/global/InstanceManager.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,9 @@ contract InstanceManagerTest is Test {
"DEPLOYMENT_FAILED"
);

vm.expectRevert("DEPLOYMENT_FAILED");
vm.prank(crossChainGovernance);
manager.deploySystemContract(contractType, version, false);
// Should not update address if deployment failed
assertEq(addressProvider.getAddressOrRevert(contractType, NO_VERSION_CONTROL), newContract);
}

/// @notice Test legacy GEAR staking deployment
Expand Down
40 changes: 4 additions & 36 deletions contracts/test/global/PriceFeedStore.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ contract PriceFeedStoreTest is Test {
abi.encodeWithSignature("isDeployedFromRepository(address)", address(ownableFeed)),
abi.encode(true)
);
vm.mockCall(address(ownableFeed), abi.encodeWithSignature("acceptOwnership()"), "");
vm.mockCall(address(ownableFeed), abi.encodeWithSignature("owner()"), abi.encode(address(store)));

// Test Ownable feed owned by other (should fail)
Expand All @@ -497,20 +498,9 @@ contract PriceFeedStoreTest is Test {
abi.encodeWithSignature("isDeployedFromRepository(address)", address(wrongOwnerFeed)),
abi.encode(true)
);
vm.mockCall(address(wrongOwnerFeed), abi.encodeWithSignature("acceptOwnership()"), "");
vm.mockCall(address(wrongOwnerFeed), abi.encodeWithSignature("owner()"), abi.encode(makeAddr("other")));

// Test Ownable2Step feed with pending transfer (should fail)
MockPriceFeed ownable2StepFeed = new MockPriceFeed();
vm.mockCall(
address(bytecodeRepository),
abi.encodeWithSignature("isDeployedFromRepository(address)", address(ownable2StepFeed)),
abi.encode(true)
);
vm.mockCall(address(ownable2StepFeed), abi.encodeWithSignature("owner()"), abi.encode(address(store)));
vm.mockCall(
address(ownable2StepFeed), abi.encodeWithSignature("pendingOwner()"), abi.encode(makeAddr("pending"))
);

vm.startPrank(owner);

// Non-ownable should pass
Expand All @@ -525,12 +515,6 @@ contract PriceFeedStoreTest is Test {
);
store.addPriceFeed(address(wrongOwnerFeed), 3600, "Wrong Owner Feed");

// Pending transfer should fail
vm.expectRevert(
abi.encodeWithSelector(IPriceFeedStore.PriceFeedIsNotOwnedByStore.selector, address(ownable2StepFeed))
);
store.addPriceFeed(address(ownable2StepFeed), 3600, "Ownable2Step Feed");

vm.stopPrank();
}

Expand Down Expand Up @@ -574,36 +558,20 @@ contract PriceFeedStoreTest is Test {
store.configurePriceFeeds(calls);
}

function test_PFS_24_configurePriceFeeds_reverts_on_ownership_transfer() public {
function test_PFS_24_configurePriceFeeds_reverts_on_renounceOwnership() public {
vm.prank(owner);
store.addPriceFeed(address(priceFeed), 3600, "ETH/USD");

Call[] memory transferCall = new Call[](1);
transferCall[0] =
Call(address(priceFeed), abi.encodeWithSignature("transferOwnership(address)", makeAddr("newOwner")));

Call[] memory renounceCall = new Call[](1);
renounceCall[0] = Call(address(priceFeed), abi.encodeWithSignature("renounceOwnership()"));

vm.startPrank(owner);

// Test transferOwnership
vm.expectRevert(
abi.encodeWithSelector(
IPriceFeedStore.ForbiddenConfigurationMethodException.selector, bytes4(transferCall[0].callData)
)
);
store.configurePriceFeeds(transferCall);

// Test renounceOwnership
vm.expectRevert(
abi.encodeWithSelector(
IPriceFeedStore.ForbiddenConfigurationMethodException.selector, bytes4(renounceCall[0].callData)
)
);
vm.prank(owner);
store.configurePriceFeeds(renounceCall);

vm.stopPrank();
}

function test_PFS_25_removePriceFeed_works() public {
Expand Down
Loading