Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExpertCommitteeArbitrator #587

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
91 changes: 68 additions & 23 deletions contracts/HATClaimsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu
bool public arbitratorCanSubmitClaims;
// Can the committee revoke the token lock
bool public isTokenLockRevocable;
mapping(bytes32 => ArbitratorChangeProposal) public arbitratorChangeProposals;

modifier onlyRegistryOwner() {
if (registry.owner() != msg.sender) revert OnlyRegistryOwner();
Expand Down Expand Up @@ -196,7 +197,16 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu
);
}

function challengeClaim(bytes32 _claimId) external isActiveClaim(_claimId) {
function challengeClaim(bytes32 _claimId, uint16 _bountyPercentage, address _beneficiary) external {
Claim memory _claim = activeClaim;
arbitratorChangeProposals[_claimId] = ArbitratorChangeProposal({
beneficiary: _claim.arbitratorCanChangeBeneficiary ? _beneficiary : address(0),
bountyPercentage: _claim.arbitratorCanChangeBounty ? _bountyPercentage : 0
});
challengeClaim(_claimId);
}

function challengeClaim(bytes32 _claimId) public isActiveClaim(_claimId) {
if (msg.sender != activeClaim.arbitrator && msg.sender != registry.owner())
revert OnlyArbitratorOrRegistryOwner();
// solhint-disable-next-line not-rely-on-time
Expand All @@ -214,28 +224,47 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu
function approveClaim(bytes32 _claimId, uint16 _bountyPercentage, address _beneficiary) external nonReentrant isActiveClaim(_claimId) {
Claim memory _claim = activeClaim;
delete activeClaim;


ArbitratorChangeProposal memory arbitratorChangeProposal = arbitratorChangeProposals[_claimId];
bool hasArbitratorProposal = arbitratorChangeProposal.beneficiary != address(0) || arbitratorChangeProposal.bountyPercentage != 0;

// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= _claim.createdAt + _claim.challengePeriod + _claim.challengeTimeOutPeriod) {
// cannot approve an expired claim
revert ClaimExpired();
// cannot approve an expired claim unless there's an arbitrator proposal
if (!hasArbitratorProposal) {
revert ClaimExpired();
}
}
if (_claim.challengedAt != 0) {
// the claim was challenged, and only the arbitrator can approve it, within the timeout period
if (
msg.sender != _claim.arbitrator ||
// solhint-disable-next-line not-rely-on-time
block.timestamp >= _claim.challengedAt + _claim.challengeTimeOutPeriod
)
bool isArbitrator = msg.sender == _claim.arbitrator;
bool afterChallengeTimeout = block.timestamp >= _claim.challengedAt + _claim.challengeTimeOutPeriod;

// the claim was challenged
if (afterChallengeTimeout) {
// after challenge timeout, can only approve if there's an arbitrator proposal
if (!hasArbitratorProposal) {
revert ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod();
}
} else if (!isArbitrator) {
// during challenge timeout, only arbitrator can approve
revert ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod();
}

// the arbitrator can update the bounty if needed
if (_claim.arbitratorCanChangeBounty && _bountyPercentage != 0) {
_claim.bountyPercentage = _bountyPercentage;
if (_claim.arbitratorCanChangeBounty) {
if (isArbitrator && _bountyPercentage != 0 && !afterChallengeTimeout) {
_claim.bountyPercentage = _bountyPercentage;
} else if (arbitratorChangeProposal.bountyPercentage != 0) {
_claim.bountyPercentage = arbitratorChangeProposal.bountyPercentage;
}
}

if (_claim.arbitratorCanChangeBeneficiary && _beneficiary != address(0)) {
_claim.beneficiary = _beneficiary;
if (_claim.arbitratorCanChangeBeneficiary) {
if (isArbitrator && _beneficiary != address(0) && !afterChallengeTimeout) {
_claim.beneficiary = _beneficiary;
} else if (arbitratorChangeProposal.beneficiary != address(0)) {
_claim.beneficiary = arbitratorChangeProposal.beneficiary;
}
}
} else {
// the claim can be approved by anyone if the challengePeriod passed without a challenge
Expand Down Expand Up @@ -321,19 +350,35 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu
function dismissClaim(bytes32 _claimId) external isActiveClaim(_claimId) {
uint256 _challengeTimeOutPeriod = activeClaim.challengeTimeOutPeriod;
uint256 _challengedAt = activeClaim.challengedAt;
ArbitratorChangeProposal memory arbitratorChangeProposal = arbitratorChangeProposals[_claimId];
bool hasArbitratorProposal = arbitratorChangeProposal.beneficiary != address(0) || arbitratorChangeProposal.bountyPercentage != 0;

// solhint-disable-next-line not-rely-on-time
if (block.timestamp <= activeClaim.createdAt + activeClaim.challengePeriod + _challengeTimeOutPeriod) {
if (_challengedAt == 0) revert OnlyCallableIfChallenged();
if (
// solhint-disable-next-line not-rely-on-time
block.timestamp <= _challengedAt + _challengeTimeOutPeriod &&
msg.sender != activeClaim.arbitrator
) revert OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod();
} // else the claim is expired and should be dismissed
// During total timeout period
if (_challengedAt != 0) {
// Challenged claim
bool isInTimeoutPeriod = block.timestamp <= _challengedAt + _challengeTimeOutPeriod;
bool isArbitrator = msg.sender == activeClaim.arbitrator;

if (!isArbitrator) {
if (hasArbitratorProposal || isInTimeoutPeriod) {
revert OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod();
}
} else if (hasArbitratorProposal && !isInTimeoutPeriod) {
revert CannotDismissArbitratorProposalAfterTimoutPeriodOrIfNotAbitrator();
}
} else {
// Unchallenged claim cannot be dismissed during timeout period
revert OnlyCallableIfChallenged();
}
} else if (hasArbitratorProposal) {
// After timeout, if there's an arbitrator proposal it can only be approved
revert CannotDismissArbitratorProposalAfterTimoutPeriodOrIfNotAbitrator();
} // else expired claim with no arbitrator proposal can be dismissed by anyone

delete activeClaim;

vault.setWithdrawPaused(false);

emit DismissClaim(_claimId);
}
/* -------------------------------------------------------------------------------- */
Expand Down
40 changes: 40 additions & 0 deletions contracts/HATCommitteeArbitrator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
// Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md

pragma solidity 0.8.16;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IHATClaimsManager.sol";

contract HATCommitteeArbitrator is Ownable {
address public expertCommittee;

error OnlyExpertCommittee();

modifier onlyExpertCommittee() {
if (msg.sender != expertCommittee) {
revert OnlyExpertCommittee();
}
_;
}

constructor(address _expertCommittee) {
expertCommittee = _expertCommittee;
}

function setExpertCommittee(address _expertCommittee) external onlyOwner {
expertCommittee = _expertCommittee;
}

function challengeClaim(IHATClaimsManager _vault, bytes32 _claimId, uint16 _bountyPercentage, address _beneficiary) external onlyExpertCommittee {
_vault.challengeClaim(_claimId, _bountyPercentage, _beneficiary);
}

function approveClaim(IHATClaimsManager _vault, bytes32 _claimId) external onlyOwner {
_vault.approveClaim(_claimId, 0, address(0));
}

function dismissClaim(IHATClaimsManager _vault, bytes32 _claimId) external onlyOwner {
_vault.dismissClaim(_claimId);
}
}
20 changes: 19 additions & 1 deletion contracts/interfaces/IHATClaimsManager.sol
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to update this interface without redeploying the registry @ben-kaufman ?

Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
uint32 timestamp;
}

struct ArbitratorChangeProposal {
address beneficiary;
uint16 bountyPercentage;
}

/**
* @notice Initialization parameters for the vault
* @param name The vault's name (concatenated as "Hats Vault " + name)
Expand Down Expand Up @@ -198,6 +203,8 @@
error CannotSetToPerviousRewardController();
// Payout must either be 100%, or up to the MAX_BOUNTY_LIMIT
error PayoutMustBeUpToMaxBountyLimitOrHundredPercent();
// Cannot dismiss an arbitrator proposal after claim has expired or during timeout period if not arbitrator
error CannotDismissArbitratorProposalAfterTimoutPeriodOrIfNotAbitrator();


event SubmitClaim(
Expand Down Expand Up @@ -273,10 +280,21 @@
* payout that had been previously submitted by the committee.
* Can only be called during the challenge period after submission of the
* claim.
* @param _claimId The claim ID
*/
function challengeClaim(bytes32 _claimId) external;

/**
* @notice Called by the arbitrator or governance to challenge a claim for a bounty
* payout that had been previously submitted by the committee.
* Can only be called during the challenge period after submission of the
* claim.
* @param _claimId The claim ID
* * @param _bountyPercentage The percentage of the vault's balance that will
* be sent as a bounty.
* @param _beneficiary where the bounty will be sent to.
*/
function challengeClaim(bytes32 _claimId, uint16 _bountyPercentage, address _beneficiary) external;

/**
* @notice Approve a claim for a bounty submitted by a committee, and
* pay out bounty to hacker and committee. Also transfer to the
Expand Down Expand Up @@ -492,5 +510,5 @@
* @notice Returns the claims manager's version
* @return The claims manager's version
*/
function VERSION() external view returns(string calldata);

Check warning on line 513 in contracts/interfaces/IHATClaimsManager.sol

View workflow job for this annotation

GitHub Actions / build (20.x)

Function name must be in mixedCase
}
52 changes: 52 additions & 0 deletions docs/dodoc/HATClaimsManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,29 @@ function arbitratorCanSubmitClaims() external view returns (bool)
|---|---|---|
| _0 | bool | undefined |

### arbitratorChangeProposals

```solidity
function arbitratorChangeProposals(bytes32) external view returns (address beneficiary, uint16 bountyPercentage)
```





#### Parameters

| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |

#### Returns

| Name | Type | Description |
|---|---|---|
| beneficiary | address | undefined |
| bountyPercentage | uint16 | undefined |

### bountySplit

```solidity
Expand Down Expand Up @@ -273,11 +296,29 @@ Called by the arbitrator or governance to challenge a claim for a bounty payout



#### Parameters

| Name | Type | Description |
|---|---|---|
| _claimId | bytes32 | undefined |

### challengeClaim

```solidity
function challengeClaim(bytes32 _claimId, uint16 _bountyPercentage, address _beneficiary) external nonpayable
```

Called by the arbitrator or governance to challenge a claim for a bounty payout that had been previously submitted by the committee. Can only be called during the challenge period after submission of the claim.



#### Parameters

| Name | Type | Description |
|---|---|---|
| _claimId | bytes32 | The claim ID |
| _bountyPercentage | uint16 | The percentage of the vault&#39;s balance that will be sent as a bounty. |
| _beneficiary | address | where the bounty will be sent to. |

### committee

Expand Down Expand Up @@ -1136,6 +1177,17 @@ error BountyPercentageHigherThanMaxBounty()



### CannotDismissArbitratorProposalAfterTimoutPeriodOrIfNotAbitrator

```solidity
error CannotDismissArbitratorProposalAfterTimoutPeriodOrIfNotAbitrator()
```






### CannotSetToPerviousRewardController

```solidity
Expand Down
Loading
Loading