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