diff --git a/packages/core/src/classes/WakuMessaging.ts b/packages/core/src/classes/WakuMessaging.ts index 66bdbcf..b289375 100644 --- a/packages/core/src/classes/WakuMessaging.ts +++ b/packages/core/src/classes/WakuMessaging.ts @@ -1,10 +1,18 @@ import { Waku } from 'js-waku' import { WakuMessage } from 'js-waku' +import { BigNumber } from 'ethers' import { Provider } from '@ethersproject/providers' +import { Contract } from '@ethersproject/contracts' +import { Interface } from '@ethersproject/abi' +import { ERC20 } from '../abi' +const ABI = [ + 'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)', +] type WakuMessageStore = { topic: string hashMap: { [id: string]: boolean } + tokenCheckArray: string[] arr: any[] updateFunction: (msg: WakuMessage[]) => void } @@ -21,13 +29,22 @@ export class WakuMessaging { protected chainId = 0 protected wakuMessages: WakuMessageStores = {} protected observers: { callback: (msg: WakuMessage) => void; topics: string[] }[] = [] + protected multicall: Contract - protected constructor(appName: string, tokenAddress: string, waku: Waku, provider: Provider, chainId: number) { + protected constructor( + appName: string, + tokenAddress: string, + waku: Waku, + provider: Provider, + chainId: number, + multicall: string + ) { this.appName = appName this.tokenAddress = tokenAddress this.waku = waku this.provider = provider this.chainId = chainId + this.multicall = new Contract(multicall, ABI, this.provider) } public cleanUp() { @@ -80,4 +97,55 @@ export class WakuMessaging { msgObj.updateFunction([wakuMessage]) } } + + protected addressesBalances: { [address: string]: BigNumber } = {} + protected lastBlockBalances = 0 + + protected async updateBalances(newAddress?: string) { + const addressesToUpdate: { [addr: string]: boolean } = {} + + const addAddressToUpdate = (addr: string) => { + if (!addressesToUpdate[addr]) { + addressesToUpdate[addr] = true + } + } + + const currentBlock = await this.provider.getBlockNumber() + + if (this.lastBlockBalances != currentBlock) { + Object.keys(this.addressesBalances).forEach(addAddressToUpdate) + if (newAddress) addAddressToUpdate(newAddress) + Object.values(this.wakuMessages).forEach((wakuMessage) => + wakuMessage.arr.forEach((msg) => wakuMessage.tokenCheckArray.forEach((field) => addAddressToUpdate(msg[field]))) + ) + } else { + Object.values(this.wakuMessages).forEach((wakuMessage) => + wakuMessage.arr.forEach((msg) => + wakuMessage.tokenCheckArray.forEach((field) => { + const address = msg[field] + if (!this.addressesBalances[address]) { + addAddressToUpdate(address) + } + }) + ) + ) + if (newAddress && !this.addressesBalances[newAddress]) addAddressToUpdate(newAddress) + } + + const addressesToUpdateArray = Object.keys(addressesToUpdate) + if (addressesToUpdateArray.length > 0) { + const erc20 = new Interface(ERC20.abi) + const callData = addressesToUpdateArray.map((addr) => { + return [this.tokenAddress, erc20.encodeFunctionData('balanceOf', [addr])] + }) + const result = (await this.multicall.aggregate(callData))[1].map((data: any) => + erc20.decodeFunctionResult('balanceOf', data) + ) + + result.forEach((e: any, idx: number) => { + this.addressesBalances[addressesToUpdateArray[idx]] = e[0] + }) + this.lastBlockBalances = currentBlock + } + } } diff --git a/packages/core/src/classes/WakuPolling.ts b/packages/core/src/classes/WakuPolling.ts index 6947bf1..a9de023 100644 --- a/packages/core/src/classes/WakuPolling.ts +++ b/packages/core/src/classes/WakuPolling.ts @@ -9,12 +9,6 @@ import { DetailedTimedPoll } from '../models/DetailedTimedPoll' import { createWaku } from '../utils/createWaku' import { WakuMessaging } from './WakuMessaging' import { Provider } from '@ethersproject/providers' -import { Contract } from '@ethersproject/contracts' -import { Interface } from '@ethersproject/abi' -import { ERC20 } from '../abi' -const ABI = [ - 'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)', -] export enum MESSEGAGE_SENDING_RESULT { ok = 0, @@ -24,8 +18,6 @@ export enum MESSEGAGE_SENDING_RESULT { } export class WakuPolling extends WakuMessaging { - protected multicall: string - protected constructor( appName: string, tokenAddress: string, @@ -34,11 +26,11 @@ export class WakuPolling extends WakuMessaging { chainId: number, multicall: string ) { - super(appName, tokenAddress, waku, provider, chainId) - this.multicall = multicall + super(appName, tokenAddress, waku, provider, chainId, multicall) this.wakuMessages['pollInit'] = { topic: `/${this.appName}/waku-polling/timed-polls-init/proto/`, hashMap: {}, + tokenCheckArray: ['owner'], arr: [], updateFunction: (msg: WakuMessage[]) => this.decodeMsgAndSetArray( @@ -51,6 +43,7 @@ export class WakuPolling extends WakuMessaging { this.wakuMessages['pollVote'] = { topic: `/${this.appName}/waku-polling/votes/proto/`, hashMap: {}, + tokenCheckArray: ['voter'], arr: [], updateFunction: (msg: WakuMessage[]) => this.decodeMsgAndSetArray(msg, TimedPollVoteMsg.decode, this.wakuMessages['pollVote']), @@ -125,54 +118,6 @@ export class WakuPolling extends WakuMessaging { } } - protected addressesBalances: { [address: string]: BigNumber } = {} - protected lastBlockBalances = 0 - - protected async updateBalances(newAddress?: string) { - const addresses: string[] = [ - ...this.wakuMessages['pollInit'].arr.map((msg) => msg.owner), - ...this.wakuMessages['pollVote'].arr.map((msg) => msg.voter), - ] - - if (newAddress) addresses.push(newAddress) - const addressesToUpdate: { [addr: string]: boolean } = {} - - const addAddressToUpdate = (addr: string) => { - if (!addressesToUpdate[addr]) { - addressesToUpdate[addr] = true - } - } - - const currentBlock = await this.provider.getBlockNumber() - if (this.lastBlockBalances != currentBlock) { - Object.keys(this.addressesBalances).forEach(addAddressToUpdate) - addresses.forEach(addAddressToUpdate) - } else { - addresses.forEach((address) => { - if (!this.addressesBalances[address]) { - addAddressToUpdate(address) - } - }) - } - - const addressesToUpdateArray = Object.keys(addressesToUpdate) - if (addressesToUpdateArray.length > 0) { - const erc20 = new Interface(ERC20.abi) - const contract = new Contract(this.multicall, ABI, this.provider) - const callData = addressesToUpdateArray.map((addr) => { - return [this.tokenAddress, erc20.encodeFunctionData('balanceOf', [addr])] - }) - const result = (await contract.aggregate(callData))[1].map((data: any) => - erc20.decodeFunctionResult('balanceOf', data) - ) - - result.forEach((e: any, idx: number) => { - this.addressesBalances[addressesToUpdateArray[idx]] = e[0] - }) - this.lastBlockBalances = currentBlock - } - } - public async getDetailedTimedPolls() { await this.updateBalances() return this.wakuMessages['pollInit'].arr diff --git a/packages/core/src/classes/WakuVoting.ts b/packages/core/src/classes/WakuVoting.ts index b0004bd..4499b40 100644 --- a/packages/core/src/classes/WakuVoting.ts +++ b/packages/core/src/classes/WakuVoting.ts @@ -6,13 +6,13 @@ import { Provider } from '@ethersproject/abstract-provider' import { createWaku } from '../utils/createWaku' import { JsonRpcSigner } from '@ethersproject/providers' import { VoteMsg } from '../models/VoteMsg' +import { VotingRoom } from '../types/PollType' const ABI = [ 'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)', ] export class WakuVoting extends WakuMessaging { - private multicall: Contract private votingContract: Contract constructor( @@ -24,13 +24,13 @@ export class WakuVoting extends WakuMessaging { chainId: number, multicallAddress: string ) { - super(appName, token, waku, provider, chainId) + super(appName, token, waku, provider, chainId, multicallAddress) this.votingContract = votingContract - this.multicall = new Contract(multicallAddress, ABI, this.provider) this.wakuMessages['vote'] = { topic: `/${this.appName}/waku-voting/votes/proto/`, hashMap: {}, arr: [], + tokenCheckArray: ['voter'], updateFunction: (msg: WakuMessage[]) => this.decodeMsgAndSetArray( msg, @@ -71,14 +71,26 @@ export class WakuVoting extends WakuMessaging { await this.votingContract.initializeVotingRoom(question, descripiton, tokenAmount) } - private lastPolls: any[] = [] + private lastPolls: VotingRoom[] = [] private lastGetPollsBlockNumber = 0 - public async getVotes() { + public async getVotingRooms() { const blockNumber = await this.provider.getBlockNumber() if (blockNumber != this.lastGetPollsBlockNumber) { this.lastGetPollsBlockNumber = blockNumber - this.lastPolls = await this.votingContract.getVotingRooms() + const polls = await this.votingContract.getVotingRooms() + this.lastPolls = polls.map((poll: any, idx: number): VotingRoom => { + return { + startBlock: poll[0], + endAt: poll[1], + question: poll[2], + description: poll[3], + totalVotesFor: poll[4], + totalVotesAgainst: poll[5], + voters: poll[6], + id: idx, + } + }) } return this.lastPolls } diff --git a/packages/core/src/types/PollType.ts b/packages/core/src/types/PollType.ts index 353223f..45f9660 100644 --- a/packages/core/src/types/PollType.ts +++ b/packages/core/src/types/PollType.ts @@ -1,4 +1,17 @@ +import { BigNumber } from 'ethers' + export enum PollType { WEIGHTED = 0, NON_WEIGHTED = 1, } + +export type VotingRoom = { + startBlock: BigNumber + endAt: BigNumber + question: string + description: string + totalVotesFor: BigNumber + totalVotesAgainst: BigNumber + voters: string[] + id: number +} diff --git a/packages/proposal-components/src/components/Proposal.tsx b/packages/proposal-components/src/components/Proposal.tsx index 5534996..98cf598 100644 --- a/packages/proposal-components/src/components/Proposal.tsx +++ b/packages/proposal-components/src/components/Proposal.tsx @@ -16,7 +16,7 @@ export function Proposal({ wakuVoting }: ProposalProps) { useEffect(() => { const interval = setInterval(async () => { - setVotes(await wakuVoting.getVotes()) + setVotes(await wakuVoting.getVotingRooms()) }, 10000) return () => clearInterval(interval) }, []) diff --git a/packages/proposal-components/src/components/ProposalList.tsx b/packages/proposal-components/src/components/ProposalList.tsx index e1bf19b..12fff2f 100644 --- a/packages/proposal-components/src/components/ProposalList.tsx +++ b/packages/proposal-components/src/components/ProposalList.tsx @@ -3,6 +3,8 @@ import styled from 'styled-components' import { Theme } from '@status-waku-voting/react-components' import { ProposalCard } from './ProposalCard' import { WakuVoting } from '@status-waku-voting/core' +import { VotingEmpty } from './VotingEmpty' +import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType' type ProposalListProps = { theme: Theme @@ -12,8 +14,17 @@ type ProposalListProps = { export function ProposalList({ theme, wakuVoting, votes }: ProposalListProps) { return ( - {votes.map((vote, idx) => { - return + {votes.map((vote) => { + return ( + + ) })} ) diff --git a/packages/proposal-components/src/components/mobile/ProposalMainMobile.tsx b/packages/proposal-components/src/components/mobile/ProposalMainMobile.tsx index 18fdbac..379b2b1 100644 --- a/packages/proposal-components/src/components/mobile/ProposalMainMobile.tsx +++ b/packages/proposal-components/src/components/mobile/ProposalMainMobile.tsx @@ -17,7 +17,7 @@ export function ProposalMainMobile({ wakuVoting }: ProposalMainMobileProps) { useEffect(() => { const interval = setInterval(async () => { - setVotes(await wakuVoting.getVotes()) + setVotes(await wakuVoting.getVotingRooms()) }, 10000) return () => clearInterval(interval) }, [])