Refactor waku messagning (#68)

This commit is contained in:
Szymon Szlachtowicz 2021-09-14 15:55:33 +02:00 committed by GitHub
parent 283edd8613
commit 5b200bc60e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 118 additions and 69 deletions

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}, [])

View File

@ -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 (
<List>
{votes.map((vote, idx) => {
return <ProposalCard heading={vote[2]} text={vote[3]} address={'#'} theme={theme} key={idx} id={idx} />
{votes.map((vote) => {
return (
<ProposalCard
heading={vote.question}
text={vote.description}
address={'#'}
theme={theme}
key={vote.id}
id={vote.id}
/>
)
})}
</List>
)

View File

@ -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)
}, [])