diff --git a/packages/mainnet-poll-example/.eslintrc.json b/packages/mainnet-poll-example/.eslintrc.json new file mode 100644 index 0000000..128cd29 --- /dev/null +++ b/packages/mainnet-poll-example/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../.eslintrc.json" + ] +} diff --git a/packages/mainnet-poll-example/.mocharc.json b/packages/mainnet-poll-example/.mocharc.json new file mode 100644 index 0000000..59d511f --- /dev/null +++ b/packages/mainnet-poll-example/.mocharc.json @@ -0,0 +1,8 @@ +{ + "spec": "test/**/*.test.{ts,tsx}", + "require": "ts-node/register", + "watchExtensions": "ts", + "extension": "ts", + "timeout": 3000, + "file": "./test/setup.ts" +} diff --git a/packages/mainnet-poll-example/README.md b/packages/mainnet-poll-example/README.md new file mode 100644 index 0000000..58e5604 --- /dev/null +++ b/packages/mainnet-poll-example/README.md @@ -0,0 +1 @@ +# Mainnet DappConnect Poll SDK Example diff --git a/packages/mainnet-poll-example/package.json b/packages/mainnet-poll-example/package.json new file mode 100644 index 0000000..080f09e --- /dev/null +++ b/packages/mainnet-poll-example/package.json @@ -0,0 +1,47 @@ +{ + "name": "@dappconnect/mainnet-poll-sdk-example", + "version": "0.1.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "clean:all": "yarn clean && rimraf node_modules/", + "clean": "rimraf dist/", + "build": "rm -rf dist && webpack --mode=production --env ENV=production", + "start": "webpack serve --mode=development --env ENV=development --https --port 8181", + "test": "mocha -r jsdom-global/register", + "lint": "yarn lint:prettier --check && yarn lint:eslint", + "lint:fix": "yarn lint:prettier --write && yarn lint:eslint --fix", + "lint:eslint": "eslint './{src,test}/**/*.{ts,tsx}'", + "lint:prettier": "yarn prettier './{src,test}/**/*.{ts,tsx}'" + }, + "dependencies": { + "@dappconnect/vote-poll-sdk-core": "^0.1.0", + "@dappconnect/poll-sdk-react-components": "^0.1.0", + "@dappconnect/poll-sdk-react-hooks": "^0.1.0", + "@dappconnect/vote-poll-sdk-react-components": "^0.1.0", + "@usedapp/core": "^0.4.7", + "ethers": "^5.4.4", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-router-dom": "^5.2.0", + "styled-components": "^5.3.0" + }, + "devDependencies": { + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.16", + "@types/styled-components": "^5.1.12", + "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/parser": "^4.29.0", + "chai": "^4.3.4", + "copyfiles": "^2.4.1", + "eslint": "^7.32.0", + "jsdom": "^16.7.0", + "jsdom-global": "^3.0.2", + "mocha": "^9.0.3", + "npm-watch": "^0.11.0", + "rimraf": "^3.0.2", + "ts-node": "^10.1.0", + "typescript": "^4.3.5" + } +} diff --git a/packages/mainnet-poll-example/prettier.config.js b/packages/mainnet-poll-example/prettier.config.js new file mode 100644 index 0000000..3bcd29e --- /dev/null +++ b/packages/mainnet-poll-example/prettier.config.js @@ -0,0 +1 @@ +module.exports = require('../../.prettierrc.json') diff --git a/packages/mainnet-poll-example/src/_redirects b/packages/mainnet-poll-example/src/_redirects new file mode 100644 index 0000000..ad37e2c --- /dev/null +++ b/packages/mainnet-poll-example/src/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/packages/mainnet-poll-example/src/assets/assets.d.ts b/packages/mainnet-poll-example/src/assets/assets.d.ts new file mode 100644 index 0000000..4ce4da1 --- /dev/null +++ b/packages/mainnet-poll-example/src/assets/assets.d.ts @@ -0,0 +1,14 @@ +declare module '*.svg' { + const url: string + export default url +} + +declare module '*.jpg' { + const url: string + export default url +} + +declare module '*.png' { + const url: string + export default url +} diff --git a/packages/mainnet-poll-example/src/assets/images/pollingIcon.png b/packages/mainnet-poll-example/src/assets/images/pollingIcon.png new file mode 100644 index 0000000..1534250 Binary files /dev/null and b/packages/mainnet-poll-example/src/assets/images/pollingIcon.png differ diff --git a/packages/mainnet-poll-example/src/components/WakuPolling.tsx b/packages/mainnet-poll-example/src/components/WakuPolling.tsx new file mode 100644 index 0000000..dcf8e8a --- /dev/null +++ b/packages/mainnet-poll-example/src/components/WakuPolling.tsx @@ -0,0 +1,74 @@ +import React, { useState, useEffect } from 'react' +import { useConfig, useEthers } from '@usedapp/core' + +import styled from 'styled-components' +import { PollList, PollCreation } from '@dappconnect/poll-sdk-react-components' +import { JsonRpcSigner } from '@ethersproject/providers' +import { useWakuPolling } from '@dappconnect/poll-sdk-react-hooks' +import { Modal, Networks, CreateButton } from '@dappconnect/vote-poll-sdk-react-components' +import { Theme } from '@dappconnect/vote-poll-sdk-react-components/dist/esm/src/style/themes' + +type WakuPollingProps = { + appName: string + signer: JsonRpcSigner | undefined + theme: Theme + tokenAddress: string +} + +export function WakuPolling({ appName, signer, theme, tokenAddress }: WakuPollingProps) { + const { activateBrowserWallet, account, library, chainId } = useEthers() + const config = useConfig() + const [showPollCreation, setShowPollCreation] = useState(false) + const [selectConnect, setSelectConnect] = useState(false) + const wakuPolling = useWakuPolling(appName, tokenAddress, library, config?.multicallAddresses?.[chainId ?? 1337]) + return ( + + {showPollCreation && signer && ( + + )} + {account ? ( + setShowPollCreation(true)}> + Create a poll + + ) : ( + { + if ((window as any)?.ethereum) { + activateBrowserWallet() + } else setSelectConnect(true) + }} + > + Connect to vote + + )} + {selectConnect && ( + + + + )} + + + + ) +} + +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; + } +` diff --git a/packages/mainnet-poll-example/src/index.html b/packages/mainnet-poll-example/src/index.html new file mode 100644 index 0000000..067de06 --- /dev/null +++ b/packages/mainnet-poll-example/src/index.html @@ -0,0 +1,16 @@ + + + + + + + + + Polling Dapp + + +
+ + + + diff --git a/packages/mainnet-poll-example/src/index.tsx b/packages/mainnet-poll-example/src/index.tsx new file mode 100644 index 0000000..05a5004 --- /dev/null +++ b/packages/mainnet-poll-example/src/index.tsx @@ -0,0 +1,91 @@ +import React, { useEffect, useState } from 'react' +import styled from 'styled-components' +import { DAppProvider, ChainId, useEthers } from '@usedapp/core' +import { DEFAULT_CONFIG } from '@usedapp/core/dist/cjs/src/model/config/default' +import { WakuPolling } from './components/WakuPolling' +import { TopBar, GlobalStyle } from '@dappconnect/vote-poll-sdk-react-components' +import pollingIcon from './assets/images/pollingIcon.png' +import { JsonRpcSigner } from '@ethersproject/providers' +import { orangeTheme } from '@dappconnect/vote-poll-sdk-react-components/dist/esm/src/style/themes' +import ReactDOM from 'react-dom' +import { BrowserRouter } from 'react-router-dom' +import { Route, Switch } from 'react-router' +import { useLocation } from 'react-router-dom' + +const sntTokenAddress = '0x744d70FDBE2Ba4CF95131626614a1763DF805B9E' + +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 Polling({ tokenAddress }: { tokenAddress: string }) { + const { account, library, activateBrowserWallet, deactivate } = useEthers() + const [signer, setSigner] = useState(undefined) + + useEffect(() => { + setSigner(library?.getSigner()) + }, [account]) + + return ( + + + + + ) +} + +export function PollingPage() { + const location = useLocation() + const tokenAddress = new URLSearchParams(location.search).get('token') + + return ( + + + + + + + ) +} + +const Page = styled.div` + height: 100%; + width: 100%; +` + +const Wrapper = styled.div` + height: 100%; + width: 100%; +` + +ReactDOM.render( +
+ + + + + +
, + document.getElementById('root') +) diff --git a/packages/mainnet-poll-example/test/index.test.ts b/packages/mainnet-poll-example/test/index.test.ts new file mode 100644 index 0000000..1dba999 --- /dev/null +++ b/packages/mainnet-poll-example/test/index.test.ts @@ -0,0 +1,7 @@ +import { expect } from 'chai' + +describe('test react-components', () => { + it('foo', async () => { + expect('Hello world').to.eq('Hello world') + }) +}) diff --git a/packages/mainnet-poll-example/test/setup.ts b/packages/mainnet-poll-example/test/setup.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/mainnet-poll-example/tsconfig.json b/packages/mainnet-poll-example/tsconfig.json new file mode 100644 index 0000000..4bd6269 --- /dev/null +++ b/packages/mainnet-poll-example/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "outDir": "dist", + "jsx":"react", + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": true, + "downlevelIteration": true, + "strict": true, + "composite": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "target": "es6", + "typeRoots": [ "./types", "./node_modules/@types", "../../node_modules/@types"] + }, + "include": [ + "src", + "src/**/*.json", + "test" + ] +} diff --git a/packages/mainnet-poll-example/webpack.config.js b/packages/mainnet-poll-example/webpack.config.js new file mode 100644 index 0000000..cc8a6a8 --- /dev/null +++ b/packages/mainnet-poll-example/webpack.config.js @@ -0,0 +1,82 @@ +const path = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') +const webpack = require('webpack') +const { ESBuildMinifyPlugin } = require('esbuild-loader') + +module.exports = (env) => { + let environment = 'development' + if (env.ENV) { + environment = env.ENV + } + + return { + entry: './src/index.tsx', + output: { + filename: 'index.[fullhash].js', + path: path.join(__dirname, 'dist'), + publicPath: "/", + }, + devtool: 'source-map', + resolve: { + extensions: ['.ts', '.tsx', '.js', '.json'], + fallback: { + "buffer": require.resolve("buffer/"), + "crypto": require.resolve("crypto-browserify"), + "stream": require.resolve("stream-browserify"), + "assert": require.resolve("assert") + } + }, + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'esbuild-loader', + exclude: /node_modules/, + options: { + loader: 'tsx', + target: 'es2018', + }, + }, + { + enforce: 'pre', + test: /\.js$/, + exclude: /node_modules/, + loader: 'source-map-loader' + }, + { + test: /\.(png|svg|jpg|gif|woff|woff2|eot|ttf|otf|ico)$/, + use: ['file-loader'], + }, + ], + }, + optimization: { + minimizer: [ + new ESBuildMinifyPlugin({ + target: 'es2018', + }), + ], + }, + plugins: [ + new ForkTsCheckerWebpackPlugin(), + new HtmlWebpackPlugin({ + template: 'src/index.html', + }), + new webpack.DefinePlugin({ + 'process.env.ENV': JSON.stringify(environment), + }), + new webpack.ProvidePlugin({ + process: 'process/browser.js', + Buffer: ['buffer', 'Buffer'], + }), + ], + devServer: { + historyApiFallback: true, + host: '0.0.0.0', + stats: 'errors-only', + overlay: true, + hot: true, + }, + stats: 'minimal' + } +} \ No newline at end of file diff --git a/packages/poll-react-components/src/components/Poll.tsx b/packages/poll-react-components/src/components/Poll.tsx index 3ad6c79..272c7d0 100644 --- a/packages/poll-react-components/src/components/Poll.tsx +++ b/packages/poll-react-components/src/components/Poll.tsx @@ -55,12 +55,12 @@ export function Poll({ poll, wakuPolling, theme, account }: PollProps) { {userInVoters < 0 && ( { - if (wakuPolling && account) { + if (wakuPolling && account && selectedAnswer !== undefined) { const result = await wakuPolling.sendTimedPollVote( poll.poll.id, - selectedAnswer ?? 0, + selectedAnswer, poll.poll.pollType === PollType.WEIGHTED ? BigNumber.from(tokenAmount) : undefined ) if (result === 1) { diff --git a/packages/poll-react-components/src/components/PollCreation.tsx b/packages/poll-react-components/src/components/PollCreation.tsx index b7bda20..35c7ce0 100644 --- a/packages/poll-react-components/src/components/PollCreation.tsx +++ b/packages/poll-react-components/src/components/PollCreation.tsx @@ -5,6 +5,8 @@ import { WakuPolling } from '@dappconnect/vote-poll-sdk-core' import { Input, addIcon, SmallButton, Modal, Theme } from '@dappconnect/vote-poll-sdk-react-components' import { MESSAGE_SENDING_RESULT } from '@dappconnect/vote-poll-sdk-core/dist/esm/src/classes/WakuPolling' +const defaultPollDuration = 7 * 24 * 60 * 60 * 1000 // One week in ms. + function getLocaleIsoTime(dateTime: Date) { const MS_PER_MINUTE = 60000 const milliseconds = dateTime.getTime() - dateTime.getTimezoneOffset() * MS_PER_MINUTE @@ -45,7 +47,7 @@ export function PollCreation({ wakuPolling, theme, setShowPollCreation }: PollCr const [showCreateConfirmation, setShowCreateConfirmation] = useState(false) const [showNotEnoughTokens, setShowNotEnoughTokens] = useState(false) const [selectedType, setSelectedType] = useState(PollType.NON_WEIGHTED) - const [endTimePicker, setEndTimePicker] = useState(new Date(new Date().getTime() + 10000000)) + const [endTimePicker, setEndTimePicker] = useState(new Date(new Date().getTime() + defaultPollDuration)) return (