Skip to content

Commit

Permalink
feat: price feed store
Browse files Browse the repository at this point in the history
  • Loading branch information
Van0k committed Nov 20, 2024
1 parent ba8bf86 commit e48e42e
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 73 deletions.
102 changes: 102 additions & 0 deletions contracts/global/AuditManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
import {IncorrectParameterException} from "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol";

import {SecurityReport, AuditorInfo} from "../interfaces/Types.sol";

interface IAuditManagerEvents {
/// @dev Emitted when a new auditor is added to the set
event AddAuditor(address indexed auditor, string name);

/// @dev Emitted when an auditor is forbidden
event ForbidAuditor(address indexed auditor, string name);
}

interface IAuditManagerExceptions {
/// @dev Thrown when an attempt is made to add an auditor that already exists
error AuditorAlreadyAddedException();

/// @dev Thrown when an auditor is not found in the set
error AuditorNotFoundException();

/// @dev Thrown if the caller does not have valid auditor permissions
error NotValidAuditorPermissionsException();
}

contract AuditManager is Ownable2Step, SanityCheckTrait, IAuditManagerEvents, IAuditManagerExceptions {
using EnumerableSet for EnumerableSet.AddressSet;

/// @dev Set of all known auditors
EnumerableSet.AddressSet internal _auditors;

/// @dev Mapping from auditor address to their info (name and whether they are forbidden)
mapping(address => AuditorInfo) public auditorInfo;

/// @dev Mapping from object hash to set of all its audits
mapping(bytes32 => SecurityReport[]) public securityReports;

/// @notice Adds a new auditor to the auditor list
function addAuditor(address auditor, string memory _name) external onlyOwner nonZeroAddress(auditor) {
if (bytes(_name).length == 0) {
revert IncorrectParameterException();
}
if (_auditors.contains(auditor)) {
revert AuditorAlreadyAddedException();
}

_auditors.add(auditor);
auditorInfo[auditor].name = _name;
emit AddAuditor(auditor, _name);
}

/// @notice Forbids an auditor, which prevents them from submitting new audits and invalidates
/// their previous audits
function forbidAuditor(address auditor) external onlyOwner nonZeroAddress(auditor) {
if (!_auditors.contains(auditor)) {
revert AuditorNotFoundException();
}

auditorInfo[auditor].forbidden = true;
emit ForbidAuditor(auditor, auditorInfo[auditor].name);
}

/// @dev Adds a new security report for a key (what constitutes a key is specific to inheriting contract)
function _addSecurityReport(bytes32 key, address auditor, string calldata reportUrl) internal {
if (!_auditors.contains(auditor) || auditorInfo[auditor].forbidden) {
revert NotValidAuditorPermissionsException();
}

securityReports[key].push(SecurityReport({auditor: auditor, url: reportUrl}));
}

/// @dev Returns a number of unique non-forbidden auditors for a particular key (what constitutes a key is specific to inheriting contract)
function _getUniqueNonForbiddenAuditorCount(bytes32 key) internal view returns (uint256 count) {
SecurityReport[] memory reports = securityReports[key];

uint256 len = reports.length;

address[] memory foundAuditors = new address[](len);
uint256 k = 0;

for (uint256 i = 0; i <= len; ++i) {
address auditor = reports[i].auditor;

for (uint256 j = 0; j <= k; ++j) {
if (j == k) {
foundAuditors[k] = auditor;
++k;
if (!auditorInfo[auditor].forbidden) ++count;
}

if (auditor == foundAuditors[j]) break;
}
}
}
}
77 changes: 7 additions & 70 deletions contracts/global/BytecodeRepository.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {AP_BYTECODE_REPOSITORY} from "../libraries/ContractLiterals.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";

import {AuditManager} from "./AuditManager.sol";
import {SecurityReport, Source, BytecodeInfo, AuditorInfo} from "../interfaces/Types.sol";

// EXCEPTIONS
Expand Down Expand Up @@ -42,7 +43,7 @@ import {LibString} from "@solady/utils/LibString.sol";
*
* This structure ensures consistency and clarity when deploying and managing contracts within the system.
*/
contract BytecodeRepository is Ownable2Step, SanityCheckTrait, IBytecodeRepository {
contract BytecodeRepository is SanityCheckTrait, AuditManager, IBytecodeRepository {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
using LibString for bytes32;
Expand Down Expand Up @@ -83,12 +84,6 @@ contract BytecodeRepository is Ownable2Step, SanityCheckTrait, IBytecodeReposito
// Thrown if someone tries to deploy a contract which wasn't audited enough
error ContractIsNotAuditedException();

// Thrown when an attempt is made to add an auditor that already exists
error AuditorAlreadyAddedException();

// Thrown when an auditor is not found in the repository
error AuditorNotFoundException();

// Thrown if the caller is not the deployer of the bytecode
error NotDeployerException();

Expand All @@ -105,12 +100,6 @@ contract BytecodeRepository is Ownable2Step, SanityCheckTrait, IBytecodeReposito
// Event emitted when a contract is audited by an auditor
event AuditContract(address indexed auditor, bytes32 indexed contractType, uint256 indexed version);

// Event emitted when a new auditor is added to the repository
event AddAuditor(address indexed auditor, string name);

// Event emitted when an auditor is forbidden from the repository
event ForbidAuditor(address indexed auditor, string name);

// Event emitted when a new source is added to the bytecode information
event SourceAdded(bytes32 indexed contractType, uint256 indexed version, string comment, string linkToSource);

Expand All @@ -126,14 +115,6 @@ contract BytecodeRepository is Ownable2Step, SanityCheckTrait, IBytecodeReposito

EnumerableSet.UintSet internal _hashStorage;

// Auditors

// Keep all audtors joined the repository
EnumerableSet.AddressSet internal _auditors;

// Store auditors info
mapping(address => AuditorInfo) public auditorInfo;

// Postfixes are used to deploy unique contract versions inherited from
// the base contract but differ when used with specific tokens.
// For example, the USDT pool, which supports fee computation without errors
Expand Down Expand Up @@ -325,69 +306,25 @@ contract BytecodeRepository is Ownable2Step, SanityCheckTrait, IBytecodeReposito
* @return bool True if the contract has been audited by at least AUDITOR_THRESHOLD auditors, false otherwise.
*/
function isDeployPermitted(bytes32 _contractType, uint256 _version) public view returns (bool) {
BytecodeInfo memory info = bytecodeInfo[computeBytecodeHash(_contractType, _version)];
uint256 numAuditors = _getUniqueNonForbiddenAuditorCount(computeBytecodeHash(_contractType, _version));

// QUESTION: should we have more complex rules depending on domain?
return info.auditors.length >= AUDITOR_THRESHOLD;
}

//
// AUDITOR MANAGEMENT
//
function addAuditor(address auditor, string memory _name) external onlyOwner nonZeroAddress(auditor) {
if (bytes(_name).length == 0) {
revert IncorrectParameterException();
}
if (_auditors.contains(auditor)) {
revert AuditorAlreadyAddedException();
}

_auditors.add(auditor);
auditorInfo[auditor].name = _name;
emit AddAuditor(auditor, _name);
}

function forbidAuditor(address auditor) external onlyOwner nonZeroAddress(auditor) {
if (!_auditors.contains(auditor)) {
revert AuditorNotFoundException();
}

auditorInfo[auditor].forbidden = true;
emit ForbidAuditor(auditor, auditorInfo[auditor].name);
return numAuditors >= AUDITOR_THRESHOLD;
}

/**
* @notice Adds a security report for a specific contract type and version.
* @param _contractType The type of the contract for which the security report is being added.
* @param _version The version of the contract for which the security report is being added.
* @param auditor The address of the auditor adding the security report.
* @param reportUrl The URL of the security report.
* @dev Reverts if the caller is not a registered auditor or if the auditor is forbidden.
* If the auditor is not already associated with the contract, they are added to the list of auditors.
* The corresponding access control logic is implemented in AuditManager
* Emits an AuditContract event upon successful addition of the report.
*/
function addSecurityReport(bytes32 _contractType, uint256 _version, address auditor, string calldata reportUrl)
external
{
if (!_auditors.contains(msg.sender) || auditorInfo[msg.sender].forbidden) {
revert NoValidAuditorPermissionsAException();
}

function addSecurityReport(bytes32 _contractType, uint256 _version, string calldata reportUrl) external {
bytes32 bytecodeHash = computeBytecodeHash(_contractType, _version);
BytecodeInfo storage info = bytecodeInfo[bytecodeHash];

bool found;
for (uint256 i = 0; i < info.auditors.length; i++) {
if (info.auditors[i] == auditor) {
found = true;
}
}

if (!found) {
info.auditors.push(msg.sender);
}

info.reports.push(SecurityReport({auditor: msg.sender, url: reportUrl}));
_addSecurityReport(bytecodeHash, msg.sender, reportUrl);

emit AuditContract(msg.sender, _contractType, _version);
}
Expand Down
Loading

0 comments on commit e48e42e

Please sign in to comment.