diff --git a/404.html b/404.html index 3f7a549..ca68e26 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@ - + + + + + + +
+ + +
+
+ +
+ + + Send and Receive Messages Using Waku Relay With Angular v13 + + +
+ + + + + + +
+ + + +

+ Send and Receive Messages Using Waku Relay With Angular v13 + # +

+

It is easy to use Waku Connect with Angular v13.

+

In this guide, we will demonstrate how your Angular dApp can use Waku Relay +to send and receive messages.

+

Before starting, you need to choose a Content Topic for your dApp. +Check out the how to choose a content topic guide to learn more about content topics.

+

For this guide, we are using a single content topic: /relay-angular-chat/1/chat/proto.

+

+ Setup + # +

+

Create a new Angular app:

+
npm install -g @angular/cli
+ng new relay-angular-chat
+cd relay-angular-chat
+

+ BigInt + # +

+

Some of js-waku’s dependencies use BigInt +that is only supported by modern browsers.

+

To ensure that Angular properly transpiles your webapp code, add the following configuration to the package.json file:

+
{
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not ie <= 99",
+      "not android <= 4.4.4",
+      "not dead",
+      "not op_mini all"
+    ]
+  }
+}
+

+ Polyfills + # +

+

A number of Web3 and libp2p dependencies need polyfills. +These must be explicitly declared when using webpack 5.

+

The latest Angular version (v13) uses webpack 5.

+

We will describe below a method to configure polyfills when using Angular v13 / webpack v5. +This may not be necessary if you use webpack 4.

+

Start by installing the polyfill libraries:

+
yarn add assert buffer crypto-browserify process stream-browserify
+

Then add the following code to src/polyfills.ts:

+
import * as process from 'process';
+(window as any).process = process;
+(window as any).global = window;
+global.Buffer = global.Buffer || require('buffer').Buffer;
+

Now tell Angular where to find these libraries by adding the following to tsconfig.json +under "compilerOptions":

+
{
+  "paths": {
+    "assert": ["node_modules/assert"],
+    "buffer": ["node_modules/buffer"],
+    "crypto": ["node_modules/crypto-browserify"],
+    "stream": ["node_modules/stream-browserify"]
+  }
+}
+

Now under "angularCompilerOptions", add:

+
"allowSyntheticDefaultImports": true
+

Finally, set the "target" to be "es2020" due to the aforementioned BigInt usage.

+

+ Module loading warnings + # +

+

There will be some warnings due to module loading. +We can fix them by setting the "allowedCommonJsDependencies" key under +architect -> build -> options with the following:

+
{
+  "allowedCommonJsDependencies": [
+    "libp2p-gossipsub/src/utils",
+    "rlp",
+    "multiaddr/src/convert",
+    "varint",
+    "multihashes",
+    "@chainsafe/libp2p-noise/dist/src/noise",
+    "debug",
+    "libp2p",
+    "libp2p-bootstrap",
+    "libp2p-crypto",
+    "libp2p-websockets",
+    "libp2p-websockets/src/filters",
+    "libp2p/src/ping",
+    "multiaddr",
+    "peer-id",
+    "buffer",
+    "crypto",
+    "ecies-geth",
+    "secp256k1",
+    "libp2p-gossipsub",
+    "it-concat",
+    "protons"
+  ]
+}
+

+ Types + # +

+

There are some type definitions we need to install and some that we don’t have.

+
yarn add @types/bl protons
+

Create a new folder under src named @types with the following structure:

+
src/@types
+├── protons
+│   └── types.d.ts
+└── time-cache
+    └── types.d.ts
+

In the protons/types.d.ts file add:

+
declare module 'protons';
+

In the time-cache/types.d.ts file add:

+
declare module "time-cache" {
+
+  interface TimeCacheInterface {
+    put(key: string, value: any, validity: number): void;
+    get(key: string): any;
+    has(key: string): boolean;
+  }
+
+  type TimeCache = TimeCacheInterface;
+
+  function TimeCache(options: object): TimeCache;
+
+  export = TimeCache;
+}
+

+ js-waku + # +

+

Then, install js-waku:

+
yarn add js-waku
+

Start the dev server and open the dApp in your browser:

+
yarn run start
+

+ Create Waku Instance + # +

+

In order to interact with the Waku network, you first need a Waku instance. +We’re going to wrap the js-waku library in a Service so we can inject it to different components when needed.

+

Generate the Waku service:

+
ng generate service waku
+

Go to waku.service.ts and add the following imports:

+
import { Waku } from "js-waku";
+import { ReplaySubject } from "rxjs";
+

replace the WakuService class with the following:

+
export class WakuService {
+
+  // Create Subject Observable to 'store' the Waku instance
+  private wakuSubject = new Subject<Waku>();
+  public waku = this.wakuSubject.asObservable();
+
+  // Create BehaviorSubject Observable to 'store' the Waku status
+  private wakuStatusSubject = new BehaviorSubject('');
+  public wakuStatus = this.wakuStatusSubject.asObservable();
+
+  constructor() { }
+
+  init() {
+    // Connect node
+    Waku.create({ bootstrap: { default: true } }).then(waku => {
+      // Update Observable values
+      this.wakuSubject.next(waku);
+      this.wakuStatusSubject.next('Connecting...');
+
+      waku.waitForRemotePeer().then(() => {
+        // Update Observable value
+        this.wakuStatusSubject.next('Connected');
+      });
+    });
+  }
+}
+

When using the bootstrap option, it may take some time to connect to other peers. +That’s why we use the waku.waitForRemotePeer function to ensure that there are relay peers available to send and receive messages.

+

Now we can inject the WakuService in to the AppComponent class to initialise the node and +subscribe to any status changes.

+

Firstly, import the WakuService:

+
import { WakuService } from "./waku.service";
+

Then update the AppComponent class with the following:

+
export class AppComponent {
+
+  title: string = 'relay-angular-chat';
+  wakuStatus!: string;
+
+  // Inject the service
+  constructor(private wakuService: WakuService) {}
+
+  ngOnInit(): void {
+    // Call the `init` function on the service
+    this.wakuService.init();
+    // Subscribe to the `wakuStatus` Observable and update the property when it changes
+    this.wakuService.wakuStatus.subscribe(wakuStatus => {
+      this.wakuStatus = wakuStatus;
+    });
+  }
+}
+

Add the following HTML to the app.component.html to show the title and render the connection status:

+
<h1>{{title}}</h1>
+<p>Waku node's status: {{ wakuStatus }}</p>
+

+ Messages + # +

+

Now we need to create a component to send, receive and render the messages.

+
ng generate component messages
+

You might need to add this to NgModule for Angular to pick up the new component. +Import and add MessagesComponent to the declarations array in app.module.ts.

+

We’re going to need the WakuService again but also the Waku and WakuMessage classes from js-waku. +We already installed protons +and we’re going to use that here so we’ll need to import it.

+
import { WakuService } from "../waku.service";
+import { Waku, WakuMessage } from "js-waku";
+import protons from "protons";
+

Let’s use protons to define the Protobuf message format with two fields: +timestamp and text:

+
const proto = protons(`
+message SimpleChatMessage {
+  uint64 timestamp = 1;
+  string text = 2;
+}
+`);
+

Let’s also define a message interface:

+
interface MessageInterface {
+  timestamp: Date;
+  text: string;
+}
+

+ Send Messages + # +

+

In order to send a message, we need to define a few things.

+

The contentTopic is the topic we want subscribe to and the payload is the message. +We’ve also defined a timestamp so let’s create that.

+

The messageCount property is just to distinguish between messages.

+

We also need our waku instance and wakuStatus property. +We will subscribe to the waku and wakuStatus Observables from the WakuService to get them.

+
export class MessagesComponent {
+
+  contentTopic: string = `/relay-angular-chat/1/chat/proto`;
+  messageCount: number = 0;
+  waku!: Waku;
+
+  // ...
+
+  // Inject the `WakuService`
+  constructor(private wakuService: WakuService) { }
+
+  ngOnInit(): void {
+    // Subscribe to the `wakuStatus` Observable and update the property when it changes
+    this.wakuService.wakuStatus.subscribe(wakuStatus => {
+      this.wakuStatus = wakuStatus;
+    });
+
+    // Subscribe to the `waku` Observable and update the property when it changes
+    this.wakuService.waku.subscribe(waku => {
+      this.waku = waku;
+    });
+  }
+
+  sendMessage(): void {
+    const time = new Date().getTime();
+
+    const payload = proto.SimpleChatMessage.encode({
+      timestamp: time,
+      text: `Here is a message #${this.messageCount}`,
+    });
+
+    WakuMessage.fromBytes(payload, this.contentTopic).then(wakuMessage => {
+      this.waku.relay.send(wakuMessage).then(() => {
+        console.log(`Message #${this.messageCount} sent`);
+        this.messageCount += 1;
+      });
+    });
+  }
+}
+

Then, add a button to the messages.component.html file to wire it up to the sendMessage() function. +It will also disable the button until the node is connected.

+
<button (click)="sendMessage()" [disabled]="wakuStatus !== 'Connected'">Send Message</button>
+

+ Receive Messages + # +

+

To process incoming messages, you need to register an observer on Waku Relay. +First, you need to define the observer function which decodes the message +and pushes it in to the messages array.

+

Again, in the messages.component.ts:

+
export class MessagesComponent {
+  // ...
+  // Store the messages in an array
+  messages: MessageInterface[] = [];
+  // ...
+
+  processIncomingMessages = (wakuMessage: WakuMessage) => {
+    if (!wakuMessage.payload) return;
+
+    const { timestamp, text } = proto.SimpleChatMessage.decode(
+      wakuMessage.payload
+    );
+
+    const time = new Date();
+    time.setTime(timestamp);
+    const message = { text, timestamp: time };
+
+    this.messages.push(message);
+  };
+}
+

We’ll also need to delete the observer when the component gets destroyed +to avoid memory leaks:

+
ngOnDestroy(): void {
+  this.waku.relay.deleteObserver(this.processIncomingMessages, [this.contentTopic]);
+}
+

Angular won’t delete the observer when the page reloads so we’ll have to hook that up ourselves. +Add the following to the ngOnInit() function:

+
window.onbeforeunload = () => this.ngOnDestroy();
+

+ Display Messages + # +

+

Congratulations! The Waku work is now done. +Your dApp is able to send and receive messages using Waku. +For the sake of completeness, let’s display received messages on the page.

+

We’ve already added the messages array and pushed the incoming message to it. +So all we have to do now is render them to the page.

+

In the messages.component.html, add the following under the button:

+
<h2>Messages</h2>
+<ul class="messages">
+  <li *ngFor="let message of messages">
+    <span>{{ message.timestamp }} {{ message.text }}</span>
+  </li>
+</ul>
+

And Voilà! You should now be able to send and receive messages. +Try it out by opening the app from different browsers!

+

You can see the complete code in the Relay Angular Chat Example App.

+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/11_nwaku/index.html b/docs/guides/11_nwaku/index.html new file mode 100644 index 0000000..24ded97 --- /dev/null +++ b/docs/guides/11_nwaku/index.html @@ -0,0 +1,761 @@ + + + + + + + + + + + + + + +Nwaku Service Node | Waku Connect Docs + + + + + + + + + + + +
+ + +
+
+ +
+ + + Nwaku Service Node + + +
+ + + + + + +
+ + + +

+ Nwaku Service Node + # +

+

JS-Waku nodes join the Waku network by connecting to service nodes using secure websocket.

+

Nwaku (prev. nim-waku) +is the reference implementation of the Waku v2 protocol and can be used as a service node.

+

When using { bootstrap: { default: true } }, +the js-waku node connects to a fleet of nwaku nodes operated by Status.

+

It is also possible to deploy your own nwaku node by following these instructions. +Be sure to setup your nwaku node with a valid SSL certificate or js-waku nodes may fail silently to connect to it.

+
+ We are making it easier for operators to run their own nodes, +this is effort is tracked with status-im/nim-waku#828. +
+ +

You may wish to connect your nwaku node to the rest of the fleet. +This can be done with the --staticnode or --dns-discovery-url. +For example:

+
`wakunode2 \
+  --dns-discovery=true \
+  --dns-discovery-url=enrtree://ANTL4SLG2COUILKAPE7EF2BYNL2SHSHVCHLRD5J7ZJLN5R3PRJD2Y@prod.waku.nodes.status.im
+

You can then use bootstrap.peers +to pass the multiaddr of your node.

+

For example (replace the multiaddr with your node’s).

+
import { Waku } from "js-waku";
+
+const waku = await Waku.create({
+  bootstrap: {
+    peers: [
+      "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
+    ],
+  },
+});
+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/angular_relay/index.html b/docs/guides/angular_relay/index.html index df47c49..a08ab55 100644 --- a/docs/guides/angular_relay/index.html +++ b/docs/guides/angular_relay/index.html @@ -23,7 +23,7 @@ For this guide, we are using a single content topic: /relay-angular-chat/1/chat/ - + + + + + + +
+ + +
+
+ +
+ + + Discovery & Bootstrap Nodes + + +
+ + + + + + +
+ + + +

+ Discovery & Bootstrap Nodes + # +

+

This guide explains the discovery and bootstrap mechanisms currently available in js-waku, +their benefits and caveats and how to use them.

+

Node discovery is the mechanism that enables a Waku node to find other nodes. +Waku is a modular protocol, several discovery mechanisms are and will be included in Waku +so that developers can select the best mechanism(s) based for their use case and the user’s environment +(e.g. mobile phone, desktop browser, server, etc).

+

When starting a Waku node, +it needs to connect to other nodes to be able to send, receive and retrieve messages. +Which means there needs to be a discovery mechanism that enable finding other nodes when not connected to any node. +This is called bootstrapping.

+

Once connected, the node needs to find additional peers to have:

+
    +
  • Enough peers in the Waku Relay mesh (target is 6),
  • +
  • Enough peers in reserve, in case current peers are overloaded or go offline,
  • +
  • Peers with specific Waku capabilities (e.g. Store, Light Push, Filter).
  • +
+

For now, we are focusing in making bootstrap discovery protocols available, +research of other discovery protocols is in progress.

+

+ Default Bootstrap Mechanism + # +

+

The default bootstrap mechanism is to connect to the Status' nim-waku prod fleet.

+

To use:

+
const waku = await Waku.create({ bootstrap: { default: true } });
+
+

When creating a Waku node without passing the bootstrap option, +the node does not connect to any remote peer or bootstrap node.

+

As the current strategy is to connect to nodes operated by Status, +we want to ensure that developers consciously opt-in +while providing a friendly developer experience.

+

We intend to change this in the future and enable boostrap by default +once we have implemented more decentralized strategies.

+ +
+ +

+ Predefined Bootstrap Nodes + # +

+

Addresses of nodes hosted by Status are predefined in the codebase:

+

https://github.com/status-im/js-waku/blob/e4024d5c7246535ddab28a4262006915d2db58be/src/lib/discovery/predefined.ts#L48

+

They can be accessed via the getPredefinedBootstrapNodes function.

+

Pros:

+
    +
  • Low latency,
  • +
  • Low resource requirements.
  • +
+

Cons:

+
    +
  • Prone to censorship: node ips can be blocked,
  • +
  • Limited: Static number of nodes,
  • +
  • Poor maintainability: Code needs to be changed to update the list.
  • +
+

+ Nwaku Prod Fleet + # +

+

The nwaku prod fleet run the latest nwaku release. +The fleet aims to provide a stable, yet not warranted, service.

+
import { Waku } from "js-waku";
+
+const waku = await Waku.create({
+  bootstrap: {
+    peers: getPredefinedBootstrapNodes(),
+  },
+});
+

+ Nwaku Test Fleet + # +

+

The nwaku test fleet run the latest commit from nwaku’s master branch. +The fleet is subject to frequent database reset, +hence messages are generally kept in store nodes for a few days at a time.

+
import { Waku, discovery } from "js-waku";
+
+const waku = await Waku.create({
+  bootstrap: {
+    peers: getPredefinedBootstrapNodes(discovery.predefined.Fleet.Test),
+  },
+});
+

+ Use your own nodes + # +

+

Developers have the choice to run their own nwaku nodes +and use them to bootstrap js-waku nodes.

+

There are two ways to set bootstrap nodes:

+
    +
  1. Using an array of multiaddrs (as string or Multiaddr):
  2. +
+
import { Waku } from "js-waku";
+
+const waku = await Waku.create({
+  bootstrap: {
+    peers: [
+      "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
+    ],
+  },
+});
+
    +
  1. Passing an async function that returns an array of multiaddr (as string or Multiaddr):
  2. +
+
import { Waku } from "js-waku";
+
+const waku = await Waku.create({
+  bootstrap: {
+    getPeers: async () => {
+      const addrs = [];
+      // Fetch the multiaddrs from somewhere...
+      return addrs;
+    },
+  },
+});
+
+ Read Nwaku Service Node to learn how to run your own node. +
+ +

Pros & Cons: Same than Predefined Bootstrap Nodes

+

+ DNS Discovery + # +

+

EIP-1459: Node Discovery via DNS has been implemented in js-waku, nwaku and go-waku +with some modification on the ENR format.

+

DNS Discovery enables anyone to register an ENR tree in the TXT field of a domain name.

+

ENR is the format used to store node connection details (ip, port, multiaddr, etc).

+

This enables a separation of software development and operations +as dApp developers can include one or several domain names to use for DNS discovery, +while operators can handle the update of the dns record.

+

It also enables more decentralized bootstrapping as anyone can register a domain name and publish it for others to use.

+
+

While this method is implemented in js-waku, +it is currently not recommended to use due to a bug in the websocket implementation of nwaku.

+

The nwaku prod fleet and test fleet have a websockify +instance deployed alongside nwaku, acting as a work around the nwaku websocket bug.

+ +
+ +

Pros:

+
    +
  • Low latency, low resource requirements,
  • +
  • Bootstrap list can be updated by editing a domain name: no code change is needed,
  • +
  • Can reference to a greater list of nodes by pointing to other domain names in the code or in the ENR tree.
  • +
+

Cons:

+
    +
  • Prone to censorship: domain names can be blocked,
  • +
  • Limited: Static number of nodes, operators must provide their ENR to the domain owner to get their node listed.
  • +
+

+ Other Discovery Mechanisms + # +

+

Other discovery mechanisms such as gossipsub peer exchange, discv5, etc are currently being research and developed.

+

They aim to improve the current status quo in the following aspects:

+
    +
  • More decentralized mechanisms: Less reliance on specific entities,
  • +
  • Less setup for node operators: Enabling their nodes to be discovered by connecting to bootstrap nodes, +without having to update a domain name.
  • +
+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/index.html b/docs/guides/index.html index f251cee..da45561 100644 --- a/docs/guides/index.html +++ b/docs/guides/index.html @@ -3,7 +3,7 @@ - + @@ -15,7 +15,7 @@ - + + + + + + +
+ + +
+
+ +
+ + + Create the DApp and Install Dependencies + + +
+ + + + + + +
+ + + +

+ 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 to access the blockchain.

+
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
+
+

@usedapp/core must be frozen to version 0.4.7 due to incompatibility between minor versions of ethers.

+

Waku Connect 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.

+ +
+ +

+ 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:

+
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.

+

+ 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:

+
yarn add -D react-app-rewired
+

Create a config-overrides.js file at the root of your app:

+
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:

+
yarn start
+

+ + + + + + + Back + + + + + + + + + + Next: Connect to the Ethereum Wallet + +

+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet/index.html b/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet/index.html new file mode 100644 index 0000000..1465eae --- /dev/null +++ b/docs/guides/vote_poll_sdk/poll_sdk/02_connect_wallet/index.html @@ -0,0 +1,835 @@ + + + + + + + + + + + + + + +Connect to the Ethereum Wallet | Waku Connect Docs + + + + + + + + + + + +
+ + +
+
+ +
+ + + Connect to the Ethereum Wallet + + +
+ + + + + + +
+ + + +

+ Connect to the Ethereum Wallet + # +

+
+ This section may be skipped if you are adding the poll feature to an existing dApp +that already connects to the user’s wallet. +
+ +

Delete the template App component:

+
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:

+
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:

+
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.

+

+ Styled-components + # +

+

styled-components is used for easy styling. +Create a Wrapper variable to use in the page component:

+
import styled from "styled-components";
+
+const Wrapper = styled.div`
+  height: 100%;
+  width: 100%;
+`;
+

+ Render + # +

+

Finally, create the App component:

+
export function App() {
+  return (
+    <Wrapper>
+      <GlobalStyle />
+      <DAppProvider config={config}>
+        <PollPage />
+      </DAppProvider>
+    </Wrapper>
+  );
+}
+

Your index.tsx should now be:

+
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")
+);
+

+ + + + + + + Back + + + + + + + + + + Next: Create-A-Poll Button + +

+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/vote_poll_sdk/poll_sdk/02_poll_creation/index.html b/docs/guides/vote_poll_sdk/poll_sdk/02_poll_creation/index.html index 9c8be97..5088185 100644 --- a/docs/guides/vote_poll_sdk/poll_sdk/02_poll_creation/index.html +++ b/docs/guides/vote_poll_sdk/poll_sdk/02_poll_creation/index.html @@ -21,7 +21,7 @@ useWakuPolling takes: - + + + + + + +
+ + +
+
+ +
+ + + Create-A-Poll Button + + +
+ + + + + + +
+ + + +

+ 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.

+
mkdir components
+touch components/Poll.tsx
+

+ Styled-components + # +

+

Again, create a Wrapper for styling:

+
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:

+
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:

+
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

+

+ + + + + + + Back + + + + + + + + + + Next: Poll Creation Component + +

+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/vote_poll_sdk/poll_sdk/03_poll_list/index.html b/docs/guides/vote_poll_sdk/poll_sdk/03_poll_list/index.html index cceb0ce..fe52744 100644 --- a/docs/guides/vote_poll_sdk/poll_sdk/03_poll_list/index.html +++ b/docs/guides/vote_poll_sdk/poll_sdk/03_poll_list/index.html @@ -23,7 +23,7 @@ import React, { useMemo, useState } from "react"; import styled from  - + + + + + + +
+ + +
+
+ +
+ + + Poll Creation Component + + +
+ + + + + + +
+ + + +

+ Poll Creation Component + # +

+

The Poll SDK provides 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 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

+
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

+
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

+

Confirmation modal

+

+ + + + + + + Back + + + + + + + + + + Next: Poll List Component + +

+
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list/index.html b/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list/index.html new file mode 100644 index 0000000..a221b18 --- /dev/null +++ b/docs/guides/vote_poll_sdk/poll_sdk/05_poll_list/index.html @@ -0,0 +1,718 @@ + + + + + + + + + + + + + + +Poll List Component | Waku Connect Docs + + + + + + + + + + + +
+ + +
+
+ +
+ + + Poll List Component + + +
+ + + + + + +
+ + + +

+ 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:

+
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:

+
<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

+

And answering them:

+

Poll list with answered

+

You can find the resulting code in the examples folder.

+
+ 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. +
+ +

The final gif:

+

Poll demo

+ + + + + + + + Back + + +
+ + + +
+ + + + + + + + + + +
+ + + +
+ +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/guides/vote_poll_sdk/poll_sdk/index.html b/docs/guides/vote_poll_sdk/poll_sdk/index.html index 7c8197a..06cccc9 100644 --- a/docs/guides/vote_poll_sdk/poll_sdk/index.html +++ b/docs/guides/vote_poll_sdk/poll_sdk/index.html @@ -18,7 +18,7 @@ The resulting code of this guide can be found at https://github."> - +