Skip to content

Commit

Permalink
fix: vouch functions
Browse files Browse the repository at this point in the history
  • Loading branch information
daopunk committed Dec 1, 2024
1 parent 1263e55 commit 80bebd7
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 71 deletions.
142 changes: 84 additions & 58 deletions src/contracts/BuildersManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
// --- Data ---

/// @notice See params @IBuildersManager
BuilderManagerParams internal _params;
BuilderManagerSettings internal _settings;

/// @inheritdoc IBuildersManager
mapping(address _attester => bool _isEligible) public optimismFoundationAttester;
Expand All @@ -54,12 +54,14 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
// --- Modifiers ---

/**
* @notice Modifier to check if the user has already vouched for the project
* @notice Modifier to check if the project exists and if voter has already vouched for the project
* @param _projectAttestation The attestation hash of the project
* @param _caller The address of the caller
*/
modifier eligible(bytes32 _projectAttestation) {
modifier eligible(bytes32 _projectAttestation, address _caller) {
if (!eligibleVoter[_caller]) revert IdAttestationRequired();
if (eligibleProject[_projectAttestation] == address(0)) revert InvalidProjectAttestation();
if (voterToProjectVouch[msg.sender][_projectAttestation]) revert AlreadyVouched();
if (voterToProjectVouch[_caller][_projectAttestation]) revert AlreadyVouched();
_;
}

Expand All @@ -77,53 +79,56 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
address _eas,
string memory _name,
string memory _version,
BuilderManagerParams memory __params
BuilderManagerSettings memory __settings
) external initializer {
__EIP712_init(_name, _version);

TOKEN = BuildersDollar(_token);
EAS = IEAS(_eas);
_params = __params;
_settings = __settings;

uint256 _l = _params.optimismFoundationAttesters.length;
uint256 _l = _settings.optimismFoundationAttesters.length;
for (uint256 _i; _i < _l; ++_i) {
optimismFoundationAttester[_params.optimismFoundationAttesters[_i]] = true;
optimismFoundationAttester[_settings.optimismFoundationAttesters[_i]] = true;
}
}

// --- External Methods ---

// TODO: does this need access control?
/// @inheritdoc IBuildersManager
function attestProject(OffchainAttestation calldata _attestation) external {
if (!_validateProject(_attestation)) revert InvalidProjectAttestation();
function vouch(OffchainAttestation calldata _offchainProjectAttestation) external {
if (!_validateProject(_offchainProjectAttestation)) revert InvalidProjectAttestation();
_vouch(_hashProject(_offchainProjectAttestation), msg.sender);
}

/// @inheritdoc IBuildersManager
function vouch(bytes32 _projectAttestation) external eligible(_projectAttestation) {
if (!eligibleVoter[msg.sender]) revert IdAttestationRequired();
voterToProjectVouch[msg.sender][_projectAttestation] = true;
function vouch(OffchainAttestation calldata _offchainProjectAttestation, bytes32 _identityAttestation) external {
if (!_validateOptimismVoter(_identityAttestation, msg.sender)) revert InvalidIdAttestation();
if (!_validateProject(_offchainProjectAttestation)) revert InvalidProjectAttestation();
_vouch(_hashProject(_offchainProjectAttestation), msg.sender);
}

/// @inheritdoc IBuildersManager
function vouch(bytes32 _projectAttestation, bytes32 _identityAttestation) external eligible(_projectAttestation) {
if (!eligibleVoter[msg.sender]) {
if (!_validateOptimismVoter(_identityAttestation, msg.sender)) revert InvalidIdAttestation();
}
voterToProjectVouch[msg.sender][_projectAttestation] = true;
function vouch(bytes32 _projectAttestation) external {
_vouch(_projectAttestation, msg.sender);
}

/// @inheritdoc IBuildersManager
function vouch(bytes32 _projectAttestation, bytes32 _identityAttestation) external {
if (!_validateOptimismVoter(_identityAttestation, msg.sender)) revert InvalidIdAttestation();
_vouch(_projectAttestation, msg.sender);
}

/// @inheritdoc IBuildersManager
function distributeYield() external {
uint256 _l = _currentProjects.length;
if (_l == 0) revert YieldNoProjects();
if (block.timestamp < _params.lastClaimedTimestamp + _params.cycleLength) revert YieldNotReady();
_params.lastClaimedTimestamp = uint64(block.timestamp);
if (block.timestamp < _settings.lastClaimedTimestamp + _settings.cycleLength) revert CycleNotReady();
_settings.lastClaimedTimestamp = uint64(block.timestamp);

for (uint256 _i; _i < _l; ++_i) {
address _project = _currentProjects[_i];
if (projectToExpiry[_project] > block.timestamp) _ejectProject(_project);
else if (projectToVouches[_project] < _params.minVouches) _ejectProject(_project);
}
_l = _currentProjects.length;
if (_l == 0) revert YieldNoProjects();
Expand Down Expand Up @@ -162,8 +167,8 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
}

/// @inheritdoc IBuildersManager
function params() external view returns (BuilderManagerParams memory __params) {
__params = _params;
function settings() external view returns (BuilderManagerSettings memory __settings) {
__settings = _settings;
}

/// @inheritdoc IBuildersManager
Expand All @@ -173,18 +178,31 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder

/// @inheritdoc IBuildersManager
function optimismFoundationAttesters() external view returns (address[] memory _opAttesters) {
_opAttesters = _params.optimismFoundationAttesters;
_opAttesters = _settings.optimismFoundationAttesters;
}

// --- Internal Utilities ---

/**
* @notice Function to validate the voucher's identity
* @notice Internal function to vouch for a project
* @param _projectAttestation The attestation hash of the project
* @param _caller The address of the caller
*/
function _vouch(bytes32 _projectAttestation, address _caller) internal eligible(_projectAttestation, _caller) {
voterToProjectVouch[_caller][_projectAttestation] = true;
address _project = eligibleProject[_projectAttestation];
projectToVouches[_project]++;
if (projectToVouches[_project] == _settings.minVouches) _currentProjects.push(_project);
}

/**
* @notice Internal function to validate the voucher's identity
* @param _identityAttestation The attestation hash of the voucher's identity
* @param _claimer The address of the voucher
* @return _verified True if the voter is elegible
*/
function _validateOptimismVoter(bytes32 _identityAttestation, address _claimer) internal returns (bool _verified) {
if (eligibleVoter[_claimer]) revert AlreadyVerified();
Attestation memory _attestation = EAS.getAttestation(_identityAttestation);

if (_attestation.uid == bytes32(0)) {
Expand All @@ -194,20 +212,21 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
} else if (_attestation.recipient != _claimer) {
_verified = false;
} else {
(uint256 farcasterID,,,,) = abi.decode(_attestation.data, (uint256, string, string, string, string));

_verified = true;
eligibleVoter[_claimer] = _verified;
emit VoterValidated(_claimer, farcasterID);
emit VoterValidated(_claimer, _identityAttestation);
}
}

/**
* @notice Function to verify the project's attestation
* @notice Internal function to verify the project's attestation
* @param _attestation The project's Attestation
* @return _verified True if the project is verified
*/
function _validateProject(OffchainAttestation calldata _attestation) internal returns (bool _verified) {
bytes32 _projectHash = _hashProject(_attestation);
if (eligibleProject[_projectHash] != address(0)) revert AlreadyVerified();

SchemaRecord memory schemaRecord = EAS.getSchemaRegistry().getSchema(_attestation.schema);

if (schemaRecord.uid == EMPTY_UID) {
Expand All @@ -216,7 +235,7 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
_verified = false;
} else if (_attestation.version != _VERSION1) {
_verified = false;
} else if (_attestation.time < _params.currentSeasonExpiry - _params.seasonDuration) {
} else if (_attestation.time < _settings.currentSeasonExpiry - _settings.seasonDuration) {
_verified = false;
} else if (_attestation.expirationTime < block.timestamp) {
_verified = false;
Expand All @@ -225,31 +244,15 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
} else if (!EAS.isAttestationValid(_attestation.refUID)) {
_verified = false;
} else {
bytes32 _projectHash = _hashTypedDataV4(
keccak256(
abi.encode(
_VERSION1_ATTEST_TYPEHASH,
_VERSION1,
_attestation.schema,
_attestation.recipient,
_attestation.time,
_attestation.expirationTime,
_attestation.revocable,
_attestation.refUID,
keccak256(_attestation.data)
)
)
);
Signature memory _sig = _attestation.signature;

_verified = SignatureChecker.isValidSignatureNow(
_attestation.attester, _projectHash, abi.encodePacked(_sig.r, _sig.s, _sig.v)
);
if (_verified) {
address _project = _attestation.recipient;
_currentProjects.push(_project);
eligibleProject[_projectHash] = _project;
projectToExpiry[_project] = _params.currentSeasonExpiry;
projectToExpiry[_project] = _settings.currentSeasonExpiry;

emit ProjectValidated(_projectHash, _project);
}
Expand Down Expand Up @@ -279,11 +282,11 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
* @param _value The new value
*/
function _modifyParams(bytes32 _param, uint256 _value) internal {
if (_param == 'cycleLength') _params.cycleLength = uint64(_value);
else if (_param == 'lastClaimedTimestamp') _params.lastClaimedTimestamp = uint64(_value);
else if (_param == 'currentSeasonExpiry') _params.currentSeasonExpiry = uint64(_value);
else if (_param == 'seasonDuration') _params.seasonDuration = _value;
else if (_param == 'minVouches') _params.minVouches = _value;
if (_param == 'cycleLength') _settings.cycleLength = uint64(_value);
else if (_param == 'lastClaimedTimestamp') _settings.lastClaimedTimestamp = uint64(_value);
else if (_param == 'currentSeasonExpiry') _settings.currentSeasonExpiry = uint64(_value);
else if (_param == 'seasonDuration') _settings.seasonDuration = _value;
else if (_param == 'minVouches') _settings.minVouches = _value;
else revert InvalidParamBytes32(_param);
}

Expand All @@ -299,15 +302,38 @@ contract BuildersManager is EIP712Upgradeable, Ownable2StepUpgradeable, IBuilder
optimismFoundationAttester[_attester] = _status;

if (_status) {
_params.optimismFoundationAttesters.push(_attester);
_settings.optimismFoundationAttesters.push(_attester);
} else {
uint256 _l = _params.optimismFoundationAttesters.length;
uint256 _l = _settings.optimismFoundationAttesters.length;
for (uint256 _i; _i < _l; _i++) {
if (_params.optimismFoundationAttesters[_i] == _attester) {
_params.optimismFoundationAttesters[_i] = _params.optimismFoundationAttesters[_l - 1];
_params.optimismFoundationAttesters.pop();
if (_settings.optimismFoundationAttesters[_i] == _attester) {
_settings.optimismFoundationAttesters[_i] = _settings.optimismFoundationAttesters[_l - 1];
_settings.optimismFoundationAttesters.pop();
}
}
}
}

/**
* @notice Internal function to get the project hash from the offchain attestation
* @param _attestation The offchain attestation
* @return _projectHash The project hash
*/
function _hashProject(OffchainAttestation calldata _attestation) internal view returns (bytes32 _projectHash) {
_projectHash = _hashTypedDataV4(
keccak256(
abi.encode(
_VERSION1_ATTEST_TYPEHASH,
_VERSION1,
_attestation.schema,
_attestation.recipient,
_attestation.time,
_attestation.expirationTime,
_attestation.revocable,
_attestation.refUID,
keccak256(_attestation.data)
)
)
);
}
}
35 changes: 22 additions & 13 deletions src/interfaces/IBuildersManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ interface IBuildersManager {
DATA
//////////////////////////////////////////////////////////////*/
/**
* @notice Builder Manager Parameters
* @notice Builder Manager settings
* @param cycleLength The yield distribution cycle length
* @param lastClaimedTimestamp The timestamp for the last time yield was claimed
* @param currentSeasonExpiry The timestamp for the current season expiry
* @param seasonDuration The duration of a season
* @param minVouches The minimum number of vouches required for a project to receive yield
* @param optimismFoundationAttesters The list of attesting addresses for the OP Foundation
*/
struct BuilderManagerParams {
struct BuilderManagerSettings {
uint64 cycleLength;
uint64 lastClaimedTimestamp;
uint64 currentSeasonExpiry;
Expand All @@ -45,9 +45,9 @@ interface IBuildersManager {
/**
* @notice Emitted when a voter is validated
* @param _claimer The caller claiming the voter validation
* @param _farcasterID The Farcaster ID to prevent sybil attacks
* @param _identityAttestation The identity attestation hash
*/
event VoterValidated(address indexed _claimer, uint256 indexed _farcasterID);
event VoterValidated(address indexed _claimer, bytes32 indexed _identityAttestation);

/**
* @notice The event emitted when yield is distributed
Expand All @@ -63,7 +63,9 @@ interface IBuildersManager {
/// @notice Throws when the parameter is already set
/// @param _attester The attester address
error AlreadyUpdated(address _attester);
/// @notice Throws when the voter is already vouched
/// @notice Throws when the project or voter is already verified
error AlreadyVerified();
/// @notice Throws when the voter has already vouched for a project
error AlreadyVouched();
/// @notice Throws when the identification-attestation is required
error IdAttestationRequired();
Expand All @@ -79,7 +81,7 @@ interface IBuildersManager {
/// @notice Throws when the project is not found
error YieldNoProjects();
/// @notice Throws when the project is not in the current projects list
error YieldNotReady();
error CycleNotReady();

/*///////////////////////////////////////////////////////////////
LOGIC
Expand All @@ -97,14 +99,21 @@ interface IBuildersManager {
address _eas,
string memory _name,
string memory _version,
BuilderManagerParams memory _params
BuilderManagerSettings memory _params
) external;

/**
* @notice Verify a project with an off-chain attestation
* @param _attestation The off-chain attestation
* @notice Verify a project with an off-chain attestation and vouch for a project
* @param _offchainProjectAttestation The attestation of the project
*/
function attestProject(OffchainAttestation calldata _attestation) external;
function vouch(OffchainAttestation calldata _offchainProjectAttestation) external;

/**
* @notice Verify a project with an off-chain attestation and vouch for a project
* @param _offchainProjectAttestation The attestation of the project
* @param _identityAttestation The attestation of the voucher's identity
*/
function vouch(OffchainAttestation calldata _offchainProjectAttestation, bytes32 _identityAttestation) external;

/**
* @notice Vouch for a project
Expand Down Expand Up @@ -226,10 +235,10 @@ interface IBuildersManager {
function voterToProjectVouch(address _voter, bytes32 _attestHash) external view returns (bool _vouched);

/**
* @notice Get the current BuilderManager parameters
* @return __params The BuilderManager parameters
* @notice Get the current BuilderManager settings
* @return __settings The BuilderManager settings
*/
function params() external view returns (BuilderManagerParams memory __params);
function settings() external view returns (BuilderManagerSettings memory __settings);

/**
* @notice Get the current projects
Expand Down

0 comments on commit 80bebd7

Please sign in to comment.