pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT import "../../core/DaoRegistry.sol"; import "../../extensions/bank/Bank.sol"; import "../../guards/MemberGuard.sol"; import "../../guards/AdapterGuard.sol"; import "../interfaces/IVoting.sol"; import "./Voting.sol"; /** MIT License Copyright (c) 2021 Openlaw Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ contract SnapshotProposalContract { string public constant EIP712_DOMAIN = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,address actionId)"; string public constant PROPOSAL_MESSAGE_TYPE = "Message(uint64 timestamp,bytes32 spaceHash,MessagePayload payload)MessagePayload(bytes32 nameHash,bytes32 bodyHash,string[] choices,uint64 start,uint64 end,string snapshot)"; string public constant PROPOSAL_PAYLOAD_TYPE = "MessagePayload(bytes32 nameHash,bytes32 bodyHash,string[] choices,uint64 start,uint64 end,string snapshot)"; string public constant VOTE_MESSAGE_TYPE = "Message(uint64 timestamp,MessagePayload payload)MessagePayload(uint32 choice,bytes32 proposalId)"; string public constant VOTE_PAYLOAD_TYPE = "MessagePayload(uint32 choice,bytes32 proposalId)"; bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256(abi.encodePacked(EIP712_DOMAIN)); bytes32 public constant PROPOSAL_MESSAGE_TYPEHASH = keccak256(abi.encodePacked(PROPOSAL_MESSAGE_TYPE)); bytes32 public constant PROPOSAL_PAYLOAD_TYPEHASH = keccak256(abi.encodePacked(PROPOSAL_PAYLOAD_TYPE)); bytes32 public constant VOTE_MESSAGE_TYPEHASH = keccak256(abi.encodePacked(VOTE_MESSAGE_TYPE)); bytes32 public constant VOTE_PAYLOAD_TYPEHASH = keccak256(abi.encodePacked(VOTE_PAYLOAD_TYPE)); struct ProposalMessage { uint256 timestamp; bytes32 spaceHash; address submitter; ProposalPayload payload; bytes sig; } struct ProposalPayload { bytes32 nameHash; bytes32 bodyHash; string[] choices; uint256 start; uint256 end; string snapshot; } struct VoteMessage { uint256 timestamp; VotePayload payload; } struct VotePayload { uint32 choice; bytes32 proposalId; } function DOMAIN_SEPARATOR( DaoRegistry dao, address actionId ) public view returns (bytes32) { return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, keccak256("Snapshot Message"), // string name keccak256("4"), // string version block.chainid, // uint256 chainId address(dao), // address verifyingContract, actionId ) ); } function hashMessage( DaoRegistry dao, address actionId, ProposalMessage memory message ) external view returns (bytes32) { return keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(dao, actionId), hashProposalMessage(message) ) ); } function hashProposalMessage( ProposalMessage memory message ) public pure returns (bytes32) { return keccak256( abi.encode( PROPOSAL_MESSAGE_TYPEHASH, message.timestamp, message.spaceHash, hashProposalPayload(message.payload) ) ); } function hashProposalPayload( ProposalPayload memory payload ) public pure returns (bytes32) { return keccak256( abi.encode( PROPOSAL_PAYLOAD_TYPEHASH, payload.nameHash, payload.bodyHash, keccak256(abi.encodePacked(toHashArray(payload.choices))), payload.start, payload.end, keccak256(abi.encodePacked(payload.snapshot)) ) ); } function hashVote( DaoRegistry dao, address actionId, VoteMessage memory message ) external view returns (bytes32) { return keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(dao, actionId), hashVoteInternal(message) ) ); } function hashVoteInternal( VoteMessage memory message ) public pure returns (bytes32) { return keccak256( abi.encode( VOTE_MESSAGE_TYPEHASH, message.timestamp, hashVotePayload(message.payload) ) ); } function hashVotePayload( VotePayload memory payload ) public pure returns (bytes32) { return keccak256( abi.encode( VOTE_PAYLOAD_TYPEHASH, payload.choice, payload.proposalId ) ); } function toHashArray( string[] memory arr ) internal pure returns (bytes32[] memory result) { result = new bytes32[](arr.length); for (uint256 i = 0; i < arr.length; i++) { result[i] = keccak256(abi.encodePacked(arr[i])); } } }