Add Poll SDK tutorial (#15)

This commit is contained in:
F 2022-01-05 11:02:48 +11:00 committed by GitHub
parent a606280a45
commit 2180e81c1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 749 additions and 1 deletions

View File

@ -32,7 +32,7 @@ params:
BookTheme: 'dark'
BookToC: true
BookSection: 'docs'
BookDateFormat: '2006/02/01'
BookDateFormat: 'Jan 2, 2006'
BookSearch: true
# GitHub edit links

View File

@ -0,0 +1,83 @@
---
weight: 100
---
# WakuConnect Vote & Poll SDK
The WakuConnect Vote & Poll SDK enables developers to add Waku powered polling and voting features to their dApp.
The repository can be found on GitHub: https://github.com/status-im/wakuconnect-vote-poll-sdk.
The SDK can be used in two different ways:
to vote (commitment to the blockchain) or poll (no interaction with the blockchain).
For both functionalities, only ERC-20 token holders can create or answer polls/votes.
The developer using the SDK can configure which ERC-20 token contract is used.
## Packages
### Common
- `@waku/vote-poll-sdk-core`: Common libraries to both vote and poll functionalities.
- `@waku/vote-poll-sdk-react-components`: Common React components to both vote and poll functionalities.
### Vote
- `@waku/vote-sdk-react-components`: React components.
- `@waku/vote-sdk-react-hooks`: React hooks.
- `@waku/vote-sdk-contracts`: Solidity contracts.
### Poll
- `@waku/poll-sdk-react-components`: React components.
- `@waku/poll-sdk-react-hooks`: React hooks.
## WakuConnect Vote SDK
The WakuConnect Vote SDK allows you to leverage Waku to save gas fees for most voters.
It uses Waku to broadcast and aggregates votes.
Most token holders will not need to spend gas to vote.
Only the party that starts an election and submit the end results need to interact with the blockchain.
For example, it can be used by a DAO to manage proposals
where proposal creation and vote results must be committed to the blockchain.
With WakuConnect Vote SDK, the DAO could be the one spending gas when creating the proposal and committing the votes,
whereas the token holders do not spend gas when voting.
### Documentation
{{< hint info >}}
The documentation effort is currently in progress.
It is tracked with [status-im/wakuconnect-vote-poll-sdk#11](https://github.com/status-im/wakuconnect-vote-poll-sdk/issues/11).
Contributions are welcome.
{{< /hint >}}
You can find more information about the Vote SDK's properties in the [README](https://github.com/status-im/wakuconnect-vote-poll-sdk#wakuconnect-vote-sdk).
A working example dApp that includes voting feature can be found in the [repo](https://github.com/status-im/wakuconnect-vote-poll-sdk/tree/main/packages/example).
{{< hint warning >}}
However, as the example is part of the yarn workspace, there may be issues with undeclared dependencies with this example.
Tracked with [status-im/wakuconnect-vote-poll-sdk#11](https://github.com/status-im/wakuconnect-vote-poll-sdk/issues/11).
{{< /hint >}}
## WakuConnect Poll SDK
The WakuConnect Poll SDK allows you to leverage Waku and enable token holder to create, answer and view polls.
The polls are not committed to the blockchain and hence do not cost gas.
As the polls use Waku, they do maintain properties expected from dApps: decentralized and censorship-resistant.
The high-level functionality is as follows:
- To create a poll, a token holder sends a message with the poll questions, possible answers and an end time over Waku,
- Other users receive the poll creation message and can view the poll,
- To avoid spam, only polls created by actual token holders are displayed,
- Any token holder can send their poll answer over Waku,
- Each user cumulates poll responses from Waku and can view them,
- To avoid spam, only responses sent by actual token holders are displayed.
### Documentation
See [How to Use the WakuConnect Poll SDK](./poll_sdk).

View File

@ -0,0 +1,116 @@
---
title: Create the DApp and Install Dependencies
date: 2022-01-03T11:00:00+1100
weight: 11
---
# Create the DApp and Install Dependencies
## Create React App
Create the new React app using the `typescript` template.
Install the Waku Poll SDK packages.
In this guide, we use [useDApp](https://usedapp.io/) to access the blockchain.
```shell
yarn create react-app poll-dapp-ts --template typescript
cd poll-dapp-ts
yarn add \
@waku/poll-sdk-react-components @waku/poll-sdk-react-hooks @waku/vote-poll-sdk-react-components \
@usedapp/core@0.4.7
yarn add -D @types/styled-components
```
{{< hint warning >}}
`@usedapp/core` must be frozen to version `0.4.7` due to incompatibility between minor versions of `ethers`.
WakuConnect Vote & Poll SDK will be upgraded to the latest version of `@usedapp/core` and `ethers` once `ethereum-waffle`
is released with the [latest version of `ethers`](https://github.com/EthWorks/Waffle/pull/603).
{{< /hint >}}
## Setup polyfills
A number of Web3 dependencies need polyfills.
Said polyfills must be explicitly declared when using webpack 5.
The latest `react-scripts` version uses webpack 5.
We will describe below a method to configure polyfills when using `create-react-app`/`react-scripts` or webpack 5.
This may not be necessary if you do not use `react-scripts` or if you use webpack 4.
Start by installing the polyfill libraries:
```shell
yarn add assert buffer crypto-browserify stream-browserify
```
### Webpack 5
If you directly use webpack 5,
then you can inspire yourself from this [webpack.config.js](https://github.com/status-im/wakuconnect-vote-poll-sdk/blob/main/examples/mainnet-poll/webpack.config.js).
### React-App-Rewired
An alternative is to let `react-scripts` control the webpack 5 config and only override some elements using `react-app-rewired`.
Install `react-app-rewired`:
```shell
yarn add -D react-app-rewired
```
Create a `config-overrides.js` file at the root of your app:
```js
const webpack = require('webpack');
module.exports = (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
"buffer": require.resolve("buffer"),
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert")
}
)
if (!config.plugins) config.plugins = []
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}));
return config;
}
```
Use `react-app-rewired` in the `package.json`, instead of `react-scripts`:
```
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
- "eject": "react-scripts eject"
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
+ "test": "react-app-rewired test",
+ "eject": "react-app-rewired eject"
},
```
## Start development server
You can now start the development server to serve your dApp at http://localhost:3000/ while we code:
```shell
yarn start
```
{{< button relref="./" >}}Back{{< /button >}}
{{< button relref="./02_connect_wallet" >}}Next: Connect to the Ethereum Wallet{{< /button >}}

View File

@ -0,0 +1,201 @@
---
title: Connect to the Ethereum Wallet
date: 2022-01-03T11:00:00+1100
weight: 12
---
# Connect to the Ethereum Wallet
{{< hint info >}}
This section may be skipped if you are adding the poll feature to an existing dApp
that already connects to the user's wallet.
{{< /hint >}}
Delete the template `App` component:
```shell
rm -f App.tsx App.css App.test.tsx
```
## Top bar
Use `TopBar` component to display wallet information.
For that, create a `PollPage` component that includes the top bar and will include the poll elements.
The component uses `ethers` to connect to the user's wallet:
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
)
}
```
## Page
### UseDApp
Create a `config` variable that contains the Ethereum network parameters:
```tsx
import {ChainId, DAppProvider, useEthers} from '@usedapp/core';
import {DEFAULT_CONFIG} from "@usedapp/core/dist/cjs/src/model/config/default";
const config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/your-infura-token',
},
multicallAddresses: {
1: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
3: '0x53c43764255c17bd724f74c4ef150724ac50a3ed',
1337: process.env.GANACHE_MULTICALL_CONTRACT ?? '0x0000000000000000000000000000000000000000',
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
}
```
Replace `your-infura-token` with your [Infura API token](https://infura.io/docs/ethereum).
### Styled-components
[`styled-components`](https://styled-components.com/) is used for easy styling.
Create a `Wrapper` variable to use in the page component:
```tsx
import styled from 'styled-components'
const Wrapper = styled.div`
height: 100%;
width: 100%;
`
```
### Render
Finally, create the `App` component:
```tsx
export function App() {
return (
<Wrapper>
<GlobalStyle/>
<DAppProvider config={config}>
<PollPage/>
</DAppProvider>
</Wrapper>
)
}
```
Your `index.tsx` should now be:
```tsx
import {ChainId, DAppProvider, useEthers} from '@usedapp/core';
import {GlobalStyle, TopBar} from '@waku/vote-poll-sdk-react-components';
import React, {useEffect, useState} from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import {JsonRpcSigner} from "@ethersproject/providers";
import {orangeTheme} from "@waku/vote-poll-sdk-react-components/dist/cjs/src/style/themes";
import {DEFAULT_CONFIG} from "@usedapp/core/dist/cjs/src/model/config/default";
import styled from 'styled-components'
const config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/b4451d780cc64a078ccf2181e872cfcf',
},
multicallAddresses: {
1: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
3: '0x53c43764255c17bd724f74c4ef150724ac50a3ed',
1337: process.env.GANACHE_MULTICALL_CONTRACT ?? '0x0000000000000000000000000000000000000000',
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
}
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
)
}
export function App() {
return (
<Wrapper>
<GlobalStyle/>
<DAppProvider config={config}>
<PollPage/>
</DAppProvider>
</Wrapper>
)
}
const Wrapper = styled.div`
height: 100%;
width: 100%;
`
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
);
```
{{< button relref="./01_create_dapp" >}}Back{{< /button >}}
{{< button relref="./03_create-a-poll_button" >}}Next: Create-A-Poll Button{{< /button >}}

View File

@ -0,0 +1,128 @@
---
title: Create-A-Poll Button
date: 2022-01-03T11:00:00+1100
weight: 13
---
# Create-A-Poll Button
Create the `Poll` component,
it will allow the user to create a new poll, view polls and answer them.
We'll start by adding a button to create a poll.
```shell
mkdir components
touch components/Poll.tsx
```
## Styled-components
Again, create a `Wrapper` for styling:
```tsx
import styled from 'styled-components'
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
max-width: 1146px;
position: relative;
margin: 0 auto;
padding: 150px 32px 50px;
width: 100%;
@media (max-width: 1146px) {
max-width: 780px;
}
@media (max-width: 600px) {
padding: 132px 16px 32px;
}
@media (max-width: 425px) {
padding: 96px 16px 84px;
}
`
```
## Button
Create a button that will display the `PollCreation` component on click.
To create a poll, we need access to the wallet,
thus the button must be disabled if the wallet is not connected.
The button is disabled if `signer` is undefined.
To give a visual clue to the user, also make the button grey when disabled.
Upon clicking the button, we set `showPollCreation` to true.
`showPollCreation` will control when to render the poll creation modal.
`components/Poll.tsx`:
```tsx
import {useState} from 'react'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
type PollProps = {
signer: JsonRpcSigner | undefined
theme: Theme
}
export function Poll({signer, theme}: PollProps) {
const [showPollCreation, setShowPollCreation] = useState(false)
const disabled = !signer;
return (
<Wrapper>
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
</Wrapper>
)
}
```
Now update the `PollPage` component to render the new `Poll` component:
`index.tsx`:
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll theme={orangeTheme} signer={signer}/>
</div>
)
}
```
Now, you have a button:
![Create a poll button](/assets/poll_sdk/create-poll-button.png)
{{< button relref="./02_connect_wallet" >}}Back{{< /button >}}
{{< button relref="./04_poll_creation" >}}Next: Poll Creation Component{{< /button >}}

View File

@ -0,0 +1,112 @@
---
title: Poll Creation Component
date: 2022-01-03T11:00:00+1100
weight: 14
---
# Poll Creation Component
The Poll SDK provide an off-the-shelf component to create a new poll: `PollCreation`.
It takes in a `WakuPolling` hook that can created with `useWakuPolling`.
`useWakuPolling` takes:
- `appName`: Your app name.
It is used to generate a unique content topic for your polls.
See [How to Choose a Content Topic](/docs/guides/01_choose_content_topic/) for more information.
- `tokenAddress`: The address of your ERC-20 token.
Only token holders can create and answer polls.
- `provider`: The Web3 provider to access the blockchain.
- `multicallAddress`: Address to this blockchain's multicall contract.
Add these parameters to `PollProps` and call `useWakuPolling`.
`components/Poll.tsx`
```tsx
import {useState} from 'react'
import {useConfig} from '@usedapp/core'
import {PollCreation} from '@waku/poll-sdk-react-components'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {useWakuPolling} from '@waku/poll-sdk-react-hooks'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
import {ChainId} from "@usedapp/core/src/constants";
type PollProps = {
appName: string
library: Web3Provider | undefined
signer: JsonRpcSigner | undefined
chainId: ChainId | undefined
theme: Theme
tokenAddress: string
}
export function Poll({appName, library, signer, chainId, theme, tokenAddress}: PollProps) {
const config = useConfig()
const [showPollCreation, setShowPollCreation] = useState(false)
const wakuPolling = useWakuPolling(appName, tokenAddress, library, config?.multicallAddresses?.[chainId ?? 1337])
const disabled = !signer;
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme}/>
)}
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
</Wrapper>
)
}
```
Then pass them in `PollPage`.
In this example, we use `demo-poll-dapp` for the app name and the mainnet SNT token contract for the token address.
Replace those with your own.
`index.tsx`
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate, chainId} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll theme={orangeTheme} appName={'demo-poll-dapp'} library={library} signer={signer} chainId={chainId}
tokenAddress={'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'}/>
</div>
)
}
```
You can now see the poll creation modal when clicking on the button:
![Create a poll modal](/assets/poll_sdk/create-a-poll-component.png)
![Confirmation modal](/assets/poll_sdk/poll-created.png)
{{< button relref="./03_create-a-poll_button" >}}Back{{< /button >}}
{{< button relref="./05_poll_list" >}}Next: Poll List Component{{< /button >}}

View File

@ -0,0 +1,88 @@
---
title: Poll List Component
date: 2022-01-03T11:00:00+1100
weight: 15
---
# Poll List Component
To display existing polls, the `PollList` component is provided.
Simply add it to the `Poll` function to render it.
It needs the `account` variable that can be passed as a property to `Poll`:
`components/Poll.tsx`:
```tsx
import {useState} from 'react'
import {useConfig} from '@usedapp/core'
import {PollCreation, PollList} from '@waku/poll-sdk-react-components'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {useWakuPolling} from '@waku/poll-sdk-react-hooks'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
import {ChainId} from "@usedapp/core/src/constants";
type PollProps = {
appName: string
library: Web3Provider | undefined
signer: JsonRpcSigner | undefined
chainId: ChainId | undefined
account: string | null | undefined
theme: Theme
tokenAddress: string
}
export function Poll({appName, library, signer, chainId, account, theme, tokenAddress}: PollProps) {
const config = useConfig()
const [showPollCreation, setShowPollCreation] = useState(false)
const wakuPolling = useWakuPolling(appName, tokenAddress, library, config?.multicallAddresses?.[chainId ?? 1337])
const disabled = !signer;
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme}/>
)}
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
<PollList wakuPolling={wakuPolling} account={account} theme={theme} />
</Wrapper>
)
}
```
Pass the `account` to `Poll` in `index.tsx`:
```tsx
<Poll theme={orangeTheme} appName={'demo-poll-dapp'} library={library} signer={signer} chainId={chainId}
account={account}
tokenAddress={'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'}/>
```
Et voila!
The `PollList` component handles the display of polls:
![Poll List](/assets/poll_sdk/listed-polls.png)
And answering them:
![Poll list with answered](/assets/poll_sdk/listed-polls-with-answer.png)
You can find the resulting code in the [examples folder](https://github.com/status-im/wakuconnect-vote-poll-sdk/tree/main/examples/mainnet-poll).
{{< hint info >}}
The example above uses webpack 5 instead of react-app-rewired.
It also allows passing a token contract address in the url, as described in the [README](https://github.com/status-im/wakuconnect-vote-poll-sdk/blob/main/examples/mainnet-poll/README.md).
{{< /hint >}}
The final gif:
![Poll demo](/assets/poll_sdk/wakuconnect-poll-demo.gif)
{{< button relref="./04_poll_creation" >}}Back{{< /button >}}

View File

@ -0,0 +1,20 @@
---
title: Poll SDK
date: 2022-01-03T11:00:00+1100
weight: 1
---
# How to Use the WakuConnect Poll SDK
To demonstrate how to use the WakuConnect Poll SDK in your dApp, we will create a TypeScript React app from scratch.
You can then adapt the steps depending on your dApp configuration and build setup.
The resulting code of this guide can be found at
https://github.com/status-im/wakuconnect-vote-poll-sdk/tree/main/examples/mainnet-poll.
Here is a preview of the end result:
![Poll demo](/assets/poll_sdk/wakuconnect-poll-demo.gif)
{{< button relref="./01_create_dapp" >}}Get Started{{< /button >}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB