Use waku relay and refactor waku polling (#45)
This commit is contained in:
parent
fbca38cf04
commit
dc37715bb8
|
@ -1,6 +1,6 @@
|
||||||
import { ethers } from 'ethers'
|
import { ethers } from 'ethers'
|
||||||
import { deployContract } from 'ethereum-waffle'
|
import { deployContract } from 'ethereum-waffle'
|
||||||
import { VotingContract, Directory } from '../abi'
|
import { VotingContract } from '../abi'
|
||||||
|
|
||||||
const deploy = async () => {
|
const deploy = async () => {
|
||||||
const providerName = process.env.ETHEREUM_PROVIDER
|
const providerName = process.env.ETHEREUM_PROVIDER
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
{
|
||||||
|
"contractName": "ERC20",
|
||||||
|
"abi": [
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Transfer",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Approval",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "totalSupply",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "spender",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "allowance",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transfer",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "approve",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transferFrom",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addedValue",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "increaseAllowance",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "subtractedValue",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "decreaseAllowance",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bytecode": "0x608060405234801561001057600080fd5b506105dd806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c01000000000000000000000000000000000000000000000000000000009004806370a082311161007857806370a0823114610166578063a457c2d71461018c578063a9059cbb146101b8578063dd62ed3e146101e4576100a5565b8063095ea7b3146100aa57806318160ddd146100ea57806323b872dd14610104578063395093511461013a575b600080fd5b6100d6600480360360408110156100c057600080fd5b50600160a060020a038135169060200135610212565b604080519115158252519081900360200190f35b6100f2610290565b60408051918252519081900360200190f35b6100d66004803603606081101561011a57600080fd5b50600160a060020a03813581169160208101359091169060400135610296565b6100d66004803603604081101561015057600080fd5b50600160a060020a03813516906020013561035f565b6100f26004803603602081101561017c57600080fd5b5035600160a060020a031661040f565b6100d6600480360360408110156101a257600080fd5b50600160a060020a03813516906020013561042a565b6100d6600480360360408110156101ce57600080fd5b50600160a060020a038135169060200135610475565b6100f2600480360360408110156101fa57600080fd5b50600160a060020a038135811691602001351661048b565b6000600160a060020a038316151561022957600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b60025490565b600160a060020a03831660009081526001602090815260408083203384529091528120546102ca908363ffffffff6104b616565b600160a060020a03851660009081526001602090815260408083203384529091529020556102f98484846104cb565b600160a060020a0384166000818152600160209081526040808320338085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b6000600160a060020a038316151561037657600080fd5b336000908152600160209081526040808320600160a060020a03871684529091529020546103aa908363ffffffff61059816565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b600160a060020a031660009081526020819052604090205490565b6000600160a060020a038316151561044157600080fd5b336000908152600160209081526040808320600160a060020a03871684529091529020546103aa908363ffffffff6104b616565b60006104823384846104cb565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b6000828211156104c557600080fd5b50900390565b600160a060020a03821615156104e057600080fd5b600160a060020a038316600090815260208190526040902054610509908263ffffffff6104b616565b600160a060020a03808516600090815260208190526040808220939093559084168152205461053e908263ffffffff61059816565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000828201838110156105aa57600080fd5b939250505056fea165627a7a72305820722c0187518ce2856a424bdba350d5a263c8f98fcb19cb4cc161372bc3b794c90029",
|
||||||
|
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a5576000357c01000000000000000000000000000000000000000000000000000000009004806370a082311161007857806370a0823114610166578063a457c2d71461018c578063a9059cbb146101b8578063dd62ed3e146101e4576100a5565b8063095ea7b3146100aa57806318160ddd146100ea57806323b872dd14610104578063395093511461013a575b600080fd5b6100d6600480360360408110156100c057600080fd5b50600160a060020a038135169060200135610212565b604080519115158252519081900360200190f35b6100f2610290565b60408051918252519081900360200190f35b6100d66004803603606081101561011a57600080fd5b50600160a060020a03813581169160208101359091169060400135610296565b6100d66004803603604081101561015057600080fd5b50600160a060020a03813516906020013561035f565b6100f26004803603602081101561017c57600080fd5b5035600160a060020a031661040f565b6100d6600480360360408110156101a257600080fd5b50600160a060020a03813516906020013561042a565b6100d6600480360360408110156101ce57600080fd5b50600160a060020a038135169060200135610475565b6100f2600480360360408110156101fa57600080fd5b50600160a060020a038135811691602001351661048b565b6000600160a060020a038316151561022957600080fd5b336000818152600160209081526040808320600160a060020a03881680855290835292819020869055805186815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a350600192915050565b60025490565b600160a060020a03831660009081526001602090815260408083203384529091528120546102ca908363ffffffff6104b616565b600160a060020a03851660009081526001602090815260408083203384529091529020556102f98484846104cb565b600160a060020a0384166000818152600160209081526040808320338085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b6000600160a060020a038316151561037657600080fd5b336000908152600160209081526040808320600160a060020a03871684529091529020546103aa908363ffffffff61059816565b336000818152600160209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b600160a060020a031660009081526020819052604090205490565b6000600160a060020a038316151561044157600080fd5b336000908152600160209081526040808320600160a060020a03871684529091529020546103aa908363ffffffff6104b616565b60006104823384846104cb565b50600192915050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b6000828211156104c557600080fd5b50900390565b600160a060020a03821615156104e057600080fd5b600160a060020a038316600090815260208190526040902054610509908263ffffffff6104b616565b600160a060020a03808516600090815260208190526040808220939093559084168152205461053e908263ffffffff61059816565b600160a060020a038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000828201838110156105aa57600080fd5b939250505056fea165627a7a72305820722c0187518ce2856a424bdba350d5a263c8f98fcb19cb4cc161372bc3b794c90029",
|
||||||
|
"compiler": {
|
||||||
|
"name": "solc",
|
||||||
|
"version": "0.5.4+commit.9549d8ff.Emscripten.clang"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import ERC20 from './ERC20.json'
|
||||||
|
|
||||||
|
export { ERC20 }
|
|
@ -3,63 +3,116 @@ import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import { PollInitMsg } from './models/PollInitMsg'
|
import { PollInitMsg } from './models/PollInitMsg'
|
||||||
import { PollType } from './types/PollType'
|
import { PollType } from './types/PollType'
|
||||||
import { BigNumber, Wallet } from 'ethers'
|
import { BigNumber, Wallet } from 'ethers'
|
||||||
import PollInit from './utils/proto/PollInit'
|
|
||||||
import { WakuMessage, StoreCodec } from 'js-waku'
|
import { WakuMessage, StoreCodec } from 'js-waku'
|
||||||
import { TimedPollVoteMsg } from './models/TimedPollVoteMsg'
|
import { TimedPollVoteMsg } from './models/TimedPollVoteMsg'
|
||||||
import TimedPollVote from './utils/proto/TimedPollVote'
|
|
||||||
import { DetailedTimedPoll } from './models/DetailedTimedPoll'
|
import { DetailedTimedPoll } from './models/DetailedTimedPoll'
|
||||||
import { isTruthy } from './utils'
|
import { isTruthy } from './utils'
|
||||||
|
import { createWaku } from './utils/createWaku'
|
||||||
|
|
||||||
function decodeWakuMessages<T>(
|
type WakuMessageStore = {
|
||||||
messages: WakuMessage[] | null | undefined,
|
topic: string
|
||||||
decode: (payload: Uint8Array | undefined, timestamp: Date | undefined) => T | undefined
|
hashMap: { [id: string]: boolean }
|
||||||
) {
|
arr: any[]
|
||||||
return messages?.map((msg) => decode(msg.payload, msg.timestamp)).filter(isTruthy) ?? []
|
updateFunction: (msg: WakuMessage[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
async function receiveNewWakuMessages(lastTimestamp: number, topic: string, waku: Waku | undefined) {
|
type WakuMessageStores = {
|
||||||
const messages = await waku?.store.queryHistory([topic])
|
[messageType: string]: WakuMessageStore
|
||||||
|
|
||||||
if (messages) {
|
|
||||||
messages.sort((a, b) => (a.timestamp && b.timestamp && a.timestamp?.getTime() < b.timestamp?.getTime() ? 1 : -1))
|
|
||||||
const lastMessageIndex = messages.findIndex((message) => message.timestamp?.getTime() === lastTimestamp)
|
|
||||||
const newMessages = lastMessageIndex === -1 ? messages : messages.slice(0, lastMessageIndex)
|
|
||||||
return newMessages
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class WakuVoting {
|
class WakuVoting {
|
||||||
private appName: string
|
protected appName: string
|
||||||
private waku: Waku | undefined
|
protected waku: Waku
|
||||||
public tokenAddress: string
|
public tokenAddress: string
|
||||||
private pollInitTopic: string
|
|
||||||
private timedPollVoteTopic: string
|
|
||||||
|
|
||||||
private timedPollInitMessages: PollInitMsg[] = []
|
protected wakuMessages: WakuMessageStores = {}
|
||||||
private timedPollVotesMessages: TimedPollVoteMsg[] = []
|
protected observers: { callback: (msg: WakuMessage) => void; topics: string[] }[] = []
|
||||||
private asyncUpdating = false
|
protected constructor(appName: string, tokenAddress: string, waku: Waku) {
|
||||||
|
|
||||||
private constructor(appName: string, tokenAddress: string, waku: Waku) {
|
|
||||||
this.appName = appName
|
this.appName = appName
|
||||||
this.tokenAddress = tokenAddress
|
this.tokenAddress = tokenAddress
|
||||||
this.pollInitTopic = `/${this.appName}/waku-polling/timed-polls-init/proto/`
|
|
||||||
this.timedPollVoteTopic = `/${this.appName}/waku-polling/votes/proto/`
|
|
||||||
this.waku = waku
|
this.waku = waku
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async create(appName: string, tokenAddress: string, waku?: Waku) {
|
public static async create(appName: string, tokenAddress: string, waku?: Waku) {
|
||||||
if (!waku) {
|
return new WakuVoting(appName, tokenAddress, await createWaku(waku))
|
||||||
waku = await Waku.create({ bootstrap: true })
|
}
|
||||||
await new Promise((resolve) => {
|
|
||||||
waku?.libp2p.peerStore.on('change:protocols', ({ protocols }) => {
|
public cleanUp() {
|
||||||
if (protocols.includes(StoreCodec)) {
|
this.observers.forEach((observer) => this.waku.relay.deleteObserver(observer.callback, observer.topics))
|
||||||
resolve('')
|
}
|
||||||
|
|
||||||
|
protected async setObserver(msgObj: WakuMessageStore) {
|
||||||
|
const storeMessages = await this.waku?.store.queryHistory([msgObj.topic])
|
||||||
|
if (storeMessages) {
|
||||||
|
msgObj.updateFunction(storeMessages)
|
||||||
|
}
|
||||||
|
this.waku.relay.addObserver((msg) => msgObj.updateFunction([msg]), [msgObj.topic])
|
||||||
|
this.observers.push({ callback: (msg) => msgObj.updateFunction([msg]), topics: [msgObj.topic] })
|
||||||
|
}
|
||||||
|
|
||||||
|
protected decodeMsgAndSetArray<T extends { id: string; timestamp: number }>(
|
||||||
|
messages: WakuMessage[],
|
||||||
|
decode: (payload: Uint8Array | undefined, timestamp: Date | undefined) => T | undefined,
|
||||||
|
msgObj: WakuMessageStore,
|
||||||
|
filterFunction?: (e: T) => boolean
|
||||||
|
) {
|
||||||
|
messages
|
||||||
|
.map((msg) => decode(msg.payload, msg.timestamp))
|
||||||
|
.sort((a, b) => ((a?.timestamp ?? new Date(0)) > (b?.timestamp ?? new Date(0)) ? 1 : -1))
|
||||||
|
.forEach((e) => {
|
||||||
|
if (e) {
|
||||||
|
if (filterFunction ? filterFunction(e) : true && !msgObj.hashMap?.[e.id]) {
|
||||||
|
msgObj.arr.unshift(e)
|
||||||
|
msgObj.hashMap[e.id] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return new WakuVoting(appName, tokenAddress, waku)
|
|
||||||
|
protected async sendWakuMessage<T extends { encode: () => Uint8Array | undefined; timestamp: number }>(
|
||||||
|
msgObj: WakuMessageStore,
|
||||||
|
decodedMsg: T | undefined
|
||||||
|
) {
|
||||||
|
const payload = decodedMsg?.encode()
|
||||||
|
if (payload && decodedMsg) {
|
||||||
|
const wakuMessage = await WakuMessage.fromBytes(payload, msgObj.topic, {
|
||||||
|
timestamp: new Date(decodedMsg.timestamp),
|
||||||
|
})
|
||||||
|
await this.waku?.relay.send(wakuMessage)
|
||||||
|
msgObj.updateFunction([wakuMessage])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WakuPolling extends WakuVoting {
|
||||||
|
protected constructor(appName: string, tokenAddress: string, waku: Waku) {
|
||||||
|
super(appName, tokenAddress, waku)
|
||||||
|
this.wakuMessages['pollInit'] = {
|
||||||
|
topic: `/${this.appName}/waku-polling/timed-polls-init/proto/`,
|
||||||
|
hashMap: {},
|
||||||
|
arr: [],
|
||||||
|
updateFunction: (msg: WakuMessage[]) =>
|
||||||
|
this.decodeMsgAndSetArray(
|
||||||
|
msg,
|
||||||
|
PollInitMsg.decode,
|
||||||
|
this.wakuMessages['pollInit'],
|
||||||
|
(e) => e.endTime > Date.now()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
this.wakuMessages['pollVote'] = {
|
||||||
|
topic: `/${this.appName}/waku-polling/votes/proto/`,
|
||||||
|
hashMap: {},
|
||||||
|
arr: [],
|
||||||
|
updateFunction: (msg: WakuMessage[]) =>
|
||||||
|
this.decodeMsgAndSetArray(msg, TimedPollVoteMsg.decode, this.wakuMessages['pollVote']),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async create(appName: string, tokenAddress: string, waku?: Waku) {
|
||||||
|
const wakuPolling = new WakuPolling(appName, tokenAddress, await createWaku(waku))
|
||||||
|
wakuPolling.setObserver(wakuPolling.wakuMessages['pollInit'])
|
||||||
|
wakuPolling.setObserver(wakuPolling.wakuMessages['pollVote'])
|
||||||
|
return wakuPolling
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createTimedPoll(
|
public async createTimedPoll(
|
||||||
|
@ -71,84 +124,28 @@ class WakuVoting {
|
||||||
endTime?: number
|
endTime?: number
|
||||||
) {
|
) {
|
||||||
const pollInit = await PollInitMsg.create(signer, question, answers, pollType, minToken, endTime)
|
const pollInit = await PollInitMsg.create(signer, question, answers, pollType, minToken, endTime)
|
||||||
if (pollInit) {
|
await this.sendWakuMessage(this.wakuMessages['pollInit'], pollInit)
|
||||||
const payload = PollInit.encode(pollInit)
|
|
||||||
if (payload) {
|
|
||||||
const wakuMessage = await WakuMessage.fromBytes(payload, this.pollInitTopic, {
|
|
||||||
timestamp: new Date(pollInit.timestamp),
|
|
||||||
})
|
|
||||||
await this.waku?.relay.send(wakuMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getTimedPolls() {
|
|
||||||
const lastTimestamp = this.timedPollInitMessages?.[0]?.timestamp ?? 0
|
|
||||||
let updated = false
|
|
||||||
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.pollInitTopic, this.waku)
|
|
||||||
const newPollInitMessages = decodeWakuMessages(newMessages, PollInit.decode)
|
|
||||||
if (newPollInitMessages.length > 0) {
|
|
||||||
updated = true
|
|
||||||
this.timedPollInitMessages = [...newPollInitMessages, ...this.timedPollInitMessages]
|
|
||||||
}
|
|
||||||
const arrayLen = this.timedPollInitMessages.length
|
|
||||||
this.timedPollInitMessages = this.timedPollInitMessages.filter((e) => e.endTime > Date.now())
|
|
||||||
if (arrayLen != this.timedPollInitMessages.length) {
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
return { polls: this.timedPollInitMessages, updatedPolls: updated }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendTimedPollVote(
|
public async sendTimedPollVote(
|
||||||
signer: JsonRpcSigner | Wallet,
|
signer: JsonRpcSigner | Wallet,
|
||||||
id: string,
|
pollId: string,
|
||||||
selectedAnswer: number,
|
selectedAnswer: number,
|
||||||
tokenAmount?: BigNumber
|
tokenAmount?: BigNumber
|
||||||
) {
|
) {
|
||||||
const pollVote = await TimedPollVoteMsg.create(signer, id, selectedAnswer, tokenAmount)
|
const pollVote = await TimedPollVoteMsg.create(signer, pollId, selectedAnswer, tokenAmount)
|
||||||
if (pollVote) {
|
await this.sendWakuMessage(this.wakuMessages['pollVote'], pollVote)
|
||||||
const payload = TimedPollVote.encode(pollVote)
|
|
||||||
if (payload) {
|
|
||||||
const wakuMessage = await WakuMessage.fromBytes(payload, this.timedPollVoteTopic, {
|
|
||||||
timestamp: new Date(pollVote.timestamp),
|
|
||||||
})
|
|
||||||
await this.waku?.relay.send(wakuMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getTimedPollsVotes() {
|
|
||||||
const lastTimestamp = this.timedPollVotesMessages?.[0]?.timestamp ?? 0
|
|
||||||
let updated = false
|
|
||||||
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.timedPollVoteTopic, this.waku)
|
|
||||||
const newVoteMessages = decodeWakuMessages(newMessages, TimedPollVote.decode)
|
|
||||||
if (newVoteMessages.length > 0) {
|
|
||||||
updated = true
|
|
||||||
this.timedPollVotesMessages = [...newVoteMessages, ...this.timedPollVotesMessages]
|
|
||||||
}
|
|
||||||
return { votes: this.timedPollVotesMessages, updatedVotes: updated }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getDetailedTimedPolls() {
|
public async getDetailedTimedPolls() {
|
||||||
let updated = false
|
return this.wakuMessages['pollInit'].arr.map(
|
||||||
if (!this.asyncUpdating) {
|
|
||||||
this.asyncUpdating = true
|
|
||||||
const { updatedPolls } = await this.getTimedPolls()
|
|
||||||
const { updatedVotes } = await this.getTimedPollsVotes()
|
|
||||||
updated = updatedPolls || updatedVotes
|
|
||||||
this.asyncUpdating = false
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
DetailedTimedPolls: this.timedPollInitMessages.map(
|
|
||||||
(poll) =>
|
(poll) =>
|
||||||
new DetailedTimedPoll(
|
new DetailedTimedPoll(
|
||||||
poll,
|
poll,
|
||||||
this.timedPollVotesMessages.filter((vote) => vote.id === poll.id)
|
this.wakuMessages['pollVote'].arr.filter((vote) => vote.pollId === poll.id)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
|
||||||
updated,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WakuVoting
|
export { WakuVoting, WakuPolling }
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
import { PollType } from '../types/PollType'
|
import { PollType } from '../types/PollType'
|
||||||
import { BigNumber, utils, Wallet } from 'ethers'
|
import { BigNumber, utils, Wallet } from 'ethers'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import { PollInit } from 'protons'
|
import protons, { PollInit } from 'protons'
|
||||||
import { createSignedMsg } from '../utils/createSignedMsg'
|
import { createSignedMsg } from '../utils/createSignedMsg'
|
||||||
|
import { recoverTypedSignature_v4 } from 'eth-sig-util'
|
||||||
|
import { verifySignature } from '../utils/verifySignature'
|
||||||
|
|
||||||
|
const proto = protons(`
|
||||||
|
message PollInit {
|
||||||
|
bytes owner = 1;
|
||||||
|
int64 timestamp = 2;
|
||||||
|
string question = 3;
|
||||||
|
repeated string answers = 4;
|
||||||
|
enum PollType {
|
||||||
|
WEIGHTED = 0;
|
||||||
|
NON_WEIGHTED = 1;
|
||||||
|
}
|
||||||
|
PollType pollType = 5;
|
||||||
|
optional bytes minToken = 6;
|
||||||
|
int64 endTime = 7;
|
||||||
|
bytes signature = 8;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
type Message = {
|
type Message = {
|
||||||
owner: string
|
owner: string
|
||||||
|
@ -114,24 +133,63 @@ export class PollInitMsg {
|
||||||
return this._createWithSignFunction(createSignedMsg(signer), signer, question, answers, pollType, minToken, endTime)
|
return this._createWithSignFunction(createSignedMsg(signer), signer, question, answers, pollType, minToken, endTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromProto(payload: PollInit, recoverFunction: ({ data, sig }: { data: any; sig: string }) => string) {
|
encode() {
|
||||||
const signature = utils.hexlify(payload.signature)
|
try {
|
||||||
|
const arrayify = utils.arrayify
|
||||||
const msg = {
|
const pollProto: PollInit = {
|
||||||
...payload,
|
owner: arrayify(this.owner),
|
||||||
owner: utils.getAddress(utils.hexlify(payload.owner)),
|
timestamp: this.timestamp,
|
||||||
minToken: payload.minToken ? BigNumber.from(payload.minToken) : undefined,
|
question: this.question,
|
||||||
|
answers: this.answers,
|
||||||
|
pollType: this.pollType,
|
||||||
|
endTime: this.endTime,
|
||||||
|
signature: arrayify(this.signature),
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = createSignMsgParams(msg)
|
if (this.pollType === PollType.NON_WEIGHTED) {
|
||||||
const verifiedAddress = recoverFunction({
|
if (this.minToken) {
|
||||||
data: params,
|
pollProto.minToken = arrayify(this.minToken)
|
||||||
sig: signature,
|
} else {
|
||||||
})
|
return undefined
|
||||||
if (verifiedAddress != msg.owner) {
|
}
|
||||||
|
}
|
||||||
|
return proto.PollInit.encode(pollProto)
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static decode(
|
||||||
|
rawPayload: Uint8Array | undefined,
|
||||||
|
timestamp: Date | undefined,
|
||||||
|
verifyFunction?: (params: any, address: string) => boolean
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const payload = proto.PollInit.decode(rawPayload)
|
||||||
|
if (!timestamp || timestamp.getTime() != payload.timestamp) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const msg: Message = {
|
||||||
|
timestamp: payload.timestamp,
|
||||||
|
question: payload.question,
|
||||||
|
answers: payload.answers,
|
||||||
|
pollType: payload.pollType,
|
||||||
|
endTime: payload.endTime,
|
||||||
|
owner: utils.getAddress(utils.hexlify(payload.owner)),
|
||||||
|
minToken: payload.minToken ? BigNumber.from(payload.minToken) : undefined,
|
||||||
|
}
|
||||||
|
const signature = utils.hexlify(payload.signature)
|
||||||
|
const params = {
|
||||||
|
data: createSignMsgParams(msg),
|
||||||
|
sig: signature,
|
||||||
|
}
|
||||||
|
if (verifyFunction ? !verifyFunction : !verifySignature(params, msg.owner)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
return new PollInitMsg(signature, msg)
|
return new PollInitMsg(signature, msg)
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
import { BigNumber, utils } from 'ethers'
|
import { BigNumber, utils } from 'ethers'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import { TimedPollVote } from 'protons'
|
import protons, { TimedPollVote } from 'protons'
|
||||||
import { Wallet } from 'ethers'
|
import { Wallet } from 'ethers'
|
||||||
import { createSignedMsg } from '../utils/createSignedMsg'
|
import { createSignedMsg } from '../utils/createSignedMsg'
|
||||||
|
import { recoverTypedSignature_v4 } from 'eth-sig-util'
|
||||||
|
import { verifySignature } from '../utils/verifySignature'
|
||||||
|
|
||||||
|
const proto = protons(`
|
||||||
|
message TimedPollVote {
|
||||||
|
bytes pollId = 1; // id of a poll
|
||||||
|
bytes voter = 2; // Address of a voter
|
||||||
|
int64 timestamp = 3; // Timestamp of a waku message
|
||||||
|
int64 answer = 4; // specified poll answer
|
||||||
|
optional bytes tokenAmount = 5; // amount of token used for WEIGHTED voting
|
||||||
|
bytes signature = 6; // signature of all above fields
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
type Message = {
|
type Message = {
|
||||||
id: string
|
pollId: string
|
||||||
voter: string
|
voter: string
|
||||||
timestamp: number
|
timestamp: number
|
||||||
answer: number
|
answer: number
|
||||||
|
@ -29,7 +42,7 @@ export function createSignMsgParams(message: Message) {
|
||||||
{ name: 'version', type: 'string' },
|
{ name: 'version', type: 'string' },
|
||||||
],
|
],
|
||||||
Mail: [
|
Mail: [
|
||||||
{ name: 'id', type: 'string' },
|
{ name: 'pollId', type: 'string' },
|
||||||
{ name: 'voter', type: 'string' },
|
{ name: 'voter', type: 'string' },
|
||||||
{ name: 'timestamp', type: 'string' },
|
{ name: 'timestamp', type: 'string' },
|
||||||
{ name: 'answer', type: 'string' },
|
{ name: 'answer', type: 'string' },
|
||||||
|
@ -45,20 +58,21 @@ export function createSignMsgParams(message: Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TimedPollVoteMsg {
|
export class TimedPollVoteMsg {
|
||||||
public id: string
|
public pollId: string
|
||||||
public voter: string
|
public voter: string
|
||||||
public timestamp: number
|
public timestamp: number
|
||||||
public answer: number
|
public answer: number
|
||||||
public tokenAmount?: BigNumber
|
public tokenAmount?: BigNumber
|
||||||
public signature: string
|
public signature: string
|
||||||
|
public id: string
|
||||||
|
|
||||||
constructor(signature: string, msg: Message) {
|
constructor(signature: string, msg: Message) {
|
||||||
this.id = msg.id
|
this.id = utils.id([msg.voter, msg.timestamp, signature].join())
|
||||||
|
this.pollId = msg.pollId
|
||||||
this.voter = msg.voter
|
this.voter = msg.voter
|
||||||
this.timestamp = msg.timestamp
|
this.timestamp = msg.timestamp
|
||||||
this.answer = msg.answer
|
this.answer = msg.answer
|
||||||
this.tokenAmount = msg.tokenAmount
|
this.tokenAmount = msg.tokenAmount
|
||||||
|
|
||||||
this.signature = signature
|
this.signature = signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,45 +83,70 @@ export class TimedPollVoteMsg {
|
||||||
Class: new (sig: string, msg: any) => TimedPollVoteMsg
|
Class: new (sig: string, msg: any) => TimedPollVoteMsg
|
||||||
) => Promise<TimedPollVoteMsg | undefined>,
|
) => Promise<TimedPollVoteMsg | undefined>,
|
||||||
signer: JsonRpcSigner | Wallet,
|
signer: JsonRpcSigner | Wallet,
|
||||||
id: string,
|
pollId: string,
|
||||||
answer: number,
|
answer: number,
|
||||||
tokenAmount?: BigNumber
|
tokenAmount?: BigNumber
|
||||||
): Promise<TimedPollVoteMsg | undefined> {
|
): Promise<TimedPollVoteMsg | undefined> {
|
||||||
const voter = await signer.getAddress()
|
const voter = await signer.getAddress()
|
||||||
const msg = { id, voter, timestamp: Date.now(), answer, tokenAmount }
|
const msg = { pollId, voter, timestamp: Date.now(), answer, tokenAmount }
|
||||||
const params = [msg.voter, JSON.stringify(createSignMsgParams(msg))]
|
const params = [msg.voter, JSON.stringify(createSignMsgParams(msg))]
|
||||||
|
|
||||||
return signFunction(msg, params, TimedPollVoteMsg)
|
return signFunction(msg, params, TimedPollVoteMsg)
|
||||||
}
|
}
|
||||||
static async create(
|
static async create(
|
||||||
signer: JsonRpcSigner | Wallet,
|
signer: JsonRpcSigner | Wallet,
|
||||||
id: string,
|
pollId: string,
|
||||||
answer: number,
|
answer: number,
|
||||||
tokenAmount?: BigNumber
|
tokenAmount?: BigNumber
|
||||||
): Promise<TimedPollVoteMsg | undefined> {
|
): Promise<TimedPollVoteMsg | undefined> {
|
||||||
return this._createWithSignFunction(createSignedMsg(signer), signer, id, answer, tokenAmount)
|
return this._createWithSignFunction(createSignedMsg(signer), signer, pollId, answer, tokenAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromProto(payload: TimedPollVote, recoverFunction: ({ data, sig }: { data: any; sig: string }) => string) {
|
encode() {
|
||||||
|
try {
|
||||||
|
const voteProto: TimedPollVote = {
|
||||||
|
pollId: utils.arrayify(this.pollId),
|
||||||
|
voter: utils.arrayify(this.voter),
|
||||||
|
timestamp: this.timestamp,
|
||||||
|
answer: this.answer,
|
||||||
|
tokenAmount: this.tokenAmount ? utils.arrayify(this.tokenAmount) : undefined,
|
||||||
|
signature: utils.arrayify(this.signature),
|
||||||
|
}
|
||||||
|
return proto.TimedPollVote.encode(voteProto)
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static decode(
|
||||||
|
rawPayload: Uint8Array | undefined,
|
||||||
|
timestamp: Date | undefined,
|
||||||
|
verifyFunction?: (params: any, address: string) => boolean
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const payload = proto.TimedPollVote.decode(rawPayload)
|
||||||
|
if (!timestamp || !payload.timestamp || timestamp?.getTime() != payload.timestamp) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
const signature = utils.hexlify(payload.signature)
|
const signature = utils.hexlify(payload.signature)
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
id: utils.hexlify(payload.id),
|
pollId: utils.hexlify(payload.pollId),
|
||||||
answer: payload.answer,
|
answer: payload.answer,
|
||||||
voter: utils.getAddress(utils.hexlify(payload.voter)),
|
voter: utils.getAddress(utils.hexlify(payload.voter)),
|
||||||
timestamp: payload.timestamp,
|
timestamp: payload.timestamp,
|
||||||
tokenAmount: payload.tokenAmount ? BigNumber.from(payload.tokenAmount) : undefined,
|
tokenAmount: payload.tokenAmount ? BigNumber.from(payload.tokenAmount) : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = createSignMsgParams(msg)
|
const params = {
|
||||||
const verifiedAddress = recoverFunction({
|
data: createSignMsgParams(msg),
|
||||||
data: params,
|
|
||||||
sig: signature,
|
sig: signature,
|
||||||
})
|
}
|
||||||
if (verifiedAddress != msg.voter) {
|
if (verifyFunction ? !verifyFunction : !verifySignature(params, msg.voter)) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TimedPollVoteMsg(signature, msg)
|
return new TimedPollVoteMsg(signature, msg)
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { StoreCodec, Waku } from 'js-waku'
|
||||||
|
|
||||||
|
export async function createWaku(waku?: Waku) {
|
||||||
|
if (!waku) {
|
||||||
|
waku = await Waku.create({ bootstrap: true })
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
waku?.libp2p.peerStore.on('change:protocols', ({ protocols }) => {
|
||||||
|
if (protocols.includes(StoreCodec)) {
|
||||||
|
resolve('')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return waku
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
import protons, { PollInit } from 'protons'
|
|
||||||
import { PollType } from '../../types/PollType'
|
|
||||||
import { utils } from 'ethers'
|
|
||||||
import { PollInitMsg } from '../../models/PollInitMsg'
|
|
||||||
import { recoverTypedSignature_v4 } from 'eth-sig-util'
|
|
||||||
|
|
||||||
const proto = protons(`
|
|
||||||
message PollInit {
|
|
||||||
bytes owner = 1;
|
|
||||||
int64 timestamp = 2;
|
|
||||||
string question = 3;
|
|
||||||
repeated string answers = 4;
|
|
||||||
enum PollType {
|
|
||||||
WEIGHTED = 0;
|
|
||||||
NON_WEIGHTED = 1;
|
|
||||||
}
|
|
||||||
PollType pollType = 5;
|
|
||||||
optional bytes minToken = 6;
|
|
||||||
int64 endTime = 7;
|
|
||||||
bytes signature = 8;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
export function encode(pollInit: PollInitMsg) {
|
|
||||||
try {
|
|
||||||
const arrayify = utils.arrayify
|
|
||||||
const pollProto: PollInit = {
|
|
||||||
owner: arrayify(pollInit.owner),
|
|
||||||
timestamp: pollInit.timestamp,
|
|
||||||
question: pollInit.question,
|
|
||||||
answers: pollInit.answers,
|
|
||||||
pollType: pollInit.pollType,
|
|
||||||
endTime: pollInit.endTime,
|
|
||||||
signature: arrayify(pollInit.signature),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pollInit.pollType === PollType.NON_WEIGHTED) {
|
|
||||||
if (pollInit.minToken) {
|
|
||||||
pollProto.minToken = arrayify(pollInit.minToken)
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return proto.PollInit.encode(pollProto)
|
|
||||||
} catch {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decode(
|
|
||||||
payload: Uint8Array | undefined,
|
|
||||||
timestamp: Date | undefined,
|
|
||||||
recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string
|
|
||||||
) {
|
|
||||||
if (!payload) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const msg = proto.PollInit.decode(payload)
|
|
||||||
if (!timestamp || timestamp.getTime() != msg.timestamp) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
msg.owner &&
|
|
||||||
msg.timestamp &&
|
|
||||||
msg.question &&
|
|
||||||
msg.answers &&
|
|
||||||
msg.pollType != undefined &&
|
|
||||||
msg.endTime &&
|
|
||||||
msg.signature
|
|
||||||
) {
|
|
||||||
if (recoverFunction) {
|
|
||||||
return PollInitMsg.fromProto(msg, recoverFunction)
|
|
||||||
}
|
|
||||||
return PollInitMsg.fromProto(msg, (e) => utils.getAddress(recoverTypedSignature_v4(e)))
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export default { encode, decode }
|
|
|
@ -1,61 +0,0 @@
|
||||||
import protons, { TimedPollVote } from 'protons'
|
|
||||||
import { utils } from 'ethers'
|
|
||||||
import { TimedPollVoteMsg } from '../../models/TimedPollVoteMsg'
|
|
||||||
import { recoverTypedSignature_v4 } from 'eth-sig-util'
|
|
||||||
|
|
||||||
const proto = protons(`
|
|
||||||
message TimedPollVote {
|
|
||||||
bytes id = 1; // id of a poll
|
|
||||||
bytes voter = 2; // Address of a voter
|
|
||||||
int64 timestamp = 3; // Timestamp of a waku message
|
|
||||||
int64 answer = 4; // specified poll answer
|
|
||||||
optional bytes tokenAmount = 5; // amount of token used for WEIGHTED voting
|
|
||||||
bytes signature = 6; // signature of all above fields
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
export function encode(timedPollVote: TimedPollVoteMsg) {
|
|
||||||
try {
|
|
||||||
const arrayify = utils.arrayify
|
|
||||||
const voteProto: TimedPollVote = {
|
|
||||||
id: arrayify(timedPollVote.id),
|
|
||||||
voter: arrayify(timedPollVote.voter),
|
|
||||||
timestamp: timedPollVote.timestamp,
|
|
||||||
answer: timedPollVote.answer,
|
|
||||||
tokenAmount: timedPollVote.tokenAmount ? arrayify(timedPollVote.tokenAmount) : undefined,
|
|
||||||
signature: arrayify(timedPollVote.signature),
|
|
||||||
}
|
|
||||||
|
|
||||||
return proto.TimedPollVote.encode(voteProto)
|
|
||||||
} catch {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decode(
|
|
||||||
payload: Uint8Array | undefined,
|
|
||||||
timestamp: Date | undefined,
|
|
||||||
recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string
|
|
||||||
) {
|
|
||||||
if (!payload) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const msg = proto.TimedPollVote.decode(payload)
|
|
||||||
if (!timestamp || timestamp.getTime() != msg.timestamp) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (msg.id && msg.voter && msg.timestamp && msg.answer != undefined && msg.signature) {
|
|
||||||
if (recoverFunction) {
|
|
||||||
return TimedPollVoteMsg.fromProto(msg, recoverFunction)
|
|
||||||
}
|
|
||||||
return TimedPollVoteMsg.fromProto(msg, (e) => utils.getAddress(recoverTypedSignature_v4(e)))
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export default { encode, decode }
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { recoverTypedSignature_v4 } from 'eth-sig-util'
|
||||||
|
import { utils } from 'ethers'
|
||||||
|
|
||||||
|
export function verifySignature(params: any, address: string) {
|
||||||
|
try {
|
||||||
|
const verifiedAddress = utils.getAddress(recoverTypedSignature_v4(params))
|
||||||
|
if (!verifiedAddress || verifiedAddress != address) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { Waku } from 'js-waku'
|
import { Waku } from 'js-waku'
|
||||||
import WakuVoting from '../src'
|
import { WakuVoting } from '../src'
|
||||||
|
|
||||||
describe('WakuVoting', () => {
|
describe('WakuVoting', () => {
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { BigNumber } from 'ethers'
|
||||||
describe('PollInitMsg', () => {
|
describe('PollInitMsg', () => {
|
||||||
const provider = new MockProvider()
|
const provider = new MockProvider()
|
||||||
const [alice] = provider.getWallets()
|
const [alice] = provider.getWallets()
|
||||||
|
describe('create', () => {
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
const poll = await PollInitMsg._createWithSignFunction(
|
const poll = await PollInitMsg._createWithSignFunction(
|
||||||
async (e) => new PollInitMsg('0x01', e),
|
async (e) => new PollInitMsg('0x01', e),
|
||||||
|
@ -76,3 +76,68 @@ describe('PollInitMsg', () => {
|
||||||
expect(poll?.endTime).to.eq(100)
|
expect(poll?.endTime).to.eq(100)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('decode/encode', () => {
|
||||||
|
it('success', async () => {
|
||||||
|
const data = await PollInitMsg._createWithSignFunction(
|
||||||
|
async (e) => new PollInitMsg('0x01', e),
|
||||||
|
alice,
|
||||||
|
'whats up',
|
||||||
|
['ab', 'cd', 'ef'],
|
||||||
|
PollType.WEIGHTED
|
||||||
|
)
|
||||||
|
expect(data).to.not.be.undefined
|
||||||
|
if (data) {
|
||||||
|
const payload = data.encode()
|
||||||
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(PollInitMsg.decode(payload, new Date(data.timestamp), () => true)).to.deep.eq(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('random decode', async () => {
|
||||||
|
expect(PollInitMsg.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('NON_WEIGHTED init', async () => {
|
||||||
|
const data = await PollInitMsg._createWithSignFunction(
|
||||||
|
async (e) => new PollInitMsg('0x01', e),
|
||||||
|
alice,
|
||||||
|
'whats up',
|
||||||
|
['ab', 'cd', 'ef'],
|
||||||
|
PollType.NON_WEIGHTED,
|
||||||
|
BigNumber.from(10)
|
||||||
|
)
|
||||||
|
expect(data).to.not.be.undefined
|
||||||
|
if (data) {
|
||||||
|
const payload = data.encode()
|
||||||
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(PollInitMsg.decode(payload, new Date(data.timestamp), () => true)).to.deep.eq(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('NON_WEIGHTED no min token', async () => {
|
||||||
|
const data = await PollInitMsg._createWithSignFunction(
|
||||||
|
async (e) => new PollInitMsg('0x01', e),
|
||||||
|
alice,
|
||||||
|
'whats up',
|
||||||
|
['ab', 'cd', 'ef'],
|
||||||
|
PollType.NON_WEIGHTED
|
||||||
|
)
|
||||||
|
expect(data).to.not.be.undefined
|
||||||
|
if (data) {
|
||||||
|
const payload = data.encode()
|
||||||
|
|
||||||
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(PollInitMsg.decode(payload, new Date(data.timestamp), () => true)).to.deep.eq({
|
||||||
|
...data,
|
||||||
|
minToken: BigNumber.from(1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -7,26 +7,28 @@ describe('TimedPollVoteMsg', () => {
|
||||||
const provider = new MockProvider()
|
const provider = new MockProvider()
|
||||||
const [alice] = provider.getWallets()
|
const [alice] = provider.getWallets()
|
||||||
const pollId = '0x14c336ef626274f156d094fc1d7ffad2bbc83cccc9817598dd55e42a86b56b72'
|
const pollId = '0x14c336ef626274f156d094fc1d7ffad2bbc83cccc9817598dd55e42a86b56b72'
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
const poll = await TimedPollVoteMsg._createWithSignFunction(
|
const vote = await TimedPollVoteMsg._createWithSignFunction(
|
||||||
async (e) => new TimedPollVoteMsg('0x01', e),
|
async (e) => new TimedPollVoteMsg('0x01', e),
|
||||||
alice,
|
alice,
|
||||||
pollId,
|
pollId,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
console.log(alice.address)
|
||||||
|
|
||||||
expect(poll).to.not.be.undefined
|
if (vote) {
|
||||||
if (poll) {
|
expect(vote.voter).to.eq(alice.address)
|
||||||
expect(poll.voter).to.eq(alice.address)
|
expect(vote.answer).to.eq(0)
|
||||||
expect(poll.answer).to.eq(0)
|
expect(vote.pollId).to.be.eq(pollId)
|
||||||
expect(poll.id).to.be.eq(pollId)
|
expect(vote.tokenAmount).to.be.undefined
|
||||||
expect(poll.tokenAmount).to.be.undefined
|
expect(vote.signature).to.eq('0x01')
|
||||||
expect(poll.signature).to.eq('0x01')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('success token amount', async () => {
|
it('success token amount', async () => {
|
||||||
const poll = await TimedPollVoteMsg._createWithSignFunction(
|
const vote = await TimedPollVoteMsg._createWithSignFunction(
|
||||||
async (e) => new TimedPollVoteMsg('0x01', e),
|
async (e) => new TimedPollVoteMsg('0x01', e),
|
||||||
alice,
|
alice,
|
||||||
pollId,
|
pollId,
|
||||||
|
@ -34,13 +36,62 @@ describe('TimedPollVoteMsg', () => {
|
||||||
BigNumber.from(100)
|
BigNumber.from(100)
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(poll).to.not.be.undefined
|
expect(vote).to.not.be.undefined
|
||||||
if (poll) {
|
if (vote) {
|
||||||
expect(poll.voter).to.eq(alice.address)
|
expect(vote.voter).to.eq(alice.address)
|
||||||
expect(poll.answer).to.eq(1)
|
expect(vote.answer).to.eq(1)
|
||||||
expect(poll.id).to.be.eq(pollId)
|
expect(vote.pollId).to.be.eq(pollId)
|
||||||
expect(poll.tokenAmount).to.deep.eq(BigNumber.from(100))
|
expect(vote.tokenAmount).to.deep.eq(BigNumber.from(100))
|
||||||
expect(poll.signature).to.eq('0x01')
|
expect(vote.signature).to.eq('0x01')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('decode/encode', () => {
|
||||||
|
it('success', async () => {
|
||||||
|
const data = await TimedPollVoteMsg._createWithSignFunction(
|
||||||
|
async (e) => new TimedPollVoteMsg('0x01', e),
|
||||||
|
alice,
|
||||||
|
pollId,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(data).to.not.be.undefined
|
||||||
|
if (data) {
|
||||||
|
const payload = await data.encode()
|
||||||
|
|
||||||
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(await TimedPollVoteMsg.decode(payload, new Date(data.timestamp), () => true)).to.deep.eq(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('random decode', async () => {
|
||||||
|
expect(TimedPollVoteMsg.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be
|
||||||
|
.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('data with token', async () => {
|
||||||
|
const data = await TimedPollVoteMsg._createWithSignFunction(
|
||||||
|
async (e) => new TimedPollVoteMsg('0x01', e),
|
||||||
|
alice,
|
||||||
|
pollId,
|
||||||
|
0,
|
||||||
|
BigNumber.from(120)
|
||||||
|
)
|
||||||
|
expect(data).to.not.be.undefined
|
||||||
|
if (data) {
|
||||||
|
const payload = data.encode()
|
||||||
|
|
||||||
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(TimedPollVoteMsg.decode(payload, new Date(data.timestamp), () => true)).to.deep.eq({
|
||||||
|
...data,
|
||||||
|
tokenAmount: BigNumber.from(120),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
import { expect } from 'chai'
|
|
||||||
import { PollType } from '../../../src/types/PollType'
|
|
||||||
import PollInit from '../../../src/utils/proto/PollInit'
|
|
||||||
import { BigNumber } from 'ethers'
|
|
||||||
import { PollInitMsg } from '../../../src/models/PollInitMsg'
|
|
||||||
import { MockProvider } from 'ethereum-waffle'
|
|
||||||
|
|
||||||
describe('PollInit', () => {
|
|
||||||
const provider = new MockProvider()
|
|
||||||
const [alice] = provider.getWallets()
|
|
||||||
it('success', async () => {
|
|
||||||
const data = await PollInitMsg._createWithSignFunction(
|
|
||||||
async (e) => new PollInitMsg('0x01', e),
|
|
||||||
alice,
|
|
||||||
'whats up',
|
|
||||||
['ab', 'cd', 'ef'],
|
|
||||||
PollType.WEIGHTED
|
|
||||||
)
|
|
||||||
expect(data).to.not.be.undefined
|
|
||||||
if (data) {
|
|
||||||
const payload = PollInit.encode(data)
|
|
||||||
expect(payload).to.not.be.undefined
|
|
||||||
if (payload) {
|
|
||||||
expect(PollInit.decode(payload, new Date(data.timestamp), () => alice.address)).to.deep.eq(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('random decode', async () => {
|
|
||||||
expect(PollInit.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be.undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
it('random data', async () => {
|
|
||||||
expect(PollInit.encode({ sadf: '0x0' } as unknown as PollInitMsg)).to.be.undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
it('NON_WEIGHTED init', async () => {
|
|
||||||
const data = await PollInitMsg._createWithSignFunction(
|
|
||||||
async (e) => new PollInitMsg('0x01', e),
|
|
||||||
alice,
|
|
||||||
'whats up',
|
|
||||||
['ab', 'cd', 'ef'],
|
|
||||||
PollType.NON_WEIGHTED,
|
|
||||||
BigNumber.from(10)
|
|
||||||
)
|
|
||||||
expect(data).to.not.be.undefined
|
|
||||||
if (data) {
|
|
||||||
const payload = PollInit.encode(data)
|
|
||||||
expect(payload).to.not.be.undefined
|
|
||||||
if (payload) {
|
|
||||||
expect(PollInit.decode(payload, new Date(data.timestamp), () => alice.address)).to.deep.eq(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('NON_WEIGHTED no min token', async () => {
|
|
||||||
const data = await PollInitMsg._createWithSignFunction(
|
|
||||||
async (e) => new PollInitMsg('0x01', e),
|
|
||||||
alice,
|
|
||||||
'whats up',
|
|
||||||
['ab', 'cd', 'ef'],
|
|
||||||
PollType.NON_WEIGHTED
|
|
||||||
)
|
|
||||||
expect(data).to.not.be.undefined
|
|
||||||
if (data) {
|
|
||||||
const payload = PollInit.encode(data)
|
|
||||||
|
|
||||||
expect(payload).to.not.be.undefined
|
|
||||||
if (payload) {
|
|
||||||
expect(PollInit.decode(payload, new Date(data.timestamp), () => alice.address)).to.deep.eq({
|
|
||||||
...data,
|
|
||||||
minToken: BigNumber.from(1),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { expect } from 'chai'
|
|
||||||
import TimedPollVote from '../../../src/utils/proto/TimedPollVote'
|
|
||||||
import { BigNumber } from 'ethers'
|
|
||||||
import { TimedPollVoteMsg } from '../../../src/models/TimedPollVoteMsg'
|
|
||||||
import { MockProvider } from 'ethereum-waffle'
|
|
||||||
|
|
||||||
describe('TimedPollVote', () => {
|
|
||||||
const provider = new MockProvider()
|
|
||||||
const [alice] = provider.getWallets()
|
|
||||||
const pollId = '0x14c336ef626274f156d094fc1d7ffad2bbc83cccc9817598dd55e42a86b56b72'
|
|
||||||
|
|
||||||
it('success', async () => {
|
|
||||||
const data = await TimedPollVoteMsg._createWithSignFunction(
|
|
||||||
async (e) => new TimedPollVoteMsg('0x01', e),
|
|
||||||
alice,
|
|
||||||
pollId,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(data).to.not.be.undefined
|
|
||||||
if (data) {
|
|
||||||
const payload = TimedPollVote.encode(data)
|
|
||||||
|
|
||||||
expect(payload).to.not.be.undefined
|
|
||||||
if (payload) {
|
|
||||||
expect(TimedPollVote.decode(payload, new Date(data.timestamp), () => alice.address)).to.deep.eq(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('random decode', async () => {
|
|
||||||
expect(TimedPollVote.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be.undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
it('random data', async () => {
|
|
||||||
expect(TimedPollVote.encode({ sadf: '0x0' } as unknown as TimedPollVoteMsg)).to.be.undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
it('data with token', async () => {
|
|
||||||
const data = await TimedPollVoteMsg._createWithSignFunction(
|
|
||||||
async (e) => new TimedPollVoteMsg('0x01', e),
|
|
||||||
alice,
|
|
||||||
pollId,
|
|
||||||
0,
|
|
||||||
BigNumber.from(120)
|
|
||||||
)
|
|
||||||
expect(data).to.not.be.undefined
|
|
||||||
if (data) {
|
|
||||||
const payload = TimedPollVote.encode(data)
|
|
||||||
|
|
||||||
expect(payload).to.not.be.undefined
|
|
||||||
if (payload) {
|
|
||||||
expect(TimedPollVote.decode(payload, new Date(data.timestamp), () => alice.address)).to.deep.eq({
|
|
||||||
...data,
|
|
||||||
tokenAmount: BigNumber.from(120),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -15,7 +15,7 @@ declare module 'protons' {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimedPollVote = {
|
export type TimedPollVote = {
|
||||||
id: Uint8Array
|
pollId: Uint8Array
|
||||||
voter: Uint8Array
|
voter: Uint8Array
|
||||||
timestamp: number
|
timestamp: number
|
||||||
answer: number
|
answer: number
|
||||||
|
@ -26,11 +26,11 @@ declare module 'protons' {
|
||||||
function protons(init: string): {
|
function protons(init: string): {
|
||||||
PollInit: {
|
PollInit: {
|
||||||
encode: (pollInit: PollInit) => Uint8Array,
|
encode: (pollInit: PollInit) => Uint8Array,
|
||||||
decode: (payload: Uint8Array) => PollInit
|
decode: (payload: Uint8Array | undefined) => PollInit
|
||||||
}
|
}
|
||||||
TimedPollVote:{
|
TimedPollVote:{
|
||||||
encode: (timedPollVote: TimedPollVote) => Uint8Array,
|
encode: (timedPollVote: TimedPollVote) => Uint8Array,
|
||||||
decode: (payload: Uint8Array) => TimedPollVote
|
decode: (payload: Uint8Array | undefined) => TimedPollVote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export = protons
|
export = protons
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import WakuVoting from '@status-waku-voting/core'
|
import { WakuPolling } from '@status-waku-voting/core'
|
||||||
import { DetailedTimedPoll } from '@status-waku-voting/core/dist/esm/src/models/DetailedTimedPoll'
|
import { DetailedTimedPoll } from '@status-waku-voting/core/dist/esm/src/models/DetailedTimedPoll'
|
||||||
import { Wallet, BigNumber } from 'ethers'
|
import { Wallet, BigNumber } from 'ethers'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
@ -11,11 +11,11 @@ import { useEthers } from '@usedapp/core'
|
||||||
|
|
||||||
type PollProps = {
|
type PollProps = {
|
||||||
poll: DetailedTimedPoll
|
poll: DetailedTimedPoll
|
||||||
wakuVoting: WakuVoting | undefined
|
wakuPolling: WakuPolling | undefined
|
||||||
signer: Wallet | JsonRpcSigner | undefined
|
signer: Wallet | JsonRpcSigner | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Poll({ poll, wakuVoting, signer }: PollProps) {
|
export function Poll({ poll, wakuPolling, signer }: PollProps) {
|
||||||
const { account } = useEthers()
|
const { account } = useEthers()
|
||||||
const [selectedAnswer, setSelectedAnswer] = useState<number | undefined>(undefined)
|
const [selectedAnswer, setSelectedAnswer] = useState<number | undefined>(undefined)
|
||||||
const [tokenAmount, setTokenAmount] = useState(0)
|
const [tokenAmount, setTokenAmount] = useState(0)
|
||||||
|
@ -64,8 +64,8 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
|
||||||
<SmallButton
|
<SmallButton
|
||||||
disabled={!signer || !account}
|
disabled={!signer || !account}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (wakuVoting && signer) {
|
if (wakuPolling && signer) {
|
||||||
wakuVoting.sendTimedPollVote(
|
wakuPolling.sendTimedPollVote(
|
||||||
signer,
|
signer,
|
||||||
poll.poll.id,
|
poll.poll.id,
|
||||||
selectedAnswer ?? 0,
|
selectedAnswer ?? 0,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Wallet } from 'ethers'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { PollType } from '@status-waku-voting/core/dist/esm/src/types/PollType'
|
import { PollType } from '@status-waku-voting/core/dist/esm/src/types/PollType'
|
||||||
import WakuVoting from '@status-waku-voting/core'
|
import { WakuPolling } from '@status-waku-voting/core'
|
||||||
import { Input, addIcon, SmallButton, Modal } from '@status-waku-voting/react-components'
|
import { Input, addIcon, SmallButton, Modal } from '@status-waku-voting/react-components'
|
||||||
|
|
||||||
function getLocaleIsoTime(dateTime: Date) {
|
function getLocaleIsoTime(dateTime: Date) {
|
||||||
|
@ -15,11 +15,11 @@ function getLocaleIsoTime(dateTime: Date) {
|
||||||
|
|
||||||
type PollCreationProps = {
|
type PollCreationProps = {
|
||||||
signer: JsonRpcSigner | Wallet
|
signer: JsonRpcSigner | Wallet
|
||||||
wakuVoting: WakuVoting | undefined
|
wakuPolling: WakuPolling | undefined
|
||||||
setShowPollCreation: (val: boolean) => void
|
setShowPollCreation: (val: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PollCreation({ signer, wakuVoting, setShowPollCreation }: PollCreationProps) {
|
export function PollCreation({ signer, wakuPolling, setShowPollCreation }: PollCreationProps) {
|
||||||
const [answers, setAnswers] = useState<string[]>(['', ''])
|
const [answers, setAnswers] = useState<string[]>(['', ''])
|
||||||
const [question, setQuestion] = useState('')
|
const [question, setQuestion] = useState('')
|
||||||
const [showCreateConfirmation, setShowCreateConfirmation] = useState(false)
|
const [showCreateConfirmation, setShowCreateConfirmation] = useState(false)
|
||||||
|
@ -65,7 +65,7 @@ export function PollCreation({ signer, wakuVoting, setShowPollCreation }: PollCr
|
||||||
<SmallButton
|
<SmallButton
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
await wakuVoting?.createTimedPoll(
|
await wakuPolling?.createTimedPoll(
|
||||||
signer,
|
signer,
|
||||||
question,
|
question,
|
||||||
answers,
|
answers,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import WakuVoting from '@status-waku-voting/core'
|
import { WakuPolling } from '@status-waku-voting/core'
|
||||||
import { DetailedTimedPoll } from '@status-waku-voting/core/dist/esm/src/models/DetailedTimedPoll'
|
import { DetailedTimedPoll } from '@status-waku-voting/core/dist/esm/src/models/DetailedTimedPoll'
|
||||||
import { Wallet } from 'ethers'
|
import { Wallet } from 'ethers'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
@ -7,24 +7,22 @@ import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
type PollListProps = {
|
type PollListProps = {
|
||||||
wakuVoting: WakuVoting | undefined
|
wakuPolling: WakuPolling | undefined
|
||||||
signer: Wallet | JsonRpcSigner | undefined
|
signer: Wallet | JsonRpcSigner | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PollList({ wakuVoting, signer }: PollListProps) {
|
export function PollList({ wakuPolling, signer }: PollListProps) {
|
||||||
const [polls, setPolls] = useState<DetailedTimedPoll[]>([])
|
const [polls, setPolls] = useState<DetailedTimedPoll[]>([])
|
||||||
const [dividedPolls, setDividedPolls] = useState<DetailedTimedPoll[][]>([[], [], []])
|
const [dividedPolls, setDividedPolls] = useState<DetailedTimedPoll[][]>([[], [], []])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
if (wakuVoting) {
|
if (wakuPolling) {
|
||||||
const { DetailedTimedPolls, updated } = await wakuVoting.getDetailedTimedPolls()
|
const DetailedTimedPolls = await wakuPolling.getDetailedTimedPolls()
|
||||||
if (updated) {
|
|
||||||
setPolls(DetailedTimedPolls)
|
setPolls(DetailedTimedPolls)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [wakuVoting])
|
}, [wakuPolling])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let arrayNo = 0
|
let arrayNo = 0
|
||||||
|
@ -45,7 +43,7 @@ export function PollList({ wakuVoting, signer }: PollListProps) {
|
||||||
return (
|
return (
|
||||||
<ColumnWrapper key={idx}>
|
<ColumnWrapper key={idx}>
|
||||||
{pollArray.map((poll) => {
|
{pollArray.map((poll) => {
|
||||||
return <Poll key={poll.poll.id} poll={poll} wakuVoting={wakuVoting} signer={signer} />
|
return <Poll key={poll.poll.id} poll={poll} wakuPolling={wakuPolling} signer={signer} />
|
||||||
})}
|
})}
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { WakuPolling } from '@status-waku-voting/core'
|
||||||
|
|
||||||
|
export function useWakuPolling(appName: string, tokenAddress: string) {
|
||||||
|
const [wakuPolling, setWakuPolling] = useState<WakuPolling | undefined>(undefined)
|
||||||
|
useEffect(() => {
|
||||||
|
WakuPolling.create(appName, tokenAddress).then((e) => setWakuPolling(e))
|
||||||
|
return () => wakuPolling?.cleanUp()
|
||||||
|
}, [])
|
||||||
|
return wakuPolling
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import WakuVoting from '@status-waku-voting/core'
|
|
||||||
|
|
||||||
export function useWakuVoting(appName: string, tokenAddress: string) {
|
|
||||||
const [wakuVoting, setWakuVoting] = useState<WakuVoting | undefined>(undefined)
|
|
||||||
useEffect(() => {
|
|
||||||
WakuVoting.create(appName, tokenAddress).then((e) => setWakuVoting(e))
|
|
||||||
}, [])
|
|
||||||
return wakuVoting
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { useWakuVoting } from './hooks/useWakuVoting'
|
import { useWakuPolling } from './hooks/useWakuPolling'
|
||||||
|
|
||||||
export { useWakuVoting }
|
export { useWakuPolling }
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useEthers } from '@usedapp/core'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { PollList, PollCreation } from '@status-waku-voting/polling-components'
|
import { PollList, PollCreation } from '@status-waku-voting/polling-components'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
import { useWakuVoting } from '@status-waku-voting/polling-hooks'
|
import { useWakuPolling } from '@status-waku-voting/polling-hooks'
|
||||||
import { Modal, Networks, Button } from '@status-waku-voting/react-components'
|
import { Modal, Networks, Button } from '@status-waku-voting/react-components'
|
||||||
|
|
||||||
type WakuPollingProps = {
|
type WakuPollingProps = {
|
||||||
|
@ -16,11 +16,11 @@ export function WakuPolling({ appName, signer }: WakuPollingProps) {
|
||||||
const { activateBrowserWallet, account } = useEthers()
|
const { activateBrowserWallet, account } = useEthers()
|
||||||
const [showPollCreation, setShowPollCreation] = useState(false)
|
const [showPollCreation, setShowPollCreation] = useState(false)
|
||||||
const [selectConnect, setSelectConnect] = useState(false)
|
const [selectConnect, setSelectConnect] = useState(false)
|
||||||
const wakuVoting = useWakuVoting(appName, '0x01')
|
const wakuPolling = useWakuPolling(appName, '0x01')
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{showPollCreation && signer && (
|
{showPollCreation && signer && (
|
||||||
<PollCreation signer={signer} wakuVoting={wakuVoting} setShowPollCreation={setShowPollCreation} />
|
<PollCreation signer={signer} wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} />
|
||||||
)}
|
)}
|
||||||
{account ? (
|
{account ? (
|
||||||
<CreatePollButton disabled={!signer} onClick={() => setShowPollCreation(true)}>
|
<CreatePollButton disabled={!signer} onClick={() => setShowPollCreation(true)}>
|
||||||
|
@ -43,7 +43,7 @@ export function WakuPolling({ appName, signer }: WakuPollingProps) {
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PollList wakuVoting={wakuVoting} signer={signer} />
|
<PollList wakuPolling={wakuPolling} signer={signer} />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue