-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathDelegatedVoting.sol
172 lines (150 loc) · 5.03 KB
/
DelegatedVoting.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
// interfaces
import {
ILSP25ExecuteRelayCall as ILSP25
} from "@lukso/lsp-smart-contracts/contracts/LSP25ExecuteRelayCall/ILSP25ExecuteRelayCall.sol";
// modules
import {
LSP8IdentifiableDigitalAsset
} from "@lukso/lsp-smart-contracts/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol";
import {
LSP25MultiChannelNonce
} from "@lukso/lsp-smart-contracts/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol";
// constants
import {
_LSP4_TOKEN_TYPE_NFT
} from "@lukso/lsp-smart-contracts/contracts/LSP4DigitalAssetMetadata/LSP4Constants.sol";
import {
_LSP8_TOKENID_FORMAT_HASH
} from "@lukso/lsp-smart-contracts/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol";
contract DelegatedVoting is
ILSP25,
LSP8IdentifiableDigitalAsset(
"Election",
"ELC",
msg.sender, // deployer (organiser of the election),
_LSP4_TOKEN_TYPE_NFT,
_LSP8_TOKENID_FORMAT_HASH
),
LSP25MultiChannelNonce
{
modifier noNativeTokens() {
require(msg.value == 0, "Not aimed to receive native tokens");
_;
}
modifier notInCastingPeriod() {
require(
block.timestamp > _castingPeriodStartAt,
"Votes cannot be submitted yet"
);
require(
block.timestamp < _castingPeriodEnd,
"Voting period has passed"
);
_;
}
uint256 immutable _castingPeriodStartAt;
uint256 immutable _castingPeriodEnd;
constructor(uint256 castingPeriodStartAt_, uint256 castingPeriodEnd_) {
_castingPeriodStartAt = castingPeriodStartAt_;
_castingPeriodEnd = castingPeriodEnd_;
}
function getNonce(
address from,
uint128 channelId
) external view returns (uint256) {
return _getNonce(from, channelId);
}
function retrieveUserVote() public {
_mint({
to: msg.sender,
tokenId: keccak256(abi.encodePacked(msg.sender)),
force: true,
data: ""
});
}
/**
* @dev Allow anyone to vote on behalf of someone else, providing a valid signature
*/
function executeRelayCall(
bytes calldata signature,
uint256 nonce,
uint256 validityTimestamps,
bytes calldata payload
) public payable noNativeTokens notInCastingPeriod returns (bytes memory) {
// 1. retrieve the address of the voter from the signature
address voter = LSP25MultiChannelNonce._recoverSignerFromLSP25Signature(
signature,
nonce,
validityTimestamps,
0,
payload
);
if (!_isValidNonce(voter, nonce)) {
revert("Invalid nonce");
}
// 2. increase nonce after successful verification
_nonceStore[voter][nonce >> 128]++;
// 3. extract the parameters from the calldata `transfer(address,address,bytes32,bool,bytes)`
(
address from,
address candidate,
bytes32 voteId,
bool force,
bytes memory data
) = abi.decode(payload, (address, address, bytes32, bool, bytes));
require(
from == voter,
"recovered address of `voter` does not match the from sending address"
);
// 4. The tokenId to represent the vote of someone is the hash of the address that voted
require(
voteId == keccak256(abi.encodePacked(voter)),
"Invalid vote ID for voter"
);
// 5. Cast the vote by transferring it to the candidate.
// This will increase the vote count of the `candidate`
_transfer({
from: voter,
to: candidate,
tokenId: voteId,
force: force,
data: data
});
return
abi.encodePacked(
"Vote from address ",
voter,
" casted successfully!"
);
}
function executeRelayCallBatch(
bytes[] calldata signatures,
uint256[] calldata nonces,
uint256[] calldata validityTimestamps,
uint256[] calldata values,
bytes[] calldata payloads
) public payable noNativeTokens returns (bytes[] memory) {
if (
signatures.length != nonces.length ||
nonces.length != validityTimestamps.length ||
validityTimestamps.length != values.length ||
values.length != payloads.length
) {
revert("Batch ExecuteRelayCall Params Length Mismatch");
}
bytes[] memory castedVotes = new bytes[](payloads.length);
for (uint256 ii; ii < payloads.length; ++ii) {
require(values[ii] == 0, "Batch entry cannot contain value");
// cast each votes one by one
castedVotes[ii] = executeRelayCall(
signatures[ii],
nonces[ii],
validityTimestamps[ii],
payloads[ii]
);
}
return castedVotes;
}
}