Refactor waku messagning (#68)
This commit is contained in:
parent
283edd8613
commit
5b200bc60e
|
@ -1,10 +1,18 @@
|
||||||
import { Waku } from 'js-waku'
|
import { Waku } from 'js-waku'
|
||||||
import { WakuMessage } from 'js-waku'
|
import { WakuMessage } from 'js-waku'
|
||||||
|
import { BigNumber } from 'ethers'
|
||||||
import { Provider } from '@ethersproject/providers'
|
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 = {
|
type WakuMessageStore = {
|
||||||
topic: string
|
topic: string
|
||||||
hashMap: { [id: string]: boolean }
|
hashMap: { [id: string]: boolean }
|
||||||
|
tokenCheckArray: string[]
|
||||||
arr: any[]
|
arr: any[]
|
||||||
updateFunction: (msg: WakuMessage[]) => void
|
updateFunction: (msg: WakuMessage[]) => void
|
||||||
}
|
}
|
||||||
|
@ -21,13 +29,22 @@ export class WakuMessaging {
|
||||||
protected chainId = 0
|
protected chainId = 0
|
||||||
protected wakuMessages: WakuMessageStores = {}
|
protected wakuMessages: WakuMessageStores = {}
|
||||||
protected observers: { callback: (msg: WakuMessage) => void; topics: string[] }[] = []
|
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.appName = appName
|
||||||
this.tokenAddress = tokenAddress
|
this.tokenAddress = tokenAddress
|
||||||
this.waku = waku
|
this.waku = waku
|
||||||
this.provider = provider
|
this.provider = provider
|
||||||
this.chainId = chainId
|
this.chainId = chainId
|
||||||
|
this.multicall = new Contract(multicall, ABI, this.provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
public cleanUp() {
|
public cleanUp() {
|
||||||
|
@ -80,4 +97,55 @@ export class WakuMessaging {
|
||||||
msgObj.updateFunction([wakuMessage])
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,6 @@ import { DetailedTimedPoll } from '../models/DetailedTimedPoll'
|
||||||
import { createWaku } from '../utils/createWaku'
|
import { createWaku } from '../utils/createWaku'
|
||||||
import { WakuMessaging } from './WakuMessaging'
|
import { WakuMessaging } from './WakuMessaging'
|
||||||
import { Provider } from '@ethersproject/providers'
|
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 {
|
export enum MESSEGAGE_SENDING_RESULT {
|
||||||
ok = 0,
|
ok = 0,
|
||||||
|
@ -24,8 +18,6 @@ export enum MESSEGAGE_SENDING_RESULT {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WakuPolling extends WakuMessaging {
|
export class WakuPolling extends WakuMessaging {
|
||||||
protected multicall: string
|
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
appName: string,
|
appName: string,
|
||||||
tokenAddress: string,
|
tokenAddress: string,
|
||||||
|
@ -34,11 +26,11 @@ export class WakuPolling extends WakuMessaging {
|
||||||
chainId: number,
|
chainId: number,
|
||||||
multicall: string
|
multicall: string
|
||||||
) {
|
) {
|
||||||
super(appName, tokenAddress, waku, provider, chainId)
|
super(appName, tokenAddress, waku, provider, chainId, multicall)
|
||||||
this.multicall = multicall
|
|
||||||
this.wakuMessages['pollInit'] = {
|
this.wakuMessages['pollInit'] = {
|
||||||
topic: `/${this.appName}/waku-polling/timed-polls-init/proto/`,
|
topic: `/${this.appName}/waku-polling/timed-polls-init/proto/`,
|
||||||
hashMap: {},
|
hashMap: {},
|
||||||
|
tokenCheckArray: ['owner'],
|
||||||
arr: [],
|
arr: [],
|
||||||
updateFunction: (msg: WakuMessage[]) =>
|
updateFunction: (msg: WakuMessage[]) =>
|
||||||
this.decodeMsgAndSetArray(
|
this.decodeMsgAndSetArray(
|
||||||
|
@ -51,6 +43,7 @@ export class WakuPolling extends WakuMessaging {
|
||||||
this.wakuMessages['pollVote'] = {
|
this.wakuMessages['pollVote'] = {
|
||||||
topic: `/${this.appName}/waku-polling/votes/proto/`,
|
topic: `/${this.appName}/waku-polling/votes/proto/`,
|
||||||
hashMap: {},
|
hashMap: {},
|
||||||
|
tokenCheckArray: ['voter'],
|
||||||
arr: [],
|
arr: [],
|
||||||
updateFunction: (msg: WakuMessage[]) =>
|
updateFunction: (msg: WakuMessage[]) =>
|
||||||
this.decodeMsgAndSetArray(msg, TimedPollVoteMsg.decode, this.wakuMessages['pollVote']),
|
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() {
|
public async getDetailedTimedPolls() {
|
||||||
await this.updateBalances()
|
await this.updateBalances()
|
||||||
return this.wakuMessages['pollInit'].arr
|
return this.wakuMessages['pollInit'].arr
|
||||||
|
|
|
@ -6,13 +6,13 @@ import { Provider } from '@ethersproject/abstract-provider'
|
||||||
import { createWaku } from '../utils/createWaku'
|
import { createWaku } from '../utils/createWaku'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import { VoteMsg } from '../models/VoteMsg'
|
import { VoteMsg } from '../models/VoteMsg'
|
||||||
|
import { VotingRoom } from '../types/PollType'
|
||||||
|
|
||||||
const ABI = [
|
const ABI = [
|
||||||
'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)',
|
'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)',
|
||||||
]
|
]
|
||||||
|
|
||||||
export class WakuVoting extends WakuMessaging {
|
export class WakuVoting extends WakuMessaging {
|
||||||
private multicall: Contract
|
|
||||||
private votingContract: Contract
|
private votingContract: Contract
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -24,13 +24,13 @@ export class WakuVoting extends WakuMessaging {
|
||||||
chainId: number,
|
chainId: number,
|
||||||
multicallAddress: string
|
multicallAddress: string
|
||||||
) {
|
) {
|
||||||
super(appName, token, waku, provider, chainId)
|
super(appName, token, waku, provider, chainId, multicallAddress)
|
||||||
this.votingContract = votingContract
|
this.votingContract = votingContract
|
||||||
this.multicall = new Contract(multicallAddress, ABI, this.provider)
|
|
||||||
this.wakuMessages['vote'] = {
|
this.wakuMessages['vote'] = {
|
||||||
topic: `/${this.appName}/waku-voting/votes/proto/`,
|
topic: `/${this.appName}/waku-voting/votes/proto/`,
|
||||||
hashMap: {},
|
hashMap: {},
|
||||||
arr: [],
|
arr: [],
|
||||||
|
tokenCheckArray: ['voter'],
|
||||||
updateFunction: (msg: WakuMessage[]) =>
|
updateFunction: (msg: WakuMessage[]) =>
|
||||||
this.decodeMsgAndSetArray(
|
this.decodeMsgAndSetArray(
|
||||||
msg,
|
msg,
|
||||||
|
@ -71,14 +71,26 @@ export class WakuVoting extends WakuMessaging {
|
||||||
await this.votingContract.initializeVotingRoom(question, descripiton, tokenAmount)
|
await this.votingContract.initializeVotingRoom(question, descripiton, tokenAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lastPolls: any[] = []
|
private lastPolls: VotingRoom[] = []
|
||||||
private lastGetPollsBlockNumber = 0
|
private lastGetPollsBlockNumber = 0
|
||||||
|
|
||||||
public async getVotes() {
|
public async getVotingRooms() {
|
||||||
const blockNumber = await this.provider.getBlockNumber()
|
const blockNumber = await this.provider.getBlockNumber()
|
||||||
if (blockNumber != this.lastGetPollsBlockNumber) {
|
if (blockNumber != this.lastGetPollsBlockNumber) {
|
||||||
this.lastGetPollsBlockNumber = blockNumber
|
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
|
return this.lastPolls
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,17 @@
|
||||||
|
import { BigNumber } from 'ethers'
|
||||||
|
|
||||||
export enum PollType {
|
export enum PollType {
|
||||||
WEIGHTED = 0,
|
WEIGHTED = 0,
|
||||||
NON_WEIGHTED = 1,
|
NON_WEIGHTED = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VotingRoom = {
|
||||||
|
startBlock: BigNumber
|
||||||
|
endAt: BigNumber
|
||||||
|
question: string
|
||||||
|
description: string
|
||||||
|
totalVotesFor: BigNumber
|
||||||
|
totalVotesAgainst: BigNumber
|
||||||
|
voters: string[]
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ export function Proposal({ wakuVoting }: ProposalProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
setVotes(await wakuVoting.getVotes())
|
setVotes(await wakuVoting.getVotingRooms())
|
||||||
}, 10000)
|
}, 10000)
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -3,6 +3,8 @@ import styled from 'styled-components'
|
||||||
import { Theme } from '@status-waku-voting/react-components'
|
import { Theme } from '@status-waku-voting/react-components'
|
||||||
import { ProposalCard } from './ProposalCard'
|
import { ProposalCard } from './ProposalCard'
|
||||||
import { WakuVoting } from '@status-waku-voting/core'
|
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 = {
|
type ProposalListProps = {
|
||||||
theme: Theme
|
theme: Theme
|
||||||
|
@ -12,8 +14,17 @@ type ProposalListProps = {
|
||||||
export function ProposalList({ theme, wakuVoting, votes }: ProposalListProps) {
|
export function ProposalList({ theme, wakuVoting, votes }: ProposalListProps) {
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
{votes.map((vote, idx) => {
|
{votes.map((vote) => {
|
||||||
return <ProposalCard heading={vote[2]} text={vote[3]} address={'#'} theme={theme} key={idx} id={idx} />
|
return (
|
||||||
|
<ProposalCard
|
||||||
|
heading={vote.question}
|
||||||
|
text={vote.description}
|
||||||
|
address={'#'}
|
||||||
|
theme={theme}
|
||||||
|
key={vote.id}
|
||||||
|
id={vote.id}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,7 +17,7 @@ export function ProposalMainMobile({ wakuVoting }: ProposalMainMobileProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
setVotes(await wakuVoting.getVotes())
|
setVotes(await wakuVoting.getVotingRooms())
|
||||||
}, 10000)
|
}, 10000)
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
Loading…
Reference in New Issue