From cde001e7f2067fa75231df77ff431e0fd035be6a Mon Sep 17 00:00:00 2001 From: PacificYield <173040337+PacificYield@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:38:44 +0100 Subject: [PATCH] docs: NatSpec updates --- contracts/governance/Comp.sol | 46 ++++++++++--------- contracts/governance/CompoundTimelock.sol | 52 +++++++++++++++++++--- contracts/governance/GovernorAlphaZama.sol | 2 +- contracts/governance/ICompoundTimelock.sol | 52 +++++++++++++++++++++- 4 files changed, 120 insertions(+), 32 deletions(-) diff --git a/contracts/governance/Comp.sol b/contracts/governance/Comp.sol index 4bdf381..a35cd9a 100644 --- a/contracts/governance/Comp.sol +++ b/contracts/governance/Comp.sol @@ -15,7 +15,7 @@ import { IComp } from "./IComp.sol"; * GovernorAlphaZama.sol. * It uses encrypted votes to delegate the voting power associated * with an account's balance. - * @dev The delegation of votes leaks information about the account's encrypted balance to the delegate. + * @dev The delegation of votes leaks information about the account's encrypted balance to the `delegatee`. */ contract Comp is IComp, EncryptedERC20, Ownable2Step { /// @notice Returned if the `blockNumber` is higher or equal to the (current) `block.number`. @@ -25,14 +25,14 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { /// @notice Returned if the `msg.sender` is not the `governor` contract. error GovernorInvalid(); - /// @notice Emitted when an account changes its delegate. + /// @notice Emitted when an `account` (i.e. `delegator`) changes its delegate. event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); - /// @notice Emitted when a delegate account's vote balance changes. + /// @notice Emitted when a `delegate` account's vote balance changes. event DelegateVotesChanged(address indexed delegate); /// @notice Emitted when the governor contract that can reencrypt votes changes. - /// @dev It can be set to a malicious contract, which could reencrypt all user votes. + /// @dev WARNING: it can be set to a malicious contract, which could reencrypt all user votes. event NewGovernor(address indexed governor); /// @notice A checkpoint for marking number of votes from a given block. @@ -58,17 +58,17 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { /// @dev The contract is expected to be a governor contract. address public governor; - /// @notice A record of each account's delegate. + /// @notice A record of each account's `delegate`. mapping(address account => address delegate) public delegates; /// @notice A record of states for signing/validating signatures. mapping(address account => uint256 nonce) public nonces; - /// @notice The number of checkpoints for each account. - mapping(address account => uint32 checkpoints) public numCheckpoints; + /// @notice The number of checkpoints for an `account`. + mapping(address account => uint32 _checkpoints) public numCheckpoints; - /// @notice A record of votes checkpoints for an `account` using incremental indices. - mapping(address account => mapping(uint32 index => Checkpoint checkpoint)) internal checkpoints; + /// @notice A record of votes _checkpoints for an `account` using incremental indices. + mapping(address account => mapping(uint32 index => Checkpoint checkpoint)) internal _checkpoints; /// @notice Constant for zero using TFHE. /// @dev Since it is expensive to compute 0, it is stored instead. @@ -144,11 +144,13 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { /** * @notice Get current votes of account. * @param account Account address - * @return votes Current votes. + * @return votes Current (encrypted) votes. */ function getCurrentVotes(address account) external view returns (euint64 votes) { uint32 nCheckpoints = numCheckpoints[account]; - votes = nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : votes; + if (nCheckpoints > 0) { + votes = _checkpoints[account][nCheckpoints - 1].votes; + } } /** @@ -167,7 +169,7 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { } /** - * @notice Set an allowed contract that can access votes. + * @notice Set a governor contract. * @param newGovernor New governor contract that can reencrypt/access votes. */ function setGovernor(address newGovernor) public onlyOwner { @@ -191,10 +193,10 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { if (nCheckpoints == 0) { return _EUINT64_ZERO; - } else if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { + } else if (_checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { // First check most recent balance - votes = checkpoints[account][nCheckpoints - 1].votes; - } else if (checkpoints[account][0].fromBlock > blockNumber) { + votes = _checkpoints[account][nCheckpoints - 1].votes; + } else if (_checkpoints[account][0].fromBlock > blockNumber) { // Next check implicit zero balance return _EUINT64_ZERO; } else { @@ -203,7 +205,7 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow - Checkpoint memory cp = checkpoints[account][center]; + Checkpoint memory cp = _checkpoints[account][center]; if (cp.fromBlock == blockNumber) { return cp.votes; } else if (cp.fromBlock < blockNumber) { @@ -212,7 +214,7 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { upper = center - 1; } } - votes = checkpoints[account][lower].votes; + votes = _checkpoints[account][lower].votes; } } @@ -220,14 +222,14 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { if (srcRep != dstRep) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; - euint64 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : _EUINT64_ZERO; + euint64 srcRepOld = srcRepNum > 0 ? _checkpoints[srcRep][srcRepNum - 1].votes : _EUINT64_ZERO; euint64 srcRepNew = TFHE.sub(srcRepOld, amount); // srcRepOld - amount; _writeCheckpoint(srcRep, srcRepNum, srcRepNew); } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; - euint64 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : _EUINT64_ZERO; + euint64 dstRepOld = dstRepNum > 0 ? _checkpoints[dstRep][dstRepNum - 1].votes : _EUINT64_ZERO; euint64 dstRepNew = TFHE.add(dstRepOld, amount); // dstRepOld + amount; _writeCheckpoint(dstRep, dstRepNum, dstRepNew); } @@ -242,10 +244,10 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step { } function _writeCheckpoint(address delegatee, uint32 nCheckpoints, euint64 newVotes) internal { - if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == block.number) { - checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; + if (nCheckpoints > 0 && _checkpoints[delegatee][nCheckpoints - 1].fromBlock == block.number) { + _checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; } else { - checkpoints[delegatee][nCheckpoints] = Checkpoint(block.number, newVotes); + _checkpoints[delegatee][nCheckpoints] = Checkpoint(block.number, newVotes); numCheckpoints[delegatee] = nCheckpoints + 1; } diff --git a/contracts/governance/CompoundTimelock.sol b/contracts/governance/CompoundTimelock.sol index 900b96f..b716fdd 100644 --- a/contracts/governance/CompoundTimelock.sol +++ b/contracts/governance/CompoundTimelock.sol @@ -12,16 +12,36 @@ import { ICompoundTimelock } from "./ICompoundTimelock.sol"; * not to be executed after a specific period following the queuing. */ contract CompoundTimelock is ICompoundTimelock { + /** + * @notice See {ICompoundTimelock-GRACE_PERIOD}. + */ uint256 public constant GRACE_PERIOD = 14 days; + + /// @notice Minimum delay that can be set in the setDelay function. uint256 public constant MINIMUM_DELAY = 2 days; + + /// @notice Maximum delay that can be set in the setDelay function. uint256 public constant MAXIMUM_DELAY = 30 days; + /// @notice Admin address. address public admin; + + /// @notice Pending admin address. + /// @dev The transer of the admin is a two-step process. address public pendingAdmin; + + /** + * @notice See {ICompoundTimelock-delay}. + */ uint256 public delay; + /// @notice Return whether the transaction is queued based on its hash. mapping(bytes32 => bool) public queuedTransactions; + /** + * @param admin_ Admin address. + * @param delay_ Delay (in timestamp). + */ constructor(address admin_, uint256 delay_) { require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); @@ -32,6 +52,11 @@ contract CompoundTimelock is ICompoundTimelock { receive() external payable {} + /** + * @notice Set the delay. + * @dev This transaction must be queued. + * @param delay_ Delay (in timestamp). + */ function setDelay(uint256 delay_) public { require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); @@ -41,6 +66,9 @@ contract CompoundTimelock is ICompoundTimelock { emit NewDelay(delay); } + /** + * @notice See {ICompoundTimelock-acceptAdmin}. + */ function acceptAdmin() public { require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); admin = msg.sender; @@ -49,6 +77,11 @@ contract CompoundTimelock is ICompoundTimelock { emit NewAdmin(admin); } + /** + * @notice Set the pending admin. + * @dev This transaction must be queued. + * @param pendingAdmin_ Pending admin address. + */ function setPendingAdmin(address pendingAdmin_) public { require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); pendingAdmin = pendingAdmin_; @@ -56,6 +89,9 @@ contract CompoundTimelock is ICompoundTimelock { emit NewPendingAdmin(pendingAdmin); } + /** + * @notice See {ICompoundTimelock-queueTransaction}. + */ function queueTransaction( address target, uint256 value, @@ -65,7 +101,7 @@ contract CompoundTimelock is ICompoundTimelock { ) public returns (bytes32) { require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); require( - eta >= getBlockTimestamp() + delay, + eta >= block.timestamp + delay, "Timelock::queueTransaction: Estimated execution block must satisfy delay." ); @@ -76,6 +112,9 @@ contract CompoundTimelock is ICompoundTimelock { return txHash; } + /** + * @notice See {ICompoundTimelock-cancelTransaction}. + */ function cancelTransaction( address target, uint256 value, @@ -91,6 +130,9 @@ contract CompoundTimelock is ICompoundTimelock { emit CancelTransaction(txHash, target, value, signature, data, eta); } + /** + * @notice See {ICompoundTimelock-executeTransaction}. + */ function executeTransaction( address target, uint256 value, @@ -102,8 +144,8 @@ contract CompoundTimelock is ICompoundTimelock { bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); - require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); - require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); + require(block.timestamp >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(block.timestamp <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); queuedTransactions[txHash] = false; @@ -122,8 +164,4 @@ contract CompoundTimelock is ICompoundTimelock { return returnData; } - - function getBlockTimestamp() internal view returns (uint256) { - return block.timestamp; - } } diff --git a/contracts/governance/GovernorAlphaZama.sol b/contracts/governance/GovernorAlphaZama.sol index 065d5e1..95ba7d2 100644 --- a/contracts/governance/GovernorAlphaZama.sol +++ b/contracts/governance/GovernorAlphaZama.sol @@ -17,7 +17,7 @@ import { ICompoundTimelock } from "./ICompoundTimelock.sol"; * - Proposal: A new proposal is made to introduce a change. * - Voting: Users can vote on the proposal, either in favor or against it. * - Quorum: A minimum number of votes (quorum) must be reached for the proposal to pass. - * - Execution: Once a proposal passes, it is executed and takes effect on the Compound protocol. + * - Execution: Once a proposal passes, it is executed and takes effect on the taregt protocol. */ contract GovernorAlphaZama is Ownable2Step, GatewayCaller { /// @notice Returned if proposal contains too many changes. diff --git a/contracts/governance/ICompoundTimelock.sol b/contracts/governance/ICompoundTimelock.sol index 46287da..ff3bb3f 100644 --- a/contracts/governance/ICompoundTimelock.sol +++ b/contracts/governance/ICompoundTimelock.sol @@ -5,9 +5,16 @@ pragma solidity ^0.8.24; * @title ICompoundTimelock */ interface ICompoundTimelock { + /// @notice Emitted when there is a change of admin. event NewAdmin(address indexed newAdmin); + + /// @notice Emtited when there is a change of pending admin. event NewPendingAdmin(address indexed newPendingAdmin); + + /// @notice Emitted when there is a new delay set. event NewDelay(uint256 indexed newDelay); + + /// @notice Emitted when the queued transaction is canceled. event CancelTransaction( bytes32 indexed txHash, address indexed target, @@ -16,6 +23,8 @@ interface ICompoundTimelock { bytes data, uint256 eta ); + + /// @notice Emitted when the queued transaction is executed. event ExecuteTransaction( bytes32 indexed txHash, address indexed target, @@ -24,6 +33,8 @@ interface ICompoundTimelock { bytes data, uint256 eta ); + + /// @notice Emitted when a transaction is queued. event QueueTransaction( bytes32 indexed txHash, address indexed target, @@ -33,22 +44,51 @@ interface ICompoundTimelock { uint256 eta ); + /** + * @notice Returns the delay (in timestamp) for a queued transaction before it can be executed. + */ function delay() external view returns (uint256); + /** + * @notice Returns the grace period (in timestamp). + * The grace period indicates how long a transaction can remain queued before it cannot be + * executed again. + */ function GRACE_PERIOD() external view returns (uint256); + /** + * @notice Accept admin role. + */ function acceptAdmin() external; + /** + * @notice Returns whether the transactions are queued. + */ function queuedTransactions(bytes32 hash) external view returns (bool); + /** + * @notice Queue a transaction. + * @param target Target address to execute the transaction. + * @param signature Function signature to execute. + * @param data The data to include in the transaction. + * @param eta The earliest eta to queue the transaction. + * @return hashTransaction The transaction's hash. + */ function queueTransaction( address target, uint256 value, string calldata signature, bytes calldata data, uint256 eta - ) external returns (bytes32); + ) external returns (bytes32 hashTransaction); + /** + * @notice Cancel a queued transaction. + * @param target Target address to execute the transaction. + * @param signature Function signature to execute. + * @param data The data to include in the transaction. + * @param eta The earliest eta to queue the transaction. + */ function cancelTransaction( address target, uint256 value, @@ -57,11 +97,19 @@ interface ICompoundTimelock { uint256 eta ) external; + /** + * @notice Cancel a queued transaction. + * @param target Target address to execute the transaction. + * @param signature Function signature to execute. + * @param data The data to include in the transaction. + * @param eta The earliest eta to queue the transaction. + * @return response The response from the transaction once executed. + */ function executeTransaction( address target, uint256 value, string calldata signature, bytes calldata data, uint256 eta - ) external payable returns (bytes memory); + ) external payable returns (bytes memory response); }