mirror of
https://github.com/status-im/community-dapp.git
synced 2025-02-22 19:18:30 +00:00
parent
135084aaf4
commit
a4164dbe11
@ -6,8 +6,8 @@ message WakuVote {
|
||||
string vote = 2;
|
||||
bytes sntAmount = 3;
|
||||
string sign = 4;
|
||||
uint32 nonce = 5;
|
||||
uint64 sessionID = 6;
|
||||
uint32 timestamp = 5;
|
||||
uint64 roomID = 6;
|
||||
}
|
||||
|
||||
message WakuFeature {
|
||||
|
@ -14,27 +14,29 @@ function getContractParameters(
|
||||
address: string,
|
||||
room: number,
|
||||
type: number,
|
||||
sntAmount: number
|
||||
): [string, BigNumber, BigNumber] {
|
||||
return [address, BigNumber.from(room).mul(2).add(type), BigNumber.from(sntAmount)]
|
||||
sntAmount: number,
|
||||
timestamp: number
|
||||
): [string, BigNumber, BigNumber, BigNumber] {
|
||||
return [address, BigNumber.from(room).mul(2).add(type), BigNumber.from(sntAmount), BigNumber.from(timestamp)]
|
||||
}
|
||||
|
||||
export function filterVerifiedVotes(
|
||||
messages: WakuVoteData[] | undefined,
|
||||
alreadyVoted: string[],
|
||||
getTypedData: (data: [string, BigNumber, BigNumber]) => TypedVote
|
||||
getTypedData: (data: [string, BigNumber, BigNumber, BigNumber]) => TypedVote
|
||||
) {
|
||||
if (!messages) {
|
||||
return []
|
||||
}
|
||||
const verified: [string, BigNumber, BigNumber, string, string][] = []
|
||||
const verified: [string, BigNumber, BigNumber, BigNumber, string, string][] = []
|
||||
|
||||
messages.forEach((msg) => {
|
||||
const params = getContractParameters(
|
||||
msg.address,
|
||||
msg.sessionID,
|
||||
msg.roomID,
|
||||
msg.vote == 'yes' ? 1 : 0,
|
||||
msg.sntAmount.toNumber()
|
||||
msg.sntAmount.toNumber(),
|
||||
msg.timestamp
|
||||
)
|
||||
|
||||
if (utils.getAddress(recoverAddress(getTypedData(params), msg.sign)) == msg.address) {
|
||||
@ -55,7 +57,7 @@ function decodeWakuVote(msg: WakuMessage): WakuVoteData | undefined {
|
||||
return undefined
|
||||
}
|
||||
const data = proto.WakuVote.decode(msg.payload)
|
||||
if (data && data.address && data.nonce && data.sessionID && data.sign && data.sntAmount && data.vote) {
|
||||
if (data && data.address && data.timestamp && data.roomID && data.sign && data.sntAmount && data.vote) {
|
||||
return { ...data, sntAmount: BigNumber.from(data.sntAmount) }
|
||||
} else {
|
||||
return undefined
|
||||
@ -86,7 +88,8 @@ export async function createWakuVote(
|
||||
room: number,
|
||||
voteAmount: number,
|
||||
type: number,
|
||||
getTypedData: (data: [string, BigNumber, BigNumber]) => any,
|
||||
timestamp: number,
|
||||
getTypedData: (data: [string, BigNumber, BigNumber, BigNumber]) => any,
|
||||
sig?: string
|
||||
) {
|
||||
if (!account || !signer) {
|
||||
@ -99,7 +102,7 @@ export async function createWakuVote(
|
||||
return undefined
|
||||
}
|
||||
|
||||
const message = getContractParameters(account, room, type, voteAmount)
|
||||
const message = getContractParameters(account, room, type, voteAmount, timestamp)
|
||||
const data = getTypedData(message)
|
||||
|
||||
const signature = sig ? sig : await provider?.send('eth_signTypedData_v3', [account, JSON.stringify(data)])
|
||||
@ -109,8 +112,8 @@ export async function createWakuVote(
|
||||
vote: type == 1 ? 'yes' : 'no',
|
||||
sntAmount: utils.arrayify(BigNumber.from(voteAmount)),
|
||||
sign: signature,
|
||||
nonce: 1,
|
||||
sessionID: room,
|
||||
timestamp: timestamp,
|
||||
roomID: room,
|
||||
})
|
||||
|
||||
return payload
|
||||
|
@ -34,11 +34,16 @@ export function useCommunities(publicKeys: string[]) {
|
||||
const communityHistory = communitiesHistories[idx]?.[0]
|
||||
if (communityHistory && communityHistory.length > 0) {
|
||||
const votingHistory = communityHistory.map((room: any) => {
|
||||
const endAt = new Date(room[1].toNumber() * 1000)
|
||||
const endAt = new Date(room.endAt.toNumber() * 1000)
|
||||
return {
|
||||
ID: room[7].toNumber(),
|
||||
type: room[2] === 1 ? 'Add' : 'Remove',
|
||||
result: endAt > new Date() ? 'Ongoing' : room[5].gt(room[6]) ? 'Passed' : 'Failed',
|
||||
ID: room.roomNumber.toNumber(),
|
||||
type: room.voteType === 1 ? 'Add' : 'Remove',
|
||||
result:
|
||||
endAt > new Date()
|
||||
? 'Ongoing'
|
||||
: room.totalVotesFor.gt(room.totalVotesAgainst)
|
||||
? 'Passed'
|
||||
: 'Failed',
|
||||
date: endAt,
|
||||
}
|
||||
})
|
||||
|
@ -14,7 +14,8 @@ export function useSendWakuVote() {
|
||||
|
||||
const sendWakuVote = useCallback(
|
||||
async (voteAmount: number, room: number, type: number) => {
|
||||
const msg = await createWakuVote(account, library?.getSigner(), room, voteAmount, type, getTypedVote)
|
||||
const timestamp = Math.floor(Date.now() / 1000)
|
||||
const msg = await createWakuVote(account, library?.getSigner(), room, voteAmount, type, timestamp, getTypedVote)
|
||||
if (msg) {
|
||||
if (waku) {
|
||||
await waku.lightPush.push(new EncoderV0(config.wakuTopic + room.toString()), { payload: msg })
|
||||
|
@ -9,7 +9,7 @@ export function useTypedVote() {
|
||||
const { votingContract } = useContracts()
|
||||
|
||||
const getTypedVote = useCallback(
|
||||
(data: [string, BigNumber, BigNumber]) => {
|
||||
(data: [string, BigNumber, BigNumber, BigNumber]) => {
|
||||
return {
|
||||
types: {
|
||||
EIP712Domain: [
|
||||
@ -22,6 +22,7 @@ export function useTypedVote() {
|
||||
{ name: 'roomIdAndType', type: 'uint256' },
|
||||
{ name: 'sntAmount', type: 'uint256' },
|
||||
{ name: 'voter', type: 'address' },
|
||||
{ name: 'timestamp', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
primaryType: 'Vote',
|
||||
@ -35,6 +36,7 @@ export function useTypedVote() {
|
||||
roomIdAndType: data[1].toHexString(),
|
||||
sntAmount: data[2].toHexString(),
|
||||
voter: data[0],
|
||||
timestamp: data[3].toHexString(),
|
||||
},
|
||||
} as TypedVote
|
||||
},
|
||||
|
@ -5,8 +5,8 @@ export type WakuVoteData = {
|
||||
address: string
|
||||
vote: string
|
||||
sign: string
|
||||
nonce: number
|
||||
sessionID: number
|
||||
timestamp: number
|
||||
roomID: number
|
||||
}
|
||||
|
||||
export type WakuFeatureData = {
|
||||
|
@ -23,8 +23,8 @@ describe('wakuMessage', () => {
|
||||
|
||||
const payload = proto.WakuVote.encode({
|
||||
address: '0x0',
|
||||
nonce: 1,
|
||||
sessionID: 2,
|
||||
timestamp: 1,
|
||||
roomID: 2,
|
||||
sign: '0x1234',
|
||||
sntAmount: utils.arrayify(BigNumber.from(123)),
|
||||
vote: 'For',
|
||||
@ -33,8 +33,8 @@ describe('wakuMessage', () => {
|
||||
|
||||
const payload2 = proto.WakuVote.encode({
|
||||
address: '0x01',
|
||||
nonce: 1,
|
||||
sessionID: 2,
|
||||
timestamp: 1,
|
||||
roomID: 2,
|
||||
sign: '0xabc1234',
|
||||
sntAmount: utils.arrayify(BigNumber.from(321)),
|
||||
vote: 'Against',
|
||||
@ -45,16 +45,16 @@ describe('wakuMessage', () => {
|
||||
|
||||
expect(data).to.not.be.undefined
|
||||
expect(data?.address).to.eq('0x0')
|
||||
expect(data?.nonce).to.eq(1)
|
||||
expect(data?.sessionID).to.eq(2)
|
||||
expect(data?.timestamp).to.eq(1)
|
||||
expect(data?.roomID).to.eq(2)
|
||||
expect(data?.sign).to.eq('0x1234')
|
||||
expect(data?.sntAmount).to.deep.eq(BigNumber.from(123))
|
||||
expect(data?.vote).to.eq('For')
|
||||
|
||||
expect(data2).to.not.be.undefined
|
||||
expect(data2?.address).to.eq('0x01')
|
||||
expect(data2?.nonce).to.eq(1)
|
||||
expect(data2?.sessionID).to.eq(2)
|
||||
expect(data2?.timestamp).to.eq(1)
|
||||
expect(data2?.roomID).to.eq(2)
|
||||
expect(data2?.sign).to.eq('0xabc1234')
|
||||
expect(data2?.sntAmount).to.deep.eq(BigNumber.from(321))
|
||||
expect(data2?.vote).to.eq('Against')
|
||||
@ -75,8 +75,8 @@ describe('wakuMessage', () => {
|
||||
|
||||
const payload2 = proto.WakuVote.encode({
|
||||
address: '0x01',
|
||||
nonce: 1,
|
||||
sessionID: 2,
|
||||
timestamp: 1,
|
||||
roomID: 2,
|
||||
sign: '0xabc1234',
|
||||
sntAmount: utils.arrayify(BigNumber.from(321)),
|
||||
vote: 'Against',
|
||||
@ -89,8 +89,8 @@ describe('wakuMessage', () => {
|
||||
const data = response[0]
|
||||
expect(data).to.not.be.undefined
|
||||
expect(data?.address).to.eq('0x01')
|
||||
expect(data?.nonce).to.eq(1)
|
||||
expect(data?.sessionID).to.eq(2)
|
||||
expect(data?.timestamp).to.eq(1)
|
||||
expect(data?.roomID).to.eq(2)
|
||||
expect(data?.sign).to.eq('0xabc1234')
|
||||
expect(data?.sntAmount).to.deep.eq(BigNumber.from(321))
|
||||
expect(data?.vote).to.eq('Against')
|
||||
@ -142,6 +142,7 @@ describe('wakuMessage', () => {
|
||||
1,
|
||||
100,
|
||||
1,
|
||||
1,
|
||||
() => [],
|
||||
'0x01'
|
||||
)
|
||||
@ -153,8 +154,8 @@ describe('wakuMessage', () => {
|
||||
expect(obj.address).to.eq(alice.address)
|
||||
expect(obj.vote).to.eq('yes')
|
||||
expect(BigNumber.from(obj.sntAmount)._hex).to.eq('0x64')
|
||||
expect(obj.nonce).to.eq(1)
|
||||
expect(obj.sessionID).to.eq(1)
|
||||
expect(obj.timestamp).to.eq(1)
|
||||
expect(obj.roomID).to.eq(1)
|
||||
}
|
||||
})
|
||||
|
||||
@ -166,6 +167,7 @@ describe('wakuMessage', () => {
|
||||
2,
|
||||
1000,
|
||||
0,
|
||||
1,
|
||||
() => [],
|
||||
'0x01'
|
||||
)
|
||||
@ -177,28 +179,28 @@ describe('wakuMessage', () => {
|
||||
expect(obj.address).to.eq(alice.address)
|
||||
expect(obj.vote).to.eq('no')
|
||||
expect(BigNumber.from(obj.sntAmount)._hex).to.eq('0x03e8')
|
||||
expect(obj.nonce).to.eq(1)
|
||||
expect(obj.sessionID).to.eq(2)
|
||||
expect(obj.timestamp).to.eq(1)
|
||||
expect(obj.roomID).to.eq(2)
|
||||
}
|
||||
})
|
||||
|
||||
it('no address', async () => {
|
||||
const encoder = new EncoderV0('/test/')
|
||||
const payload = await wakuMessage.create(undefined, alice as unknown as JsonRpcSigner, 1, 100, 1, () => [])
|
||||
const payload = await wakuMessage.create(undefined, alice as unknown as JsonRpcSigner, 1, 100, 1, 1, () => [])
|
||||
const msg = await encoder.toProtoObj({ payload })
|
||||
expect(msg?.payload).to.be.undefined
|
||||
})
|
||||
|
||||
it('no signer', async () => {
|
||||
const encoder = new EncoderV0('/test/')
|
||||
const payload = await wakuMessage.create(alice.address, undefined, 1, 100, 1, () => [])
|
||||
const payload = await wakuMessage.create(alice.address, undefined, 1, 100, 1, 1, () => [])
|
||||
const msg = await encoder.toProtoObj({ payload })
|
||||
expect(msg?.payload).to.be.undefined
|
||||
})
|
||||
|
||||
it('different signer', async () => {
|
||||
const encoder = new EncoderV0('/test/')
|
||||
const payload = await wakuMessage.create(alice.address, bob as unknown as JsonRpcSigner, 1, 100, 1, () => [])
|
||||
const payload = await wakuMessage.create(alice.address, bob as unknown as JsonRpcSigner, 1, 100, 1, 1, () => [])
|
||||
const msg = await encoder.toProtoObj({ payload })
|
||||
expect(msg?.payload).to.be.undefined
|
||||
})
|
||||
|
8
packages/DApp/types/protons/index.d.ts
vendored
8
packages/DApp/types/protons/index.d.ts
vendored
@ -4,10 +4,10 @@ declare module 'protons' {
|
||||
vote: string
|
||||
sntAmount: Uint8Array
|
||||
sign: string
|
||||
nonce: number
|
||||
sessionID: number
|
||||
timestamp: number
|
||||
roomID: number
|
||||
}
|
||||
|
||||
|
||||
export type WakuFeature = {
|
||||
voter: string
|
||||
sntAmount: Uint8Array
|
||||
@ -27,4 +27,4 @@ declare module 'protons' {
|
||||
}
|
||||
}
|
||||
export = protons
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ contract VotingContract {
|
||||
using SafeMath for uint256;
|
||||
|
||||
enum VoteType {
|
||||
REMOVE,
|
||||
ADD
|
||||
AGAINST,
|
||||
FOR
|
||||
}
|
||||
|
||||
struct Vote {
|
||||
@ -26,6 +26,8 @@ contract VotingContract {
|
||||
|
||||
struct VotingRoom {
|
||||
uint256 startBlock;
|
||||
uint256 startAt;
|
||||
uint256 verificationStartAt;
|
||||
uint256 endAt;
|
||||
VoteType voteType;
|
||||
bool finalized;
|
||||
@ -39,22 +41,26 @@ contract VotingContract {
|
||||
address voter;
|
||||
uint256 roomIdAndType;
|
||||
uint256 sntAmount;
|
||||
uint256 timestamp;
|
||||
bytes32 r;
|
||||
bytes32 vs;
|
||||
}
|
||||
|
||||
event VotingRoomStarted(uint256 roomId, bytes publicKey);
|
||||
event VotingRoomVerificationStarted(uint256 roomId, bytes publicKey);
|
||||
event VotingRoomFinalized(uint256 roomId, bytes publicKey, bool passed, VoteType voteType);
|
||||
|
||||
event VoteCast(uint256 roomId, address voter);
|
||||
event NotEnoughToken(uint256 roomId, address voter);
|
||||
event AlreadyVoted(uint256 roomId, address voter);
|
||||
event InvalidSignature(uint256 roomId, address voter);
|
||||
|
||||
address public owner;
|
||||
Directory public directory;
|
||||
IERC20 public token;
|
||||
|
||||
uint256 public votingLength;
|
||||
uint256 public votingVerificationLength;
|
||||
uint256 public timeBetweenVoting;
|
||||
|
||||
VotingRoom[] public votingRooms;
|
||||
@ -89,21 +95,23 @@ contract VotingContract {
|
||||
);
|
||||
}
|
||||
|
||||
bytes32 public constant VOTE_TYPEHASH = keccak256('Vote(uint256 roomIdAndType,uint256 sntAmount,address voter)');
|
||||
bytes32 public constant VOTE_TYPEHASH =
|
||||
keccak256('Vote(uint256 roomIdAndType,uint256 sntAmount,address voter,uint256 timestamp)');
|
||||
|
||||
function hash(SignedVote calldata vote) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encode(VOTE_TYPEHASH, vote.roomIdAndType, vote.sntAmount, vote.voter));
|
||||
return keccak256(abi.encode(VOTE_TYPEHASH, vote.roomIdAndType, vote.sntAmount, vote.voter, vote.timestamp));
|
||||
}
|
||||
|
||||
function verify(SignedVote calldata vote) internal view returns (bool) {
|
||||
function verifySignature(SignedVote calldata vote) internal view returns (bool) {
|
||||
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, hash(vote)));
|
||||
return digest.recover(vote.r, vote.vs) == vote.voter;
|
||||
}
|
||||
|
||||
constructor(IERC20 _address, uint256 _votingLength, uint256 _timeBetweenVoting) {
|
||||
constructor(IERC20 _address, uint256 _votingLength, uint256 _votingVerificationLength, uint256 _timeBetweenVoting) {
|
||||
owner = msg.sender;
|
||||
token = _address;
|
||||
votingLength = _votingLength;
|
||||
votingVerificationLength = _votingVerificationLength;
|
||||
timeBetweenVoting = _timeBetweenVoting;
|
||||
DOMAIN_SEPARATOR = hash(
|
||||
EIP712Domain({
|
||||
@ -172,10 +180,10 @@ contract VotingContract {
|
||||
|
||||
function initializeVotingRoom(VoteType voteType, bytes calldata publicKey, uint256 voteAmount) public {
|
||||
require(activeRoomIDByCommunityID[publicKey] == 0, 'vote already ongoing');
|
||||
if (voteType == VoteType.REMOVE) {
|
||||
if (voteType == VoteType.AGAINST) {
|
||||
require(directory.isCommunityInDirectory(publicKey), 'Community not in directory');
|
||||
}
|
||||
if (voteType == VoteType.ADD) {
|
||||
if (voteType == VoteType.FOR) {
|
||||
require(!directory.isCommunityInDirectory(publicKey), 'Community already in directory');
|
||||
}
|
||||
uint256 historyLength = roomIDsByCommunityID[publicKey].length;
|
||||
@ -192,13 +200,15 @@ contract VotingContract {
|
||||
|
||||
activeRoomIDByCommunityID[publicKey] = votingRoomID;
|
||||
roomIDsByCommunityID[publicKey].push(votingRoomID);
|
||||
votesByRoomID[votingRoomID].push(Vote({ voter: msg.sender, voteType: VoteType.ADD, sntAmount: voteAmount }));
|
||||
votesByRoomID[votingRoomID].push(Vote({ voter: msg.sender, voteType: VoteType.FOR, sntAmount: voteAmount }));
|
||||
votedAddressesByRoomID[votingRoomID][msg.sender] = true;
|
||||
|
||||
votingRooms.push(
|
||||
VotingRoom({
|
||||
startBlock: block.number,
|
||||
endAt: block.timestamp.add(votingLength),
|
||||
startAt: block.timestamp,
|
||||
verificationStartAt: block.timestamp.add(votingLength),
|
||||
endAt: block.timestamp.add(votingLength + votingVerificationLength),
|
||||
voteType: voteType,
|
||||
finalized: false,
|
||||
community: publicKey,
|
||||
@ -220,7 +230,7 @@ contract VotingContract {
|
||||
for (uint256 i = 0; i < votesByRoomID[votingRoom.roomNumber].length; i++) {
|
||||
Vote storage vote = votesByRoomID[votingRoom.roomNumber][i];
|
||||
if (token.balanceOf(vote.voter) >= vote.sntAmount) {
|
||||
if (vote.voteType == VoteType.ADD) {
|
||||
if (vote.voteType == VoteType.FOR) {
|
||||
votingRoom.totalVotesFor = votingRoom.totalVotesFor.add(vote.sntAmount);
|
||||
} else {
|
||||
votingRoom.totalVotesAgainst = votingRoom.totalVotesAgainst.add(vote.sntAmount);
|
||||
@ -233,7 +243,7 @@ contract VotingContract {
|
||||
}
|
||||
|
||||
function _populateDirectory(VotingRoom storage votingRoom) private {
|
||||
if (votingRoom.voteType == VoteType.ADD) {
|
||||
if (votingRoom.voteType == VoteType.FOR) {
|
||||
directory.addCommunity(votingRoom.community);
|
||||
} else {
|
||||
directory.removeCommunity(votingRoom.community);
|
||||
@ -258,36 +268,46 @@ contract VotingContract {
|
||||
emit VotingRoomFinalized(roomId, votingRoom.community, passed, votingRoom.voteType);
|
||||
}
|
||||
|
||||
function castVote(SignedVote calldata vote) private {
|
||||
if (!verifySignature(vote)) {
|
||||
emit InvalidSignature(vote.roomIdAndType >> 1, vote.voter);
|
||||
return;
|
||||
}
|
||||
|
||||
uint256 roomId = vote.roomIdAndType >> 1;
|
||||
VotingRoom storage room = _getVotingRoom(roomId);
|
||||
|
||||
require(block.timestamp < room.endAt, 'vote closed');
|
||||
require(!room.finalized, 'room finalized');
|
||||
require(vote.timestamp < room.verificationStartAt, 'invalid vote timestamp');
|
||||
require(vote.timestamp >= room.startAt, 'invalid vote timestamp');
|
||||
|
||||
if (votedAddressesByRoomID[roomId][vote.voter]) {
|
||||
emit AlreadyVoted(roomId, vote.voter);
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.balanceOf(vote.voter) < vote.sntAmount) {
|
||||
emit NotEnoughToken(roomId, vote.voter);
|
||||
return;
|
||||
}
|
||||
|
||||
votedAddressesByRoomID[roomId][vote.voter] = true;
|
||||
votesByRoomID[roomId].push(
|
||||
Vote({
|
||||
voter: vote.voter,
|
||||
voteType: vote.roomIdAndType & 1 == 1 ? VoteType.FOR : VoteType.AGAINST,
|
||||
sntAmount: vote.sntAmount
|
||||
})
|
||||
);
|
||||
|
||||
_evaluateVotes(room); // TODO: optimise - aggregate votes by room id and only then evaluate
|
||||
emit VoteCast(roomId, vote.voter);
|
||||
}
|
||||
|
||||
function castVotes(SignedVote[] calldata votes) public {
|
||||
for (uint256 i = 0; i < votes.length; i++) {
|
||||
SignedVote calldata signedVote = votes[i];
|
||||
|
||||
if (verify(signedVote)) {
|
||||
uint256 roomId = signedVote.roomIdAndType >> 1;
|
||||
VotingRoom storage room = _getVotingRoom(roomId);
|
||||
|
||||
require(room.endAt > block.timestamp, 'vote closed');
|
||||
require(!room.finalized, 'room finalized');
|
||||
|
||||
if (votedAddressesByRoomID[roomId][signedVote.voter] == false) {
|
||||
if (token.balanceOf(signedVote.voter) >= signedVote.sntAmount) {
|
||||
votedAddressesByRoomID[roomId][signedVote.voter] = true;
|
||||
votesByRoomID[roomId].push(
|
||||
Vote({
|
||||
voter: signedVote.voter,
|
||||
voteType: signedVote.roomIdAndType & 1 == 1 ? VoteType.ADD : VoteType.REMOVE,
|
||||
sntAmount: signedVote.sntAmount
|
||||
})
|
||||
);
|
||||
_evaluateVotes(room); // TODO: optimise - aggregate votes by room id and only then evaluate
|
||||
emit VoteCast(roomId, signedVote.voter);
|
||||
} else {
|
||||
emit NotEnoughToken(roomId, signedVote.voter);
|
||||
}
|
||||
} else {
|
||||
emit AlreadyVoted(roomId, signedVote.voter);
|
||||
}
|
||||
}
|
||||
castVote(votes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,87 +4,110 @@
|
||||
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
|
||||
// will compile your contracts, add the Hardhat Runtime Environment's members to the
|
||||
// global scope, and execute the script.
|
||||
const hre = require("hardhat");
|
||||
const hre = require('hardhat')
|
||||
|
||||
import { ERC20Mock, MultiCall } from '../abi';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { ERC20Mock, MultiCall } from '../abi'
|
||||
import { BigNumber } from 'ethers'
|
||||
|
||||
async function deployVotingContract(tokenAddress: string, votingLength, timeBetweenVoting) {
|
||||
const contractFactory = await hre.ethers.getContractFactory('VotingContract');
|
||||
const contract = await contractFactory.deploy(tokenAddress, votingLength, timeBetweenVoting);
|
||||
await contract.deployed();
|
||||
async function deployVotingContract(
|
||||
tokenAddress: string,
|
||||
votingLength: number,
|
||||
votingVerificationLength: number,
|
||||
timeBetweenVoting: number
|
||||
) {
|
||||
const contractFactory = await hre.ethers.getContractFactory('VotingContract')
|
||||
const contract = await contractFactory.deploy(tokenAddress, votingLength, votingVerificationLength, timeBetweenVoting)
|
||||
await contract.deployed()
|
||||
|
||||
console.log(`Voting contract deployed with address: ${contract.address}`);
|
||||
console.log(`Voting contract deployed with address: ${contract.address}`)
|
||||
|
||||
return contract;
|
||||
return contract
|
||||
}
|
||||
|
||||
async function deployDirectoryContract(votingContractAddress: string) {
|
||||
const contractFactory = await hre.ethers.getContractFactory('Directory');
|
||||
const contract = await contractFactory.deploy(votingContractAddress);
|
||||
await contract.deployed();
|
||||
const contractFactory = await hre.ethers.getContractFactory('Directory')
|
||||
const contract = await contractFactory.deploy(votingContractAddress)
|
||||
await contract.deployed()
|
||||
|
||||
console.log(`Directory contract deployed with address: ${contract.address}`);
|
||||
console.log(`Directory contract deployed with address: ${contract.address}`)
|
||||
|
||||
return contract;
|
||||
return contract
|
||||
}
|
||||
|
||||
async function deployERC20MockContract(deployerAddress: string) {
|
||||
const contractFactory = await hre.ethers.getContractFactory(ERC20Mock.abi, ERC20Mock.bytecode);
|
||||
const contract = await contractFactory.deploy('MSNT', 'Mock SNT', deployerAddress, BigNumber.from('0x33B2E3C9FD0803CE8000000'));
|
||||
await contract.deployed();
|
||||
const contractFactory = await hre.ethers.getContractFactory(ERC20Mock.abi, ERC20Mock.bytecode)
|
||||
const contract = await contractFactory.deploy(
|
||||
'MSNT',
|
||||
'Mock SNT',
|
||||
deployerAddress,
|
||||
BigNumber.from('0x33B2E3C9FD0803CE8000000')
|
||||
)
|
||||
await contract.deployed()
|
||||
|
||||
console.log(`ERC20Mock contract deployed with address: ${contract.address}`);
|
||||
console.log(`ERC20Mock contract deployed with address: ${contract.address}`)
|
||||
|
||||
return contract;
|
||||
const [_, bob, alice] = await hre.ethers.getSigners()
|
||||
await contract.increaseAllowance(deployerAddress, BigNumber.from('0x33B2E3C9FD0803CE8000000'))
|
||||
await contract.transferFrom(deployerAddress, bob.address, BigNumber.from('0x52B7D2DCC80CD2E4000000'))
|
||||
await contract.transferFrom(deployerAddress, alice.address, BigNumber.from('0x52B7D2DCC80CD2E4000000'))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
async function deployMultiCallContract() {
|
||||
const contractFactory = await hre.ethers.getContractFactory(MultiCall.abi, MultiCall.bytecode);
|
||||
const contract = await contractFactory.deploy();
|
||||
await contract.deployed();
|
||||
const contractFactory = await hre.ethers.getContractFactory(MultiCall.abi, MultiCall.bytecode)
|
||||
const contract = await contractFactory.deploy()
|
||||
await contract.deployed()
|
||||
|
||||
console.log(`MultiCall contract deployed with address: ${contract.address}`);
|
||||
console.log(`MultiCall contract deployed with address: ${contract.address}`)
|
||||
|
||||
return contract;
|
||||
return contract
|
||||
}
|
||||
|
||||
function isTestNetwork(chainId) {
|
||||
return chainId == 1337 || chainId == 31337;
|
||||
function isTestNetwork(chainId: number) {
|
||||
return chainId == 1337 || chainId == 31337
|
||||
}
|
||||
|
||||
async function obtainTokenAddress(deployer, chainId): Promise<string> {
|
||||
let tokenAddress = process.env.TOKEN_CONTRACT;
|
||||
async function obtainTokenAddress(deployer: any, chainId: number): Promise<string> {
|
||||
let tokenAddress = process.env.TOKEN_CONTRACT
|
||||
if (!tokenAddress && isTestNetwork(chainId)) {
|
||||
const tokenContract = await deployERC20MockContract(deployer.address);
|
||||
tokenAddress = tokenContract.address;
|
||||
const tokenContract = await deployERC20MockContract(deployer.address)
|
||||
tokenAddress = tokenContract.address
|
||||
} else {
|
||||
throw new Error('TOKEN_ADDRESS should be provided');
|
||||
throw new Error('TOKEN_ADDRESS should be provided')
|
||||
}
|
||||
return tokenAddress ? tokenAddress : '';
|
||||
return tokenAddress ? tokenAddress : ''
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [deployer] = await hre.ethers.getSigners();
|
||||
const network = await hre.ethers.provider.getNetwork();
|
||||
const votingLengthInSeconds = isTestNetwork(network.chainId) ? 2 * 60 : 30 * 24 * 3600 // 2 minutes or 30 days
|
||||
const [deployer] = await hre.ethers.getSigners()
|
||||
const network = await hre.ethers.provider.getNetwork()
|
||||
const votingLengthInSeconds = isTestNetwork(network.chainId) ? 4 * 60 : 14 * 24 * 3600 // 4 minutes or 14 days
|
||||
const votingVerificationLengthInSeconds = isTestNetwork(network.chainId) ? 2 * 60 : 7 * 24 * 3600 // 2 minutes or 7 days
|
||||
const timeBetweenVotingInSeconds = isTestNetwork(network.chainId) ? 60 : 7 * 24 * 3600 // 1 minute or 7 days
|
||||
|
||||
console.log(`Deploying contracts on the network: ${network.name}(${network.chainId}), with the account: ${deployer.address}`);
|
||||
console.log(
|
||||
`Deploying contracts on the network: ${network.name}(${network.chainId}), with the account: ${deployer.address}`
|
||||
)
|
||||
|
||||
if (isTestNetwork(network.chainId)) {
|
||||
await deployMultiCallContract();
|
||||
await deployMultiCallContract()
|
||||
}
|
||||
|
||||
const tokenAddress = await obtainTokenAddress(deployer, network.chainId);
|
||||
const votingContract = await deployVotingContract(tokenAddress, votingLengthInSeconds, timeBetweenVotingInSeconds);
|
||||
const directoryContract = await deployDirectoryContract(votingContract.address);
|
||||
const tokenAddress = await obtainTokenAddress(deployer, network.chainId)
|
||||
const votingContract = await deployVotingContract(
|
||||
tokenAddress,
|
||||
votingLengthInSeconds,
|
||||
votingVerificationLengthInSeconds,
|
||||
timeBetweenVotingInSeconds
|
||||
)
|
||||
const directoryContract = await deployDirectoryContract(votingContract.address)
|
||||
await votingContract.setDirectory(directoryContract.address)
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
|
@ -10,7 +10,7 @@
|
||||
"lint": "yarn lint:prettier --check && yarn lint:solhint && yarn lint:eslint",
|
||||
"lint:fix": "yarn lint:prettier --write && yarn lint:eslint --fix",
|
||||
"lint:eslint": "eslint './test/**/*.ts'",
|
||||
"lint:prettier": "yarn prettier './{contracts,test}/**/*.{ts,sol}'",
|
||||
"lint:prettier": "yarn prettier './{contracts,test,deploy}/**/*.{ts,sol}'",
|
||||
"lint:solhint": "yarn solhint -f table contracts/**/*.sol",
|
||||
"flatten": "hardhat flatten",
|
||||
"test": "hardhat test"
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
|
||||
import { Contract } from 'ethers'
|
||||
import { ERC20Mock } from '../abi'
|
||||
|
||||
import { time, loadFixture } from '@nomicfoundation/hardhat-network-helpers'
|
||||
import { expect } from 'chai'
|
||||
import { ethers } from 'hardhat'
|
||||
|
||||
import { ERC20Mock } from '../abi'
|
||||
import { VotingContract } from '../typechain-types'
|
||||
|
||||
const { utils } = ethers
|
||||
|
||||
const publicKeys = [
|
||||
@ -14,12 +14,23 @@ const publicKeys = [
|
||||
'0xe84e64498172551d998a220e1d8e5893c818ee9aa90bdb855aec0c9e65e89014b8',
|
||||
]
|
||||
|
||||
const votingLength = 1000
|
||||
const votingVerificationLength = 200
|
||||
const timeBetweenVoting = 3600
|
||||
const votingWithVerificationLength = votingLength + votingVerificationLength
|
||||
|
||||
enum VoteType {
|
||||
AGAINST,
|
||||
FOR,
|
||||
}
|
||||
|
||||
const typedData = {
|
||||
types: {
|
||||
Vote: [
|
||||
{ name: 'roomIdAndType', type: 'uint256' },
|
||||
{ name: 'sntAmount', type: 'uint256' },
|
||||
{ name: 'voter', type: 'address' },
|
||||
{ name: 'timestamp', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
domain: {
|
||||
@ -30,62 +41,47 @@ const typedData = {
|
||||
},
|
||||
}
|
||||
|
||||
const createSignedVote = async (
|
||||
signer: SignerWithAddress,
|
||||
room: number,
|
||||
type: VoteType,
|
||||
sntAmount: number,
|
||||
timestamp = 0
|
||||
): Promise<VotingContract.SignedVoteStruct> => {
|
||||
const vote = {
|
||||
voter: signer.address,
|
||||
roomIdAndType: BigNumber.from(room).mul(2).add(type),
|
||||
sntAmount: BigNumber.from(sntAmount),
|
||||
timestamp: timestamp ? BigNumber.from(timestamp) : BigNumber.from(await time.latest()),
|
||||
}
|
||||
|
||||
const message = {
|
||||
roomIdAndType: vote.roomIdAndType.toHexString(),
|
||||
sntAmount: vote.sntAmount.toHexString(),
|
||||
voter: vote.voter,
|
||||
timestamp: vote.timestamp.toHexString(),
|
||||
}
|
||||
const signature = await signer._signTypedData(typedData.domain, typedData.types, message)
|
||||
const splitSignature = utils.splitSignature(signature)
|
||||
|
||||
return { ...vote, r: splitSignature.r, vs: splitSignature._vs }
|
||||
}
|
||||
|
||||
const getSignedVotes = async (
|
||||
firstSigner: SignerWithAddress,
|
||||
secondSigner: SignerWithAddress,
|
||||
thirdSigner: SignerWithAddress
|
||||
): Promise<{ voter: string; roomIdAndType: BigNumber; sntAmount: BigNumber; r: string; vs: string }[]> => {
|
||||
const votes = [
|
||||
{
|
||||
voter: firstSigner,
|
||||
vote: 1,
|
||||
sntAmount: BigNumber.from(100),
|
||||
sessionID: 1,
|
||||
},
|
||||
{
|
||||
voter: secondSigner,
|
||||
vote: 0,
|
||||
sntAmount: BigNumber.from(100),
|
||||
sessionID: 1,
|
||||
},
|
||||
{
|
||||
voter: thirdSigner,
|
||||
vote: 1,
|
||||
sntAmount: BigNumber.from(100),
|
||||
sessionID: 1,
|
||||
},
|
||||
): Promise<VotingContract.SignedVoteStruct[]> => {
|
||||
return [
|
||||
await createSignedVote(firstSigner, 1, VoteType.FOR, 100),
|
||||
await createSignedVote(secondSigner, 1, VoteType.AGAINST, 100),
|
||||
await createSignedVote(thirdSigner, 1, VoteType.FOR, 100),
|
||||
]
|
||||
const messages: [string, BigNumber, BigNumber][] = votes.map((vote) => [
|
||||
vote.voter.address,
|
||||
BigNumber.from(vote.sessionID).mul(2).add(vote.vote),
|
||||
vote.sntAmount,
|
||||
])
|
||||
|
||||
const signedMessages = await Promise.all(
|
||||
messages.map(async (msg, idx) => {
|
||||
const message = { roomIdAndType: msg[1].toHexString(), sntAmount: msg[2].toHexString(), voter: msg[0] }
|
||||
const signature = await votes[idx].voter._signTypedData(typedData.domain, typedData.types, message)
|
||||
const sig = utils.splitSignature(signature)
|
||||
return { voter: msg[0], roomIdAndType: msg[1], sntAmount: msg[2], r: sig.r, vs: sig._vs }
|
||||
})
|
||||
)
|
||||
|
||||
return signedMessages
|
||||
}
|
||||
|
||||
const voteAndFinalize = async (room: number, type: number, signer: SignerWithAddress, contract: Contract) => {
|
||||
const vote: [string, BigNumber, BigNumber] = [
|
||||
signer.address,
|
||||
BigNumber.from(room).mul(2).add(type),
|
||||
BigNumber.from(100),
|
||||
]
|
||||
|
||||
const message = { roomIdAndType: vote[1].toHexString(), sntAmount: vote[2].toHexString(), voter: vote[0] }
|
||||
const signature = await signer._signTypedData(typedData.domain, typedData.types, message)
|
||||
const sig = utils.splitSignature(signature)
|
||||
|
||||
await contract.castVotes([{ voter: vote[0], roomIdAndType: vote[1], sntAmount: vote[2], r: sig.r, vs: sig._vs }])
|
||||
await time.increase(10000)
|
||||
const voteAndFinalize = async (room: number, type: VoteType, signer: SignerWithAddress, contract: VotingContract) => {
|
||||
await contract.castVotes([await createSignedVote(signer, room, type, 100)])
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
await contract.finalizeVotingRoom(room)
|
||||
}
|
||||
|
||||
@ -99,7 +95,12 @@ async function fixture() {
|
||||
await erc20Contract.transfer(thirdSigner.address, 10000)
|
||||
|
||||
const votingContractFactory = await ethers.getContractFactory('VotingContract')
|
||||
const votingContract = await votingContractFactory.deploy(erc20Contract.address, 1000, 3600)
|
||||
const votingContract = await votingContractFactory.deploy(
|
||||
erc20Contract.address,
|
||||
votingLength,
|
||||
votingVerificationLength,
|
||||
timeBetweenVoting
|
||||
)
|
||||
|
||||
const directoryContractFactory = await ethers.getContractFactory('Directory')
|
||||
const directoryContract = await directoryContractFactory.deploy(votingContract.address)
|
||||
@ -110,7 +111,6 @@ async function fixture() {
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
// this.timeout(10000)
|
||||
const { votingContract: voting } = await loadFixture(fixture)
|
||||
typedData.domain.chainId = 31337
|
||||
typedData.domain.verifyingContract = voting.address
|
||||
@ -132,41 +132,41 @@ describe('VotingContract', () => {
|
||||
describe('initialization', () => {
|
||||
it('initializes', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await expect(await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100)))
|
||||
await expect(await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100)))
|
||||
.to.emit(votingContract, 'VotingRoomStarted')
|
||||
.withArgs(1, publicKeys[0])
|
||||
await expect(await votingContract.initializeVotingRoom(1, publicKeys[1], BigNumber.from(100)))
|
||||
await expect(await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[1], BigNumber.from(100)))
|
||||
.to.emit(votingContract, 'VotingRoomStarted')
|
||||
.withArgs(2, publicKeys[1])
|
||||
await expect(votingContract.initializeVotingRoom(1, publicKeys[1], BigNumber.from(100))).to.be.revertedWith(
|
||||
'vote already ongoing'
|
||||
)
|
||||
await expect(
|
||||
votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[1], BigNumber.from(100))
|
||||
).to.be.revertedWith('vote already ongoing')
|
||||
})
|
||||
|
||||
it('not enough token', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await expect(
|
||||
votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(10000000000000))
|
||||
votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(10000000000000))
|
||||
).to.be.revertedWith('not enough token')
|
||||
})
|
||||
|
||||
describe('directory interaction', () => {
|
||||
it('remove missing', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await expect(votingContract.initializeVotingRoom(0, publicKeys[0], BigNumber.from(100))).to.be.revertedWith(
|
||||
'Community not in directory'
|
||||
)
|
||||
await expect(
|
||||
votingContract.initializeVotingRoom(VoteType.AGAINST, publicKeys[0], BigNumber.from(100))
|
||||
).to.be.revertedWith('Community not in directory')
|
||||
})
|
||||
|
||||
it('add already in', async () => {
|
||||
const { votingContract, directoryContract, firstSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, firstSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, firstSigner, votingContract)
|
||||
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([publicKeys[0]])
|
||||
await expect(votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))).to.be.revertedWith(
|
||||
'Community already in directory'
|
||||
)
|
||||
await expect(
|
||||
votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
).to.be.revertedWith('Community already in directory')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -174,7 +174,7 @@ describe('VotingContract', () => {
|
||||
it('gets', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
let votingRoom1 = await votingContract.votingRooms(0)
|
||||
expect(votingRoom1.voteType).to.eq(1)
|
||||
expect(votingRoom1.finalized).to.eq(false)
|
||||
@ -188,7 +188,7 @@ describe('VotingContract', () => {
|
||||
expect(history[0].voteType).to.eq(1)
|
||||
expect(history[0].community).to.eq(publicKeys[0])
|
||||
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[1], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[1], BigNumber.from(100))
|
||||
const votingRoom2 = await votingContract.votingRooms(1)
|
||||
expect(votingRoom2.voteType).to.eq(1)
|
||||
expect(votingRoom2.finalized).to.eq(false)
|
||||
@ -209,12 +209,12 @@ describe('VotingContract', () => {
|
||||
describe('history', () => {
|
||||
it('saves to history', async () => {
|
||||
const { votingContract, firstSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, firstSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, firstSigner, votingContract)
|
||||
expect((await votingContract.getVotingHistory(publicKeys[0])).length).to.eq(1)
|
||||
await time.increase(10000)
|
||||
await votingContract.initializeVotingRoom(0, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, 0, firstSigner, votingContract)
|
||||
await time.increase(timeBetweenVoting + 1)
|
||||
await votingContract.initializeVotingRoom(VoteType.AGAINST, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, VoteType.AGAINST, firstSigner, votingContract)
|
||||
const history = await votingContract.getVotingHistory(publicKeys[0])
|
||||
expect(history.length).to.eq(2)
|
||||
expect(history[0].voteType).to.eq(1)
|
||||
@ -234,18 +234,18 @@ describe('VotingContract', () => {
|
||||
|
||||
it("can't start vote to fast", async () => {
|
||||
const { votingContract, firstSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, firstSigner, votingContract)
|
||||
await expect(votingContract.initializeVotingRoom(0, publicKeys[0], BigNumber.from(100))).to.be.revertedWith(
|
||||
'Community was in a vote recently'
|
||||
)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, firstSigner, votingContract)
|
||||
await expect(
|
||||
votingContract.initializeVotingRoom(VoteType.AGAINST, publicKeys[0], BigNumber.from(100))
|
||||
).to.be.revertedWith('Community was in a vote recently')
|
||||
})
|
||||
})
|
||||
|
||||
describe('finalization', () => {
|
||||
it('finalizes', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
let votingRoom = await votingContract.votingRooms(0)
|
||||
expect(votingRoom.voteType).to.eq(1)
|
||||
expect(votingRoom.finalized).to.eq(false)
|
||||
@ -254,7 +254,7 @@ describe('VotingContract', () => {
|
||||
expect(votingRoom.totalVotesAgainst).to.eq(0)
|
||||
expect(votingRoom.roomNumber).to.eq(1)
|
||||
|
||||
await time.increase(2000)
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
await expect(await votingContract.finalizeVotingRoom(1))
|
||||
.to.emit(votingContract, 'VotingRoomFinalized')
|
||||
.withArgs(1, publicKeys[0], true, 1)
|
||||
@ -270,14 +270,14 @@ describe('VotingContract', () => {
|
||||
|
||||
it('verifies votes', async () => {
|
||||
const { votingContract, erc20Contract, firstSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
// clear balance
|
||||
const firstSignerBalance = await erc20Contract.balanceOf(firstSigner.address)
|
||||
await erc20Contract.increaseAllowance(firstSigner.address, firstSignerBalance)
|
||||
await erc20Contract.transferFrom(firstSigner.address, erc20Contract.address, firstSignerBalance)
|
||||
|
||||
await time.increase(2000)
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
await expect(await votingContract.finalizeVotingRoom(1))
|
||||
.to.emit(votingContract, 'NotEnoughToken')
|
||||
.withArgs(1, firstSigner.address)
|
||||
@ -288,8 +288,8 @@ describe('VotingContract', () => {
|
||||
describe('directory interaction', () => {
|
||||
it('add community', async () => {
|
||||
const { votingContract, directoryContract, secondSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, secondSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, secondSigner, votingContract)
|
||||
|
||||
const votingRoom = await votingContract.votingRooms(0)
|
||||
expect(votingRoom.voteType).to.eq(1)
|
||||
@ -304,33 +304,33 @@ describe('VotingContract', () => {
|
||||
|
||||
it('remove community', async () => {
|
||||
const { votingContract, directoryContract, secondSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, secondSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, secondSigner, votingContract)
|
||||
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([publicKeys[0]])
|
||||
await time.increase(10000)
|
||||
await votingContract.initializeVotingRoom(0, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, 1, secondSigner, votingContract)
|
||||
await time.increase(timeBetweenVoting + 1)
|
||||
await votingContract.initializeVotingRoom(VoteType.AGAINST, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, VoteType.FOR, secondSigner, votingContract)
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([])
|
||||
})
|
||||
|
||||
it('failed add vote', async () => {
|
||||
const { votingContract, directoryContract, secondSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 0, secondSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.AGAINST, secondSigner, votingContract)
|
||||
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([])
|
||||
})
|
||||
|
||||
it('failed remove vote', async () => {
|
||||
const { votingContract, directoryContract, secondSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, 1, secondSigner, votingContract)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(1, VoteType.FOR, secondSigner, votingContract)
|
||||
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([publicKeys[0]])
|
||||
await time.increase(10000)
|
||||
await votingContract.initializeVotingRoom(0, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, 0, secondSigner, votingContract)
|
||||
await time.increase(timeBetweenVoting + 1)
|
||||
await votingContract.initializeVotingRoom(VoteType.AGAINST, publicKeys[0], BigNumber.from(100))
|
||||
await voteAndFinalize(2, VoteType.AGAINST, secondSigner, votingContract)
|
||||
expect(await directoryContract.getCommunities()).to.deep.eq([publicKeys[0]])
|
||||
})
|
||||
})
|
||||
@ -340,7 +340,7 @@ describe('VotingContract', () => {
|
||||
describe('helpers', () => {
|
||||
it('getActiveVotingRoom', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
const votingRoom1 = await votingContract.getActiveVotingRoom(publicKeys[0])
|
||||
expect(votingRoom1.voteType).to.eq(1)
|
||||
@ -350,8 +350,7 @@ describe('VotingContract', () => {
|
||||
expect(votingRoom1.totalVotesAgainst).to.eq(0)
|
||||
expect(votingRoom1.roomNumber).to.eq(1)
|
||||
|
||||
await time.increase(10000)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[1], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[1], BigNumber.from(100))
|
||||
|
||||
const votingRoom2 = await votingContract.getActiveVotingRoom(publicKeys[1])
|
||||
expect(votingRoom2.voteType).to.eq(1)
|
||||
@ -364,14 +363,17 @@ describe('VotingContract', () => {
|
||||
|
||||
it('get active votes', async () => {
|
||||
const { votingContract } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
expect(await votingContract.getActiveVotingRooms()).to.deep.eq([BigNumber.from(1)])
|
||||
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[1], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[1], BigNumber.from(100))
|
||||
expect(await votingContract.getActiveVotingRooms()).to.deep.eq([BigNumber.from(1), BigNumber.from(2)])
|
||||
await time.increase(2000)
|
||||
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
|
||||
await votingContract.finalizeVotingRoom(1)
|
||||
expect(await votingContract.getActiveVotingRooms()).to.deep.eq([BigNumber.from(2)])
|
||||
|
||||
await votingContract.finalizeVotingRoom(2)
|
||||
expect(await votingContract.getActiveVotingRooms()).to.deep.eq([])
|
||||
})
|
||||
@ -380,8 +382,8 @@ describe('VotingContract', () => {
|
||||
describe('voting', () => {
|
||||
it('check voters', async () => {
|
||||
const { votingContract, firstSigner, secondSigner, thirdSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
const votes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
expect(await votingContract.listRoomVoters(1)).to.deep.eq([firstSigner.address])
|
||||
await votingContract.castVotes(votes.slice(2))
|
||||
@ -390,20 +392,26 @@ describe('VotingContract', () => {
|
||||
|
||||
it('not enough tokens', async () => {
|
||||
const { votingContract, secondSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
const vote: [string, BigNumber, BigNumber] = [
|
||||
const vote: [string, BigNumber, BigNumber, BigNumber] = [
|
||||
secondSigner.address,
|
||||
BigNumber.from(1).mul(2).add(1),
|
||||
BigNumber.from(100000000000),
|
||||
BigNumber.from(await time.latest()),
|
||||
]
|
||||
const message = { roomIdAndType: vote[1].toHexString(), sntAmount: vote[2].toHexString(), voter: vote[0] }
|
||||
const message = {
|
||||
roomIdAndType: vote[1].toHexString(),
|
||||
sntAmount: vote[2].toHexString(),
|
||||
voter: vote[0],
|
||||
timestamp: vote[3].toHexString(),
|
||||
}
|
||||
const signature = await secondSigner._signTypedData(typedData.domain, typedData.types, message)
|
||||
const sig = utils.splitSignature(signature)
|
||||
|
||||
await expect(
|
||||
await votingContract.castVotes([
|
||||
{ voter: vote[0], roomIdAndType: vote[1], sntAmount: vote[2], r: sig.r, vs: sig._vs },
|
||||
{ voter: vote[0], roomIdAndType: vote[1], sntAmount: vote[2], timestamp: vote[3], r: sig.r, vs: sig._vs },
|
||||
])
|
||||
)
|
||||
.to.emit(votingContract, 'NotEnoughToken')
|
||||
@ -420,8 +428,8 @@ describe('VotingContract', () => {
|
||||
|
||||
it('success', async () => {
|
||||
const { votingContract, firstSigner, secondSigner, thirdSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
const votes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.castVotes(votes)
|
||||
|
||||
const votingRoom = await votingContract.votingRooms(0)
|
||||
@ -435,8 +443,8 @@ describe('VotingContract', () => {
|
||||
|
||||
it('double vote', async () => {
|
||||
const { votingContract, firstSigner, secondSigner, thirdSigner } = await loadFixture(fixture)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
const votes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await votingContract.castVotes(votes)
|
||||
await votingContract.castVotes(votes)
|
||||
|
||||
@ -457,19 +465,22 @@ describe('VotingContract', () => {
|
||||
|
||||
it('old room', async () => {
|
||||
const { votingContract, firstSigner, secondSigner, thirdSigner } = await loadFixture(fixture)
|
||||
const votes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await time.increase(2000)
|
||||
await expect(votingContract.castVotes(votes)).to.be.reverted
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
|
||||
const signedVotes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await expect(votingContract.castVotes(signedVotes)).to.be.reverted
|
||||
})
|
||||
|
||||
it('wrong signature', async () => {
|
||||
const { votingContract, firstSigner, secondSigner, thirdSigner } = await loadFixture(fixture)
|
||||
const signedVotes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
await votingContract.initializeVotingRoom(1, publicKeys[0], BigNumber.from(100))
|
||||
await time.increase(2000)
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
|
||||
const wronglySignedMessages = signedVotes.map((msg) => {
|
||||
await time.increase(votingWithVerificationLength + 1)
|
||||
|
||||
const signedVotes = await getSignedVotes(firstSigner, secondSigner, thirdSigner)
|
||||
const wronglySignedVotes = signedVotes.map((msg) => {
|
||||
return {
|
||||
...msg,
|
||||
r: '0x2d63286985277c440b9f01a987fbbc9bc9ca32cb4e9e55ee3ffcab4e67c211e6',
|
||||
@ -477,8 +488,22 @@ describe('VotingContract', () => {
|
||||
}
|
||||
})
|
||||
|
||||
await votingContract.castVotes(wronglySignedMessages)
|
||||
await votingContract.castVotes(wronglySignedVotes)
|
||||
await expect(await votingContract.listRoomVoters(1)).to.deep.eq([firstSigner.address])
|
||||
})
|
||||
|
||||
it('validates vote timestamp', async () => {
|
||||
const { votingContract, secondSigner } = await loadFixture(fixture)
|
||||
|
||||
const voteBefore = await createSignedVote(secondSigner, 1, VoteType.FOR, 100)
|
||||
await time.increase(1)
|
||||
|
||||
await votingContract.initializeVotingRoom(VoteType.FOR, publicKeys[0], BigNumber.from(100))
|
||||
await expect(votingContract.castVotes([voteBefore])).to.be.revertedWith('invalid vote timestamp')
|
||||
|
||||
await time.increase(votingLength + 1)
|
||||
const voteAfter = await createSignedVote(secondSigner, 1, VoteType.FOR, 100)
|
||||
await expect(votingContract.castVotes([voteAfter])).to.be.revertedWith('invalid vote timestamp')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user