From ac0279fda3351dbf9435eb65b4c18617f819ce1f Mon Sep 17 00:00:00 2001 From: Szymon Szlachtowicz <38212223+Szymx95@users.noreply.github.com> Date: Fri, 13 Aug 2021 13:41:08 +0200 Subject: [PATCH] Refactor initMsg typed signing (#11) --- packages/core/src/models/PollInitMsg.ts | 122 +++++++----------- packages/core/test/models/PollInitMsg.test.ts | 32 ++--- packages/react-components/src/Poll.tsx | 2 + packages/react-components/src/index.tsx | 2 +- 4 files changed, 68 insertions(+), 90 deletions(-) diff --git a/packages/core/src/models/PollInitMsg.ts b/packages/core/src/models/PollInitMsg.ts index 22e6be2..5b01393 100644 --- a/packages/core/src/models/PollInitMsg.ts +++ b/packages/core/src/models/PollInitMsg.ts @@ -3,6 +3,16 @@ import { BigNumber, utils, Wallet } from 'ethers' import { JsonRpcSigner } from '@ethersproject/providers' import { PollInit } from 'protons' +function signFunction(signer: JsonRpcSigner | Wallet) { + return async (params: string[]) => { + if ('send' in signer.provider) { + return signer.provider.send('eth_signTypedData_v4', params) + } else { + throw `No send function in provider` + } + } +} + type Message = { owner: string timestamp: number @@ -10,7 +20,7 @@ type Message = { answers: string[] pollType: PollType endTime: number - minToken: BigNumber | undefined + minToken?: BigNumber } export function createSignMsgParams(message: Message) { @@ -20,10 +30,8 @@ export function createSignMsgParams(message: Message) { version: '1', }, message: { - owner: message.owner, + ...message, timestamp: new Date(message.timestamp).toLocaleDateString(), - question: message.question, - answers: message.answers, pollType: message.pollType === PollType.WEIGHTED ? 'Weighted' : 'Non weighted', endTime: new Date(message.endTime).toLocaleDateString(), }, @@ -44,11 +52,9 @@ export function createSignMsgParams(message: Message) { }, } - if (message.pollType === PollType.NON_WEIGHTED) { - if (message.minToken) { - msgParams.message = { ...msgParams.message, minToken: message.minToken.toString() } - msgParams.types.Mail.push({ name: 'minToken', type: 'uint256' }) - } + if (message.pollType === PollType.NON_WEIGHTED && message.minToken) { + msgParams.message = { ...msgParams.message, minToken: message.minToken.toString() } + msgParams.types.Mail.push({ name: 'minToken', type: 'uint256' }) } return msgParams } @@ -64,27 +70,16 @@ export class PollInitMsg { public signature: string public id: string - private constructor( - id: string, - owner: string, - signature: string, - timestamp: number, - question: string, - answers: string[], - pollType: PollType, - endTime: number, - minToken?: BigNumber - ) { - this.id = id - this.owner = owner - this.timestamp = timestamp - this.question = question - this.answers = answers - this.pollType = pollType - this.minToken = minToken - this.endTime = endTime - + private constructor(signature: string, msg: Message) { + this.id = utils.id([msg.owner, msg.timestamp, signature].join()) this.signature = signature + this.owner = msg.owner + this.timestamp = msg.timestamp + this.question = msg.question + this.answers = msg.answers + this.pollType = msg.pollType + this.minToken = msg.minToken + this.endTime = msg.endTime } static async _createWithSignFunction( @@ -96,34 +91,31 @@ export class PollInitMsg { minToken?: BigNumber, endTime?: number ): Promise { - const owner = await signer.getAddress() const timestamp = Date.now() - let newEndTime = timestamp + 10000000 - if (endTime) { - newEndTime = endTime - } - if (pollType === PollType.NON_WEIGHTED && !minToken) { minToken = BigNumber.from(1) } - - const params = createSignMsgParams({ - owner, + const msg = { + owner: await signer.getAddress(), timestamp, question, answers, pollType, - endTime: newEndTime, + endTime: endTime ? endTime : timestamp + 100000000, minToken, - }) - - const signature = await signFunction([owner, JSON.stringify(params)]) - if (!signature) { - return undefined } - const id = utils.solidityKeccak256(['address', 'uint256'], [owner, timestamp]) - return new PollInitMsg(id, owner, signature, timestamp, question, answers, pollType, newEndTime, minToken) + const params = createSignMsgParams(msg) + + try { + const signature = await signFunction([msg.owner, JSON.stringify(params)]) + if (!signature) { + return undefined + } + return new PollInitMsg(signature, msg) + } catch { + return undefined + } } static async create( @@ -134,43 +126,27 @@ export class PollInitMsg { minToken?: BigNumber, endTime?: number ): Promise { - const signFunction = async (params: string[]) => { - if ('send' in signer.provider) { - return signer.provider.send('eth_signTypedData_v4', params) - } else { - return undefined - } - } - return this._createWithSignFunction(signFunction, signer, question, answers, pollType, minToken, endTime) + return this._createWithSignFunction(signFunction(signer), signer, question, answers, pollType, minToken, endTime) } static fromProto(payload: PollInit, recoverFunction: ({ data, sig }: { data: any; sig: string }) => string) { - 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) - const minToken = payload.minToken ? BigNumber.from(payload.minToken) : undefined - const params = createSignMsgParams({ - owner, - timestamp, - question, - answers, - endTime, - minToken, - pollType, - }) + const msg = { + ...payload, + owner: utils.getAddress(utils.hexlify(payload.owner)), + minToken: payload.minToken ? BigNumber.from(payload.minToken) : undefined, + } + + const params = createSignMsgParams(msg) const verifiedAddress = recoverFunction({ data: params, sig: signature, }) - if (verifiedAddress != owner) { + if (verifiedAddress != msg.owner) { return undefined } - const id = utils.solidityKeccak256(['address', 'uint256'], [owner, timestamp]) - return new PollInitMsg(id, owner, signature, timestamp, question, answers, pollType, endTime, minToken) + + return new PollInitMsg(signature, msg) } } diff --git a/packages/core/test/models/PollInitMsg.test.ts b/packages/core/test/models/PollInitMsg.test.ts index 766dbb8..16efbd4 100644 --- a/packages/core/test/models/PollInitMsg.test.ts +++ b/packages/core/test/models/PollInitMsg.test.ts @@ -20,7 +20,7 @@ describe('PollInitMsg', () => { expect(poll).to.not.be.undefined if (poll) { expect(poll.owner).to.eq(alice.address) - expect(poll.endTime).to.eq(poll.timestamp + 10000000) + expect(poll.endTime).to.eq(poll.timestamp + 100000000) expect(poll.answers).to.deep.eq(['one', 'two', 'three']) expect(poll.minToken).to.be.undefined expect(poll.pollType).to.eq(PollType.WEIGHTED) @@ -32,12 +32,12 @@ describe('PollInitMsg', () => { JSON.stringify( createSignMsgParams({ owner: poll.owner, - answers: poll.answers, - endTime: poll.endTime, - pollType: poll.pollType, - minToken: poll.minToken, - question: poll.question, timestamp: poll.timestamp, + question: poll.question, + answers: poll.answers, + pollType: poll.pollType, + endTime: poll.endTime, + minToken: poll.minToken, }) ), ].join() @@ -63,12 +63,12 @@ describe('PollInitMsg', () => { JSON.stringify( createSignMsgParams({ owner: poll.owner, - answers: poll.answers, - endTime: poll.endTime, - pollType: poll.pollType, - minToken: poll.minToken, - question: poll.question, timestamp: poll.timestamp, + question: poll.question, + answers: poll.answers, + pollType: poll.pollType, + endTime: poll.endTime, + minToken: poll.minToken, }) ), ].join() @@ -90,12 +90,12 @@ describe('PollInitMsg', () => { if (poll) { const msg = createSignMsgParams({ owner: poll.owner, - answers: poll.answers, - endTime: poll.endTime, - pollType: poll.pollType, - minToken: poll.minToken, - question: poll.question, timestamp: poll.timestamp, + question: poll.question, + answers: poll.answers, + pollType: poll.pollType, + endTime: poll.endTime, + minToken: poll.minToken, }) expect(poll.signature).to.eq([poll.owner, JSON.stringify(msg)].join()) diff --git a/packages/react-components/src/Poll.tsx b/packages/react-components/src/Poll.tsx index c7c7c4a..3330f2d 100644 --- a/packages/react-components/src/Poll.tsx +++ b/packages/react-components/src/Poll.tsx @@ -16,9 +16,11 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) { const [selectedAnswer, setSelectedAnswer] = useState(0) const [tokenAmount, setTokenAmount] = useState(0) const [address, setAddress] = useState('') + useEffect(() => { signer.getAddress().then((e) => setAddress(e)) }, [signer]) + return ( diff --git a/packages/react-components/src/index.tsx b/packages/react-components/src/index.tsx index 108bf74..8aab2a9 100644 --- a/packages/react-components/src/index.tsx +++ b/packages/react-components/src/index.tsx @@ -22,7 +22,7 @@ function Example({ appName }: ExampleProps) { const [selectedType, setSelectedType] = useState(PollType.WEIGHTED) useEffect(() => { - provider.on('accountsChanged', async () => { + ;(window as any).ethereum.on('accountsChanged', async () => { provider.send('eth_requestAccounts', []) setSigner(provider.getSigner()) })