diff --git a/config.yaml b/config.yaml index 202a166..42825d8 100644 --- a/config.yaml +++ b/config.yaml @@ -32,7 +32,7 @@ params: BookTheme: 'dark' BookToC: true BookSection: 'docs' - BookDateFormat: '2006/02/01' + BookDateFormat: 'Jan 2, 2006' BookSearch: true # GitHub edit links diff --git a/content/docs/guides/vote_poll_sdk/_index.md b/content/docs/guides/vote_poll_sdk/_index.md new file mode 100644 index 0000000..1d063ae --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/_index.md @@ -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). diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/01_create_dapp.md b/content/docs/guides/vote_poll_sdk/poll_sdk/01_create_dapp.md new file mode 100644 index 0000000..ee3b405 --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/01_create_dapp.md @@ -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 >}} diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet.md b/content/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet.md new file mode 100644 index 0000000..90a16e0 --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet.md @@ -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) + + useEffect(() => { + if (account) { + setSigner(library?.getSigner()) + } else { + // Deactivate signer if signed out + setSigner(undefined) + } + }, [account]) + + return ( +
+ +
+ ) +} +``` + +## 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 ( + + + + + + + ) +} +``` + +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) + + useEffect(() => { + if (account) { + setSigner(library?.getSigner()) + } else { + // Deactivate signer if signed out + setSigner(undefined) + } + }, [account]) + + return ( +
+ +
+ ) +} + +export function App() { + return ( + + + + + + + ) +} + + +const Wrapper = styled.div` + height: 100%; + width: 100%; +` + +ReactDOM.render( + + + , + document.getElementById('root') +); +``` + +{{< button relref="./01_create_dapp" >}}Back{{< /button >}} +{{< button relref="./03_create-a-poll_button" >}}Next: Create-A-Poll Button{{< /button >}} diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/03_create-a-poll_button.md b/content/docs/guides/vote_poll_sdk/poll_sdk/03_create-a-poll_button.md new file mode 100644 index 0000000..f7a33e8 --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/03_create-a-poll_button.md @@ -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 ( + + { + setShowPollCreation(true)}> + Create a poll + + } + + ) +} +``` + +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) + + useEffect(() => { + if (account) { + setSigner(library?.getSigner()) + } else { + // Deactivate signer if signed out + setSigner(undefined) + } + }, [account]) + + return ( +
+ + +
+ ) +} +``` + +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 >}} diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/04_poll_creation.md b/content/docs/guides/vote_poll_sdk/poll_sdk/04_poll_creation.md new file mode 100644 index 0000000..109aced --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/04_poll_creation.md @@ -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 ( + + {showPollCreation && signer && ( + + )} + { + setShowPollCreation(true)}> + Create a poll + + } + + ) +} +``` + +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) + + useEffect(() => { + if (account) { + setSigner(library?.getSigner()) + } else { + // Deactivate signer if signed out + setSigner(undefined) + } + }, [account]) + + return ( +
+ + +
+ ) +} +``` + +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 >}} diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list.md b/content/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list.md new file mode 100644 index 0000000..0689ad0 --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list.md @@ -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 ( + + {showPollCreation && signer && ( + + )} + { + setShowPollCreation(true)}> + Create a poll + + } + + + ) +} +``` + +Pass the `account` to `Poll` in `index.tsx`: +```tsx + +``` + +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 >}} diff --git a/content/docs/guides/vote_poll_sdk/poll_sdk/_index.md b/content/docs/guides/vote_poll_sdk/poll_sdk/_index.md new file mode 100644 index 0000000..6e8f0b9 --- /dev/null +++ b/content/docs/guides/vote_poll_sdk/poll_sdk/_index.md @@ -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 >}} diff --git a/static/assets/poll_sdk/create-a-poll-component.png b/static/assets/poll_sdk/create-a-poll-component.png new file mode 100644 index 0000000..6baa1a2 Binary files /dev/null and b/static/assets/poll_sdk/create-a-poll-component.png differ diff --git a/static/assets/poll_sdk/create-poll-button.png b/static/assets/poll_sdk/create-poll-button.png new file mode 100644 index 0000000..de98b53 Binary files /dev/null and b/static/assets/poll_sdk/create-poll-button.png differ diff --git a/static/assets/poll_sdk/listed-polls-with-answer.png b/static/assets/poll_sdk/listed-polls-with-answer.png new file mode 100644 index 0000000..ea9192b Binary files /dev/null and b/static/assets/poll_sdk/listed-polls-with-answer.png differ diff --git a/static/assets/poll_sdk/listed-polls.png b/static/assets/poll_sdk/listed-polls.png new file mode 100644 index 0000000..3c380d4 Binary files /dev/null and b/static/assets/poll_sdk/listed-polls.png differ diff --git a/static/assets/poll_sdk/poll-created.png b/static/assets/poll_sdk/poll-created.png new file mode 100644 index 0000000..996da76 Binary files /dev/null and b/static/assets/poll_sdk/poll-created.png differ diff --git a/static/assets/poll_sdk/sign-message.png b/static/assets/poll_sdk/sign-message.png new file mode 100644 index 0000000..e0d0997 Binary files /dev/null and b/static/assets/poll_sdk/sign-message.png differ diff --git a/static/assets/poll_sdk/wakuconnect-poll-demo.gif b/static/assets/poll_sdk/wakuconnect-poll-demo.gif new file mode 100644 index 0000000..0c86c38 Binary files /dev/null and b/static/assets/poll_sdk/wakuconnect-poll-demo.gif differ