-
Notifications
You must be signed in to change notification settings - Fork 0
/
PowerVoting-filecoin.sol
203 lines (173 loc) · 6.93 KB
/
PowerVoting-filecoin.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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023-2024 StorSwift Inc.
// This file is part of the PowerVoting library.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma solidity ^0.8.19;
import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IPowerVoting } from "./interfaces/IPowerVoting-filecoin.sol";
import { Proposal, VoteInfo, VoterInfo } from "./types.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract PowerVoting is IPowerVoting, Ownable2StepUpgradeable, UUPSUpgradeable {
using Counters for Counters.Counter;
// proposal id
Counters.Counter public proposalId;
// Power Oracle contract address
address public oracleContract;
// add task function selector
bytes4 public immutable ADD_TASK_SELECTOR = bytes4(keccak256('addTask(string)'));
// add f4 task function selector
bytes4 public immutable ADD_F4_TASK_SELECTOR = bytes4(keccak256('addF4Task(address)'));
// add miner id function selector
bytes4 public immutable ADD_MINER_IDS_SELECTOR = bytes4(keccak256('addMinerIds(uint64[],address)'));
// get voter info
bytes4 public immutable GET_VOTER_INFO_SELECTOR = bytes4(keccak256('getVoterInfo(address)'));
// proposal mapping, key: proposal id, value: Proposal
mapping(uint256 => Proposal) public idToProposal;
// fip map
mapping(address => bool) public fipMap;
// proposal id to vote, out key: proposal id, inner key: vote id, value: vote info
mapping(uint256 => mapping(uint256 => VoteInfo)) public proposalToVote;
modifier nonZeroAddress(address addr){
if(addr == address(0)){
revert ZeroAddressError("Zero address error.");
}
_;
}
// override from UUPSUpgradeable
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
function initialize(address oracleAddress) public initializer nonZeroAddress(oracleAddress) {
oracleContract = oracleAddress;
__UUPSUpgradeable_init();
__Ownable_init(msg.sender);
}
/**
* update oracle contract address
*
* @param oracleAddress: new oracle contract address
*/
function updateOracleContract(address oracleAddress) external onlyOwner nonZeroAddress(oracleAddress) {
oracleContract = oracleAddress;
}
/**
* addFIP: add FIP
* @param fipAddress: address
*/
function addFIP(
address fipAddress
) external override onlyOwner nonZeroAddress(fipAddress) {
bool exist = fipMap[fipAddress];
// FIP Editor is not allowed to have other roles currently.
if (exist) {
revert AddFIPError("Add FIP editor error.");
}
fipMap[fipAddress] = true;
}
/**
* removeFIP: remove FIP
* @param fipAddress: address
*/
function removeFIP(
address fipAddress
) external override onlyOwner nonZeroAddress(fipAddress) {
fipMap[fipAddress] = false;
}
/**
* create a proposal and store it into mapping
*
* @param proposalCid: proposal content is stored in ipfs, proposal cid is ipfs cid for proposal content
* @param startTime: proposal start timestamp
* @param expTime: proposal expiration timestamp
* @param proposalType: proposal type
*/
function createProposal(string calldata proposalCid, uint248 startTime, uint248 expTime, uint256 proposalType) override external {
bool fip = fipMap[msg.sender];
if(!fip){
revert CallError("Not FIP.");
}
// increment proposal id
proposalId.increment();
uint256 id = proposalId.current();
// create proposal
Proposal storage proposal = idToProposal[id];
proposal.cid = proposalCid;
proposal.creator = msg.sender;
proposal.startTime = startTime;
proposal.expTime = expTime;
proposal.proposalType = proposalType;
emit ProposalCreate(id, proposal);
}
/**
* vote
*
* @param id: proposal id
* @param info: vote info, IPFS cid
*/
function vote(uint256 id, string calldata info) override external{
Proposal storage proposal = idToProposal[id];
// if proposal is not start, won't be allowed to vote
if(proposal.startTime > block.timestamp){
revert TimeError("Proposal not start yet.");
}
// if proposal is expired, won't be allowed to vote
if(proposal.expTime <= block.timestamp){
revert TimeError("Proposal expiration time reached.");
}
_addF4Task();
// increment votesCount
uint256 vid = ++proposal.votesCount;
// use votesCount as vote id
VoteInfo storage voteInfo = proposalToVote[id][vid];
voteInfo.voteInfo = info;
voteInfo.voter = msg.sender;
emit Vote(id, msg.sender, info);
}
/**
* addMinerId
*
* @param minerIds: miner id list
*/
function addMinerId(uint64[] memory minerIds)override external{
_addF4Task();
(bool addMinerSuccess, ) = oracleContract.call(abi.encodeWithSelector(ADD_MINER_IDS_SELECTOR, minerIds, msg.sender));
if(!addMinerSuccess){
revert CallError("Call oracle contract to add miner id failed.");
}
}
/**
* _addF4Task
*/
function _addF4Task() private {
(bool getVoterInfoSuccess, bytes memory data) = oracleContract.call(abi.encodeWithSelector(GET_VOTER_INFO_SELECTOR, msg.sender));
if (!getVoterInfoSuccess) {
revert CallError("Call oracle contract to get voter info failed.");
}
VoterInfo memory voterInfo = abi.decode(data, (VoterInfo));
if (voterInfo.actorIds.length == 0) {
(bool addF4TaskSuccess, ) = oracleContract.call(abi.encodeWithSelector(ADD_F4_TASK_SELECTOR, msg.sender));
if (!addF4TaskSuccess) {
revert CallError("Call oracle contract to add F4 task failed.");
}
}
}
/**
* ucanDelegate
*
* @param ucanCid: ucan cid
*/
function ucanDelegate(string calldata ucanCid) override external{
// call kyc oracle to add task
(bool success, ) = oracleContract.call(abi.encodeWithSelector(ADD_TASK_SELECTOR, ucanCid));
if(!success){
revert CallError("Call oracle contract to add task failed.");
}
}
}