Skip to content

Instantly share code, notes, and snippets.

@Lohann
Created June 30, 2022 14:29
Show Gist options
  • Save Lohann/0c64f84ac16c2ea90abd8df37af0b3f5 to your computer and use it in GitHub Desktop.
Save Lohann/0c64f84ac16c2ea90abd8df37af0b3f5 to your computer and use it in GitHub Desktop.
Solidity encode/decode
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
library Vote {
uint256 constant BITS_PER_CANDIDATE = 5;
uint256 constant BITMASK = (2**BITS_PER_CANDIDATE) - 1;
// Storage Layout:
// Voter (160bit) | size (5bit) | votes (5~90bit)
struct Encoded {
uint256 data;
}
struct Decoded {
address voter;
uint256[] rank;
}
function encode(Decoded memory decoded) internal pure returns(Encoded memory encoded) {
unchecked {
require(decoded.rank.length <= 18, "number of candidates exceed the limit");
// Encode vote address
uint256 encodedVote = uint256(uint160(decoded.voter)) << 96;
// Encode number of candidates
uint256 size = decoded.rank.length;
encodedVote |= size << 91;
// Encode votes
for (uint256 i = 0; i < size; i++) {
encodedVote |= (decoded.rank[i] << (BITS_PER_CANDIDATE * i));
}
encoded.data = encodedVote;
}
}
function decode(Encoded memory encoded) internal pure returns(Decoded memory decoded) {
unchecked {
uint256 encodedVote = encoded.data;
decoded.voter = address(uint160(encodedVote >> 96));
uint256 size = (encodedVote >> 91) & BITMASK;
decoded.rank = new uint256[](size);
for (uint256 i = 0; i < size; i++) {
decoded.rank[i] = encodedVote & BITMASK;
encodedVote >>= BITS_PER_CANDIDATE;
}
}
}
}
contract TestVote {
using Vote for Vote.Encoded;
using Vote for Vote.Decoded;
// Should
mapping (address => Vote.Encoded) private _votes;
function submitVote(uint256[] memory rank) external {
// Read and decode vote from storage
Vote.Decoded memory vote = _votes[msg.sender].decode();
require(vote.voter == address(0), "Account already voted, use changeVote instead");
vote.voter = msg.sender;
vote.rank = rank;
// Encode and save vote
_votes[msg.sender] = vote.encode();
}
function changeVote(uint256[] memory newRank) external {
// Read and decode vote from storage
Vote.Decoded memory vote = _votes[msg.sender].decode();
require(vote.voter == msg.sender, "This account hasn't voted, use submitVote instead");
vote.rank = newRank;
// Encode and save vote
_votes[msg.sender] = vote.encode();
}
function voteOf(address voter) external view returns (Vote.Decoded memory vote) {
vote = _votes[voter].decode();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment