Skip to content

Commit

Permalink
docs: NatSpec updates
Browse files Browse the repository at this point in the history
  • Loading branch information
PacificYield committed Nov 7, 2024
1 parent 32b38ae commit cde001e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 32 deletions.
46 changes: 24 additions & 22 deletions contracts/governance/Comp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}
}

/**
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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) {
Expand All @@ -212,22 +214,22 @@ contract Comp is IComp, EncryptedERC20, Ownable2Step {
upper = center - 1;
}
}
votes = checkpoints[account][lower].votes;
votes = _checkpoints[account][lower].votes;
}
}

function _moveDelegates(address srcRep, address dstRep, euint64 amount) internal {
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);
}
Expand All @@ -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;
}

Expand Down
52 changes: 45 additions & 7 deletions contracts/governance/CompoundTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand All @@ -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.");
Expand All @@ -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;
Expand All @@ -49,13 +77,21 @@ 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_;

emit NewPendingAdmin(pendingAdmin);
}

/**
* @notice See {ICompoundTimelock-queueTransaction}.
*/
function queueTransaction(
address target,
uint256 value,
Expand All @@ -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."
);

Expand All @@ -76,6 +112,9 @@ contract CompoundTimelock is ICompoundTimelock {
return txHash;
}

/**
* @notice See {ICompoundTimelock-cancelTransaction}.
*/
function cancelTransaction(
address target,
uint256 value,
Expand All @@ -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,
Expand All @@ -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;

Expand All @@ -122,8 +164,4 @@ contract CompoundTimelock is ICompoundTimelock {

return returnData;
}

function getBlockTimestamp() internal view returns (uint256) {
return block.timestamp;
}
}
2 changes: 1 addition & 1 deletion contracts/governance/GovernorAlphaZama.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
52 changes: 50 additions & 2 deletions contracts/governance/ICompoundTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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);
}

0 comments on commit cde001e

Please sign in to comment.