Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into theo/update-quorums-and-thresholds
Browse files Browse the repository at this point in the history
  • Loading branch information
dd0sxx committed Dec 14, 2023
2 parents b11e1bb + 516a332 commit 2a9d28f
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 93 deletions.
6 changes: 6 additions & 0 deletions src/lib/LlamaUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ library LlamaUtils {
return uint16(n);
}

/// @dev Reverts if `n` does not fit in a `uint48`.
function toUint48(uint256 n) internal pure returns (uint48) {
if (n > type(uint48).max) revert UnsafeCast(n);
return uint48(n);
}

/// @dev Reverts if `n` does not fit in a `uint64`.
function toUint64(uint256 n) internal pure returns (uint64) {
if (n > type(uint64).max) revert UnsafeCast(n);
Expand Down
11 changes: 8 additions & 3 deletions src/token-voting/ILlamaTokenClockAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ pragma solidity ^0.8.23;
/// @notice This contract provides an interface for clock adapters. Clock adapters enable voting tokens that don't use
/// timestamp-based checkpointing to work with the Llama token voting module.
interface ILlamaTokenClockAdapter {
/// @notice Returns the most recent timepoint in the past.
function currentTimepointMinusOne() external view returns (uint256 timepoint);
/// @notice Returns the current timepoint according to the token's clock.
/// @return timepoint the current timepoint
function clock() external view returns (uint48 timepoint);

/// @notice Machine-readable description of the clock as specified in ERC-6372.
function CLOCK_MODE() external view returns (string memory);

/// @notice Converts a timestamp to timepoint units.
/// @param timestamp The timestamp to convert.
function timestampToTimepoint(uint256 timestamp) external view returns (uint256 timepoint);
/// @return timepoint the current timepoint
function timestampToTimepoint(uint256 timestamp) external view returns (uint48 timepoint);

/// @notice Returns true if the clock mode is supported and false if it is unsupported.
/// @param clockMode The clock mode to check.
Expand Down
6 changes: 3 additions & 3 deletions src/token-voting/LlamaERC20TokenActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ contract LlamaERC20TokenActionCreator is LlamaTokenActionCreator {
}

/// @inheritdoc LlamaTokenActionCreator
function _getPastVotes(address account, uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastVotes(address account, uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastVotes(account, timepoint);
}

/// @inheritdoc LlamaTokenActionCreator
function _getPastTotalSupply(uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastTotalSupply(uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastTotalSupply(timepoint);
}

/// @inheritdoc LlamaTokenActionCreator
function _getClockMode() internal view virtual override returns (string memory) {
function _getClockMode() internal view virtual override returns (string memory clockmode) {
return token.CLOCK_MODE();
}
}
6 changes: 3 additions & 3 deletions src/token-voting/LlamaERC20TokenCaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ contract LlamaERC20TokenCaster is LlamaTokenCaster {
}

/// @inheritdoc LlamaTokenCaster
function _getPastVotes(address account, uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastVotes(address account, uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastVotes(account, timepoint);
}

/// @inheritdoc LlamaTokenCaster
function _getPastTotalSupply(uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastTotalSupply(uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastTotalSupply(timepoint);
}

/// @inheritdoc LlamaTokenCaster
function _getClockMode() internal view virtual override returns (string memory) {
function _getClockMode() internal view virtual override returns (string memory clockmode) {
return token.CLOCK_MODE();
}
}
6 changes: 3 additions & 3 deletions src/token-voting/LlamaERC721TokenActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ contract LlamaERC721TokenActionCreator is LlamaTokenActionCreator {
}

/// @inheritdoc LlamaTokenActionCreator
function _getPastVotes(address account, uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastVotes(address account, uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastVotes(account, timepoint);
}

/// @inheritdoc LlamaTokenActionCreator
function _getPastTotalSupply(uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastTotalSupply(uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastTotalSupply(timepoint);
}

/// @inheritdoc LlamaTokenActionCreator
function _getClockMode() internal view virtual override returns (string memory) {
function _getClockMode() internal view virtual override returns (string memory clockmode) {
return token.CLOCK_MODE();
}
}
6 changes: 3 additions & 3 deletions src/token-voting/LlamaERC721TokenCaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@ contract LlamaERC721TokenCaster is LlamaTokenCaster {
}

/// @inheritdoc LlamaTokenCaster
function _getPastVotes(address account, uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastVotes(address account, uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastVotes(account, timepoint);
}

/// @inheritdoc LlamaTokenCaster
function _getPastTotalSupply(uint256 timepoint) internal view virtual override returns (uint256) {
function _getPastTotalSupply(uint48 timepoint) internal view virtual override returns (uint256) {
return token.getPastTotalSupply(timepoint);
}

/// @inheritdoc LlamaTokenCaster
function _getClockMode() internal view virtual override returns (string memory) {
function _getClockMode() internal view virtual override returns (string memory clockmode) {
return token.CLOCK_MODE();
}
}
13 changes: 6 additions & 7 deletions src/token-voting/LlamaTokenActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -276,23 +276,22 @@ abstract contract LlamaTokenActionCreator is Initializable {
}

/// @dev Returns the current timepoint minus one.
function _currentTimepointMinusOne() internal view returns (uint256) {
if (_isClockModeTimestamp()) return block.timestamp - 1;
return clockAdapter.currentTimepointMinusOne();
function _currentTimepointMinusOne() internal view returns (uint48) {
if (_isClockModeTimestamp()) return LlamaUtils.toUint48(block.timestamp - 1);
return clockAdapter.clock() - 1;
}

// Returns true if the clock mode is timestamp
function _isClockModeTimestamp() internal view returns (bool) {
string memory clockMode = _getClockMode();
if (keccak256(abi.encodePacked(clockMode)) == keccak256(abi.encodePacked("mode=timestamp"))) return true;
return false;
return keccak256(abi.encodePacked(clockMode)) == keccak256(abi.encodePacked("mode=timestamp"));
}

/// @dev Returns the number of votes for a given token holder at a given timestamp.
function _getPastVotes(address account, uint256 timepoint) internal view virtual returns (uint256) {}
function _getPastVotes(address account, uint48 timepoint) internal view virtual returns (uint256) {}

/// @dev Returns the total supply of the token at a given timestamp.
function _getPastTotalSupply(uint256 timepoint) internal view virtual returns (uint256) {}
function _getPastTotalSupply(uint48 timepoint) internal view virtual returns (uint256) {}

/// @dev Returns the clock mode of the token (https://eips.ethereum.org/EIPS/eip-6372).
function _getClockMode() internal view virtual returns (string memory) {}
Expand Down
90 changes: 47 additions & 43 deletions src/token-voting/LlamaTokenCaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ abstract contract LlamaTokenCaster is Initializable {
/// @dev Thrown when a user tries to submit an approval but there are not enough votes.
error InsufficientVotes(uint256 votes, uint256 threshold);

/// @dev Thrown when a user tries to cast but does not have enough tokens.
error InsufficientBalance(uint256 balance);

/// @dev Thrown when an invalid `voteQuorumPct` is passed to the constructor.
error InvalidVoteQuorumPct(uint16 voteQuorumPct);

Expand Down Expand Up @@ -111,19 +108,19 @@ abstract contract LlamaTokenCaster is Initializable {
// ========================

/// @dev Emitted when a vote is cast.
event VoteCast(uint256 id, address indexed tokenholder, uint8 indexed support, uint256 quantity, string reason);
event VoteCast(uint256 id, address indexed tokenholder, uint8 indexed support, uint256 weight, string reason);

/// @dev Emitted when a cast approval is submitted to the `LlamaCore` contract.
event ApprovalSubmitted(
uint256 id, address indexed caller, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain
uint256 id, address indexed caller, uint256 weightFor, uint256 weightAgainst, uint256 weightAbstain
);

/// @dev Emitted when a veto is cast.
event VetoCast(uint256 id, address indexed tokenholder, uint8 indexed support, uint256 quantity, string reason);
event VetoCast(uint256 id, address indexed tokenholder, uint8 indexed support, uint256 weight, string reason);

/// @dev Emitted when a cast disapproval is submitted to the `LlamaCore` contract.
event DisapprovalSubmitted(
uint256 id, address indexed caller, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain
uint256 id, address indexed caller, uint256 weightFor, uint256 weightAgainst, uint256 weightAbstain
);

/// @dev Emitted when the voting quorum and/or vetoing quorum is set.
Expand Down Expand Up @@ -220,8 +217,8 @@ abstract contract LlamaTokenCaster is Initializable {
/// 1 = For
/// 2 = Abstain, but this is not currently supported.
/// @param reason The reason given for the approval by the tokenholder.
function castVote(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external {
_castVote(msg.sender, actionInfo, support, reason);
function castVote(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external returns (uint96) {
return _castVote(msg.sender, actionInfo, support, reason);
}

function castVoteBySig(
Expand All @@ -232,11 +229,11 @@ abstract contract LlamaTokenCaster is Initializable {
uint8 v,
bytes32 r,
bytes32 s
) external {
) external returns (uint96) {
bytes32 digest = _getCastVoteTypedDataHash(caster, support, actionInfo, reason);
address signer = ecrecover(digest, v, r, s);
if (signer == address(0) || signer != caster) revert InvalidSignature();
_castVote(signer, actionInfo, support, reason);
return _castVote(signer, actionInfo, support, reason);
}

/// @notice How tokenholders add their support of the disapproval of an action with a reason.
Expand All @@ -247,8 +244,8 @@ abstract contract LlamaTokenCaster is Initializable {
/// 1 = For
/// 2 = Abstain, but this is not currently supported.
/// @param reason The reason given for the approval by the tokenholder.
function castVeto(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external {
_castVeto(msg.sender, actionInfo, support, reason);
function castVeto(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external returns (uint96) {
return _castVeto(msg.sender, actionInfo, support, reason);
}

function castVetoBySig(
Expand All @@ -259,11 +256,11 @@ abstract contract LlamaTokenCaster is Initializable {
uint8 v,
bytes32 r,
bytes32 s
) external {
) external returns (uint96) {
bytes32 digest = _getCastVetoTypedDataHash(caster, support, actionInfo, reason);
address signer = ecrecover(digest, v, r, s);
if (signer == address(0) || signer != caster) revert InvalidSignature();
_castVeto(signer, actionInfo, support, reason);
return _castVeto(signer, actionInfo, support, reason);
}

/// @notice Submits a cast approval to the `LlamaCore` contract.
Expand All @@ -284,7 +281,7 @@ abstract contract LlamaTokenCaster is Initializable {

_isClockModeSupported(); // reverts if clock mode is not supported

uint256 totalSupply = _getPastTotalSupply(_timestampToTimepoint(action.creationTime - 1));
uint256 totalSupply = _getPastTotalSupply(_timestampToTimepoint(action.creationTime) - 1);
uint96 votesFor = casts[actionInfo.id].votesFor;
uint96 votesAgainst = casts[actionInfo.id].votesAgainst;
uint96 votesAbstain = casts[actionInfo.id].votesAbstain;
Expand Down Expand Up @@ -316,7 +313,7 @@ abstract contract LlamaTokenCaster is Initializable {

_isClockModeSupported(); // reverts if clock mode is not supported

uint256 totalSupply = _getPastTotalSupply(_timestampToTimepoint(action.creationTime - 1));
uint256 totalSupply = _getPastTotalSupply(_timestampToTimepoint(action.creationTime) - 1);
uint96 vetoesFor = casts[actionInfo.id].vetoesFor;
uint96 vetoesAgainst = casts[actionInfo.id].vetoesAgainst;
uint96 vetoesAbstain = casts[actionInfo.id].vetoesAbstain;
Expand Down Expand Up @@ -354,7 +351,10 @@ abstract contract LlamaTokenCaster is Initializable {
// ======== Internal Logic ========
// ================================

function _castVote(address caster, ActionInfo calldata actionInfo, uint8 support, string calldata reason) internal {
function _castVote(address caster, ActionInfo calldata actionInfo, uint8 support, string calldata reason)
internal
returns (uint96)
{
Action memory action = llamaCore.getAction(actionInfo.id);

actionInfo.strategy.checkIfApprovalEnabled(actionInfo, address(this), role); // Reverts if not allowed.
Expand All @@ -365,17 +365,22 @@ abstract contract LlamaTokenCaster is Initializable {
> action.creationTime + (actionInfo.strategy.approvalPeriod() * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS
) revert CastingPeriodOver();

uint256 balance = _getPastVotes(caster, _timestampToTimepoint(action.creationTime - 1));
_preCastAssertions(balance, support);
uint96 weight = LlamaUtils.toUint96(_getPastVotes(caster, _timestampToTimepoint(action.creationTime) - 1));
_preCastAssertions(support);

if (support == uint8(VoteType.Against)) casts[actionInfo.id].votesAgainst += LlamaUtils.toUint96(balance);
else if (support == uint8(VoteType.For)) casts[actionInfo.id].votesFor += LlamaUtils.toUint96(balance);
else if (support == uint8(VoteType.Abstain)) casts[actionInfo.id].votesAbstain += LlamaUtils.toUint96(balance);
if (support == uint8(VoteType.Against)) casts[actionInfo.id].votesAgainst += weight;
else if (support == uint8(VoteType.For)) casts[actionInfo.id].votesFor += weight;
else if (support == uint8(VoteType.Abstain)) casts[actionInfo.id].votesAbstain += weight;
casts[actionInfo.id].castVote[caster] = true;
emit VoteCast(actionInfo.id, caster, support, balance, reason);
emit VoteCast(actionInfo.id, caster, support, weight, reason);

return weight;
}

function _castVeto(address caster, ActionInfo calldata actionInfo, uint8 support, string calldata reason) internal {
function _castVeto(address caster, ActionInfo calldata actionInfo, uint8 support, string calldata reason)
internal
returns (uint96)
{
Action memory action = llamaCore.getAction(actionInfo.id);

actionInfo.strategy.checkIfDisapprovalEnabled(actionInfo, address(this), role); // Reverts if not allowed.
Expand All @@ -386,22 +391,22 @@ abstract contract LlamaTokenCaster is Initializable {
> action.minExecutionTime - (actionInfo.strategy.queuingPeriod() * ONE_THIRD_IN_BPS) / ONE_HUNDRED_IN_BPS
) revert CastingPeriodOver();

uint256 balance = _getPastVotes(caster, _timestampToTimepoint(action.creationTime - 1));
_preCastAssertions(balance, support);
uint96 weight = LlamaUtils.toUint96(_getPastVotes(caster, _timestampToTimepoint(action.creationTime) - 1));
_preCastAssertions(support);

if (support == uint8(VoteType.Against)) casts[actionInfo.id].vetoesAgainst += LlamaUtils.toUint96(balance);
else if (support == uint8(VoteType.For)) casts[actionInfo.id].vetoesFor += LlamaUtils.toUint96(balance);
else if (support == uint8(VoteType.Abstain)) casts[actionInfo.id].vetoesAbstain += LlamaUtils.toUint96(balance);
if (support == uint8(VoteType.Against)) casts[actionInfo.id].vetoesAgainst += weight;
else if (support == uint8(VoteType.For)) casts[actionInfo.id].vetoesFor += weight;
else if (support == uint8(VoteType.Abstain)) casts[actionInfo.id].vetoesAbstain += weight;
casts[actionInfo.id].castVeto[caster] = true;
emit VetoCast(actionInfo.id, caster, support, balance, reason);
emit VetoCast(actionInfo.id, caster, support, weight, reason);

return weight;
}

function _preCastAssertions(uint256 balance, uint8 support) internal view {
function _preCastAssertions(uint8 support) internal view {
if (support > uint8(VoteType.Abstain)) revert InvalidSupport(support);

_isClockModeSupported(); // reverts if clock mode is not supported

if (balance == 0) revert InsufficientBalance(balance);
}

/// @dev Sets the voting quorum and vetoing quorum.
Expand All @@ -422,29 +427,28 @@ abstract contract LlamaTokenCaster is Initializable {
}

/// @dev Returns the timestamp or timepoint depending on the clock mode.
function _timestampToTimepoint(uint256 timestamp) internal view returns (uint256) {
if (_isClockModeTimestamp()) return timestamp;
function _timestampToTimepoint(uint256 timestamp) internal view returns (uint48) {
if (_isClockModeTimestamp()) return LlamaUtils.toUint48(timestamp);
return clockAdapter.timestampToTimepoint(timestamp);
}

/// @dev Returns the current timepoint minus one.
function _currentTimepointMinusOne() internal view returns (uint256) {
if (_isClockModeTimestamp()) return block.timestamp - 1;
return clockAdapter.currentTimepointMinusOne();
function _currentTimepointMinusOne() internal view returns (uint48) {
if (_isClockModeTimestamp()) return LlamaUtils.toUint48(block.timestamp - 1);
return clockAdapter.clock() - 1;
}

/// @dev Returns true if the clock mode is timestamp.
function _isClockModeTimestamp() internal view returns (bool) {
string memory clockMode = _getClockMode();
if (keccak256(abi.encodePacked(clockMode)) == keccak256(abi.encodePacked("mode=timestamp"))) return true;
return false;
return keccak256(abi.encodePacked(clockMode)) == keccak256(abi.encodePacked("mode=timestamp"));
}

/// @dev Returns the number of votes for a given token holder at a given timestamp.
function _getPastVotes(address account, uint256 timepoint) internal view virtual returns (uint256) {}
function _getPastVotes(address account, uint48 timepoint) internal view virtual returns (uint256) {}

/// @dev Returns the total supply of the token at a given timestamp.
function _getPastTotalSupply(uint256 timepoint) internal view virtual returns (uint256) {}
function _getPastTotalSupply(uint48 timepoint) internal view virtual returns (uint256) {}

/// @dev Returns the clock mode of the token (https://eips.ethereum.org/EIPS/eip-6372).
function _getClockMode() internal view virtual returns (string memory) {}
Expand Down
Loading

0 comments on commit 2a9d28f

Please sign in to comment.