Add waku init poll mock (#5)
This commit is contained in:
parent
624f126f72
commit
c1b326ab9c
|
@ -1,30 +1,67 @@
|
||||||
import { Waku, getStatusFleetNodes } from 'js-waku'
|
import { Waku, getStatusFleetNodes } from 'js-waku'
|
||||||
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
|
import { PollInitMsg } from './models/PollInitMsg'
|
||||||
|
import { PollType } from './types/PollType'
|
||||||
|
import { BigNumber, Wallet } from 'ethers'
|
||||||
|
import PollInit from './utils/proto/PollInit'
|
||||||
|
import { WakuMessage } from 'js-waku'
|
||||||
|
|
||||||
class WakuVoting {
|
class WakuVoting {
|
||||||
private appName: string
|
private appName: string
|
||||||
private waku: Waku | undefined
|
private waku: Waku | undefined
|
||||||
public tokenAddress: string
|
public tokenAddress: string
|
||||||
|
private pollInitTopic: string
|
||||||
|
|
||||||
private async createWaku() {
|
private static async createWaku() {
|
||||||
this.waku = await Waku.create()
|
const waku = await Waku.create()
|
||||||
const nodes = await getStatusFleetNodes()
|
const nodes = await getStatusFleetNodes()
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
nodes.map((addr) => {
|
nodes.map((addr) => {
|
||||||
if (this.waku) {
|
if (waku) {
|
||||||
return this.waku.dial(addr)
|
return waku.dial(addr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
return waku
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
if (waku) {
|
this.pollInitTopic = `/${this.appName}/waku-polling/timed-polls-init/proto/`
|
||||||
this.waku = waku
|
this.waku = waku
|
||||||
} else {
|
}
|
||||||
this.createWaku()
|
|
||||||
|
public static async create(appName: string, tokenAddress: string, waku?: Waku) {
|
||||||
|
if (!waku) {
|
||||||
|
waku = await this.createWaku()
|
||||||
}
|
}
|
||||||
|
return new WakuVoting(appName, tokenAddress, waku)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createTimedPoll(
|
||||||
|
signer: JsonRpcSigner | Wallet,
|
||||||
|
question: string,
|
||||||
|
answers: string[],
|
||||||
|
pollType: PollType,
|
||||||
|
minToken?: BigNumber,
|
||||||
|
endTime?: number
|
||||||
|
) {
|
||||||
|
const pollInit = await PollInitMsg.create(signer, question, answers, pollType, minToken, endTime)
|
||||||
|
const payload = PollInit.encode(pollInit)
|
||||||
|
if (payload && pollInit) {
|
||||||
|
const wakuMessage = await WakuMessage.fromBytes(payload, this.pollInitTopic, {
|
||||||
|
timestamp: new Date(pollInit.timestamp),
|
||||||
|
})
|
||||||
|
await this.waku?.relay.send(wakuMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTimedPolls() {
|
||||||
|
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.pollInitTopic] })
|
||||||
|
return messages
|
||||||
|
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload)
|
||||||
|
.map((msg) => PollInit.decode(msg.payload, msg.timestamp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { PollType } from '../types/PollType'
|
import { PollType } from '../types/PollType'
|
||||||
import { BigNumber, utils } from 'ethers'
|
import { BigNumber, utils } from 'ethers'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
import { JsonRpcSigner } from '@ethersproject/providers'
|
||||||
|
import { PollInit } from 'protons'
|
||||||
|
import { Wallet } from 'ethers'
|
||||||
export class PollInitMsg {
|
export class PollInitMsg {
|
||||||
public owner: string
|
public owner: string
|
||||||
public timestamp: number
|
public timestamp: number
|
||||||
|
@ -34,7 +35,7 @@ export class PollInitMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async create(
|
static async create(
|
||||||
signer: JsonRpcSigner,
|
signer: JsonRpcSigner | Wallet,
|
||||||
question: string,
|
question: string,
|
||||||
answers: string[],
|
answers: string[],
|
||||||
pollType: PollType,
|
pollType: PollType,
|
||||||
|
@ -72,4 +73,42 @@ export class PollInitMsg {
|
||||||
|
|
||||||
return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, newEndTime, minToken)
|
return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, newEndTime, minToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromProto(payload: PollInit) {
|
||||||
|
const owner = utils.getAddress(utils.hexlify(payload.owner))
|
||||||
|
const timestamp = payload.timestamp
|
||||||
|
const question = payload.question
|
||||||
|
const answers = payload.answers
|
||||||
|
const pollType = payload.pollType
|
||||||
|
const endTime = payload.endTime
|
||||||
|
const signature = utils.hexlify(payload.signature)
|
||||||
|
let minToken = payload.minToken ? BigNumber.from(payload.minToken) : undefined
|
||||||
|
|
||||||
|
const msg: (string | number | BigNumber | PollType)[] = [
|
||||||
|
owner,
|
||||||
|
timestamp,
|
||||||
|
question,
|
||||||
|
answers.join(),
|
||||||
|
pollType,
|
||||||
|
endTime,
|
||||||
|
]
|
||||||
|
const types = ['address', 'uint256', 'string', 'string', 'uint8', 'uint256']
|
||||||
|
if (pollType === PollType.NON_WEIGHTED) {
|
||||||
|
if (minToken) {
|
||||||
|
msg.push(minToken)
|
||||||
|
types.push('uint256')
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
minToken = undefined
|
||||||
|
}
|
||||||
|
const packedData = utils.arrayify(utils.solidityPack(types, msg))
|
||||||
|
const verifiedAddress = utils.verifyMessage(packedData, signature)
|
||||||
|
if (verifiedAddress != owner) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, endTime, minToken)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import protons, { PollInit } from 'protons'
|
import protons, { PollInit } from 'protons'
|
||||||
import { PollType } from '../../types/PollType'
|
import { PollType } from '../../types/PollType'
|
||||||
import { utils, BigNumber } from 'ethers'
|
import { utils } from 'ethers'
|
||||||
import { PollInitMsg } from '../../models/PollInitMsg'
|
import { PollInitMsg } from '../../models/PollInitMsg'
|
||||||
|
|
||||||
const proto = protons(`
|
const proto = protons(`
|
||||||
|
@ -46,9 +46,12 @@ export function encode(pollInit: PollInitMsg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decode(payload: Uint8Array) {
|
export function decode(payload: Uint8Array, timestamp: Date | undefined) {
|
||||||
try {
|
try {
|
||||||
const msg = proto.PollInit.decode(payload)
|
const msg = proto.PollInit.decode(payload)
|
||||||
|
if (!timestamp || timestamp.getTime() != msg.timestamp) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
msg.owner &&
|
msg.owner &&
|
||||||
msg.timestamp &&
|
msg.timestamp &&
|
||||||
|
@ -58,24 +61,7 @@ export function decode(payload: Uint8Array) {
|
||||||
msg.endTime &&
|
msg.endTime &&
|
||||||
msg.signature
|
msg.signature
|
||||||
) {
|
) {
|
||||||
const pollInit: PollInitMsg = {
|
return PollInitMsg.fromProto(msg)
|
||||||
owner: utils.hexlify(msg.owner),
|
|
||||||
timestamp: msg.timestamp,
|
|
||||||
question: msg.question,
|
|
||||||
answers: msg.answers,
|
|
||||||
pollType: msg.pollType,
|
|
||||||
endTime: msg.endTime,
|
|
||||||
signature: utils.hexlify(msg.signature),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.pollType === PollType.NON_WEIGHTED) {
|
|
||||||
if (msg.minToken) {
|
|
||||||
pollInit.minToken = BigNumber.from(msg.minToken)
|
|
||||||
return pollInit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return pollInit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return undefined
|
return undefined
|
||||||
|
|
|
@ -4,7 +4,7 @@ import WakuVoting from '../src'
|
||||||
|
|
||||||
describe('WakuVoting', () => {
|
describe('WakuVoting', () => {
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
const wakuVoting = new WakuVoting('test', '0x0', {} as unknown as Waku)
|
const wakuVoting = await WakuVoting.create('test', '0x0', {} as unknown as Waku)
|
||||||
|
|
||||||
expect(wakuVoting).to.not.be.undefined
|
expect(wakuVoting).to.not.be.undefined
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { expect } from 'chai'
|
||||||
import { PollInitMsg } from '../../src/models/PollInitMsg'
|
import { PollInitMsg } from '../../src/models/PollInitMsg'
|
||||||
import { MockProvider } from 'ethereum-waffle'
|
import { MockProvider } from 'ethereum-waffle'
|
||||||
import { PollType } from '../../src/types/PollType'
|
import { PollType } from '../../src/types/PollType'
|
||||||
import { JsonRpcSigner } from '@ethersproject/providers'
|
|
||||||
import { BigNumber, utils } from 'ethers'
|
import { BigNumber, utils } from 'ethers'
|
||||||
|
|
||||||
describe('PollInitMsg', () => {
|
describe('PollInitMsg', () => {
|
||||||
|
@ -10,12 +9,7 @@ describe('PollInitMsg', () => {
|
||||||
const [alice] = provider.getWallets()
|
const [alice] = provider.getWallets()
|
||||||
|
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
const poll = await PollInitMsg.create(
|
const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.WEIGHTED)
|
||||||
alice as unknown as JsonRpcSigner,
|
|
||||||
'test',
|
|
||||||
['one', 'two', 'three'],
|
|
||||||
PollType.WEIGHTED
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(poll).to.not.be.undefined
|
expect(poll).to.not.be.undefined
|
||||||
expect(poll.owner).to.eq(alice.address)
|
expect(poll.owner).to.eq(alice.address)
|
||||||
|
@ -35,7 +29,7 @@ describe('PollInitMsg', () => {
|
||||||
|
|
||||||
it('success NON_WEIGHTED', async () => {
|
it('success NON_WEIGHTED', async () => {
|
||||||
const poll = await PollInitMsg.create(
|
const poll = await PollInitMsg.create(
|
||||||
alice as unknown as JsonRpcSigner,
|
alice,
|
||||||
'test',
|
'test',
|
||||||
['one', 'two', 'three'],
|
['one', 'two', 'three'],
|
||||||
PollType.NON_WEIGHTED,
|
PollType.NON_WEIGHTED,
|
||||||
|
@ -61,12 +55,7 @@ describe('PollInitMsg', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('NON_WEIGHTED no minToken', async () => {
|
it('NON_WEIGHTED no minToken', async () => {
|
||||||
const poll = await PollInitMsg.create(
|
const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.NON_WEIGHTED)
|
||||||
alice as unknown as JsonRpcSigner,
|
|
||||||
'test',
|
|
||||||
['one', 'two', 'three'],
|
|
||||||
PollType.NON_WEIGHTED
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(poll?.minToken?.toNumber()).to.eq(1)
|
expect(poll?.minToken?.toNumber()).to.eq(1)
|
||||||
|
|
||||||
|
@ -87,14 +76,7 @@ describe('PollInitMsg', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('specific end time', async () => {
|
it('specific end time', async () => {
|
||||||
const poll = await PollInitMsg.create(
|
const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.NON_WEIGHTED, undefined, 100)
|
||||||
alice as unknown as JsonRpcSigner,
|
|
||||||
'test',
|
|
||||||
['one', 'two', 'three'],
|
|
||||||
PollType.NON_WEIGHTED,
|
|
||||||
undefined,
|
|
||||||
100
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(poll?.endTime).to.eq(100)
|
expect(poll?.endTime).to.eq(100)
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,29 +3,24 @@ import { PollType } from '../../../src/types/PollType'
|
||||||
import PollInit from '../../../src/utils/proto/PollInit'
|
import PollInit from '../../../src/utils/proto/PollInit'
|
||||||
import { BigNumber } from 'ethers'
|
import { BigNumber } from 'ethers'
|
||||||
import { PollInitMsg } from '../../../src/models/PollInitMsg'
|
import { PollInitMsg } from '../../../src/models/PollInitMsg'
|
||||||
|
import { MockProvider } from 'ethereum-waffle'
|
||||||
|
|
||||||
describe('PollInit', () => {
|
describe('PollInit', () => {
|
||||||
|
const provider = new MockProvider()
|
||||||
|
const [alice] = provider.getWallets()
|
||||||
it('success', async () => {
|
it('success', async () => {
|
||||||
const data: PollInitMsg = {
|
const data = await PollInitMsg.create(alice, 'whats up', ['ab', 'cd', 'ef'], PollType.WEIGHTED)
|
||||||
owner: '0x02',
|
|
||||||
answers: ['ab', 'cd', 'ef'],
|
|
||||||
endTime: 10,
|
|
||||||
pollType: PollType.WEIGHTED,
|
|
||||||
question: 'whats up',
|
|
||||||
signature: '0x11',
|
|
||||||
timestamp: 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = PollInit.encode(data)
|
const payload = PollInit.encode(data)
|
||||||
|
|
||||||
expect(payload).to.not.be.undefined
|
expect(payload).to.not.be.undefined
|
||||||
if (payload) {
|
if (payload) {
|
||||||
expect(PollInit.decode(payload)).to.deep.eq(data)
|
expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('random decode', async () => {
|
it('random decode', async () => {
|
||||||
expect(PollInit.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]))).to.be.undefined
|
expect(PollInit.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be.undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
it('random data', async () => {
|
it('random data', async () => {
|
||||||
|
@ -33,36 +28,29 @@ describe('PollInit', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('NON_WEIGHTED init', async () => {
|
it('NON_WEIGHTED init', async () => {
|
||||||
const data: PollInitMsg = {
|
const data = await PollInitMsg.create(
|
||||||
owner: '0x02',
|
alice,
|
||||||
answers: ['ab', 'cd', 'ef'],
|
'whats up',
|
||||||
endTime: 10,
|
['ab', 'cd', 'ef'],
|
||||||
pollType: PollType.NON_WEIGHTED,
|
PollType.NON_WEIGHTED,
|
||||||
minToken: BigNumber.from(10),
|
BigNumber.from(10)
|
||||||
question: 'whats up',
|
)
|
||||||
signature: '0x11',
|
|
||||||
timestamp: 10,
|
|
||||||
}
|
|
||||||
const payload = PollInit.encode(data)
|
const payload = PollInit.encode(data)
|
||||||
expect(payload).to.not.be.undefined
|
expect(payload).to.not.be.undefined
|
||||||
if (payload) {
|
if (payload) {
|
||||||
expect(PollInit.decode(payload)).to.deep.eq(data)
|
expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('NON_WEIGHTED no min token', async () => {
|
it('NON_WEIGHTED no min token', async () => {
|
||||||
const data: PollInitMsg = {
|
const data = await PollInitMsg.create(alice, 'whats up', ['ab', 'cd', 'ef'], PollType.NON_WEIGHTED)
|
||||||
owner: '0x02',
|
|
||||||
answers: ['ab', 'cd', 'ef'],
|
|
||||||
endTime: 10,
|
|
||||||
pollType: PollType.NON_WEIGHTED,
|
|
||||||
question: 'whats up',
|
|
||||||
signature: '0x11',
|
|
||||||
timestamp: 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = PollInit.encode(data)
|
const payload = PollInit.encode(data)
|
||||||
|
|
||||||
expect(payload).to.be.undefined
|
expect(payload).to.not.be.undefined
|
||||||
|
if (payload) {
|
||||||
|
expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq({ ...data, minToken: BigNumber.from(1) })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue