1 line
133 KiB
JSON
1 line
133 KiB
JSON
[{"id":0,"href":"/docs/guides/vote_poll_sdk/dapp_creation/","title":"Create a DApp","section":"Vote Poll Sdk","content":"Create a DApp # This part is the same for both Poll and Vote SDK.\nTo demonstrate how to use the Waku Connect Poll/Vote SDK in your dApp, we will create a TypeScript React app from scratch.\nYou can then adapt the steps depending on your dApp configuration and build setup.\nThe Poll \u0026amp; Vote SDK features can only be used by token holders, you must pass the ERC20 token contract of your choice when using the SDK. Hence, you need to have an ERC-20 token contract address ready.\nGet Started "},{"id":1,"href":"/docs/guides/vote_poll_sdk/dapp_creation/01_create_dapp/","title":"Create the DApp and Install Dependencies","section":"Create a DApp","content":"Create the DApp and Install Dependencies # Create React App # Create the new React app using the typescript template. Install the Waku Poll SDK packages.\nTo connect dapp with Wallet you can use any package that is compatible with ethers Web3Provider (e.g. @usedapp, web3-react).\nSetup polyfills # A number of Web3 dependencies need polyfills. Said polyfills must be explicitly declared when using webpack 5.\nThe latest react-scripts version uses webpack 5.\nWe 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.\nStart by installing the polyfill libraries:\nyarn 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.\nReact-App-Rewired # An alternative is to let react-scripts control the webpack 5 config and only override some elements using react-app-rewired.\nInstall react-app-rewired:\nyarn add -D react-app-rewired Create a config-overrides.js file at the root of your app:\nconst webpack = require(\u0026#34;webpack\u0026#34;); module.exports = (config) =\u0026gt; { // 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(\u0026#34;buffer\u0026#34;), crypto: require.resolve(\u0026#34;crypto-browserify\u0026#34;), stream: require.resolve(\u0026#34;stream-browserify\u0026#34;), assert: require.resolve(\u0026#34;assert\u0026#34;), }); if (!config.plugins) config.plugins = []; config.plugins.push( new webpack.ProvidePlugin({ Buffer: [\u0026#34;buffer\u0026#34;, \u0026#34;Buffer\u0026#34;], }) ); return config; }; Use react-app-rewired in the package.json, instead of react-scripts:\n \u0026quot;scripts\u0026quot;: { - \u0026quot;start\u0026quot;: \u0026quot;react-scripts start\u0026quot;, - \u0026quot;build\u0026quot;: \u0026quot;react-scripts build\u0026quot;, - \u0026quot;test\u0026quot;: \u0026quot;react-scripts test\u0026quot;, - \u0026quot;eject\u0026quot;: \u0026quot;react-scripts eject\u0026quot; + \u0026quot;start\u0026quot;: \u0026quot;react-app-rewired start\u0026quot;, + \u0026quot;build\u0026quot;: \u0026quot;react-app-rewired build\u0026quot;, + \u0026quot;test\u0026quot;: \u0026quot;react-app-rewired test\u0026quot;, + \u0026quot;eject\u0026quot;: \u0026quot;react-app-rewired eject\u0026quot; }, Start development server # You can now start the development server to serve your dApp at http://localhost:3000/ while we code:\nyarn start Back Next: Connect to the Ethereum Wallet "},{"id":2,"href":"/docs/guides/vote_poll_sdk/vote_sdk/01_deploying_smart_contract/","title":"Deploy smart contract","section":"Vote SDK","content":"Deploy smart contract # Creating new package # For this deployment we will create a new package.\nmkdir contract-deployment cd contract-deployment yarn init yarn add @waku/vote-sdk-contracts ethers ts-node typescript Create a tsconfig.json with:\n{ \u0026#34;compilerOptions\u0026#34;: { \u0026#34;target\u0026#34;: \u0026#34;es2020\u0026#34;, \u0026#34;module\u0026#34;: \u0026#34;commonJS\u0026#34;, \u0026#34;esModuleInterop\u0026#34;: true, \u0026#34;moduleResolution\u0026#34;: \u0026#34;node\u0026#34;, \u0026#34;resolveJsonModule\u0026#34;: true, \u0026#34;noEmit\u0026#34;: true } } And now we can add a deploy script index.ts:\nimport { ContractFactory, getDefaultProvider, Wallet } from \u0026#34;ethers\u0026#34;; import VotingContract from \u0026#34;@waku/vote-sdk-contracts/build/VotingContract.json\u0026#34;; import readline from \u0026#34;readline\u0026#34;; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); const prompt = (query: string) =\u0026gt; new Promise((resolve) =\u0026gt; rl.question(query, resolve)); try { const privateKey = process.argv[2]; const providerName = process.argv[3]; const tokenAddress = process.argv[4]; const voteDuration = process.argv[5]; const provider = getDefaultProvider(providerName); const wallet = new Wallet(privateKey, provider); const contract = ContractFactory.fromSolidity(VotingContract, wallet); new Promise(async () =\u0026gt; { console.log(\u0026#34;\\x1b[1m\u0026#34;); console.log(`You are about to deploy a voting smart contract\\n`); console.log(`Wallet address: \\t${wallet.address}\\n`); console.log(`Provider name: \\t\\t${provider.network.name}\\n`); console.log(`Provider chainID: \\t${provider.network.chainId}\\n`); console.log(`Token address to use: \\t${tokenAddress}\\n`); console.log(`Vote duration: \\t\\t${voteDuration ?? 1000}seconds\\n`); console.log(\u0026#34;Please verify that above parameters are correct\u0026#34;); console.log(\u0026#34;WARNING: this operation WILL use ether\u0026#34;); const answer = await prompt( \u0026#34;If you are sure that you want to continue write [yes]:\u0026#34; ); if (answer === \u0026#34;yes\u0026#34; || answer === \u0026#34;Yes\u0026#34;) { const deployedContract = await contract.deploy( tokenAddress, voteDuration ?? 1000 ); console.log(`contract deployed with address ${deployedContract.address}`); } else { console.log(\u0026#34;Aborted\u0026#34;); } rl.close(); }); } catch { console.log(\u0026#34;Error creating smart contract\u0026#34;); rl.close(); } Running script # To run deploying script we call in shell:\nyarn ts-node index.ts WALLET_PRIVATE_KEY PROVIDER_NAME TOKEN_ADDRESS VOTING_DURATION you need to substitute parameters:\n WALLET_PRIVATE_KEY: private key of wallet that will deploy smart contract PROVIDER_NAME: a name of network for example mainnet, ropsten or an url to network TOKEN_ADDRESS: address of a token that is to be used by voting contract VOTING_DURATION: how long proposals will be open to accept votes After that the information with input parameters will be displayed, and you will be asked to verify them and accept them.\nGetting smart contract address # When the script is complete smart contract address will be printed in the shell. If you missed it, you can check last wallet interaction on Etherscan and there you can also find new smart contract address.\n Back Next: Create Voting component "},{"id":3,"href":"/docs/guides/01_choose_content_topic/","title":"How to Choose a Content Topic","section":"Guides","content":"How to Choose a Content Topic # A content topic is used for content based filtering.\nIt allows you to filter out the messages that your dApp processes, both when receiving live messages (Relay) or retrieving historical messages (Store).\nThe format for content topics is as follows:\n/{dapp-name}/{version}/{content-topic-name}/{encoding}\n dapp-name: The name of your dApp, it must be unique to avoid conflict with other dApps. version: We usually start at 1, useful when introducing breaking changes in your messages. content-topic-name: The actual content topic name to use for filtering. If your dApp uses Waku Connect for several features, you should use a content topic per feature. encoding: The encoding format of the message, Protobuf is most often used: proto. For example: Your dApp\u0026rsquo;s name is SuperCrypto, it enables users to receive notifications and send private messages. You may want to use the following content topics:\n /supercrypto/1/notification/proto /supercrypto/1/private-message/proto You can learn more about Waku topics in the 23/WAKU2-TOPICS specs.\n"},{"id":4,"href":"/docs/guides/discovery_bootstrap/","title":"Discovery \u0026 Bootstrap Nodes","section":"Guides","content":"Discovery \u0026amp; Bootstrap Nodes # This guide explains the discovery and bootstrap mechanisms currently available in js-waku, their benefits and caveats and how to use them.\nNode 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\u0026rsquo;s environment (e.g. mobile phone, desktop browser, server, etc).\nWhen 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.\nOnce connected, the node needs to find additional peers to have:\n 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.\nDefault Bootstrap Mechanism # The default bootstrap mechanism is to connect to the Status' nim-waku prod fleet.\nTo use:\nconst 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.\nAs 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.\nWe intend to change this in the future and enable bootstrap by default once we have implemented more decentralized strategies.\n Predefined Bootstrap Nodes # Addresses of nodes hosted by Status are predefined in the codebase:\nhttps://github.com/status-im/js-waku/blob/e4024d5c7246535ddab28a4262006915d2db58be/src/lib/discovery/predefined.ts#L48\nThey can be accessed via the getPredefinedBootstrapNodes function.\nPros:\n Low latency, Low resource requirements. Cons:\n 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.\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { peers: getPredefinedBootstrapNodes(), }, }); Nwaku Test Fleet # The nwaku test fleet run the latest commit from nwaku\u0026rsquo;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.\nimport { Waku, discovery } from \u0026#34;js-waku\u0026#34;; 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.\nThere are two ways to set bootstrap nodes:\n Using an array of multiaddrs (as string or Multiaddr): import { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { peers: [ \u0026#34;/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm\u0026#34;, ], }, }); Passing an async function that returns an array of multiaddr (as string or Multiaddr): import { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { getPeers: async () =\u0026gt; { const addresses = []; // Fetch the multiaddrs from somewhere... return addresses; }, }, }); Read Nwaku Service Node to learn how to run your own node. Pros \u0026amp; Cons: Same than Predefined Bootstrap Nodes\nDNS Discovery # EIP-1459: Node Discovery via DNS has been implemented in js-waku, nwaku and go-waku with some modification on the ENR format.\nDNS Discovery enables anyone to register an ENR tree in the TXT field of a domain name.\nENR is the format used to store node connection details (ip, port, multiaddr, etc).\nThis 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.\nIt also enables more decentralized bootstrapping as anyone can register a domain name and publish it for others to use.\nWhile this method is implemented in js-waku, it is currently not recommended to use due to a bug in the websocket implementation of nwaku.\nThe nwaku prod fleet and test fleet have a websockify instance deployed alongside nwaku, acting as a work around the nwaku websocket bug.\n Pros:\n 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:\n 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.\nThey aim to improve the current status quo in the following aspects:\n 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. "},{"id":5,"href":"/docs/guides/vote_poll_sdk/dapp_creation/02_connect_wallet/","title":"Connect to the Ethereum Wallet","section":"Create a DApp","content":"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\u0026rsquo;s wallet. If you want to use ethers as a package to connect to web3 wallet you can follow this guide and skip the next step. Next step demonstrates how to use @useDapp for this purpose. In this we will use ethers to keep amount of dependencies to minimum but feel free to use other packages.\nyarn add ethers@5.4.6 The SDK use ethers version 5.4.6 due to incompatibility between minor versions it is recommended to use this version. Delete the template App component:\nrm -f App.tsx App.css App.test.tsx Hook for connecting to Wallet # In this example we will use this hook to connect to a Wallet.\nKeep in mind this hook is barebones and can\u0026rsquo;t handle multiple networks, in next chapter it will be shown how to use different web3 connector.\nTop 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\u0026rsquo;s wallet:\nMULTICALL_ADDRESS is an address to multicall smart contract that allows aggregating multiple contract calls into one, thus reducing number of calls to blockchain needed.\nExample multicall addresses: - Mainnet: 0xeefba1e63905ef1d7acba5a8513c70307c1ce441, - Ropsten: 0x53c43764255c17bd724f74c4ef150724ac50a3ed\nBut if you want you can deploy your own multicall smart contract.\nconst MULTICALL_ADDRESS = \u0026#34;0xeefba1e63905ef1d7acba5a8513c70307c1ce441\u0026#34;; const SUPPORTED_CHAIN_ID = 1; export function MainPage() { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); return ( \u0026lt;Wrapper\u0026gt; \u0026lt;TopBar logo={pollingIcon} logoWidth={84} title={\u0026#34;WakuConnect Poll Demo\u0026#34;} theme={orangeTheme} activate={activate} account={account} deactivate={deactivate} /\u0026gt; //Place for poll or vote component \u0026lt;/Wrapper\u0026gt; ); } Page # Styled-components # styled-components is used for easy styling. Create a Wrapper variable to use in the page component:\nimport styled from \u0026#34;styled-components\u0026#34;; const Wrapper = styled.div` height: 100%; width: 100%; `; Render # Finally, create the App component:\nexport function App() { return ( \u0026lt;Wrapper\u0026gt; \u0026lt;GlobalStyle /\u0026gt; \u0026lt;MainPage /\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } Your index.tsx should now be:\nimport React from \u0026#34;react\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; import { Poll } from \u0026#34;./components/Poll\u0026#34;; import { GlobalStyle, TopBar } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import pollingIcon from \u0026#34;./assets/images/pollingIcon.png\u0026#34;; import { orangeTheme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; import ReactDOM from \u0026#34;react-dom\u0026#34;; import { BrowserRouter, useLocation } from \u0026#34;react-router-dom\u0026#34;; import { Route, Switch } from \u0026#34;react-router\u0026#34;; import { useWeb3Connect } from \u0026#34;./hooks/useWeb3Connect\u0026#34;; const MULTICALL_ADDRESS = \u0026#34;0xeefba1e63905ef1d7acba5a8513c70307c1ce441\u0026#34;; const SUPPORTED_CHAIN_ID = 1; export function MainPage({ tokenAddress }: { tokenAddress: string }) { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); return ( \u0026lt;Wrapper\u0026gt; \u0026lt;TopBar logo={pollingIcon} logoWidth={84} title={\u0026#34;WakuConnect Poll Demo\u0026#34;} theme={orangeTheme} activate={activate} account={account} deactivate={deactivate} /\u0026gt; //Place for poll or vote component \u0026lt;/Wrapper\u0026gt; ); } export function App() { const location = useLocation(); const tokenAddress = new URLSearchParams(location.search).get(\u0026#34;token\u0026#34;); return ( \u0026lt;Wrapper\u0026gt; \u0026lt;GlobalStyle /\u0026gt; \u0026lt;MainPage tokenAddress={tokenAddress ?? TOKEN_ADDRESS} /\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } const Wrapper = styled.div` height: 100%; width: 100%; `; ReactDOM.render( \u0026lt;div style={{ height: \u0026#34;100%\u0026#34; }}\u0026gt; \u0026lt;BrowserRouter\u0026gt; \u0026lt;Switch\u0026gt; \u0026lt;Route exact path=\u0026#34;/\u0026#34; component={App} /\u0026gt; \u0026lt;/Switch\u0026gt; \u0026lt;/BrowserRouter\u0026gt; \u0026lt;/div\u0026gt;, document.getElementById(\u0026#34;root\u0026#34;) ); Back Next: Connect using useDapp "},{"id":6,"href":"/docs/guides/vote_poll_sdk/vote_sdk/02_voting_creation/","title":"Creating Voting component","section":"Vote SDK","content":"Create Voting component # With the smart contract deployed we can go back to our dApp.\nWe assume that the skeleton for the dApp with connection to the wallet is already done, if not please go to dApp creation.\nCreate components # Let\u0026rsquo;s start by creating a new folder components with file named Voting.tsx inside.\nAfter that we can start with styling and defining which theme we will be using:\nimport { blueTheme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; const THEME = blueTheme; const Wrapper = styled.div` display: flex; flex-direction: column; align-items: center; max-width: 1000px; margin: 0 auto; padding: 150px 32px 50px; width: 100%; min-height: 100vh; @media (max-width: 600px) { padding: 132px 16px 32px; } @media (max-width: 425px) { padding: 64px 16px 84px; } `; Adding react component # Now, create a Voting component that uses the components from the Vote SDK.\nimport React, { useCallback, useState } from \u0026#34;react\u0026#34;; import { NewVotingRoomModal, VotingRoomList, VotingRoomListHeader, } from \u0026#34;@waku/vote-sdk-react-components\u0026#34;; import { WakuVoting } from \u0026#34;@waku/vote-poll-sdk-core\u0026#34;; import { useVotingRoomsId } from \u0026#34;@waku/vote-sdk-react-hooks\u0026#34;; import { useTokenBalance } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; type VotingProps = { wakuVoting: WakuVoting; account: string | null | undefined; activate: () =\u0026gt; void; }; export function Voting({ wakuVoting, account, activate }: VotingProps) { const [showNewVoteModal, setShowNewVoteModal] = useState(false); const onCreateClick = useCallback(() =\u0026gt; { setShowNewVoteModal(true); }, []); const votes = useVotingRoomsId(wakuVoting); const tokenBalance = useTokenBalance(account, wakuVoting); return ( \u0026lt;Wrapper\u0026gt; \u0026lt;NewVotingRoomModal theme={THEME} availableAmount={tokenBalance} setShowModal={setShowNewVoteModal} showModal={showNewVoteModal} wakuVoting={wakuVoting} /\u0026gt; \u0026lt;VotingRoomListHeader account={account} theme={THEME} onConnectClick={activate} onCreateClick={onCreateClick} /\u0026gt; \u0026lt;VotingRoomList account={account} theme={THEME} wakuVoting={wakuVoting} votes={votes} availableAmount={tokenBalance} /\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } With that voting component is complete now we can use it in our MainPage\n Back Next: Use Voting Component "},{"id":7,"href":"/docs/guides/vote_poll_sdk/poll_sdk/","title":"Poll SDK","section":"Vote Poll Sdk","content":"How to Use the Waku Connect Poll SDK # To demonstrate how to use the Waku Connect Poll SDK in your dApp, we will create a TypeScript React app from scratch.\nYou can then adapt the steps depending on your dApp configuration and build setup.\nOnly token holders can create \u0026amp; answer polls. Hence, you need to have an ERC-20 token contract address ready.\nThe resulting code of this guide can be found at https://github.com/status-im/wakuconnect-vote-poll-sdk/tree/main/examples/mainnet-poll.\nHere is a preview of the end result:\nAfter following a dapp creation guide you should have a dapp that can connect to wallet ready. We will continue from this point.\nBefore starting first add poll packages:\nyarn add \\ @waku/poll-sdk-react-components @waku/poll-sdk-react-hooks @waku/vote-poll-sdk-react-components Get Started "},{"id":8,"href":"/docs/guides/02_relay_receive_send_messages/","title":"Receive and Send Messages Using Waku Relay","section":"Guides","content":"Receive and Send Messages Using Waku Relay # Waku Relay is a gossip protocol that enables you to send and receive messages. You can find Waku Relay\u0026rsquo;s specifications on Vac RFC.\nBefore 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.\nFor this guide, we are using a single content topic: /relay-guide/1/chat/proto.\nInstallation # You can install js-waku using your favorite package manager:\nnpm install js-waku Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { default: true } }); Passing the bootstrap option will connect your node to predefined Waku nodes. If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { peers: [ \u0026#34;/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm\u0026#34;, \u0026#34;/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ\u0026#34;, ], }, }); Wait to be connected # When using the bootstrap option, it may take some time to connect to other peers. To ensure that you have relay peers available to send and receive messages, use the following function:\nawait waku.waitForRemotePeer(); The returned Promise will resolve once you are connected to a Waku Relay peer.\nReceive messages # To receive messages for your app, you need to register an observer on relay for your app\u0026rsquo;s content topic:\nconst processIncomingMessage = (wakuMessage) =\u0026gt; { console.log(`Message Received: ${wakuMessage.payloadAsUtf8}`); }; waku.relay.addObserver(processIncomingMessage, [\u0026#34;/relay-guide/1/chat/proto\u0026#34;]); Send Messages # You are now ready to send messages. Let\u0026rsquo;s start by sending simple strings as messages.\nTo send a message, you need to wrap the message in a WakuMessage. When using a basic string payload, you can use the WakuMessage.fromUtf8String helper:\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const wakuMessage = await WakuMessage.fromUtf8String( \u0026#34;Here is a message\u0026#34;, `/relay-guide/1/chat/proto` ); Then, use the relay module to send the message to our peers, the message will then be relayed to the rest of the network thanks to Waku Relay:\nawait waku.relay.send(wakuMessage); Use Protobuf # Sending strings as messages in unlikely to cover your dApps needs.\nWaku v2 protocols use protobuf by default.\nLet\u0026rsquo;s review how you can use protobuf to include structured objects in Waku Messages.\nFirst, define a data structure. For this guide, we will use a simple chat message that contains a timestamp and text:\n{ timestamp: Date; text: string; } To encode and decode protobuf payloads, you can use the protons package.\nInstall Protobuf Library # First, install protons:\nnpm install protons Protobuf Definition # Then define the simple chat message:\nimport protons from \u0026#34;protons\u0026#34;; const proto = protons(` message SimpleChatMessage { uint64 timestamp = 1; string text = 2; } `); You can learn about protobuf message definitions here: Protocol Buffers Language Guide.\nEncode Messages # Instead of wrapping an utf-8 string in a Waku Message, you are going to wrap a protobuf payload.\nFirst, encode the object:\nconst payload = proto.SimpleChatMessage.encode({ timestamp: Date.now(), text: \u0026#34;Here is a message\u0026#34;, }); Then, wrap it in a Waku Message:\nconst wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic); Now, you can send the message over Waku Relay the same way than before:\nawait waku.relay.send(wakuMessage); Decode Messages # To decode the messages received over Waku Relay, you need to extract the protobuf payload and decode it using protons.\nconst processIncomingMessage = (wakuMessage) =\u0026gt; { // No need to attempt to decode a message if the payload is absent if (!wakuMessage.payload) return; const { timestamp, text } = proto.SimpleChatMessage.decode( wakuMessage.payload ); console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`); }; Like before, add this callback as an observer to Waku Relay:\nwaku.relay.addObserver(processIncomingMessage, [\u0026#34;/relay-guide/1/chat/proto\u0026#34;]); Conclusion # That is it! Now, you know how to send and receive messages over Waku using the Waku Relay protocol.\nHere is the final code:\nimport { getBootstrapNodes, Waku, WakuMessage } from \u0026#34;js-waku\u0026#34;; import protons from \u0026#34;protons\u0026#34;; const proto = protons(` message SimpleChatMessage { uint64 timestamp = 1; string text = 2; } `); const wakuNode = await Waku.create(); const nodes = await getBootstrapNodes(); await Promise.all(nodes.map((addr) =\u0026gt; waku.dial(addr))); const processIncomingMessage = (wakuMessage) =\u0026gt; { // No need to attempt to decode a message if the payload is absent if (!wakuMessage.payload) return; const { timestamp, text } = proto.SimpleChatMessage.decode( wakuMessage.payload ); console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`); }; waku.relay.addObserver(processIncomingMessage, [\u0026#34;/relay-guide/1/chat/proto\u0026#34;]); const payload = proto.SimpleChatMessage.encode({ timestamp: Date.now(), text: \u0026#34;Here is a message\u0026#34;, }); const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic); await waku.relay.send(wakuMessage); "},{"id":9,"href":"/docs/guides/vote_poll_sdk/vote_sdk/","title":"Vote SDK","section":"Vote Poll Sdk","content":"How to Use the Waku Connect Vote SDK # To demonstrate how to use the Waku Connect Vote SDK in your dApp, we will create a TypeScript React app from scratch.\nYou can then adapt the steps depending on your dApp configuration and build setup.\nOnly token holders can create, vote and finalize proposals. Hence, you need to have an ERC-20 token contract address ready.\nThe resulting code of this guide can be found in the repo at examples/ropsten-voting.\nHere is a preview of the end result:\nCreate a proposal: Proposal card: After following the create a dApp guide you should have a dapp that can connect to wallet ready. We will continue from this point.\nFirst, add the Vote SDK packages:\nyarn add \\ @waku/vote-sdk-react-components @waku/vote-sdk-react-hooks @waku/vote-poll-sdk-react-components Get Started "},{"id":10,"href":"/docs/guides/vote_poll_sdk/dapp_creation/03_connect_wallet_usedapp/","title":"Connect to the Ethereum Wallet useDapp","section":"Create a DApp","content":"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\u0026rsquo;s wallet. This section can be used instead of previous step. It demonstrates how to use @useDapp for wallet connection. In this guide, we use useDApp to access the blockchain.\nyarn add @usedapp/core@0.4.7 @usedapp/core must be frozen to version 0.4.7 due to incompatibility between minor versions of ethers.\nWaku Connect Vote \u0026amp; 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.\n Delete the template App component:\nrm -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\u0026rsquo;s wallet:\nexport function PollPage() { const { account, library, activateBrowserWallet, deactivate } = useEthers(); const [signer, setSigner] = useState\u0026lt;undefined | JsonRpcSigner\u0026gt;(undefined); useEffect(() =\u0026gt; { if (account) { setSigner(library?.getSigner()); } else { // Deactivate signer if signed out setSigner(undefined); } }, [account]); return ( \u0026lt;div\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;Poll dApp\u0026#34;} theme={orangeTheme} activate={activateBrowserWallet} account={account} deactivate={deactivate} /\u0026gt; \u0026lt;/div\u0026gt; ); } Page # UseDApp # Create a config variable that contains the Ethereum network parameters:\nimport { ChainId, DAppProvider, useEthers } from \u0026#34;@usedapp/core\u0026#34;; const config = { readOnlyChainId: ChainId.Mainnet, readOnlyUrls: { [ChainId.Mainnet]: \u0026#34;https://mainnet.infura.io/v3/your-infura-token\u0026#34;, }, multicallAddresses: { 1: \u0026#34;0xeefba1e63905ef1d7acba5a8513c70307c1ce441\u0026#34;, 3: \u0026#34;0x53c43764255c17bd724f74c4ef150724ac50a3ed\u0026#34;, }, notifications: { checkInterval: 500, expirationPeriod: 50000, }, }; Replace your-infura-token with your Infura API token.\nStyled-components # styled-components is used for easy styling. Create a Wrapper variable to use in the page component:\nimport styled from \u0026#34;styled-components\u0026#34;; const Wrapper = styled.div` height: 100%; width: 100%; `; Render # Finally, create the App component:\nfunction App() { return ( \u0026lt;Wrapper\u0026gt; \u0026lt;GlobalStyle /\u0026gt; \u0026lt;DAppProvider config={config}\u0026gt; \u0026lt;PollPage /\u0026gt; \u0026lt;/DAppProvider\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } Your index.tsx should now be:\nimport { ChainId, DAppProvider, useEthers } from \u0026#34;@usedapp/core\u0026#34;; import { GlobalStyle, TopBar } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import React, { useEffect, useState } from \u0026#34;react\u0026#34;; import ReactDOM from \u0026#34;react-dom\u0026#34;; import \u0026#34;./index.css\u0026#34;; import { JsonRpcSigner } from \u0026#34;@ethersproject/providers\u0026#34;; import { orangeTheme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/cjs/src/style/themes\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; const config = { readOnlyChainId: ChainId.Mainnet, readOnlyUrls: { [ChainId.Mainnet]: \u0026#34;https://mainnet.infura.io/v3/your-infura-token\u0026#34;, }, multicallAddresses: { 1: \u0026#34;0xeefba1e63905ef1d7acba5a8513c70307c1ce441\u0026#34;, 3: \u0026#34;0x53c43764255c17bd724f74c4ef150724ac50a3ed\u0026#34;, }, notifications: { checkInterval: 500, expirationPeriod: 50000, }, }; function PollPage() { const { account, library, activateBrowserWallet, deactivate } = useEthers(); const [signer, setSigner] = useState\u0026lt;undefined | JsonRpcSigner\u0026gt;(undefined); useEffect(() =\u0026gt; { if (account) { setSigner(library?.getSigner()); } else { // Deactivate signer if signed out setSigner(undefined); } }, [account]); return ( \u0026lt;div\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;Poll dApp\u0026#34;} theme={orangeTheme} activate={activateBrowserWallet} account={account} deactivate={deactivate} /\u0026gt; //Place for poll or vote component \u0026lt;/div\u0026gt; ); } function App() { return ( \u0026lt;Wrapper\u0026gt; \u0026lt;GlobalStyle /\u0026gt; \u0026lt;DAppProvider config={config}\u0026gt; \u0026lt;PollPage /\u0026gt; \u0026lt;/DAppProvider\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } const Wrapper = styled.div` height: 100%; width: 100%; `; ReactDOM.render( \u0026lt;React.StrictMode\u0026gt; \u0026lt;App /\u0026gt; \u0026lt;/React.StrictMode\u0026gt;, document.getElementById(\u0026#34;root\u0026#34;) ); Back "},{"id":11,"href":"/docs/guides/vote_poll_sdk/vote_sdk/03_using_voting/","title":"Use Voting Component","section":"Vote SDK","content":"Use Voting Component # Define Configuration # Configure the dApp by setting:\n Address of the multicall smart contract of the target chain, Address of the voting smart contract, Your dApp name. const VOTING_ADDRESS = \u0026#34;VOTING_ADDRESS\u0026#34;; const MULTICALL_ADDRESS = \u0026#34;MULTICALL_ADDRESS\u0026#34;; const DAPP_NAME = \u0026#34;YOUR_DAPP_NAME\u0026#34;; Use Waku Voting # Now, we need a Waku voting object. For that, call useWakuVoting:\nimport { useWakuVoting } from \u0026#34;@waku/vote-sdk-react-hooks\u0026#34;; export function MainPage() { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); const wakuVoting = useWakuVoting( DAPP_NAME, VOTING_ADDRESS, provider, MULTICALL_ADDRESS ); } Display Voting Component # Modify the MainPage to render a Voting component. Before rendering the component, check if wakuVoting has initialized:\nreturn ( \u0026lt;Wrapper\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;WakuConnect Vote Demo\u0026#34;} theme={blueTheme} activate={activate} account={account} deactivate={deactivate} /\u0026gt; {wakuVoting \u0026amp;\u0026amp; ( \u0026lt;Voting wakuVoting={wakuVoting} account={account} activate={activate} /\u0026gt; )} \u0026lt;/Wrapper\u0026gt; ); Resulting index.tsx File # Your index.tsx should now look like:\nimport React from \u0026#34;react\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; import { GlobalStyle, TopBar } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import { blueTheme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; import ReactDOM from \u0026#34;react-dom\u0026#34;; import { useWeb3Connect } from \u0026#34;./hooks/useWeb3Connect\u0026#34;; import { Voting } from \u0026#34;./components/Voting\u0026#34;; import { useWakuVoting } from \u0026#34;@waku/vote-sdk-react-hooks\u0026#34;; const VOTING_ADDRESS = \u0026#34;0xCA4093D66280Ec1242b660088188b50fDC14dcC4\u0026#34;; const MULTICALL_ADDRESS = \u0026#34;0x53c43764255c17bd724f74c4ef150724ac50a3ed\u0026#34;; const DAPP_NAME = \u0026#34;test\u0026#34;; const SUPPORTED_CHAIN_ID = 3; export function MainPage() { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); const wakuVoting = useWakuVoting( DAPP_NAME, VOTING_ADDRESS, provider, MULTICALL_ADDRESS ); return ( \u0026lt;Wrapper\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;WakuConnect Vote Demo\u0026#34;} theme={blueTheme} activate={activate} account={account} deactivate={deactivate} /\u0026gt; {wakuVoting \u0026amp;\u0026amp; ( \u0026lt;Voting wakuVoting={wakuVoting} account={account} activate={activate} /\u0026gt; )} \u0026lt;/Wrapper\u0026gt; ); } export function App() { return ( \u0026lt;Wrapper\u0026gt; \u0026lt;GlobalStyle /\u0026gt; \u0026lt;MainPage /\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } const Wrapper = styled.div` height: 100%; width: 100%; `; ReactDOM.render( \u0026lt;div style={{ height: \u0026#34;100%\u0026#34; }}\u0026gt; \u0026lt;App /\u0026gt; \u0026lt;/div\u0026gt;, document.getElementById(\u0026#34;root\u0026#34;) ); After starting a page you should be able to see a main page that looks like this: You can then create a proposal: Here is a proposal card after votes have happened: Back "},{"id":12,"href":"/docs/guides/03_store_retrieve_messages/","title":"Retrieve Messages Using Waku Store","section":"Guides","content":"Retrieve Messages Using Waku Store # DApps running on a phone or in a browser are often offline: The browser could be closed or mobile app in the background.\nWaku Relay is a gossip protocol. As a user, it means that your peers forward you messages they just received. If you cannot be reached by your peers, then messages are not relayed; relay peers do not save messages for later.\nHowever, Waku Store peers do save messages they relay, allowing you to retrieve them at a later time. The Waku Store protocol is best-effort and does not guarantee data availability. Waku Relay should still be preferred when online; Waku Store can be used after resuming connectivity: For example, when the dApp starts.\nIn this guide, we\u0026rsquo;ll review how you can use Waku Store to retrieve messages.\nBefore 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.\nFor this guide, we are using a single content topic: /store-guide/1/news/proto.\nInstallation # You can install js-waku using your favorite package manager:\nnpm install js-waku Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const wakuNode = await Waku.create({ bootstrap: { default: true } }); Passing the bootstrap option will connect your node to predefined Waku nodes. If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const wakuNode = await Waku.create({ bootstrap: { peers: [ \u0026#34;/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm\u0026#34;, \u0026#34;/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ\u0026#34;, ], }, }); Wait to be connected # When using the bootstrap option, it may take some times to connect to other peers. To ensure that you have store peers available to retrieve historical messages from, use the following function:\nawait waku.waitForRemotePeer(); The returned Promise will resolve once you are connected to a Waku Store peer.\nUse Protobuf # Waku v2 protocols use protobuf by default.\nLet\u0026rsquo;s review how you can use protobuf to send structured data.\nFirst, define a data structure. For this guide, we will use a simple news article that contains a date of publication, title and body:\n{ date: Date; title: string; body: string; } To encode and decode protobuf payloads, you can use the protons package.\nInstall Protobuf Library # First, install protons:\nnpm install protons Protobuf Definition # Then specify the data structure:\nimport protons from \u0026#34;protons\u0026#34;; const proto = protons(` message ArticleMessage { uint64 date = 1; string title = 2; string body = 3; } `); You can learn about protobuf message definitions here: Protocol Buffers Language Guide.\nDecode Messages # To decode the messages retrieved from a Waku Store node, you need to extract the protobuf payload and decode it using protons.\nconst decodeWakuMessage = (wakuMessage) =\u0026gt; { // No need to attempt to decode a message if the payload is absent if (!wakuMessage.payload) return; const { date, title, body } = proto.SimpleChatMessage.decode( wakuMessage.payload ); // In protobuf, fields are optional so best to check if (!date || !title || !body) return; const publishDate = new Date(); publishDate.setTime(date); return { publishDate, title, body }; }; Retrieve messages # You now have all the building blocks to retrieve and decode messages for a store node.\nStore node responses are paginated. The WakuStore.queryHistory API automatically query all the pages in a sequential manner. To process messages as soon as they received (page by page), use the callback option:\nconst ContentTopic = \u0026#34;/store-guide/1/news/proto\u0026#34;; const callback = (retrievedMessages) =\u0026gt; { const articles = retrievedMessages .map(decodeWakuMessage) // Decode messages .filter(Boolean); // Filter out undefined values console.log(`${articles.length}articles have been retrieved`); }; waku.store.queryHistory([ContentTopic], { callback }).catch((e) =\u0026gt; { // Catch any potential error console.log(\u0026#34;Failed to retrieve messages from store\u0026#34;, e); }); Note that WakuStore.queryHistory select an available store node for you. However, it can only select a connected node, which is why the bootstrapping is necessary. It will throw an error if no store node is available.\nFilter messages by send time # By default, Waku Store nodes store messages for 30 days. Depending on your use case, you may not need to retrieve 30 days worth of messages.\nWaku Message defines an optional unencrypted timestamp field. The timestamp is set by the sender. By default, js-waku sets the timestamp of outgoing message to the current time.\nYou can filter messages that include a timestamp within given bounds with the timeFilter option.\nRetrieve messages up to a week old:\n// [..] `ContentTopic` and `callback` definitions const startTime = new Date(); // 7 days/week, 24 hours/day, 60min/hour, 60secs/min, 100ms/sec startTime.setTime(startTime.getTime() - 7 * 24 * 60 * 60 * 1000); waku.store .queryHistory([ContentTopic], { callback, timeFilter: { startTime, endTime: new Date() }, }) .catch((e) =\u0026gt; { console.log(\u0026#34;Failed to retrieve messages from store\u0026#34;, e); }); End result # You can see a similar example implemented in ReactJS in the Minimal ReactJS Waku Store App.\n"},{"id":13,"href":"/docs/guides/04_encrypt_messages_version_1/","title":"Encrypt Messages Using Waku Message Version 1","section":"Guides","content":"Encrypt Messages Using Waku Message Version 1 # The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption. The encryption comes with several handy design requirements: confidentiality, authenticity and integrity. It also allows the sender to sign messages, see Sign Messages Using Waku Message Version 1 to learn how.\nYou can find more details about Waku Message Payload Encryption in 26/WAKU-PAYLOAD.\nSee Cryptographic Libraries for more details on the cryptographic libraries used by js-waku.\nWhat data is encrypted # With Waku Message Version 1, the entire payload is encrypted.\nWhich means that the only discriminating data available in clear text is the content topic and timestamp (if present). Hence, if Alice expects to receive messages under a given content topic, she needs to try to decrypt all messages received on said content topic.\nThis needs to be kept in mind for scalability and forward secrecy concerns:\n If there is high traffic on a given content topic then all clients need to process and attempt decryption of all messages with said content topic; If a content topic is only used by a given (group of) user(s) then it is possible to deduce some information about said user(s) communications such as sent time and frequency of messages. Key management # By using Waku Message Version 1, you will need to provide a way to your users to generate and store keys in a secure manner. Storing, backing up and recovering key is out of the scope of this guide.\nIf key recovery is important for your dApp, then check out SubtleCrypto.wrapKey() which can be used to securely store or export private keys.\nAn example to save and load a key pair in local storage, protected with a password, can be found in Eth-PM.\nWhich encryption method should I use? # Whether you should use symmetric or asymmetric encryption depends on your use case.\nSymmetric encryption is done using a single key to encrypt and decrypt.\nWhich means that if Alice knows the symmetric key K and uses it to encrypt a message, she can also use K to decrypt any message encrypted with K, even if she is not the sender.\nGroup chats is a possible use case for symmetric encryption: All participants can use an out-of-band method to agree on a K. Participants can then use K to encrypt and decrypt messages within the group chat. Participants MUST keep K secret to ensure that no external party can decrypt the group chat messages.\nAsymmetric encryption is done using a key pair: the public key is used to encrypt messages, the matching private key is used to decrypt messages.\nFor Alice to encrypt a message for Bob, she needs to know Bob\u0026rsquo;s Public Key K. Bob can then use his private key k to decrypt the message. As long as Bob keep his private key k secret, then he, and only he, can decrypt messages encrypted with K.\nPrivate 1:1 messaging is a possible use case for asymmetric encryption: When Alice sends an encrypted message for Bob, only Bob can decrypt it.\nSymmetric Encryption # Generate Key # To use symmetric encryption, you first need to generate a key. Use generateSymmetricKey for secure key generation:\nimport { generateSymmetricKey } from \u0026#34;js-waku\u0026#34;; const symmetricKey = generateSymmetricKey(); Encrypt Message # To encrypt a message with the previously generated key, pass the key in the symKey property to WakuMessage.fromBytes.\nSame as Waku Messages version 0 (unencrypted), payload is your message payload and contentTopic is the content topic for your dApp. See Receive and Send Messages Using Waku Relay for details.\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const message = await WakuMessage.fromBytes(payload, contentTopic, { symKey: symmetricKey, }); The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:\nawait waku.lightPush.push(message); Decrypt Messages # To decrypt messages, whether they are received over Waku Relay or using Waku Store, add the symmetric key as a decryption key to your Waku instance.\nwaku.addDecryptionKey(symmetricKey); Alternatively, you can pass the key when creating the instance:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = Waku.create({ decryptionKeys: [symmetricKey] }); It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.\nYou can call addDecryptionKey several times if you are using multiple keys, symmetric key and asymmetric private keys can be used together.\nMessages that are not successfully decrypted are dropped.\nAsymmetric Encryption # Generate Key Pair # To use asymmetric encryption, you first need to generate a private key and calculate the corresponding public key. Use generatePrivateKey for secure key generation:\nimport { generatePrivateKey, getPublicKey } from \u0026#34;js-waku\u0026#34;; const privateKey = generatePrivateKey(); const publicKey = getPublicKey(privateKey); The private key must be securely stored and remain private. If leaked then other parties may be able to decrypt the user\u0026rsquo;s messages.\nThe public key is unique for a given private key and can always be recovered given the private key, hence it is not needed to save it as long as as the private key can be recovered.\nEncrypt Message # The public key is used to encrypt messages; to do so, pass it in the encPublicKey property to WakuMessage.fromBytes.\nSame as clear Waku Messages, payload is your message payload and contentTopic is the content topic for your dApp. See Receive and Send Messages Using Waku Relay for details.\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const message = await WakuMessage.fromBytes(payload, contentTopic, { encPublicKey: publicKey, }); The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:\nawait waku.lightPush.push(message); Decrypt Messages # The private key is needed to decrypt messages.\nTo decrypt messages, whether they are received over Waku Relay or using Waku Store, add the private key as a decryption key to your Waku instance.\nwaku.addDecryptionKey(privateKey); Alternatively, you can pass the key when creating the instance:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = Waku.create({ decryptionKeys: [privateKey] }); It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.\nYou can call addDecryptionKey several times if you are using multiple keys, symmetric key and asymmetric private keys can be used together.\nMessages that are not successfully decrypted are dropped.\nHandling WakuMessage instances # When creating a Waku Message using WakuMessage.fromBytes with an encryption key (symmetric or asymmetric), the payload gets encrypted. Which means that wakuMessage.payload returns an encrypted payload:\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const message = await WakuMessage.fromBytes(payload, contentTopic, { encPublicKey: publicKey, }); console.log(message.payload); // This is encrypted However, WakuMessage instances returned by WakuRelay or WakuStore are always decrypted.\nWakuRelay and WakuStore never return messages that are encrypted. If a message was not successfully decrypted, then it will be dropped from the results.\nWhich means that WakuMessage instances returned by WakuRelay and WakuStore always have a clear payload (in regard to Waku Message version 1):\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = Waku.create({ decryptionKeys: [privateKey] }); const messages = await waku.store.queryHistory([contentTopic]); if (messages \u0026amp;\u0026amp; messages[0]) { console.log(messages[0].payload); // This payload is decrypted } waku.relay.addObserver( (message) =\u0026gt; { console.log(message.payload); // This payload is decrypted }, [contentTopic] ); Code Example # The Eth-PM Web App example demonstrates both the use of symmetric and asymmetric encryption.\nAsymmetric encryption is used for private messages so that only the intended recipient can read said messages.\nSymmetric encryption is used for the public key messages. In this instance, the same key is used for all users: the Keccak-256 hash of the content topic (which results in 32 bytes array). While this does not add functional value, it does demonstrate the usage of symmetric encryption in a web app.\nA live version of Eth-PM can be found at https://js-waku.wakuconnect.dev/examples/eth-pm.\nThe specifications of the protocol it implements can be found at 20/TOY-ETH-PM.\n"},{"id":14,"href":"/docs/guides/sign_messages_web3_eip712/","title":"Sign Messages Using a Web3 Wallet (EIP-712)","section":"Guides","content":"Sign Messages Using a Web3 Wallet (EIP-712) # Depending on your use case, you may need users to certify the ownership of their Ethereum account. They can do so by using their wallet to sign data.\nIn this guide, we demonstrate how to use the Ethers.js library to request the user to sign typed data (EIP-712) and then broadcast the signature over Waku.\nFor this guide, we are build a dApp that implements 20/TOY-ETH-PM: A simple protocols for end-to-end encrypted messages where Ethereum accounts are used as identity.\nAlice owns an Ethereum account A. She wants other Ethereum users to find her and contact her using her Ethereum address A. For example, Alice could be the pseudonym creator of an NFT series and wants to open her DMs.\nHence, Alice generates a public key K to be used for encryption purposes.\nAlice will need to certify that the public key K can be used to contact her, the owner of Ethereum address A.\nSigning Data # First, you need to determine what data a user must sign.\nIn our current example, Alice needs to certify that her encryption public key K can be used to contact her, owner of Ethereum address A.\nHence, she will need to sign K using her Ethereum account A.\nAlso, Alice does not want her signature to be used on other dApps, so she will need to sign some context data, referred to as domain data in the EIP-712 spec.\nHence, the data to sign:\n name: \u0026quot;My Cool Ethereum Private Message App\u0026quot;: A unique name to bind the signature to your dApp, version: \u0026quot;1\u0026quot;: The version of the signature scheme for your dApp, encryptionPublicKey: The encryption public key of the user, ownerAddress: The Ethereum address used to sign and owner of the encryption public key, message: A human-readable message to provide instructions to the user. Write the following function to build the data to sign. The data must be formatted as defined in EIP-712:\nfunction buildMsgParams( encryptionPublicKeyHex: string, ownerAddressHex: string ) { return JSON.stringify({ domain: { name: \u0026#34;My Cool Ethereum Private Message App\u0026#34;, version: \u0026#34;1\u0026#34;, }, message: { message: \u0026#34;By signing this message you certify that messages addressed to `ownerAddress` must be encrypted with `encryptionPublicKey`\u0026#34;, encryptionPublicKey: encryptionPublicKeyHex, ownerAddress: ownerAddressHex, }, primaryType: \u0026#34;PublishEncryptionPublicKey\u0026#34;, types: { EIP712Domain: [ { name: \u0026#34;name\u0026#34;, type: \u0026#34;string\u0026#34; }, { name: \u0026#34;version\u0026#34;, type: \u0026#34;string\u0026#34; }, ], PublishEncryptionPublicKey: [ { name: \u0026#34;message\u0026#34;, type: \u0026#34;string\u0026#34; }, { name: \u0026#34;encryptionPublicKey\u0026#34;, type: \u0026#34;string\u0026#34; }, { name: \u0026#34;ownerAddress\u0026#34;, type: \u0026#34;string\u0026#34; }, ], }, }); } The function takes two parameters:\n encryptionPublicKeyHex: The public key of the user, as a hex string, ownerAddressHex: The Ethereum address of the user, as a hex string. Sign operation # To sign the data using ethers, define the following function:\nasync function signEncryptionKey( encryptionPublicKeyHex: string, ownerAddressHex: string, providerRequest: (request: { method: string; params?: Array\u0026lt;any\u0026gt;; }) =\u0026gt; Promise\u0026lt;any\u0026gt; ): Promise\u0026lt;string\u0026gt; { const msgParams = buildMsgParams(encryptionPublicKeyHex, ownerAddressHex); const result = await providerRequest({ method: \u0026#34;eth_signTypedData_v4\u0026#34;, params: [ownerAddressHex, msgParams], from: ownerAddressHex, }); return result; } This function takes 3 arguments:\n encryptionPublicKeyHex: Alice\u0026rsquo;s encryption public key K, ownerAddressHex: Alice\u0026rsquo;s Ethereum address, providerRequest: Ethers' request object. Instantiate the providerRequest when you connect to the wallet. You can read Create a DApp to learn how to connect to a wallet.\nimport { ethers } from \u0026#34;ethers\u0026#34;; const web3Provider = new ethers.providers.Web3Provider(window.ethereum); const providerRequest = web3Provider?.provider?.request; signEncryptionKey returns the signature in hex format. You can now add this signature to your Waku payload.\nSend Signature # You can use Waku Relay to send and receive messages or Waku Light Push to send messages.\nFollow the guides above and replace their protobuf message definition with the following:\nsyntax = \u0026#34;proto3\u0026#34;;message PublicKeyMessage { bytes encryptionPublicKey = 1; bytes ethAddress = 2; bytes signature = 3;}Note: You can use the hexToBytes function to convert the signature from hex string to byte array.\nValidate Signature # Users that wishes to encrypt messages for Alice need to ensure that the signature is indeed valid and was generated from Alice\u0026rsquo;s Ethereum account A.\nUse the following function to do so:\nimport * as sigUtil from \u0026#34;eth-sig-util\u0026#34;; import { keccak256 } from \u0026#34;ethers/lib/utils\u0026#34;; import { utils } from \u0026#34;js-waku\u0026#34;; interface PublicKeyMessage { encryptionPublicKey: Uint8Array; ethAddress: Uint8Array; signature: Uint8Array; } function validatePublicKeyMessage(msg: PublicKeyMessage): boolean { const recovered = sigUtil.recoverTypedSignature_v4({ data: JSON.parse( buildMsgParams( utils.bytesToHex(msg.encryptionPublicKey), \u0026#34;0x\u0026#34; + utils.bytesToHex(msg.ethAddress) ) ), sig: \u0026#34;0x\u0026#34; + utils.bytesToHex(msg.signature), }); return utils.equalByteArrays(recovered, msg.ethAddress); } If the function returns true then the signature can be trusted and the encryption public key can be used to encrypt messages.\nConclusion # We reviewed how to use Web3 wallet signature to certify the authenticity of an encryption public key sent over Waku.\nThe Ethereum Private Message Web App example demonstrates this guide. Relevant code is in the crypto.ts file.\nThe WakuConnect Vote Poll SDK implements a similar logic where the signature is then used in a smart contract.\n"},{"id":15,"href":"/docs/guides/05_sign_messages_version_1/","title":"Sign Messages Using Waku Message Version 1","section":"Guides","content":"Sign Messages Using Waku Message Version 1 # The Waku Message format provides an easy way to sign messages using elliptic curve cryptography.\nIt also allows the sender to encrypt messages, see Encrypt Messages Using Waku Message Version 1 to learn how.\nYou can find more details about Waku Message Payload Signature in 26/WAKU-PAYLOAD.\nSee Cryptographic Libraries for more details on the cryptographic libraries used by js-waku.\nCreate new keypair # Generate a new keypair to sign your messages:\nimport { generatePrivateKey, getPublicKey } from \u0026#34;js-waku\u0026#34;; const privateKey = generatePrivateKey(); const publicKey = getPublicKey(privateKey); Sign Waku Messages # As per version 1\u0026rsquo;s specs, signatures are only included in encrypted messages. In the case where your app does not need encryption then you could use symmetric encryption with a trivial key.\nYou can learn more about encryption at Encrypt Messages Using Waku Message Version 1.\nUsing symmetric encryption # Given symKey the symmetric key used for encryption:\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const message = await WakuMessage.fromBytes(payload, myAppContentTopic, { encPublicKey: symKey, sigPrivKey: privateKey, }); If encryption is not needed for your use case, then you can create a symmetric key from the content topic:\nimport { hexToBuf } from \u0026#34;js-waku/lib/utils\u0026#34;; import { keccak256 } from \u0026#34;ethers/lib/utils\u0026#34;; const symKey = hexToBuf(keccak256(Buffer.from(myAppContentTopic, \u0026#34;utf-8\u0026#34;))); symKey can then be used to encrypt and decrypt messages on myAppContentTopic content topic. Read How to Choose a Content Topic to learn more about content topics.\nUsing asymmetric encryption # Given recipientPublicKey the public key of the message\u0026rsquo;s recipient:\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const message = await WakuMessage.fromBytes(payload, myAppContentTopic, { encPublicKey: recipientPublicKey, sigPrivKey: privateKey, }); Verify Waku Message signatures # Two fields are available on signed WakuMessages:\n signaturePublicKey: Holds the public key of the signer, signature: Holds the actual signature. Thus, if you expect messages to be signed by Alice, you can simply compare WakuMessage.signaturePublicKey with Alice\u0026rsquo;s public key. As comparing hex string can lead to issues (is the 0x prefix present?), simply use helper function equalByteArrays.\nimport { equalByteArrays } from \u0026#34;js-waku/lib/utils\u0026#34;; const sigPubKey = wakuMessage.signaturePublicKey; const isSignedByAlice = sigPubKey \u0026amp;\u0026amp; equalByteArrays(sigPubKey, alicePublicKey); if (!isSignedByAlice) { // Message is not signed by Alice } "},{"id":16,"href":"/docs/guides/06_light_push_send_messages/","title":"Send Messages Using Waku Light Push","section":"Guides","content":"Send Messages Using Waku Light Push # Waku Light Push enables a client to receive a confirmation when sending a message.\nThe Waku Relay protocol sends messages to connected peers but does not provide any information on whether said peers have received messages. This can be an issue when facing potential connectivity issues. For example, when the connection drops easily, or it is connected to a small number of relay peers.\nWaku Light Push allows a client to get a response from a remote peer when sending a message. Note this only guarantees that the remote peer has received the message, it cannot guarantee propagation to the network.\nIt also means weaker privacy properties as the remote peer knows the client is the originator of the message. Whereas with Waku Relay, a remote peer would not know whether the client created or forwarded the message.\nYou can find Waku Light Push\u0026rsquo;s specifications on Vac RFC.\nContent Topic # 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.\nFor this guide, we are using a single content topic: /light-push-guide/1/guide/proto.\nInstallation # You can install js-waku using your favorite package manager:\nnpm install js-waku Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const wakuNode = await Waku.create({ bootstrap: { default: true } }); Passing the bootstrap option will connect your node to predefined Waku nodes. If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { peers: [ \u0026#34;/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm\u0026#34;, \u0026#34;/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ\u0026#34;, ], }, }); Wait to be connected # When using the bootstrap option, it may take some time to connect to other peers. To ensure that you have a light push peer available to send messages to, use the following function:\nawait waku.waitForRemotePeer(); The returned Promise will resolve once you are connected to a Waku peer.\nSend messages # You can now send a message using Waku Light Push. By default, it sends the messages to a single randomly selected light push peer. The peer is selected among the dApp\u0026rsquo;s connected peers.\nIf the dApp is not connected to any light push peer, an error is thrown.\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const wakuMessage = await WakuMessage.fromUtf8String( \u0026#34;Here is a message\u0026#34;, `/light-push-guide/1/guide/proto` ); const ack = await waku.lightPush.push(wakuMessage); if (!ack?.isSuccess) { // Message was not sent } "},{"id":17,"href":"/docs/guides/07_reactjs_relay/","title":"Receive and Send Messages Using Waku Relay With ReactJS","section":"Guides","content":"Receive and Send Messages Using Waku Relay With ReactJS # It is easy to use Waku Connect with ReactJS. In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages.\nBefore 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: /min-react-js-chat/1/chat/proto.\nSetup # Create a new React app:\nnpx create-react-app relay-reactjs-chat cd relay-reactjs-chat BigInt # Some of js-waku\u0026rsquo;s dependencies use BigInt that is only supported by modern browsers.\nTo ensure that react-scripts properly transpile your webapp code, update the package.json file:\n{ \u0026#34;browserslist\u0026#34;: { \u0026#34;production\u0026#34;: [ \u0026#34;\u0026gt;0.2%\u0026#34;, \u0026#34;not ie \u0026lt;= 99\u0026#34;, \u0026#34;not android \u0026lt;= 4.4.4\u0026#34;, \u0026#34;not dead\u0026#34;, \u0026#34;not op_mini all\u0026#34; ] } } Setup polyfills # A number of Web3 dependencies need polyfills. Said polyfills must be explicitly declared when using webpack 5.\nThe latest react-scripts version uses webpack 5.\nWe 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.\nStart by installing the polyfill libraries:\nnpm install --save assert buffer crypto-browserify process stream-browserify Webpack 5 # If you directly use webpack 5, then you can inspire yourself from this webpack.config.js.\ncra-webpack-rewired # An alternative is to let react-scripts control the webpack 5 config and only override some elements using cra-webpack-rewired.\nInstall cra-webpack-rewired:\nnpm install -D cra-webpack-rewired Create a config/webpack.extend.js file at the root of your app:\nconst webpack = require(\u0026#34;webpack\u0026#34;); module.exports = { dev: (config) =\u0026gt; { // 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(\u0026#34;buffer\u0026#34;), crypto: require.resolve(\u0026#34;crypto-browserify\u0026#34;), stream: require.resolve(\u0026#34;stream-browserify\u0026#34;), }); if (!config.plugins) config.plugins = []; config.plugins.push( new webpack.DefinePlugin({ \u0026#34;process.env.ENV\u0026#34;: JSON.stringify(\u0026#34;dev\u0026#34;), }) ); config.plugins.push( new webpack.ProvidePlugin({ process: \u0026#34;process/browser.js\u0026#34;, Buffer: [\u0026#34;buffer\u0026#34;, \u0026#34;Buffer\u0026#34;], }) ); if (!config.ignoreWarnings) config.ignoreWarnings = []; config.ignoreWarnings.push(/Failed to parse source map/); return config; }, prod: (config) =\u0026gt; { // 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(\u0026#34;buffer\u0026#34;), crypto: require.resolve(\u0026#34;crypto-browserify\u0026#34;), stream: require.resolve(\u0026#34;stream-browserify\u0026#34;), }); if (!config.plugins) config.plugins = []; config.plugins.push( new webpack.DefinePlugin({ \u0026#34;process.env.ENV\u0026#34;: JSON.stringify(\u0026#34;prod\u0026#34;), }) ); config.plugins.push( new webpack.ProvidePlugin({ process: \u0026#34;process/browser.js\u0026#34;, Buffer: [\u0026#34;buffer\u0026#34;, \u0026#34;Buffer\u0026#34;], }) ); if (!config.ignoreWarnings) config.ignoreWarnings = []; config.ignoreWarnings.push(/Failed to parse source map/); return config; }, }; Use cra-webpack-rewired in the package.json, instead of react-scripts:\n \u0026quot;scripts\u0026quot;: { - \u0026quot;start\u0026quot;: \u0026quot;react-scripts start\u0026quot;, - \u0026quot;build\u0026quot;: \u0026quot;react-scripts build\u0026quot;, - \u0026quot;test\u0026quot;: \u0026quot;react-scripts test\u0026quot;, - \u0026quot;eject\u0026quot;: \u0026quot;react-scripts eject\u0026quot; + \u0026quot;start\u0026quot;: \u0026quot;cra-webpack-rewired start\u0026quot;, + \u0026quot;build\u0026quot;: \u0026quot;cra-webpack-rewired build\u0026quot;, + \u0026quot;test\u0026quot;: \u0026quot;cra-webpack-rewired test\u0026quot;, + \u0026quot;eject\u0026quot;: \u0026quot;cra-webpack-rewired eject\u0026quot; }, Then, install js-waku:\nnpm install --save js-waku Start the dev server and open the dApp in your browser:\nnpm run start Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance. Go to App.js and modify the App function:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; import * as React from \u0026#34;react\u0026#34;; function App() { const [waku, setWaku] = React.useState(undefined); const [wakuStatus, setWakuStatus] = React.useState(\u0026#34;None\u0026#34;); // Start Waku React.useEffect(() =\u0026gt; { // If Waku is already assigned, the job is done if (!!waku) return; // If Waku status not None, it means we are already starting Waku if (wakuStatus !== \u0026#34;None\u0026#34;) return; setWakuStatus(\u0026#34;Starting\u0026#34;); // Create Waku Waku.create({ bootstrap: { default: true } }).then((waku) =\u0026gt; { // Once done, put it in the state setWaku(waku); // And update the status setWakuStatus(\u0026#34;Started\u0026#34;); }); }, [waku, wakuStatus]); return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Waku node\u0026#39;s status: {wakuStatus}\u0026lt;/p\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } export default App; Wait to be connected # When using the bootstrap option, it may take some time to connect to other peers. To ensure that you have relay peers available to send and receive messages, use the Waku.waitForRemotePeer() async function:\nReact.useEffect(() =\u0026gt; { if (!!waku) return; if (wakuStatus !== \u0026#34;None\u0026#34;) return; setWakuStatus(\u0026#34;Starting\u0026#34;); Waku.create({ bootstrap: { default: true } }).then((waku) =\u0026gt; { setWaku(waku); setWakuStatus(\u0026#34;Connecting\u0026#34;); waku.waitForRemotePeer().then(() =\u0026gt; { setWakuStatus(\u0026#34;Ready\u0026#34;); }); }); }, [waku, wakuStatus]); Define Message Format # To define the Protobuf message format, you can use protobufjs:\nnpm install protobufjs Define SimpleChatMessage with two fields: timestamp and text.\nimport protobuf from \u0026#34;protobufjs\u0026#34;; const SimpleChatMessage = new protobuf.Type(\u0026#34;SimpleChatMessage\u0026#34;) .add(new protobuf.Field(\u0026#34;timestamp\u0026#34;, 1, \u0026#34;uint64\u0026#34;)) .add(new protobuf.Field(\u0026#34;text\u0026#34;, 2, \u0026#34;string\u0026#34;)); Send Messages # Create a function that takes the Waku instance and a message to send:\nimport {WakuMessage} from \u0026#34;js-waku\u0026#34;; const ContentTopic = `/relay-reactjs-chat/1/chat/proto`; function sendMessage(message, waku, timestamp) { const time = timestamp.getTime(); // Encode to protobuf const protoMsg = SimpleChatMessage.create({ timestamp: time, text: message, }); const payload = SimpleChatMessage.encode(protoMsg).finish(); // Wrap in a Waku Message return WakuMessage.fromBytes(payload, ContentTopic).then((wakuMessage) =\u0026gt; // Send over Waku Relay waku.relay.send(wakuMessage) ); } Then, add a button to the App function:\nfunction App() { const [waku, setWaku] = React.useState(undefined); const [wakuStatus, setWakuStatus] = React.useState(\u0026#34;None\u0026#34;); // Using a counter just for the messages to be different const [sendCounter, setSendCounter] = React.useState(0); React.useEffect(() =\u0026gt; { // ... creates Waku }, [waku, wakuStatus]); const sendMessageOnClick = () =\u0026gt; { // Check Waku is started and connected first. if (wakuStatus !== \u0026#34;Ready\u0026#34;) return; sendMessage(`Here is message #${sendCounter}`, waku, new Date()).then(() =\u0026gt; console.log(\u0026#34;Message sent\u0026#34;) ); // For demonstration purposes. setSendCounter(sendCounter + 1); }; return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;p\u0026gt;{wakuStatus}\u0026lt;/p\u0026gt; \u0026lt;button onClick={sendMessageOnClick} disabled={wakuStatus !== \u0026#34;Ready\u0026#34;}\u0026gt; Send Message \u0026lt;/button\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } Receive Messages # To process incoming messages, you need to register an observer on Waku Relay. First, you need to define the observer function.\nYou will need to remove the observer when the component unmount. Hence, you need the reference to the function to remain the same. For that, use React.useCallback:\nconst processIncomingMessage = React.useCallback((wakuMessage) =\u0026gt; { // Empty message? if (!wakuMessage.payload) return; // Decode the protobuf payload const {text, timestamp} = SimpleChatMessage.decode(wakuMessage.payload); const time = new Date(); time.setTime(timestamp); // For now, just log new messages on the console console.log(`message received at ${time.toString()}: ${text}`); }, []); Then, add this observer to Waku Relay. Do not forget to delete the observer is the component is being unmounted:\nReact.useEffect(() =\u0026gt; { if (!waku) return; // Pass the content topic to only process messages related to your dApp waku.relay.addObserver(processIncomingMessage, [ContentTopic]); // `cleanUp` is called when the component is unmounted, see ReactJS doc. return function cleanUp() { waku.relay.deleteObserver(processIncomingMessage, [ContentTopic]); }; }, [waku, wakuStatus, processIncomingMessage]); Display Messages # The Waku work is now done. Your dApp is able to send and receive messages using Waku. For the sake of completeness, let\u0026rsquo;s display received messages on the page.\nFirst, add incoming messages to the state of the App component:\nfunction App() { //... const [messages, setMessages] = React.useState([]); const processIncomingMessage = React.useCallback((wakuMessage) =\u0026gt; { if (!wakuMessage.payload) return; const {text, timestamp} = SimpleChatMessage.decode(wakuMessage.payload); const time = new Date(); time.setTime(timestamp); const message = {text, timestamp: time}; setMessages((messages) =\u0026gt; { return [message].concat(messages); }); }, []); // ... } Then, render the messages:\nfunction App() { // ... return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;p\u0026gt;{wakuStatus}\u0026lt;/p\u0026gt; \u0026lt;button onClick={sendMessageOnClick} disabled={wakuStatus !== \u0026#34;Ready\u0026#34;}\u0026gt; Send Message \u0026lt;/button\u0026gt; \u0026lt;ul\u0026gt; {messages.map((msg) =\u0026gt; { return ( \u0026lt;li\u0026gt; \u0026lt;p\u0026gt; {msg.timestamp.toString()}: {msg.text} \u0026lt;/p\u0026gt; \u0026lt;/li\u0026gt; ); })} \u0026lt;/ul\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } And Voilà! You should now be able to send and receive messages. Try out by opening the app from different browsers.\nYou can see the complete code in the Relay ReactJS Chat Example App.\n"},{"id":18,"href":"/docs/guides/08_reactjs_store/","title":"Retrieve Messages Using Waku Store With ReactJS","section":"Guides","content":"Retrieve Messages Using Waku Store With ReactJS # It is easy to use Waku Connect with ReactJS. In this guide, we will demonstrate how your ReactJS dApp can use Waku Store to retrieve messages.\nDApps running on a phone or in a browser are often offline: The browser could be closed or mobile app in the background.\nWaku Relay is a gossip protocol. As a user, it means that your peers forward you messages they just received. If you cannot be reached by your peers, then messages are not relayed; relay peers do not save messages for later.\nHowever, Waku Store peers do save messages they relay, allowing you to retrieve them at a later time. The Waku Store protocol is best-effort and does not guarantee data availability. Waku Relay should still be preferred when online; Waku Store can be used after resuming connectivity: For example, when the dApp starts.\nIn this guide, we\u0026rsquo;ll review how you can use Waku Store to retrieve messages.\nBefore 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.\nSetup # Create a new React app:\nnpx create-react-app store-reactjs-chat cd store-reactjs-chat BigInt # Some of js-waku\u0026rsquo;s dependencies use BigInt that is only supported by modern browsers.\nTo ensure that react-scripts properly transpile your webapp code, update the package.json file:\n{ \u0026#34;browserslist\u0026#34;: { \u0026#34;production\u0026#34;: [ \u0026#34;\u0026gt;0.2%\u0026#34;, \u0026#34;not ie \u0026lt;= 99\u0026#34;, \u0026#34;not android \u0026lt;= 4.4.4\u0026#34;, \u0026#34;not dead\u0026#34;, \u0026#34;not op_mini all\u0026#34; ] } } Setup polyfills # A number of Web3 dependencies need polyfills. Said polyfills must be explicitly declared when using webpack 5.\nThe latest react-scripts version uses webpack 5.\nWe 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.\nStart by installing the polyfill libraries:\nnpm install assert buffer crypto-browserify stream-browserify Webpack 5 # If you directly use webpack 5, then you can inspire yourself from this webpack.config.js.\ncra-webpack-rewired # An alternative is to let react-scripts control the webpack 5 config and only override some elements using cra-webpack-rewired.\nInstall cra-webpack-rewired:\nnpm install -D cra-webpack-rewired Create a config/webpack.extend.js file at the root of your app:\nconst webpack = require(\u0026#34;webpack\u0026#34;); module.exports = { dev: (config) =\u0026gt; { // 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(\u0026#34;buffer\u0026#34;), crypto: require.resolve(\u0026#34;crypto-browserify\u0026#34;), stream: require.resolve(\u0026#34;stream-browserify\u0026#34;), }); if (!config.plugins) config.plugins = []; config.plugins.push( new webpack.DefinePlugin({ \u0026#34;process.env.ENV\u0026#34;: JSON.stringify(\u0026#34;dev\u0026#34;), }) ); config.plugins.push( new webpack.ProvidePlugin({ process: \u0026#34;process/browser.js\u0026#34;, Buffer: [\u0026#34;buffer\u0026#34;, \u0026#34;Buffer\u0026#34;], }) ); if (!config.ignoreWarnings) config.ignoreWarnings = []; config.ignoreWarnings.push(/Failed to parse source map/); return config; }, prod: (config) =\u0026gt; { // 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(\u0026#34;buffer\u0026#34;), crypto: require.resolve(\u0026#34;crypto-browserify\u0026#34;), stream: require.resolve(\u0026#34;stream-browserify\u0026#34;), }); if (!config.plugins) config.plugins = []; config.plugins.push( new webpack.DefinePlugin({ \u0026#34;process.env.ENV\u0026#34;: JSON.stringify(\u0026#34;prod\u0026#34;), }) ); config.plugins.push( new webpack.ProvidePlugin({ process: \u0026#34;process/browser.js\u0026#34;, Buffer: [\u0026#34;buffer\u0026#34;, \u0026#34;Buffer\u0026#34;], }) ); if (!config.ignoreWarnings) config.ignoreWarnings = []; config.ignoreWarnings.push(/Failed to parse source map/); return config; }, }; Use cra-webpack-rewired in the package.json, instead of react-scripts:\n \u0026quot;scripts\u0026quot;: { - \u0026quot;start\u0026quot;: \u0026quot;react-scripts start\u0026quot;, - \u0026quot;build\u0026quot;: \u0026quot;react-scripts build\u0026quot;, - \u0026quot;test\u0026quot;: \u0026quot;react-scripts test\u0026quot;, - \u0026quot;eject\u0026quot;: \u0026quot;react-scripts eject\u0026quot; + \u0026quot;start\u0026quot;: \u0026quot;cra-webpack-rewired start\u0026quot;, + \u0026quot;build\u0026quot;: \u0026quot;cra-webpack-rewired build\u0026quot;, + \u0026quot;test\u0026quot;: \u0026quot;cra-webpack-rewired test\u0026quot;, + \u0026quot;eject\u0026quot;: \u0026quot;cra-webpack-rewired eject\u0026quot; }, Then, install js-waku:\nnpm install js-waku Start the dev server and open the dApp in your browser:\nnpm run start We have noticed some issues with React bundling due to npm pulling an old version of babel. If you are getting an error about the optional chaining (?.) character not being valid, try cleaning up and re-installing your dependencies:\nrm -rf node_modules package-lock.json npm install Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance. Go to App.js and modify the App function:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; import * as React from \u0026#34;react\u0026#34;; function App() { const [waku, setWaku] = React.useState(undefined); const [wakuStatus, setWakuStatus] = React.useState(\u0026#34;None\u0026#34;); // Start Waku React.useEffect(() =\u0026gt; { // If Waku status not None, it means we are already starting Waku if (wakuStatus !== \u0026#34;None\u0026#34;) return; setWakuStatus(\u0026#34;Starting\u0026#34;); // Create Waku Waku.create({ bootstrap: { default: true } }).then((waku) =\u0026gt; { // Once done, put it in the state setWaku(waku); // And update the status setWakuStatus(\u0026#34;Connecting\u0026#34;); }); }, [waku, wakuStatus]); return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;p\u0026gt;{wakuStatus}\u0026lt;/p\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } export default App; Wait to be connected # When using the bootstrap option, it may take some time to connect to other peers. To ensure that you have store peers available to retrieve messages from, use the Waku.waitForRemotePeer() async function:\nReact.useEffect(() =\u0026gt; { if (!waku) return; if (wakuStatus === \u0026#34;Connected\u0026#34;) return; waku.waitForRemotePeer().then(() =\u0026gt; { setWakuStatus(\u0026#34;Connected\u0026#34;); }); }, [waku, wakuStatus]); Use Protobuf # Waku v2 protocols use protobuf by default.\nLet\u0026rsquo;s review how you can use protobuf to decode structured data.\nFirst, define a data structure. For this guide, we will use a simple chat message that contains a timestamp, nick and text:\n{ timestamp: Date; nick: string; text: string; } To encode and decode protobuf payloads, you can use the protons package.\nInstall Protobuf Library # npm install protons Protobuf Definition # Define the data structure with protons:\nimport protons from \u0026#34;protons\u0026#34;; const proto = protons(` message ChatMessage { uint64 timestamp = 1; string nick = 2; bytes text = 3; } `); You can learn about protobuf message definitions here: Protocol Buffers Language Guide.\nDecode Messages # To decode the messages retrieved from a Waku Store node, you need to extract the protobuf payload and decode it using protons.\nfunction decodeMessage(wakuMessage) { if (!wakuMessage.payload) return; const { timestamp, nick, text } = proto.ChatMessage.decode( wakuMessage.payload ); // All fields in protobuf are optional so be sure to check if (!timestamp || !text || !nick) return; const time = new Date(); time.setTime(timestamp); const utf8Text = Buffer.from(text).toString(\u0026#34;utf-8\u0026#34;); return { text: utf8Text, timestamp: time, nick }; } Retrieve messages # You now have all the building blocks to retrieve and decode messages for a store node.\nNote that Waku Store queries are paginated. The API provided by js-waku automatically traverses all pages of the Waku Store response. By default, the most recent page is retrieved first but this can be changed with the pageDirection option.\nFirst, define a React state to save the messages:\nfunction App() { const [messages, setMessages] = React.useState([]); /// [..] } Then, define processMessages to decode and then store messages in the React state. You will pass processMessages as a callback option to WakuStore.queryHistory. processMessages will be called each time a page is received from the Waku Store.\nconst processMessages = (retrievedMessages) =\u0026gt; { const messages = retrievedMessages.map(decodeMessage).filter(Boolean); setMessages((currentMessages) =\u0026gt; { return currentMessages.concat(messages.reverse()); }); }; Pass processMessage in WakuStore.queryHistory as the callback value:\nwaku.store.queryHistory([ContentTopic], { callback: processMessages }); Finally, create a Messages component to render the messages:\nfunction Messages(props) { return props.messages.map(({ text, timestamp, nick }) =\u0026gt; { return ( \u0026lt;li\u0026gt; ({formatDate(timestamp)}) {nick}: {text} \u0026lt;/li\u0026gt; ); }); } function formatDate(timestamp) { return timestamp.toLocaleString([], { month: \u0026#34;short\u0026#34;, day: \u0026#34;numeric\u0026#34;, hour: \u0026#34;numeric\u0026#34;, minute: \u0026#34;2-digit\u0026#34;, second: \u0026#34;2-digit\u0026#34;, hour12: false, }); } Use Messages in the App function:\nfunction App() { // [..] return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;h2\u0026gt;{wakuStatus}\u0026lt;/h2\u0026gt; \u0026lt;h3\u0026gt;Messages\u0026lt;/h3\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;Messages messages={messages} /\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } All together, you should now have:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; import * as React from \u0026#34;react\u0026#34;; import protons from \u0026#34;protons\u0026#34;; const ContentTopic = \u0026#34;/toy-chat/2/huilong/proto\u0026#34;; const proto = protons(` message ChatMessage { uint64 timestamp = 1; string nick = 2; bytes text = 3; } `); function App() { const [waku, setWaku] = React.useState(undefined); const [wakuStatus, setWakuStatus] = React.useState(\u0026#34;None\u0026#34;); const [messages, setMessages] = React.useState([]); // Start Waku React.useEffect(() =\u0026gt; { // If Waku status not None, it means we are already starting Waku if (wakuStatus !== \u0026#34;None\u0026#34;) return; setWakuStatus(\u0026#34;Starting\u0026#34;); // Create Waku Waku.create({ bootstrap: { default: true } }).then((waku) =\u0026gt; { // Once done, put it in the state setWaku(waku); // And update the status setWakuStatus(\u0026#34;Connecting\u0026#34;); }); }, [waku, wakuStatus]); React.useEffect(() =\u0026gt; { if (!waku) return; if (wakuStatus === \u0026#34;Connected\u0026#34;) return; waku.waitForRemotePeer().then(() =\u0026gt; { setWakuStatus(\u0026#34;Connected\u0026#34;); }); }, [waku, wakuStatus]); React.useEffect(() =\u0026gt; { if (wakuStatus !== \u0026#34;Connected\u0026#34;) return; const processMessages = (retrievedMessages) =\u0026gt; { const messages = retrievedMessages.map(decodeMessage).filter(Boolean); setMessages((currentMessages) =\u0026gt; { return currentMessages.concat(messages.reverse()); }); }; waku.store .queryHistory([ContentTopic], { callback: processMessages }) .catch((e) =\u0026gt; { console.log(\u0026#34;Failed to retrieve messages\u0026#34;, e); }); }, [waku, wakuStatus]); return ( \u0026lt;div className=\u0026#34;App\u0026#34;\u0026gt; \u0026lt;header className=\u0026#34;App-header\u0026#34;\u0026gt; \u0026lt;h2\u0026gt;{wakuStatus}\u0026lt;/h2\u0026gt; \u0026lt;h3\u0026gt;Messages\u0026lt;/h3\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;Messages messages={messages} /\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;/div\u0026gt; ); } export default App; function decodeMessage(wakuMessage) { if (!wakuMessage.payload) return; const { timestamp, nick, text } = proto.ChatMessage.decode( wakuMessage.payload ); // All fields in protobuf are optional so be sure to check if (!timestamp || !text || !nick) return; const time = new Date(); time.setTime(timestamp); const utf8Text = Buffer.from(text).toString(\u0026#34;utf-8\u0026#34;); return { text: utf8Text, timestamp: time, nick }; } function Messages(props) { return props.messages.map(({ text, timestamp, nick }) =\u0026gt; { return ( \u0026lt;li\u0026gt; ({formatDate(timestamp)}) {nick}: {text} \u0026lt;/li\u0026gt; ); }); } function formatDate(timestamp) { return timestamp.toLocaleString([], { month: \u0026#34;short\u0026#34;, day: \u0026#34;numeric\u0026#34;, hour: \u0026#34;numeric\u0026#34;, minute: \u0026#34;2-digit\u0026#34;, second: \u0026#34;2-digit\u0026#34;, hour12: false, }); } Note that WakuStore.queryHistory select an available store node for you. However, it can only select a connected node, which is why the bootstrapping is necessary. It will throw an error if no store node is available.\nIf no message are returned, then you can use https://js-waku.wakuconnect.dev/examples/web-chat/ to send some messages on the toy chat topic and refresh your app.\nFilter messages by send time # By default, Waku Store nodes store messages for 30 days. Depending on your use case, you may not need to retrieve 30 days worth of messages.\nWaku Message defines an optional unencrypted timestamp field. The timestamp is set by the sender. By default, js-waku sets the timestamp of outgoing message to the current time.\nYou can filter messages that include a timestamp within given bounds with the timeFilter option.\nRetrieve messages up to a week old:\nconst startTime = new Date(); // 7 days/week, 24 hours/day, 60min/hour, 60secs/min, 100ms/sec startTime.setTime(startTime.getTime() - 7 * 24 * 60 * 60 * 1000); waku.store.queryHistory([ContentTopic], { callback: processMessages, timeFilter: { startTime, endTime: new Date() }, }); End result # You can see the complete code in the Minimal ReactJS Waku Store App.\n"},{"id":19,"href":"/docs/guides/angular_relay/","title":"Send and Receive Messages Using Waku Relay With Angular v13","section":"Guides","content":"Send and Receive Messages Using Waku Relay With Angular v13 # It is easy to use Waku Connect with Angular v13.\nIn this guide, we will demonstrate how your Angular dApp can use Waku Relay to send and receive messages.\nBefore 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.\nFor this guide, we are using a single content topic: /relay-angular-chat/1/chat/proto.\nSetup # Create a new Angular app:\nnpm install -g @angular/cli ng new relay-angular-chat cd relay-angular-chat BigInt # Some of js-waku\u0026rsquo;s dependencies use BigInt that is only supported by modern browsers.\nTo ensure that Angular properly transpiles your webapp code, add the following configuration to the package.json file:\n{ \u0026#34;browserslist\u0026#34;: { \u0026#34;production\u0026#34;: [ \u0026#34;\u0026gt;0.2%\u0026#34;, \u0026#34;not ie \u0026lt;= 99\u0026#34;, \u0026#34;not android \u0026lt;= 4.4.4\u0026#34;, \u0026#34;not dead\u0026#34;, \u0026#34;not op_mini all\u0026#34; ] } } Polyfills # A number of Web3 and libp2p dependencies need polyfills. These must be explicitly declared when using webpack 5.\nThe latest Angular version (v13) uses webpack 5.\nWe will describe below a method to configure polyfills when using Angular v13 / webpack v5. This may not be necessary if you use webpack 4.\nStart by installing the polyfill libraries:\nyarn add assert buffer crypto-browserify process stream-browserify Then add the following code to src/polyfills.ts:\nimport * as process from \u0026#39;process\u0026#39;; (window as any).process = process; (window as any).global = window; global.Buffer = global.Buffer || require(\u0026#39;buffer\u0026#39;).Buffer; Now tell Angular where to find these libraries by adding the following to tsconfig.json under \u0026quot;compilerOptions\u0026quot;:\n{ \u0026#34;paths\u0026#34;: { \u0026#34;assert\u0026#34;: [\u0026#34;node_modules/assert\u0026#34;], \u0026#34;buffer\u0026#34;: [\u0026#34;node_modules/buffer\u0026#34;], \u0026#34;crypto\u0026#34;: [\u0026#34;node_modules/crypto-browserify\u0026#34;], \u0026#34;stream\u0026#34;: [\u0026#34;node_modules/stream-browserify\u0026#34;] } } Now under \u0026quot;angularCompilerOptions\u0026quot;, add:\n\u0026#34;allowSyntheticDefaultImports\u0026#34;: true Finally, set the \u0026quot;target\u0026quot; to be \u0026quot;es2020\u0026quot; due to the aforementioned BigInt usage.\nModule loading warnings # There will be some warnings due to module loading. We can fix them by setting the \u0026quot;allowedCommonJsDependencies\u0026quot; key under architect -\u0026gt; build -\u0026gt; options with the following:\n{ \u0026#34;allowedCommonJsDependencies\u0026#34;: [ \u0026#34;libp2p-gossipsub/src/utils\u0026#34;, \u0026#34;rlp\u0026#34;, \u0026#34;multiaddr/src/convert\u0026#34;, \u0026#34;varint\u0026#34;, \u0026#34;multihashes\u0026#34;, \u0026#34;@chainsafe/libp2p-noise/dist/src/noise\u0026#34;, \u0026#34;debug\u0026#34;, \u0026#34;libp2p\u0026#34;, \u0026#34;libp2p-bootstrap\u0026#34;, \u0026#34;libp2p-crypto\u0026#34;, \u0026#34;libp2p-websockets\u0026#34;, \u0026#34;libp2p-websockets/src/filters\u0026#34;, \u0026#34;libp2p/src/ping\u0026#34;, \u0026#34;multiaddr\u0026#34;, \u0026#34;peer-id\u0026#34;, \u0026#34;buffer\u0026#34;, \u0026#34;crypto\u0026#34;, \u0026#34;ecies-geth\u0026#34;, \u0026#34;secp256k1\u0026#34;, \u0026#34;libp2p-gossipsub\u0026#34;, \u0026#34;it-concat\u0026#34;, \u0026#34;protons\u0026#34; ] } Types # There are some type definitions we need to install and some that we don\u0026rsquo;t have.\nyarn add @types/bl protons Create a new folder under src named @types with the following structure:\nsrc/@types ├── protons │ └── types.d.ts └── time-cache └── types.d.ts In the protons/types.d.ts file add:\ndeclare module \u0026#39;protons\u0026#39;; In the time-cache/types.d.ts file add:\ndeclare module \u0026#34;time-cache\u0026#34; { 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:\nyarn add js-waku Start the dev server and open the dApp in your browser:\nyarn run start Create Waku Instance # In order to interact with the Waku network, you first need a Waku instance. We\u0026rsquo;re going to wrap the js-waku library in a Service so we can inject it to different components when needed.\nGenerate the Waku service:\nng generate service waku Go to waku.service.ts and add the following imports:\nimport { Waku } from \u0026#34;js-waku\u0026#34;; import { ReplaySubject } from \u0026#34;rxjs\u0026#34;; replace the WakuService class with the following:\nexport class WakuService { // Create Subject Observable to \u0026#39;store\u0026#39; the Waku instance private wakuSubject = new Subject\u0026lt;Waku\u0026gt;(); public waku = this.wakuSubject.asObservable(); // Create BehaviorSubject Observable to \u0026#39;store\u0026#39; the Waku status private wakuStatusSubject = new BehaviorSubject(\u0026#39;\u0026#39;); public wakuStatus = this.wakuStatusSubject.asObservable(); constructor() { } init() { // Connect node Waku.create({ bootstrap: { default: true } }).then(waku =\u0026gt; { // Update Observable values this.wakuSubject.next(waku); this.wakuStatusSubject.next(\u0026#39;Connecting...\u0026#39;); waku.waitForRemotePeer().then(() =\u0026gt; { // Update Observable value this.wakuStatusSubject.next(\u0026#39;Connected\u0026#39;); }); }); } } When using the bootstrap option, it may take some time to connect to other peers. That\u0026rsquo;s why we use the waku.waitForRemotePeer function to ensure that there are relay peers available to send and receive messages.\nNow we can inject the WakuService in to the AppComponent class to initialize the node and subscribe to any status changes.\nFirstly, import the WakuService:\nimport { WakuService } from \u0026#34;./waku.service\u0026#34;; Then update the AppComponent class with the following:\nexport class AppComponent { title: string = \u0026#39;relay-angular-chat\u0026#39;; 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 =\u0026gt; { this.wakuStatus = wakuStatus; }); } } Add the following HTML to the app.component.html to show the title and render the connection status:\n\u0026lt;h1\u0026gt;{{title}}\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;Waku node\u0026#39;s status: {{ wakuStatus }}\u0026lt;/p\u0026gt; Messages # Now we need to create a component to send, receive and render the messages.\nng 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.\nWe\u0026rsquo;re going to need the WakuService again but also the Waku and WakuMessage classes from js-waku. We already installed protons and we\u0026rsquo;re going to use that here so we\u0026rsquo;ll need to import it.\nimport { WakuService } from \u0026#34;../waku.service\u0026#34;; import { Waku, WakuMessage } from \u0026#34;js-waku\u0026#34;; import protons from \u0026#34;protons\u0026#34;; Let\u0026rsquo;s use protons to define the Protobuf message format with two fields: timestamp and text:\nconst proto = protons(` message SimpleChatMessage { uint64 timestamp = 1; string text = 2; } `); Let\u0026rsquo;s also define a message interface:\ninterface MessageInterface { timestamp: Date; text: string; } Send Messages # In order to send a message, we need to define a few things.\nThe contentTopic is the topic we want subscribe to and the payload is the message. We\u0026rsquo;ve also defined a timestamp so let\u0026rsquo;s create that.\nThe messageCount property is just to distinguish between messages.\nWe also need our waku instance and wakuStatus property. We will subscribe to the waku and wakuStatus Observables from the WakuService to get them.\nexport 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 =\u0026gt; { this.wakuStatus = wakuStatus; }); // Subscribe to the `waku` Observable and update the property when it changes this.wakuService.waku.subscribe(waku =\u0026gt; { 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 =\u0026gt; { this.waku.relay.send(wakuMessage).then(() =\u0026gt; { 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.\n\u0026lt;button (click)=\u0026quot;sendMessage()\u0026quot; [disabled]=\u0026quot;wakuStatus !== 'Connected'\u0026quot;\u0026gt;Send Message\u0026lt;/button\u0026gt; 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.\nAgain, in the messages.component.ts:\nexport class MessagesComponent { // ... // Store the messages in an array messages: MessageInterface[] = []; // ... processIncomingMessages = (wakuMessage: WakuMessage) =\u0026gt; { 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\u0026rsquo;ll also need to delete the observer when the component gets destroyed to avoid memory leaks:\nngOnDestroy(): void { this.waku.relay.deleteObserver(this.processIncomingMessages, [this.contentTopic]); } Angular won\u0026rsquo;t delete the observer when the page reloads so we\u0026rsquo;ll have to hook that up ourselves. Add the following to the ngOnInit() function:\nwindow.onbeforeunload = () =\u0026gt; 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\u0026rsquo;s display received messages on the page.\nWe\u0026rsquo;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.\nIn the messages.component.html, add the following under the button:\n\u0026lt;h2\u0026gt;Messages\u0026lt;/h2\u0026gt; \u0026lt;ul class=\u0026#34;messages\u0026#34;\u0026gt; \u0026lt;li *ngFor=\u0026#34;let message of messages\u0026#34;\u0026gt; \u0026lt;span\u0026gt;{{ message.timestamp }} {{ message.text }}\u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; And Voilà! You should now be able to send and receive messages. Try it out by opening the app from different browsers!\nYou can see the complete code in the Relay Angular Chat Example App.\n"},{"id":20,"href":"/docs/guides/nwaku/","title":"Nwaku Service Node","section":"Guides","content":"Nwaku Service Node # JS-Waku nodes join the Waku network by connecting to service nodes using secure websocket.\nNwaku (prev. nim-waku) is the reference implementation of the Waku v2 protocol and can be used as a service node.\nWhen using { bootstrap: { default: true } }, the js-waku node connects to a fleet of nwaku nodes operated by Status.\nIt 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.\nWe 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:\n`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.\nFor example (replace the multiaddr with your node\u0026rsquo;s).\nimport { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { peers: [ \u0026#34;/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm\u0026#34;, ], }, }); "},{"id":21,"href":"/docs/introduction/","title":"Introduction","section":"Introduction","content":"Waku Connect Docs # Waku Connect is a suite of libraries, SDKs and documentations to help you use Waku in your dApp.\nWaku is a decentralized, censorship-resistant, network and protocol family. It enables you to add communication features to your dApp in a decentralized manner, ensuring to your users that they will not be censored or de-platformed.\nWaku can be used for chat purposes and for many machine-to-machine use cases. You can learn more about Waku at waku.org.\nJS-Waku is the TypeScript implementation of the Waku protocol, built for browser environment.\nThe quick start presents an easy way to send and receive messages using js-waku. The FAQ lists frequently asked questions.\nIf you prefer video content, check out the presentations.\nIf you are looking for inspiration, check out the use cases Waku can enable.\nThe guides explain specific js-waku features and how it can be used with popular web frameworks.\nThe js-waku repository also holds a number of examples. The examples are working Proof-of-Concepts that demonstrate how to use js-waku. Check out the example list to see what usage each example demonstrates.\nYou can also try out some of the examples at the following locations:\n web-chat: A simple public chat (docs). eth-pm: End-to-end encrypted private messages (docs). eth-pm-wallet-encryption: Eth-pm using Web3 wallet encryption API (docs). Finally, if you want to learn how Waku works under the hoods, check the specs at rfc.vac.dev.\nBugs, Questions \u0026amp; Support # If you encounter any bug or would like to propose new features, feel free to open an issue.\nFor general discussion, get help or latest news, join #js-waku on Vac Discord or the Waku Telegram Group.\n"},{"id":22,"href":"/docs/","title":"Introduction","section":"Introduction","content":"Waku Connect Docs # Waku Connect is a suite of libraries, SDKs and documentations to help you use Waku in your dApp.\nWaku is a decentralized, censorship-resistant, network and protocol family. It enables you to add communication features to your dApp in a decentralized manner, ensuring to your users that they will not be censored or de-platformed.\nWaku can be used for chat purposes and for many machine-to-machine use cases. You can learn more about Waku at waku.org.\nJS-Waku is the TypeScript implementation of the Waku protocol, built for browser environment.\nThe quick start presents an easy way to send and receive messages using js-waku. The FAQ lists frequently asked questions.\nIf you prefer video content, check out the presentations.\nIf you are looking for inspiration, check out the use cases Waku can enable.\nThe guides explain specific js-waku features and how it can be used with popular web frameworks.\nThe js-waku repository also holds a number of examples. The examples are working Proof-of-Concepts that demonstrate how to use js-waku. Check out the example list to see what usage each example demonstrates.\nYou can also try out some of the examples at the following locations:\n web-chat: A simple public chat (docs). eth-pm: End-to-end encrypted private messages (docs). eth-pm-wallet-encryption: Eth-pm using Web3 wallet encryption API (docs). Finally, if you want to learn how Waku works under the hoods, check the specs at rfc.vac.dev.\nBugs, Questions \u0026amp; Support # If you encounter any bug or would like to propose new features, feel free to open an issue.\nFor general discussion, get help or latest news, join #js-waku on Vac Discord or the Waku Telegram Group.\n"},{"id":23,"href":"/docs/guides/vote_poll_sdk/poll_sdk/01_create-a-poll_button/","title":"Create-A-Poll Button","section":"Poll SDK","content":"Create-A-Poll Button # Create the Poll component. It will allow the user to create a new poll, view polls and answer them. We\u0026rsquo;ll start by adding a button to create a poll.\nmkdir components touch components/Poll.tsx Styled-components # Again, create a Wrapper for styling:\nimport styled from \u0026#34;styled-components\u0026#34;; 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.\nThe button is disabled if signer is undefined. To give a visual clue to the user, also make the button grey when disabled.\nUpon clicking the button, we set showPollCreation to true. showPollCreation will control when to render the poll creation modal.\ncomponents/Poll.tsx:\nimport { useMemo, useState } from \u0026#34;react\u0026#34;; import { Web3Provider } from \u0026#34;@ethersproject/providers\u0026#34;; import { CreateButton } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import { Theme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; type PollProps = { account: string | null | undefined; theme: Theme; }; export function Poll({ account, theme }: PollProps) { const [showPollCreation, setShowPollCreation] = useState(false); const disabled = useMemo(() =\u0026gt; !account, [account]); return ( \u0026lt;Wrapper\u0026gt; { \u0026lt;CreateButton style={{ backgroundColor: disabled ? \u0026#34;lightgrey\u0026#34; : theme.primaryColor, }} theme={theme} disabled={disabled} onClick={() =\u0026gt; setShowPollCreation(true)} \u0026gt; Create a poll \u0026lt;/CreateButton\u0026gt; } \u0026lt;/Wrapper\u0026gt; ); } Now update the MainPage component to render the new Poll component:\nindex.tsx:\nexport function MainPage() { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); return ( \u0026lt;div\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;Poll dApp\u0026#34;} theme={orangeTheme} activate={activateBrowserWallet} account={account} deactivate={deactivate} /\u0026gt; \u0026lt;Poll theme={orangeTheme} signer={signer} /\u0026gt; \u0026lt;/div\u0026gt; ); } Now, you have a button:\n Back Next: Poll Creation Component "},{"id":24,"href":"/docs/guides/vote_poll_sdk/poll_sdk/02_poll_creation/","title":"Poll Creation Component","section":"Poll SDK","content":"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.\nuseWakuPolling takes:\n 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\u0026rsquo;s multicall contract. Add these parameters to PollProps and call useWakuPolling.\ncomponents/Poll.tsx\nimport React, { useMemo, useState } from \u0026#34;react\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; import { PollCreation } from \u0026#34;@waku/poll-sdk-react-components\u0026#34;; import { Web3Provider } from \u0026#34;@ethersproject/providers\u0026#34;; import { useWakuPolling } from \u0026#34;@waku/poll-sdk-react-hooks\u0026#34;; import { CreateButton } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import { Theme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; type PollProps = { appName: string; library: Web3Provider | undefined; account: string | null | undefined; theme: Theme; tokenAddress: string; multicallAddress: string; }; export function Poll({ appName, library, signer, chainId, theme, tokenAddress, }: PollProps) { const [showPollCreation, setShowPollCreation] = useState(false); const wakuPolling = useWakuPolling( appName, tokenAddress, library, multicallAddress ); const disabled = useMemo(() =\u0026gt; !account, [account]); return ( \u0026lt;Wrapper\u0026gt; {showPollCreation \u0026amp;\u0026amp; signer \u0026amp;\u0026amp; ( \u0026lt;PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme} /\u0026gt; )} { \u0026lt;CreateButton style={{ backgroundColor: disabled ? \u0026#34;lightgrey\u0026#34; : theme.primaryColor, }} theme={theme} disabled={disabled} onClick={() =\u0026gt; setShowPollCreation(true)} \u0026gt; Create a poll \u0026lt;/CreateButton\u0026gt; } \u0026lt;/Wrapper\u0026gt; ); } Then pass them in MainPage.\nIn 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.\nTOKEN_ADDRESS can be any ERC20 token that will be used to token gate people from voting and creating polls.\nindex.tsx\nconst TOKEN_ADDRESS = \u0026#34;0x744d70FDBE2Ba4CF95131626614a1763DF805B9E\u0026#34;; const MULTICALL_ADDRESS = \u0026#34;0xeefba1e63905ef1d7acba5a8513c70307c1ce441\u0026#34;; export function MainPage() { const { activate, deactivate, account, provider } = useWeb3Connect(SUPPORTED_CHAIN_ID); return ( \u0026lt;div\u0026gt; \u0026lt;TopBar logo={\u0026#34;\u0026#34;} logoWidth={84} title={\u0026#34;Poll dApp\u0026#34;} theme={orangeTheme} activate={activateBrowserWallet} account={account} deactivate={deactivate} /\u0026gt; \u0026lt;Poll theme={orangeTheme} appName={\u0026#34;demo-poll-dapp\u0026#34;} library={provider} account={account} tokenAddress={TOKEN_ADDRESS} multicallAddress={MULTICALL_ADDRESS} /\u0026gt; \u0026lt;/div\u0026gt; ); } If you are using ethers web3 connector it is recommended to only render Poll component if provider is not undefined. You can now see the poll creation modal when clicking on the button:\n Back Next: Poll List Component "},{"id":25,"href":"/docs/guides/vote_poll_sdk/poll_sdk/03_poll_list/","title":"Poll List Component","section":"Poll SDK","content":"Poll List Component # To display existing polls, the PollList component is provided.\nSimply add it to the Poll function to render it. It needs the account variable that can be passed as a property to Poll:\ncomponents/Poll.tsx:\nimport React, { useMemo, useState } from \u0026#34;react\u0026#34;; import styled from \u0026#34;styled-components\u0026#34;; import { PollCreation, PollList } from \u0026#34;@waku/poll-sdk-react-components\u0026#34;; import { Web3Provider } from \u0026#34;@ethersproject/providers\u0026#34;; import { useWakuPolling } from \u0026#34;@waku/poll-sdk-react-hooks\u0026#34;; import { CreateButton } from \u0026#34;@waku/vote-poll-sdk-react-components\u0026#34;; import { Theme } from \u0026#34;@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes\u0026#34;; type PollProps = { appName: string; library: Web3Provider | undefined; account: string | null | undefined; theme: Theme; tokenAddress: string; multicallAddress: string; }; export function Poll({ appName, library, account, theme, tokenAddress, multicallAddress, }: PollProps) { const [showPollCreation, setShowPollCreation] = useState(false); const wakuPolling = useWakuPolling( appName, tokenAddress, library, multicallAddress ); const disabled = useMemo(() =\u0026gt; !account, [account]); return ( \u0026lt;Wrapper\u0026gt; {showPollCreation \u0026amp;\u0026amp; account \u0026amp;\u0026amp; ( \u0026lt;PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme} /\u0026gt; )} { \u0026lt;CreateButton style={{ backgroundColor: disabled ? \u0026#34;lightgrey\u0026#34; : theme.primaryColor, }} theme={theme} disabled={disabled} onClick={() =\u0026gt; setShowPollCreation(true)} \u0026gt; Create a poll \u0026lt;/CreateButton\u0026gt; } \u0026lt;PollList wakuPolling={wakuPolling} account={account} theme={theme} /\u0026gt; \u0026lt;/Wrapper\u0026gt; ); } Et voila! The PollList component handles the display of polls:\nAnd answering them:\nYou can find the resulting code in the examples folder.\nThe 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:\nBack "},{"id":26,"href":"/docs/guides/debug/","title":"How to Debug your Waku dApp","section":"Guides","content":"How to Debug your Waku dApp # Enable Debug Logs # JS-Waku and its most relevant dependencies (libp2p) uses debug to handle logs.\nNodeJS # To enable debug logs when running js-waku with NodeJS, simply set the DEBUG environment variable.\nTo only enable js-waku debug logs:\nexport DEBUG=waku* To enable js-waku and libp2p debug logs:\nexport DEBUG=waku*,libp2p* To enable all debug logs:\nexport DEBUG=* Browser # To see the debug logs in your browser\u0026rsquo;s console, you need to modify the local storage and add debug key.\nHere are guides for some modern browsers:\n Firefox Chrome key value effect debug waku* enable js-waku debug logs debug waku*,libp2p* enable js-waku and libp2p debug logs debug * enable all debug logs Check Websocket Setup # Nwaku natively supports WebSocket (ws) and WebSocket Secure (wss).\nThese are currently the only transports supported to connect to the Waku network from a browser.\nModern browsers are restrictive with the usage of WebSocket:\n Within a secure context insecure subroutines are disallowed: On a https:// webpage, only wss connections are allowed, not ws, Certificate validation checks are the same for https and wss: Certificate must not be expired, certificate needs to come from a CA recognize by the browser or system (no self-signed cert, no ip cert), domain name must match, etc, Subroutines errors are not displayed to the user: If a WebSocket connection fails, the user will not be informed, you need to check the browser\u0026rsquo;s console. Finally, these rules do not apply if the webpage is served locally (ie, on localhost or 127.0.0.1).\nIf you have difficulties to connect to a remote node via wss:\n1. Check that the certificate is valid by opening the wss connection directly in the browser:\nIf the multiaddr is /dns4/nwakunode.com/tcp/1234/wss/p2p/16... then open https://nwakunode.com:1234 in a modern browser. If you get a certificate error, then check why the browser returns this certificate error as this is the issue.\nIf you get a blank page, or any other error, go to step 2.\n2. Try to connect with websocat:\nCheck if you can connect to the WebSocket port using websocat:\n(assuming multiaddr is /dns4/nwakunode.com/tcp/1234/wss/p2p/16...)\nwebsocat -v wss://nwakunode.com:1234 # ... /multistream/1.0.0 If the last line is /multistream/1.0.0 then it works! In this case, the issue might be somewhere in your code. Do not hesitate to get support on the Vac Discord.\nIf you get an error, try with option -k, --insecure Accept invalid certificates and hostnames while connecting to TLS:\nwebsocat -vk wss://nwakunode.com:1234 # ... /multistream/1.0.0 If it works, then your certificate being invalid is the issue.\nIf it does not work then indeed, your nwaku node does not accept WebSocket connections, go to 3 for a last check.\n3. Verify the WebSocket port is accessible:\nUse telnet (or any other networking tool) to check that the WebSocket port is indeed open and accessible:\n(assuming multiaddr is /dns4/nwakunode.com/tcp/1234/wss/p2p/16...)\ntelnet nwakunode.com 1234 Trying 123.123.123.123... Connected nwakunode.com. Escape character is \u0026#39;^]\u0026#39; (Press CTRL-] to escape).\nIf this works then indeed, there is an issue with nwaku, come get support on the Vac Discord or open an issue.\nIf this does not work then ensure the WebSocket port is open.\n"},{"id":27,"href":"/docs/quick_start/","title":"Quick Start","section":"Introduction","content":"Quick Start # In this section you will learn how to receive and send messages using Waku Relay.\nA more in depth guide for Waku Relay can be found here.\nInstall # Install the js-waku package:\nnpm install js-waku # or with yarn yarn add js-waku Start a waku node # import { Waku } from \u0026#34;js-waku\u0026#34;; const waku = await Waku.create({ bootstrap: { default: true } }); Listen for messages # The contentTopic is a metadata string that allows categorization of messages on the waku network. Depending on your use case, you can either create one (or several) new contentTopic(s) or look at the RFCs and use an existing contentTopic. See How to Choose a Content Topic for more details.\nFor example, if you were to use a new contentTopic such as /my-cool-app/1/my-use-case/proto, here is how to listen to new messages received via Waku v2 Relay:\nwaku.relay.addObserver( (msg) =\u0026gt; { console.log(\u0026#34;Message received:\u0026#34;, msg.payloadAsUtf8); }, [\u0026#34;/my-cool-app/1/my-use-case/proto\u0026#34;] ); Send messages # Messages are wrapped in a WakuMessage envelop.\nimport { WakuMessage } from \u0026#34;js-waku\u0026#34;; const msg = await WakuMessage.fromUtf8String( \u0026#34;Here is a message!\u0026#34;, \u0026#34;/my-cool-app/1/my-use-case/proto\u0026#34; ); await waku.relay.send(msg); Building an app # Check out the ReactJS Waku Relay guide to learn how you can use the code above in a React app.\n"},{"id":28,"href":"/docs/faq/","title":"FAQ","section":"Introduction","content":"FAQ # Frequently Asked Questions for developers using js-waku:\n1. Why should I build a frontend only webapp (no NodeJS backend)? # Waku enables dApp to add communication, e.g. interaction between users, in a fully decentralized manner. A webapp that uses NodeJS as a backend implies that a party runs said NodeJS software in a centralized infrastructure.\nDespite using Waku \u0026amp; Ethereum, such webapp cannot become decentralized.\nBy building a frontend only webapp, that entirely runs in the browser, one can distribute the frontend code in many manners: host it, mirror it, have it on GitHub, deploy it on IPFS, etc. Enabling anyone to download this code and run in the browser, making the webapp a truly decentralized dApp.\n2. I am getting a Module parse failed: Unexpected token error # When using an older version of babel (used by react-scripts), the following error may appear when running the webapp:\n./node_modules/multistream-select/src/ls.js 55:2 Module parse failed: Unexpected token (55:2) File was processed with these loaders: * ./node_modules/babel-loader/lib/index.js You may need an additional loader to handle the result of these loaders. | await pipe(protocolsReader, lp.decode(), async | /** @type {AsyncIterable\u0026lt;BufferList\u0026gt;} */ \u0026gt; source =\u0026gt; { | for await (const protocol of source) { | // Remove the newline ./node_modules/js-waku/build/module/lib/waku_relay/index.js 228:16 Module parse failed: Unexpected token (228:16) File was processed with these loaders: * ./node_modules/react-scripts/node_modules/babel-loader/lib/index.js You may need an additional loader to handle the result of these loaders. | } | \u0026gt; meshPeers?.forEach(peer =\u0026gt; { | toSend.add(peer); | }); As documented in issue #165, this error comes from an older babel version. You need babel version 7.13.2 or above.\nYou can check your version using npm ls:\n▶ npm ls @babel/preset-env waku-pres@0.1.0 └─┬ react-scripts@4.0.3 ├─┬ @svgr/webpack@5.5.0 │ └── @babel/preset-env@7.12.17 ├─┬ babel-preset-react-app@10.0.0 │ └── @babel/preset-env@7.12.1 └─┬ workbox-webpack-plugin@5.1.4 └─┬ workbox-build@5.1.4 └── @babel/preset-env@7.12.17 deduped The best way to fix this is by using a more recent ReactJS stack. This might not always possible, in this case force the installation of babel 7.14:\nnpm i --save-dev @babel/preset-env@7.14 rm -rf node_modules package-lock.json npm install 3. Store nodes only keep 30 days of messages by default, what if I need to keep messages permanently? # Waku store protocol at this point does not provide a scalable solution to serve a large size archival data (as store nodes need to persist the entire history on their own local storage space), due to this, this protocol is more suitable for serving recent historical messages.\nNevertheless, research on the scalability aspect has been done and documented in here.\nNote that it is possible for operators to run their own store node and set a higher value than the default ones:\nnwaku:\n--store-capacity Maximum number of messages to keep in store. [=50000]. go-waku:\n--store-days value maximum number of days before a message is removed from the store (default: 30) --store-capacity value maximum number of messages to store (default: 50000) "},{"id":29,"href":"/docs/use_cases/","title":"Use Cases","section":"Introduction","content":"Use Cases # Waku is a generalized communication network. It can enable numerous use cases, both person-to-person (e.g. messenger) and machine-to-machine (e.g. state channels).\nThis is a non-exhaustive list of use cases that we have considered and their current status.\nIf we are aware of other projects using js-waku and other use cases that could be implemented, feel free to open a PR.\nLegend:\n Live: We are aware of projects who have implemented this use case. SDK Available: An SDK is available to easily implement this use case. Work In Progress: We are aware of projects working to implement this use case. Proof of Concept: A Proof of concept was created, sometimes as part of a hackathon. Idea: This is an unexplored use case, more research and work may be needed. Chat Messenger # Work In Progress Waku can be used as the communication layer to a private, decentralized, censorship-resistant messenger.\n Status Web: repo Polls # SDK Available Create, answer and view polls which are censorship-resistant.\n Waku Connect Poll SDK: docs, repo NFT Marketplace # Live Use Waku to take NFT bids and offers off-chain and save gas. Add a social media layer, allowing NFT owners to like, comments, etc.\n https://smolpuddle.io/ repo State Channels # Idea Use Waku to enable two parties to setup and maintain a state channel.\n Discussion: statechannels.org Discourse Voting and Proposals # SDK Available For proposals submitted on the blockchain, exchange votes over Waku to save gas. Votes can then be aggregated and submitted to the blockchain to commit the result.\nCreate, answer and view polls which are censorship-resistant.\n Waku Connect Vote SDK: docs, repo Signature Exchange for Multi-Sig Wallets # Idea Use Waku to enable several owners of a given multi-sig wallets to exchange signatures in a decentralized, private \u0026amp; censorship-resistant manner to approve transactions. Gameplay Communication # Proof of Concept Use Waku as the communication layer for a peer-to-peer, decentralize game. Remove the need of a centralized infrastructure for gameplay communications.\n Super Card Game dApp to Wallet Communication # Live Communication between a user\u0026rsquo;s wallet and a dApp can be used by dApp operators to notify users (e.g. governance token holders get notified to vote on a proposal), or for a dApp to request transaction signature to the wallet.\n WalletConnect 2.0 HashPack Layer 2 Communication # Idea Use Waku as an existing communication network to broadcast and aggregate layer 2 transactions. Possibly increasing privacy, anonymity and resilience. Generalized Marketplace # Proof of Concept Use Waku to enable users to offer, bid, accept and trade goods and services to create a ride-sharing or tradings apps.\n Waku-Uber: article, repo Social Media Platform # Idea Chat Messenger is one form of social media that can be empowered by Waku to be decentralized and censorship-resistant. Other form of social media: news feed, blog posts, audio or video sharing, can also benefit of Waku. "},{"id":30,"href":"/docs/presentations/","title":"Presentations \u0026 Videos","section":"Introduction","content":"Presentations \u0026amp; Videos # 21 Apr 2022 - Secureum TrustX - Waku: Enabling a New Dimension for dApps # Presenter: Corey Petty.\n27 Jan 2022 - Web 3.0 Conference # Presenter: Franck Royer. mirror: https://odysee.com/@Waku:c/2022-07-web3-conference:5\n17 Sep 2021 - EthOnline # Pre-recorded video for hackathon participants.\n Presenter: Franck Royer. Slides: https://notes.status.im/eth-global-2021.\n21 Jul 2021 - EthCC 2021 # Note: DappConnect is now named Waku Connect.\n Presenter: Franck Royer. Slides: https://notes.status.im/dappconnect-pres.\n21 Jul 2021 - Ethereum Engineering Group meetup # Note: DappConnect is now named Waku Connect.\n Presenter: Franck Royer. Slides: https://notes.status.im/dappconnect-pres.\n"},{"id":31,"href":"/docs/guides/","title":"Guides","section":"Introduction","content":"Guides # Can\u0026rsquo;t find what you are looking for? Check out the examples.\nGeneral Concepts # How to Choose a Content Topic Discovery \u0026amp; Bootstrap Nodes How to Debug your Waku dApp JavaScript # Receive and Send Messages Using Waku Relay Retrieve Messages Using Waku Store Send Messages Using Waku Light Push Encrypt Messages Using Waku Message Version 1 Sign Messages Using Waku Message Version 1 Sign Messages Using a Web3 Wallet (EIP-712) ReactJS # Receive and Send Messages Using Waku Relay With ReactJS Retrieve Messages Using Waku Store With ReactJS Angular # Receive and Send Messages Using Waku Relay With ReactJS Service Nodes # Nwaku Service Node "},{"id":32,"href":"/docs/examples/","title":"Examples","section":"Introduction","content":"Examples # Here is the list of the code examples and the features they demonstrate. To run or studies the example, click on the code links.\nMinimal ReactJS Chat App # Code: relay-reactjs-chat.\nDemonstrates:\n Group chat React/JavaScript Waku Relay Protobuf using protons No async/await syntax Minimal ReactJS Waku Store App # Code: store-reactjs-chat.\nDemonstrates:\n Waku Store React/JavaScript Protobuf using protons Minimal Angular Chat App # Code: relay-angular-chat.\nDemonstrates:\n Group chat Angular/JavaScript Waku Relay Protobuf using protons No async/await syntax Observables Using Waku Store in JavaScript With Minified Library # Code: store-js.\nDemonstrates:\n How to stop retrieving results from Waku Store on condition Use minified bundle from unpkg.com Pure JavaScript application Using Waku Relay in JavaScript With Minified Library # Code: relay-js.\nDemonstrates:\n Waku Relay: Send and receive messages using Waku Relay Use minified bundle from unpkg.com Pure JavaScript application Web Chat App # Code: web-chat. Live: https://js-waku.wakuconnect.dev/examples/web-chat/\nDemonstrates:\n Group chat React/TypeScript Waku Relay Waku Store Protobuf using .proto files + bufbuild + ts-proto Ethereum Private Message Web App # Code: eth-pm. Live: https://js-waku.wakuconnect.dev/examples/eth-pm/\nDemonstrates:\n Private messaging React/TypeScript Waku Light Push Signature with Web3 Wallet Asymmetric Encryption Symmetric Encryption Protobuf using protobufjs Ethereum Private Message Using Web3 Wallet Encryption API Web App # Code: eth-pm-wallet-encryption. Live: https://js-waku.wakuconnect.dev/examples/eth-pm-wallet-encryption/\nDemonstrates:\n Private Messaging React/TypeScript Waku Light Push Signature with Web3 using EIP-712: eth_signTypedData_v4 Asymmetric Encryption Usage of eth_decrypt Web3 Wallet API Protobuf using protobufjs Uber-like minimalistic car sharing app suing Vue.js # Code: TheBojda/waku-uber.\nArticle: Decentralized Uber: Here\u0026rsquo;s How I Built It With Status.im, Waku, and Vue.js.\nDemonstrates:\n Vue.js Waku Relay Protobuf using protons "},{"id":33,"href":"/docs/crypto_libraries/","title":"Cryptographic Libraries","section":"Introduction","content":"Cryptographic Libraries # A note on the cryptographic libraries used as it is a not a straightforward affair.\nAsymmetric encryption # Uses ecies-geth which in turns uses SubtleCrypto Web API (browser), secp256k1 (native binding for node) or elliptic (pure JS if none of the other libraries are available).\nSymmetric encryption # Uses SubtleCrypto Web API (browser) or NodeJS' crypto module.\n"},{"id":34,"href":"/docs/waku_protocols/","title":"Implemented Waku Protocols","section":"Introduction","content":"Waku Protocol Support # You can track progress on the project board.\n ✔: Supported 🚧: Implementation in progress ⛔: Support is not planned Spec Implementation Status 6/WAKU1 ⛔ 7/WAKU-DATA ⛔ 8/WAKU-MAIL ⛔ 9/WAKU-RPC ⛔ 10/WAKU2 🚧 11/WAKU2-RELAY ✔ 12/WAKU2-FILTER 13/WAKU2-STORE ✔ (querying node only) 14/WAKU2-MESSAGE ✔ 15/WAKU2-BRIDGE 16/WAKU2-RPC ⛔ 17/WAKU2-RLNRELAY 18/WAKU2-SWAP 19/WAKU2-LIGHTPUSH ✔ 20/TOY-ETH-PM ✔ (as example) 21/WAKU2-FTSTORE ✔ 22/TOY-CHAT ✔ (as example) 25/LIBP2P-DNS-DISCOVERY 🚧 26/WAKU2-PAYLOAD ✔ "},{"id":35,"href":"/docs/guides/vote_poll_sdk/","title":"Vote Poll Sdk","section":"Guides","content":"Waku Connect Vote \u0026amp; Poll SDK # The Waku Connect Vote \u0026amp; Poll SDK enables developers to add Waku powered polling and voting features to their dApp.\nThe repository can be found on GitHub: https://github.com/status-im/wakuconnect-vote-poll-sdk.\nThe SDK can be used in two different ways: to vote (commitment to the blockchain) or poll (no interaction with the blockchain).\nFor 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.\nDocumentation # For either SDKs, you need to start by creating a dApp. To do so, you can follow the Create a DApp instructions.\nPackages # 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. Waku Connect Vote SDK # The Waku Connect Vote SDK allows you to leverage Waku to save gas fees for most voters. It uses Waku to broadcast and aggregate votes. Most token holders will not need to spend gas to vote.\nOnly the party that starts an election and submit the end results need to interact with the blockchain.\nFor example, it can be used by a DAO to manage proposals where proposal creation and vote results must be committed to the blockchain.\nWith Waku Connect 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.\nDocumentation # You can find more information about the Vote SDK\u0026rsquo;s properties in the README.\nSee How to Use the Waku Connect Vote SDK.\nWaku Connect Poll SDK # The Waku Connect Poll SDK allows you to leverage Waku and enable token holders to create, answer and view polls. The polls are not committed to the blockchain and hence do not cost gas.\nAs the polls use Waku, they do maintain properties expected from dApps: decentralized and censorship-resistant.\nThe high-level functionality is as follows:\n 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 Waku Connect Poll SDK.\n"}] |