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

fix: EIP-712 fixes #64

Merged
merged 3 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions src/lib/QuorumCheckpoints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {LlamaUtils} from "src/lib/LlamaUtils.sol";
* @dev This library defines the `History` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block timestamp.
*
* To create a history of checkpoints define a variable type `PolicyholderCheckpoints.History` in your contract, and store a new
* To create a history of checkpoints define a variable type `QuorumCheckpoints.History` in your contract, and store a new
* checkpoint for the current transaction timestamp using the {push} function.
*
* @dev This was created by modifying then running the OpenZeppelin `Checkpoints.js` script, which generated a version
* of this library that uses a 64 bit `timestamp` and 96 bit `quantity` field in the `Checkpoint` struct. The struct
* was then modified to add two uint16 quorum fields. For simplicity, safe cast and math methods were inlined from
* was then modified to work with the below `Checkpoint` struct. For simplicity, safe cast and math methods were inlined from
* the OpenZeppelin versions at the same commit. We disable forge-fmt for this file to simplify diffing against the
* original OpenZeppelin version: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d00acef4059807535af0bd0dd0ddf619747a044b/contracts/utils/Checkpoints.sol
*/
Expand All @@ -35,7 +35,7 @@ library QuorumCheckpoints {
* timestamp of checkpoints.
*/
function getAtProbablyRecentTimestamp(History storage self, uint256 timestamp) internal view returns (uint16, uint16) {
require(timestamp < block.timestamp, "PolicyholderCheckpoints: timestamp is not in the past");
require(timestamp < block.timestamp, "QuorumCheckpoints: timestamp is not in the past");
uint224 _timestamp = LlamaUtils.toUint224(timestamp);

uint256 len = self._checkpoints.length;
Expand Down Expand Up @@ -131,7 +131,7 @@ library QuorumCheckpoints {
Checkpoint memory last = _unsafeAccess(self, pos - 1);

// Checkpoints timestamps must be increasing.
require(last.timestamp <= timestamp, "Role Checkpoint: invalid timestamp");
require(last.timestamp <= timestamp, "Quorum Checkpoint: invalid timestamp");

// Update or push new checkpoint
if (last.timestamp == timestamp) {
Expand Down
4 changes: 2 additions & 2 deletions src/token-voting/LlamaTokenActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ abstract contract LlamaTokenActionCreator is Initializable {
return _createAction(msg.sender, strategy, target, value, data, description);
}

/// @notice Creates an action via an off-chain signature. The creator needs to hold a policy with the permission ID
/// of the provided `(target, selector, strategy)`.
/// @notice Creates an action via an off-chain signature. The creator needs to have sufficient token balance that is
/// greater than or equal to the creation threshold.
/// @dev Use `""` for `description` if there is no description.
/// @param tokenHolder The tokenHolder that signed the message.
/// @param strategy The strategy contract that will determine how the action is executed.
Expand Down
59 changes: 45 additions & 14 deletions src/token-voting/LlamaTokenCaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ abstract contract LlamaTokenCaster is Initializable {

/// @notice EIP-712 castVote typehash.
bytes32 internal constant CAST_VOTE_TYPEHASH = keccak256(
"CastVote(address tokenHolder,uint8 support,ActionInfo actionInfo,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
"CastVote(address tokenHolder,ActionInfo actionInfo,uint8 support,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
);

/// @notice EIP-712 castVeto typehash.
bytes32 internal constant CAST_VETO_TYPEHASH = keccak256(
"CastVeto(address tokenHolder,uint8 role,ActionInfo actionInfo,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
"CastVeto(address tokenHolder,ActionInfo actionInfo,uint8 support,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
);

/// @dev EIP-712 actionInfo typehash.
Expand All @@ -161,11 +161,12 @@ abstract contract LlamaTokenCaster is Initializable {
/// @dev Equivalent to 2/3, but in basis points.
uint256 internal constant TWO_THIRDS_IN_BPS = 6667;

/// @dev The quorum checkpoints for this token voting module.
QuorumCheckpoints.History internal quorumCheckpoints;

/// @notice The core contract for this Llama instance.
ILlamaCore public llamaCore;

QuorumCheckpoints.History internal quorumCheckpoints;

/// @notice The contract that manages the timepoints for this token voting module.
ILlamaTokenClockAdapter public clockAdapter;

Expand Down Expand Up @@ -218,22 +219,37 @@ abstract contract LlamaTokenCaster is Initializable {
/// @param support The tokenholder's support of the approval of the action.
/// 0 = Against
/// 1 = For
/// 2 = Abstain, but this is not currently supported.
/// 2 = Abstain
/// @param reason The reason given for the approval by the tokenholder.
/// @return The weight of the cast.
function castVote(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external returns (uint96) {
return _castVote(msg.sender, actionInfo, support, reason);
}

/// @notice How tokenholders add their support of the approval of an action with a reason via an off-chain
/// signature.
/// @dev Use `""` for `reason` if there is no reason.
/// @param caster The tokenholder that signed the message.
/// @param actionInfo Data required to create an action.
/// @param support The tokenholder's support of the approval of the action.
/// 0 = Against
/// 1 = For
/// 2 = Abstain
/// @param reason The reason given for the approval by the tokenholder.
/// @param v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param r ECDSA signature component: x-coordinate of `R`
/// @param s ECDSA signature component: `s` value of the signature
/// @return The weight of the cast.
function castVoteBySig(
address caster,
uint8 support,
ActionInfo calldata actionInfo,
uint8 support,
string calldata reason,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint96) {
bytes32 digest = _getCastVoteTypedDataHash(caster, support, actionInfo, reason);
bytes32 digest = _getCastVoteTypedDataHash(caster, actionInfo, support, reason);
address signer = ecrecover(digest, v, r, s);
if (signer == address(0) || signer != caster) revert InvalidSignature();
return _castVote(signer, actionInfo, support, reason);
Expand All @@ -245,22 +261,37 @@ abstract contract LlamaTokenCaster is Initializable {
/// @param support The tokenholder's support of the approval of the action.
/// 0 = Against
/// 1 = For
/// 2 = Abstain, but this is not currently supported.
/// 2 = Abstain
/// @param reason The reason given for the approval by the tokenholder.
/// @return The weight of the cast.
function castVeto(ActionInfo calldata actionInfo, uint8 support, string calldata reason) external returns (uint96) {
return _castVeto(msg.sender, actionInfo, support, reason);
}

/// @notice How tokenholders add their support of the disapproval of an action with a reason via an off-chain
/// signature.
/// @dev Use `""` for `reason` if there is no reason.
/// @param caster The tokenholder that signed the message.
/// @param actionInfo Data required to create an action.
/// @param support The tokenholder's support of the approval of the action.
/// 0 = Against
/// 1 = For
/// 2 = Abstain
/// @param reason The reason given for the approval by the tokenholder.
/// @param v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param r ECDSA signature component: x-coordinate of `R`
/// @param s ECDSA signature component: `s` value of the signature
/// @return The weight of the cast.
function castVetoBySig(
address caster,
uint8 support,
ActionInfo calldata actionInfo,
uint8 support,
string calldata reason,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint96) {
bytes32 digest = _getCastVetoTypedDataHash(caster, support, actionInfo, reason);
bytes32 digest = _getCastVetoTypedDataHash(caster, actionInfo, support, reason);
address signer = ecrecover(digest, v, r, s);
if (signer == address(0) || signer != caster) revert InvalidSignature();
return _castVeto(signer, actionInfo, support, reason);
Expand Down Expand Up @@ -500,16 +531,16 @@ abstract contract LlamaTokenCaster is Initializable {
/// recover the signer.
function _getCastVoteTypedDataHash(
address tokenholder,
uint8 support,
ActionInfo calldata actionInfo,
uint8 support,
string calldata reason
) internal returns (bytes32) {
bytes32 castVoteHash = keccak256(
abi.encode(
CAST_VOTE_TYPEHASH,
tokenholder,
support,
_getActionInfoHash(actionInfo),
support,
keccak256(bytes(reason)),
_useNonce(tokenholder, msg.sig)
)
Expand All @@ -522,16 +553,16 @@ abstract contract LlamaTokenCaster is Initializable {
/// recover the signer.
function _getCastVetoTypedDataHash(
address tokenholder,
uint8 support,
ActionInfo calldata actionInfo,
uint8 support,
string calldata reason
) internal returns (bytes32) {
bytes32 castVetoHash = keccak256(
abi.encode(
CAST_VETO_TYPEHASH,
tokenholder,
support,
_getActionInfoHash(actionInfo),
support,
keccak256(bytes(reason)),
_useNonce(tokenholder, msg.sig)
)
Expand Down
4 changes: 2 additions & 2 deletions test/token-voting/LlamaERC20TokenCaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ contract CastVoteBySig is LlamaERC20TokenCasterTest {
}

function castVoteBySig(ActionInfo memory _actionInfo, uint8 support, uint8 v, bytes32 r, bytes32 s) internal {
llamaERC20TokenCaster.castVoteBySig(tokenHolder1, support, _actionInfo, "", v, r, s);
llamaERC20TokenCaster.castVoteBySig(tokenHolder1, _actionInfo, support, "", v, r, s);
}

function test_CastsVoteBySig() public {
Expand Down Expand Up @@ -438,7 +438,7 @@ contract CastVetoBySig is LlamaERC20TokenCasterTest {
}

function castVetoBySig(ActionInfo memory _actionInfo, uint8 v, bytes32 r, bytes32 s) internal {
llamaERC20TokenCaster.castVetoBySig(tokenHolder1, uint8(VoteType.For), _actionInfo, "", v, r, s);
llamaERC20TokenCaster.castVetoBySig(tokenHolder1, _actionInfo, uint8(VoteType.For), "", v, r, s);
}

function test_CastsVetoBySig() public {
Expand Down
4 changes: 2 additions & 2 deletions test/token-voting/LlamaERC721TokenCaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ contract CastApprovalBySig is LlamaERC721TokenCasterTest {
}

function castVoteBySig(ActionInfo memory _actionInfo, uint8 support, uint8 v, bytes32 r, bytes32 s) internal {
llamaERC721TokenCaster.castVoteBySig(tokenHolder1, support, _actionInfo, "", v, r, s);
llamaERC721TokenCaster.castVoteBySig(tokenHolder1, _actionInfo, support, "", v, r, s);
}

function test_CastsVoteBySig() public {
Expand Down Expand Up @@ -440,7 +440,7 @@ contract CastVetoBySig is LlamaERC721TokenCasterTest {
}

function castVetoBySig(ActionInfo memory _actionInfo, uint8 v, bytes32 r, bytes32 s) internal {
llamaERC721TokenCaster.castVetoBySig(tokenHolder1, uint8(VoteType.For), _actionInfo, "", v, r, s);
llamaERC721TokenCaster.castVetoBySig(tokenHolder1, _actionInfo, uint8(VoteType.For), "", v, r, s);
}

function test_CastsVetoBySig() public {
Expand Down
12 changes: 6 additions & 6 deletions test/utils/LlamaCoreSigUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ contract LlamaCoreSigUtils {

struct CastVote {
address tokenHolder;
uint8 support;
ActionInfo actionInfo;
uint8 support;
string reason;
uint256 nonce;
}

struct CastVeto {
address tokenHolder;
uint8 support;
ActionInfo actionInfo;
uint8 support;
string reason;
uint256 nonce;
}
Expand All @@ -59,12 +59,12 @@ contract LlamaCoreSigUtils {

/// @notice EIP-712 castVote typehash.
bytes32 internal constant CAST_VOTE_TYPEHASH = keccak256(
"CastVote(address tokenHolder,uint8 support,ActionInfo actionInfo,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
"CastVote(address tokenHolder,ActionInfo actionInfo,uint8 support,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
);

/// @notice EIP-712 castVeto typehash.
bytes32 internal constant CAST_VETO_TYPEHASH = keccak256(
"CastVeto(address tokenHolder,uint8 role,ActionInfo actionInfo,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
"CastVeto(address tokenHolder,ActionInfo actionInfo,uint8 support,string reason,uint256 nonce)ActionInfo(uint256 id,address creator,uint8 creatorRole,address strategy,address target,uint256 value,bytes data)"
);

/// @notice EIP-712 actionInfo typehash.
Expand Down Expand Up @@ -130,8 +130,8 @@ contract LlamaCoreSigUtils {
abi.encode(
CAST_VOTE_TYPEHASH,
castVote.tokenHolder,
castVote.support,
getActionInfoHash(castVote.actionInfo),
castVote.support,
keccak256(bytes(castVote.reason)),
castVote.nonce
)
Expand All @@ -150,8 +150,8 @@ contract LlamaCoreSigUtils {
abi.encode(
CAST_VETO_TYPEHASH,
castVeto.tokenHolder,
castVeto.support,
getActionInfoHash(castVeto.actionInfo),
castVeto.support,
keccak256(bytes(castVeto.reason)),
castVeto.nonce
)
Expand Down
Loading