mirror of
https://github.com/acid-info/docs.wakuconnect.dev.git
synced 2025-02-22 14:48:06 +00:00
1375 lines
73 KiB
HTML
1375 lines
73 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="sidebar-visible no-js light">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>DappConnect Docs</title>
|
|
<meta name="robots" content="noindex" />
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
<meta name="description" content="">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#ffffff" />
|
|
|
|
<link rel="icon" href="favicon.svg">
|
|
<link rel="shortcut icon" href="favicon.png">
|
|
<link rel="stylesheet" href="css/variables.css">
|
|
<link rel="stylesheet" href="css/general.css">
|
|
<link rel="stylesheet" href="css/chrome.css">
|
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|
|
|
<!-- Fonts -->
|
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|
<link rel="stylesheet" href="fonts/fonts.css">
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
<link rel="stylesheet" href="highlight.css">
|
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
<link rel="stylesheet" href="custom.css">
|
|
|
|
</head>
|
|
<body>
|
|
<!-- Provide site root to javascript -->
|
|
<script type="text/javascript">
|
|
var path_to_root = "";
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
|
</script>
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
<script type="text/javascript">
|
|
try {
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
}
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
}
|
|
} catch (e) { }
|
|
</script>
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
<script type="text/javascript">
|
|
var theme;
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
var html = document.querySelector('html');
|
|
html.classList.remove('no-js')
|
|
html.classList.remove('light')
|
|
html.classList.add(theme);
|
|
html.classList.add('js');
|
|
</script>
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
<script type="text/javascript">
|
|
var html = document.querySelector('html');
|
|
var sidebar = 'hidden';
|
|
if (document.body.clientWidth >= 1080) {
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
sidebar = sidebar || 'visible';
|
|
}
|
|
html.classList.remove('sidebar-visible');
|
|
html.classList.add("sidebar-" + sidebar);
|
|
</script>
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
<div class="sidebar-scrollbox">
|
|
<ol class="chapter"><li class="chapter-item expanded affix "><a href="introduction.html">Introduction</a></li><li class="chapter-item expanded "><a href="quick_start.html"><strong aria-hidden="true">1.</strong> Quick Start</a></li><li class="chapter-item expanded "><a href="guides/index.html"><strong aria-hidden="true">2.</strong> Guides</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="guides/choose_content_topic.html"><strong aria-hidden="true">2.1.</strong> How to Choose a Content Topic</a></li><li class="chapter-item expanded "><a href="guides/relay_receive_send_messages.html"><strong aria-hidden="true">2.2.</strong> Receive and Send Messages Using Waku Relay</a></li><li class="chapter-item expanded "><a href="guides/store_retrieve_messages.html"><strong aria-hidden="true">2.3.</strong> Retrieve Messages Using Waku Store</a></li><li class="chapter-item expanded "><a href="guides/encrypt_messages_version_1.html"><strong aria-hidden="true">2.4.</strong> Encrypt Messages Using Waku Message Version 1</a></li><li class="chapter-item expanded "><a href="guides/reactjs_relay.html"><strong aria-hidden="true">2.5.</strong> Receive and Send Messages Using Waku Relay With ReactJS</a></li><li class="chapter-item expanded "><a href="guides/reactjs_store.html"><strong aria-hidden="true">2.6.</strong> Retrieve Messages Using Waku Store With ReactJS</a></li><li class="chapter-item expanded "><a href="guides/light_push_send_messages.html"><strong aria-hidden="true">2.7.</strong> Send Messages Using Waku Light Push</a></li></ol></li><li class="chapter-item expanded "><a href="examples.html"><strong aria-hidden="true">3.</strong> Examples</a></li><li class="chapter-item expanded affix "><a href="waku_protocols.html">Implemented Waku Protocols</a></li></ol> </div>
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|
</nav>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
<div id="menu-bar" class="menu-bar sticky bordered">
|
|
<div class="left-buttons">
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</button>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<h1 class="menu-title">DappConnect Docs</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
<a href="https://github.com/vacp2p/docs.dappconnect.dev" title="Git repository" aria-label="Git repository">
|
|
<i id="git-repository-button" class="fa fa-github"></i>
|
|
</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script type="text/javascript">
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<h1 id="dappconnect-docs"><a class="header" href="#dappconnect-docs">DappConnect Docs</a></h1>
|
|
<p>DappConnect is a suite of libraries, SDKs and documentations to help you use Waku in your dApp.</p>
|
|
<p>Waku 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.</p>
|
|
<p>Waku can be used for chat purposes and for many machine-to-machine use cases.
|
|
You can learn more about Waku at <a href="https://waku.vac.dev">waku.vac.dev</a>.</p>
|
|
<p>JS-Waku is the TypeScript implementation of the Waku protocol,
|
|
built for browser environment.</p>
|
|
<p>The <a href="./quick_start.html">quick start</a> presents an easy way to send and receive messages using js-waku.</p>
|
|
<p>The <a href="./guides">guides</a> explain specific js-waku features
|
|
and how it can be used with popular web frameworks.</p>
|
|
<p>The js-waku repository also holds a number of <a href="https://github.com/status-im/js-waku/tree/main/examples">examples</a>.
|
|
The examples are working Proof-of-Concepts that demonstrate how to use js-waku.
|
|
Check out the <a href="./examples.html">example list</a> to see what usage each example demonstrates. </p>
|
|
<p>Finally, if you want to learn how Waku works under the hoods, check the specs at <a href="https://rfc.vac.dev/">rfc.vac.dev</a>.</p>
|
|
<h2 id="bugs-questions--support"><a class="header" href="#bugs-questions--support">Bugs, Questions & Support</a></h2>
|
|
<p>If you encounter any bug or would like to propose new features, feel free to <a href="https://github.com/status-im/js-waku/issues/new/">open an issue</a>.</p>
|
|
<p>To get help, join #dappconnect-support on <a href="https://discord.gg/j5pGbn7MHZ">Vac Discord</a> or <a href="https://t.me/dappconnectsupport">Telegram</a>.
|
|
For more general discussion and latest news, join #dappconnect on <a href="https://discord.gg/9DgykdmpZ6">Vac Discord</a> or <a href="https://t.me/dappconnect">Telegram</a>.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="quick-start"><a class="header" href="#quick-start">Quick Start</a></h1>
|
|
<p>In this section you will learn how to receive and send messages using Waku Relay.</p>
|
|
<p>A more in depth guide for Waku Relay can be found <a href="guides/relay_receive_send_messages.html">here</a>.</p>
|
|
<h2 id="install"><a class="header" href="#install">Install</a></h2>
|
|
<p>Install the <code>js-waku</code> package:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
# or with yarn
|
|
yarn add js-waku
|
|
</code></pre>
|
|
<h3 id="start-a-waku-node"><a class="header" href="#start-a-waku-node">Start a waku node</a></h3>
|
|
<pre><code class="language-ts">import { Waku } from 'js-waku';
|
|
|
|
const waku = await Waku.create({ bootstrap: true });
|
|
</code></pre>
|
|
<h3 id="listen-for-messages"><a class="header" href="#listen-for-messages">Listen for messages</a></h3>
|
|
<p>The <code>contentTopic</code> is a metadata <code>string</code> that allows categorization of messages on the waku network.
|
|
Depending on your use case, you can either create one (or several) new <code>contentTopic</code>(s)
|
|
or look at the <a href="https://rfc.vac.dev/">RFCs</a> and use an existing <code>contentTopic</code>.
|
|
See <a href="guides/choose_content_topic.html">How to Choose a Content Topic</a> for more details.</p>
|
|
<p>For example, if you were to use a new <code>contentTopic</code> such as <code>/my-cool-app/1/my-use-case/proto</code>,
|
|
here is how to listen to new messages received via <a href="https://rfc.vac.dev/spec/11/">Waku v2 Relay</a>:</p>
|
|
<pre><code class="language-ts">waku.relay.addObserver((msg) => {
|
|
console.log("Message received:", msg.payloadAsUtf8)
|
|
}, ["/my-cool-app/1/my-use-case/proto"]);
|
|
</code></pre>
|
|
<h3 id="send-messages"><a class="header" href="#send-messages">Send messages</a></h3>
|
|
<p>Messages are wrapped in a <code>WakuMessage</code> envelop.</p>
|
|
<pre><code class="language-ts">import { WakuMessage } from 'js-waku';
|
|
|
|
const msg = await WakuMessage.fromUtf8String("Here is a message!", "/my-cool-app/1/my-use-case/proto")
|
|
await waku.relay.send(msg);
|
|
</code></pre>
|
|
<h3 id="building-an-app"><a class="header" href="#building-an-app">Building an app</a></h3>
|
|
<p>Check out the <a href="./guides/reactjs_relay.html">ReactJS Waku Relay guide</a> to learn how you can use the code above in a React app. </p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="guides"><a class="header" href="#guides">Guides</a></h1>
|
|
<h2 id="waku-concepts"><a class="header" href="#waku-concepts">Waku Concepts</a></h2>
|
|
<ul>
|
|
<li><a href="guides/choose_content_topic.html">How to Choose a Content Topic</a></li>
|
|
</ul>
|
|
<h2 id="javascript"><a class="header" href="#javascript">JavaScript</a></h2>
|
|
<ul>
|
|
<li><a href="guides/relay_receive_send_messages.html">Receive and Send Messages Using Waku Relay</a></li>
|
|
<li><a href="guides/store_retrieve_messages.html">Retrieve Messages Using Waku Store</a></li>
|
|
<li><a href="guides/encrypt_messages_version_1.html">Encrypt Messages Using Waku Message Version 1</a></li>
|
|
<li><a href="guides/light_push_send_messages.html">Send Messages Using Waku Light Push</a></li>
|
|
</ul>
|
|
<h2 id="reactjs"><a class="header" href="#reactjs">ReactJS</a></h2>
|
|
<ul>
|
|
<li><a href="guides/reactjs_relay.html">Receive and Send Messages Using Waku Relay With ReactJS</a></li>
|
|
<li><a href="guides/reactjs_store.html">Retrieve Messages Using Waku Store With ReactJS</a></li>
|
|
</ul>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="how-to-choose-a-content-topic"><a class="header" href="#how-to-choose-a-content-topic">How to Choose a Content Topic</a></h1>
|
|
<p>A content topic is used for content based filtering.</p>
|
|
<p>It allows you to filter out the messages that your dApp processes,
|
|
both when receiving live messages (Relay) or retrieving historical messages (Store).</p>
|
|
<p>The format for content topics is as follows:</p>
|
|
<p><code>/{dapp-name}/{version}/{content-topic-name}/{encoding}</code></p>
|
|
<ul>
|
|
<li><code>dapp-name</code>: The name of your dApp, it must be unique to avoid conflict with other dApps.</li>
|
|
<li><code>version</code>: We usually start at <code>1</code>, useful when introducing breaking changes in your messages.</li>
|
|
<li><code>content-topic-name</code>: The actual content topic name to use for filtering.
|
|
If your dApp uses DappConnect for several features,
|
|
you should use a content topic per feature.</li>
|
|
<li><code>encoding</code>: The encoding format of the message, Protobuf is most often used: <code>proto</code>.</li>
|
|
</ul>
|
|
<p>For example: Your dApp's name is SuperCrypto,
|
|
it enables users to receive notifications and send private messages.
|
|
You may want to use the following content topics:</p>
|
|
<ul>
|
|
<li><code>/supercrypto/1/notification/proto</code></li>
|
|
<li><code>/supercrypto/1/private-message/proto</code></li>
|
|
</ul>
|
|
<p>You can learn more about Waku topics in the <a href="https://rfc.vac.dev/spec/23/">23/WAKU2-TOPICS</a> specs.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="receive-and-send-messages-using-waku-relay"><a class="header" href="#receive-and-send-messages-using-waku-relay">Receive and Send Messages Using Waku Relay</a></h1>
|
|
<p>Waku Relay is a gossip protocol that enables you to send and receive messages.
|
|
You can find Waku Relay's specifications on <a href="https://rfc.vac.dev/spec/11/">Vac RFC</a>.</p>
|
|
<p>Before starting, you need to choose a <em>Content Topic</em> for your dApp.
|
|
Check out the <a href="guides/./choose_content_topic.html">how to choose a content topic guide</a> to learn more about content topics.</p>
|
|
<p>For this guide, we are using a single content topic: <code>/relay-guide/1/chat/proto</code>.</p>
|
|
<h1 id="installation"><a class="header" href="#installation">Installation</a></h1>
|
|
<p>You can install <a href="https://npmjs.com/package/js-waku">js-waku</a> using your favorite package manager:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
</code></pre>
|
|
<h1 id="create-waku-instance"><a class="header" href="#create-waku-instance">Create Waku Instance</a></h1>
|
|
<p>In order to interact with the Waku network, you first need a Waku instance:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = await Waku.create({ bootstrap: true });
|
|
</code></pre>
|
|
<p>Passing the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = await Waku.create({
|
|
bootstrap: [
|
|
'/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
|
|
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
|
|
]
|
|
});
|
|
</code></pre>
|
|
<h1 id="wait-to-be-connected"><a class="header" href="#wait-to-be-connected">Wait to be connected</a></h1>
|
|
<p>When using the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">await waku.waitForConnectedPeer();
|
|
</code></pre>
|
|
<p>The returned <code>Promise</code> will resolve once you are connected to a Waku Relay peer.</p>
|
|
<h1 id="receive-messages"><a class="header" href="#receive-messages">Receive messages</a></h1>
|
|
<p>To receive messages for your app,
|
|
you need to register an observer on relay for your app's content topic:</p>
|
|
<pre><code class="language-js">const processIncomingMessage = (wakuMessage) => {
|
|
console.log(`Message Received: ${wakuMessage.payloadAsUtf8}`);
|
|
};
|
|
|
|
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
|
|
</code></pre>
|
|
<h1 id="send-messages-1"><a class="header" href="#send-messages-1">Send Messages</a></h1>
|
|
<p>You are now ready to send messages.
|
|
Let's start by sending simple strings as messages.</p>
|
|
<p>To send a message, you need to wrap the message in a <code>WakuMessage</code>.
|
|
When using a basic string payload, you can use the <code>WakuMessage.fromUtf8String</code> helper:</p>
|
|
<pre><code class="language-js">import { WakuMessage } from 'js-waku';
|
|
|
|
const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/relay-guide/1/chat/proto`);
|
|
</code></pre>
|
|
<p>Then, use the <code>relay</code> module to send the message to our peers,
|
|
the message will then be relayed to the rest of the network thanks to Waku Relay:</p>
|
|
<pre><code class="language-js">await waku.relay.send(wakuMessage);
|
|
</code></pre>
|
|
<h1 id="use-protobuf"><a class="header" href="#use-protobuf">Use Protobuf</a></h1>
|
|
<p>Sending strings as messages in unlikely to cover your dApps needs.</p>
|
|
<p>Waku v2 protocols use <a href="https://developers.google.com/protocol-buffers/">protobuf</a> <a href="https://rfc.vac.dev/spec/10/">by default</a>.</p>
|
|
<p>Let's review how you can use protobuf to include structured objects in Waku Messages.</p>
|
|
<p>First, define a data structure.
|
|
For this guide, we will use a simple chat message that contains a timestamp and text:</p>
|
|
<pre><code class="language-js">{
|
|
timestamp: Date;
|
|
text: string;
|
|
}
|
|
</code></pre>
|
|
<p>To encode and decode protobuf payloads, you can use the <a href="https://www.npmjs.com/package/protons">protons</a> package.</p>
|
|
<h2 id="install-protobuf-library"><a class="header" href="#install-protobuf-library">Install Protobuf Library</a></h2>
|
|
<p>First, install protons:</p>
|
|
<pre><code class="language-shell">npm install protons
|
|
</code></pre>
|
|
<h2 id="protobuf-definition"><a class="header" href="#protobuf-definition">Protobuf Definition</a></h2>
|
|
<p>Then define the simple chat message:</p>
|
|
<pre><code class="language-js">import protons from 'protons';
|
|
|
|
const proto = protons(`
|
|
message SimpleChatMessage {
|
|
uint64 timestamp = 1;
|
|
string text = 2;
|
|
}
|
|
`);
|
|
</code></pre>
|
|
<p>You can learn about protobuf message definitions here:
|
|
<a href="https://developers.google.com/protocol-buffers/docs/proto">Protocol Buffers Language Guide</a>.</p>
|
|
<h2 id="encode-messages"><a class="header" href="#encode-messages">Encode Messages</a></h2>
|
|
<p>Instead of wrapping an utf-8 string in a Waku Message,
|
|
you are going to wrap a protobuf payload.</p>
|
|
<p>First, encode the object:</p>
|
|
<pre><code class="language-js">const payload = proto.SimpleChatMessage.encode({
|
|
timestamp: Date.now(),
|
|
text: 'Here is a message'
|
|
});
|
|
</code></pre>
|
|
<p>Then, wrap it in a Waku Message:</p>
|
|
<pre><code class="language-js">const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
|
|
</code></pre>
|
|
<p>Now, you can send the message over Waku Relay the same way than before:</p>
|
|
<pre><code class="language-js">await waku.relay.send(wakuMessage);
|
|
</code></pre>
|
|
<h2 id="decode-messages"><a class="header" href="#decode-messages">Decode Messages</a></h2>
|
|
<p>To decode the messages received over Waku Relay,
|
|
you need to extract the protobuf payload and decode it using <code>protons</code>.</p>
|
|
<pre><code class="language-js">const processIncomingMessage = (wakuMessage) => {
|
|
// 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()}`);
|
|
};
|
|
</code></pre>
|
|
<p>Like before, add this callback as an observer to Waku Relay:</p>
|
|
<pre><code class="language-js">waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
|
|
</code></pre>
|
|
<h1 id="conclusion"><a class="header" href="#conclusion">Conclusion</a></h1>
|
|
<p>That is it! Now, you know how to send and receive messages over Waku using the Waku Relay protocol.</p>
|
|
<p>Feel free to check out other <a href="guides/./">guides</a> or <a href="guides//examples.html">examples</a>.</p>
|
|
<p>Here is the final code:</p>
|
|
<pre><code class="language-js">import { getBootstrapNodes, Waku, WakuMessage } from 'js-waku';
|
|
import protons from 'protons';
|
|
|
|
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) => waku.dial(addr)));
|
|
|
|
const processIncomingMessage = (wakuMessage) => {
|
|
// 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, ['/relay-guide/1/chat/proto']);
|
|
|
|
const payload = proto.SimpleChatMessage.encode({
|
|
timestamp: Date.now(),
|
|
text: 'Here is a message'
|
|
});
|
|
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
|
|
await waku.relay.send(wakuMessage);
|
|
</code></pre>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="retrieve-messages-using-waku-store"><a class="header" href="#retrieve-messages-using-waku-store">Retrieve Messages Using Waku Store</a></h1>
|
|
<p>DApps running on a phone or in a browser are often offline:
|
|
The browser could be closed or mobile app in the background.</p>
|
|
<p><a href="https://rfc.vac.dev/spec/11/">Waku Relay</a> 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 <strong>not</strong> save messages for later.</p>
|
|
<p>However, <a href="https://rfc.vac.dev/spec/13/">Waku Store</a> 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.</p>
|
|
<p>In this guide, we'll review how you can use Waku Store to retrieve messages.</p>
|
|
<p>Before starting, you need to choose a <em>Content Topic</em> for your dApp.
|
|
Check out the <a href="guides/./choose_content_topic.html">how to choose a content topic guide</a> to learn more about content topics.</p>
|
|
<p>For this guide, we are using a single content topic: <code>/store-guide/1/news/proto</code>.</p>
|
|
<h1 id="installation-1"><a class="header" href="#installation-1">Installation</a></h1>
|
|
<p>You can install <a href="https://npmjs.com/package/js-waku">js-waku</a> using your favorite package manager:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
</code></pre>
|
|
<h1 id="create-waku-instance-1"><a class="header" href="#create-waku-instance-1">Create Waku Instance</a></h1>
|
|
<p>In order to interact with the Waku network, you first need a Waku instance:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const wakuNode = await Waku.create({ bootstrap: true });
|
|
</code></pre>
|
|
<p>Passing the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const wakuNode = await Waku.create({
|
|
bootstrap: [
|
|
'/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
|
|
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
|
|
]
|
|
});
|
|
</code></pre>
|
|
<h1 id="wait-to-be-connected-1"><a class="header" href="#wait-to-be-connected-1">Wait to be connected</a></h1>
|
|
<p>When using the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">await waku.waitForConnectedPeer();
|
|
</code></pre>
|
|
<p>The returned Promise will resolve once you are connected to a Waku Store peer.</p>
|
|
<h1 id="use-protobuf-1"><a class="header" href="#use-protobuf-1">Use Protobuf</a></h1>
|
|
<p>Waku v2 protocols use <a href="https://developers.google.com/protocol-buffers/">protobuf</a> <a href="https://rfc.vac.dev/spec/10/">by default</a>.</p>
|
|
<p>Let's review how you can use protobuf to send structured data.</p>
|
|
<p>First, define a data structure.
|
|
For this guide, we will use a simple news article that contains a date of publication, title and body:</p>
|
|
<pre><code class="language-js">{
|
|
date: Date;
|
|
title: string;
|
|
body: string;
|
|
}
|
|
</code></pre>
|
|
<p>To encode and decode protobuf payloads, you can use the <a href="https://www.npmjs.com/package/protons">protons</a> package.</p>
|
|
<h2 id="install-protobuf-library-1"><a class="header" href="#install-protobuf-library-1">Install Protobuf Library</a></h2>
|
|
<p>First, install protons:</p>
|
|
<pre><code class="language-shell">npm install protons
|
|
</code></pre>
|
|
<h2 id="protobuf-definition-1"><a class="header" href="#protobuf-definition-1">Protobuf Definition</a></h2>
|
|
<p>Then specify the data structure:</p>
|
|
<pre><code class="language-js">import protons from 'protons';
|
|
|
|
const proto = protons(`
|
|
message ArticleMessage {
|
|
uint64 date = 1;
|
|
string title = 2;
|
|
string body = 3;
|
|
}
|
|
`);
|
|
</code></pre>
|
|
<p>You can learn about protobuf message definitions here:
|
|
<a href="https://developers.google.com/protocol-buffers/docs/proto">Protocol Buffers Language Guide</a>.</p>
|
|
<h2 id="decode-messages-1"><a class="header" href="#decode-messages-1">Decode Messages</a></h2>
|
|
<p>To decode the messages retrieved from a Waku Store node,
|
|
you need to extract the protobuf payload and decode it using <code>protons</code>.</p>
|
|
<pre><code class="language-js">const decodeWakuMessage = (wakuMessage) => {
|
|
// 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 };
|
|
};
|
|
</code></pre>
|
|
<h2 id="retrieve-messages"><a class="header" href="#retrieve-messages">Retrieve messages</a></h2>
|
|
<p>You now have all the building blocks to retrieve and decode messages for a store node.</p>
|
|
<p>Store node responses are paginated.
|
|
The <code>WakuStore.queryHistory</code> API automatically query all the pages in a sequential manner.
|
|
To process messages as soon as they received (page by page), use the <code>callback</code> option:</p>
|
|
<pre><code class="language-js">const ContentTopic = '/store-guide/1/news/proto';
|
|
|
|
const callback = (retrievedMessages) => {
|
|
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) => {
|
|
// Catch any potential error
|
|
console.log('Failed to retrieve messages from store', e);
|
|
});
|
|
</code></pre>
|
|
<p>Note that <code>WakuStore.queryHistory</code> 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.</p>
|
|
<h2 id="filter-messages-by-send-time"><a class="header" href="#filter-messages-by-send-time">Filter messages by send time</a></h2>
|
|
<p>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.</p>
|
|
<p><a href="https://rfc.vac.dev/spec/14/">Waku Message</a> defines an optional unencrypted <code>timestamp</code> field.
|
|
The timestamp is set by the sender.
|
|
By default, js-waku sets the timestamp of outgoing message to the current time.</p>
|
|
<p>You can filter messages that include a timestamp within given bounds with the <code>timeFilter</code> option.</p>
|
|
<p>Retrieve messages up to a week old:</p>
|
|
<pre><code class="language-js">// [..] `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) => {
|
|
console.log('Failed to retrieve messages from store', e);
|
|
});
|
|
</code></pre>
|
|
<h2 id="end-result"><a class="header" href="#end-result">End result</a></h2>
|
|
<p>You can see a similar example implemented in ReactJS in the <a href="https://github.com/status-im/js-waku/tree/main/examples/store-reactjs-chat">Minimal ReactJS Waku Store App</a>.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="encrypt-messages-using-waku-message-version-1"><a class="header" href="#encrypt-messages-using-waku-message-version-1">Encrypt Messages Using Waku Message Version 1</a></h1>
|
|
<p>The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption.
|
|
The encryption comes with several handy <a href="https://rfc.vac.dev/spec/26/#design-requirements">design requirements</a>:
|
|
confidentiality, authenticity and integrity.</p>
|
|
<p>You can find more details about Waku Message Payload Encryption in <a href="https://rfc.vac.dev/spec/26/">26/WAKU-PAYLOAD</a>.</p>
|
|
<h2 id="what-data-is-encrypted"><a class="header" href="#what-data-is-encrypted">What data is encrypted</a></h2>
|
|
<p>With Waku Message Version 1, the entire payload is encrypted.</p>
|
|
<p>Which 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.</p>
|
|
<p>This needs to be kept in mind for scalability and forward secrecy concerns:</p>
|
|
<ul>
|
|
<li>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;</li>
|
|
<li>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.</li>
|
|
</ul>
|
|
<h2 id="key-management"><a class="header" href="#key-management">Key management</a></h2>
|
|
<p>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.</p>
|
|
<p>If key recovery is important for your dApp, then check out
|
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey">SubtleCrypto.wrapKey()</a> which can be used to securely store or export private keys.</p>
|
|
<p>An example to save and load a key pair in local storage, protected with a password, can be found in <a href="https://github.com/status-im/js-waku/blob/main/examples/eth-pm/src/key_pair_handling/key_pair_storage.ts">Eth-PM</a>.</p>
|
|
<h2 id="which-encryption-method-should-i-use"><a class="header" href="#which-encryption-method-should-i-use">Which encryption method should I use?</a></h2>
|
|
<p>Whether you should use symmetric or asymmetric encryption depends on your use case.</p>
|
|
<p><strong>Symmetric</strong> encryption is done using a single key to encrypt and decrypt.</p>
|
|
<p>Which means that if Alice knows the symmetric key <code>K</code> and uses it to encrypt a message,
|
|
she can also use <code>K</code> to decrypt any message encrypted with <code>K</code>,
|
|
even if she is not the sender.</p>
|
|
<p>Group chats is a possible use case for symmetric encryption:
|
|
All participants can use an out-of-band method to agree on a <code>K</code>.
|
|
Participants can then use <code>K</code> to encrypt and decrypt messages within the group chat.
|
|
Participants MUST keep <code>K</code> secret to ensure that no external party can decrypt the group chat messages.</p>
|
|
<p><strong>Asymmetric</strong> encryption is done using a key pair:
|
|
the public key is used to encrypt messages,
|
|
the matching private key is used to decrypt messages.</p>
|
|
<p>For Alice to encrypt a message for Bob, she needs to know Bob's Public Key <code>K</code>.
|
|
Bob can then use his private key <code>k</code> to decrypt the message.
|
|
As long as Bob keep his private key <code>k</code> secret, then he, and only he, can decrypt messages encrypted with <code>K</code>.</p>
|
|
<p>Private 1:1 messaging is a possible use case for asymmetric encryption:
|
|
When Alice sends an encrypted message for Bob, only Bob can decrypt it.</p>
|
|
<h2 id="symmetric-encryption"><a class="header" href="#symmetric-encryption">Symmetric Encryption</a></h2>
|
|
<h3 id="generate-key"><a class="header" href="#generate-key">Generate Key</a></h3>
|
|
<p>To use symmetric encryption, you first need to generate a key.
|
|
Use <code>generateSymmetricKey</code> for secure key generation:</p>
|
|
<pre><code class="language-js">import { generateSymmetricKey } from 'js-waku';
|
|
|
|
const symmetricKey = generateSymmetricKey();
|
|
</code></pre>
|
|
<h3 id="encrypt-message"><a class="header" href="#encrypt-message">Encrypt Message</a></h3>
|
|
<p>To encrypt a message with the previously generated key,
|
|
pass the key in the <code>symKey</code> property to <code>WakuMessage.fromBytes</code>.</p>
|
|
<p>Same as Waku Messages version 0 (unencrypted),
|
|
<code>payload</code> is your message payload and <code>contentTopic</code> is the content topic for your dApp.
|
|
See <a href="guides/./relay_receive_send_messages.html">Receive and Send Messages Using Waku Relay</a> for details.</p>
|
|
<pre><code class="language-js">import { WakuMessage } from 'js-waku';
|
|
|
|
const message = await WakuMessage.fromBytes(payload, contentTopic, {
|
|
symKey: symmetricKey
|
|
});
|
|
</code></pre>
|
|
<p>The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:</p>
|
|
<pre><code class="language-js">await waku.lightPush.push(message);
|
|
</code></pre>
|
|
<h3 id="decrypt-messages"><a class="header" href="#decrypt-messages">Decrypt Messages</a></h3>
|
|
<p>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.</p>
|
|
<pre><code class="language-js">waku.addDecryptionKey(symmetricKey);
|
|
</code></pre>
|
|
<p>Alternatively, you can pass the key when creating the instance:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = Waku.create({ decryptionKeys: [symmetricKey] });
|
|
</code></pre>
|
|
<p>It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.</p>
|
|
<p>You can call <code>addDecryptionKey</code> several times if you are using multiple keys,
|
|
symmetric key and asymmetric private keys can be used together.</p>
|
|
<p>Messages that are not successfully decrypted are dropped.</p>
|
|
<h2 id="asymmetric-encryption"><a class="header" href="#asymmetric-encryption">Asymmetric Encryption</a></h2>
|
|
<h3 id="generate-key-pair"><a class="header" href="#generate-key-pair">Generate Key Pair</a></h3>
|
|
<p>To use asymmetric encryption, you first need to generate a private key and calculate the corresponding public key.
|
|
Use <code>generatePrivateKey</code> for secure key generation:</p>
|
|
<pre><code class="language-js">import { generatePrivateKey, getPublicKey } from 'js-waku';
|
|
|
|
const privateKey = generatePrivateKey();
|
|
const publicKey = getPublicKey(privateKey);
|
|
</code></pre>
|
|
<p>The private key must be securely stored and remain private.
|
|
If leaked then other parties may be able to decrypt the user's messages.</p>
|
|
<p>The 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.</p>
|
|
<h3 id="encrypt-message-1"><a class="header" href="#encrypt-message-1">Encrypt Message</a></h3>
|
|
<p>The public key is used to encrypt messages;
|
|
to do so, pass it in the <code>encPublicKey</code> property to <code>WakuMessage.fromBytes</code>.</p>
|
|
<p>Same as clear Waku Messages,
|
|
<code>payload</code> is your message payload and <code>contentTopic</code> is the content topic for your dApp.
|
|
See <a href="guides/./relay_receive_send_messages.html">Receive and Send Messages Using Waku Relay</a> for details.</p>
|
|
<pre><code class="language-js">import { WakuMessage } from 'js-waku';
|
|
|
|
const message = await WakuMessage.fromBytes(payload, contentTopic, {
|
|
encPublicKey: publicKey
|
|
});
|
|
</code></pre>
|
|
<p>The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:</p>
|
|
<pre><code class="language-js">await waku.lightPush.push(message);
|
|
</code></pre>
|
|
<h3 id="decrypt-messages-1"><a class="header" href="#decrypt-messages-1">Decrypt Messages</a></h3>
|
|
<p>The private key is needed to decrypt messages.</p>
|
|
<p>To 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.</p>
|
|
<pre><code class="language-js">waku.addDecryptionKey(privateKey);
|
|
</code></pre>
|
|
<p>Alternatively, you can pass the key when creating the instance:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = Waku.create({ decryptionKeys: [privateKey] });
|
|
</code></pre>
|
|
<p>It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.</p>
|
|
<p>You can call <code>addDecryptionKey</code> several times if you are using multiple keys,
|
|
symmetric key and asymmetric private keys can be used together.</p>
|
|
<p>Messages that are not successfully decrypted are dropped.</p>
|
|
<h2 id="handling-wakumessage-instances"><a class="header" href="#handling-wakumessage-instances">Handling <code>WakuMessage</code> instances</a></h2>
|
|
<p>When creating a Waku Message using <code>WakuMessage.fromBytes</code> with an encryption key (symmetric or asymmetric),
|
|
the payload gets encrypted.
|
|
Which means that <code>wakuMessage.payload</code> returns an encrypted payload:</p>
|
|
<pre><code class="language-js">import { WakuMessage } from 'js-waku';
|
|
|
|
const message = await WakuMessage.fromBytes(payload, contentTopic, {
|
|
encPublicKey: publicKey
|
|
});
|
|
|
|
console.log(message.payload); // This is encrypted
|
|
</code></pre>
|
|
<p>However, <code>WakuMessage</code> instances returned by <code>WakuRelay</code> or <code>WakuStore</code> are always decrypted.</p>
|
|
<p><code>WakuRelay</code> and <code>WakuStore</code> never return messages that are encrypted.
|
|
If a message was not successfully decrypted, then it will be dropped from the results.</p>
|
|
<p>Which means that <code>WakuMessage</code> instances returned by <code>WakuRelay</code> and <code>WakuStore</code> always have a clear payload (in regard to Waku Message version 1):</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = Waku.create({ decryptionKeys: [privateKey] });
|
|
|
|
const messages = await waku.store.queryHistory([contentTopic]);
|
|
|
|
if (messages && messages[0]) {
|
|
console.log(messages[0].payload); // This payload is decrypted
|
|
}
|
|
|
|
waku.relay.addObserver((message) => {
|
|
console.log(message.payload); // This payload is decrypted
|
|
}, [contentTopic]);
|
|
</code></pre>
|
|
<h2 id="code-example"><a class="header" href="#code-example">Code Example</a></h2>
|
|
<p>The <a href="https://github.com/status-im/js-waku/tree/main/examples/eth-pm">Eth-PM</a> Web App example demonstrates both the use of symmetric and asymmetric encryption.</p>
|
|
<p>Asymmetric encryption is used for private messages so that only the intended recipient can read said messages.</p>
|
|
<p>Symmetric 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.</p>
|
|
<p>A live version of Eth-PM can be found at https://status-im.github.io/js-waku/eth-pm/.</p>
|
|
<p>The specifications of the protocol it implements can be found at <a href="https://rfc.vac.dev/spec/20/">20/TOY-ETH-PM</a>.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="receive-and-send-messages-using-waku-relay-with-reactjs"><a class="header" href="#receive-and-send-messages-using-waku-relay-with-reactjs">Receive and Send Messages Using Waku Relay With ReactJS</a></h1>
|
|
<p>It is easy to use DappConnect with ReactJS.
|
|
In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages. </p>
|
|
<p>Before starting, you need to choose a <em>Content Topic</em> for your dApp.
|
|
Check out the <a href="guides/./choose_content_topic.html">how to choose a content topic guide</a> to learn more about content topics.
|
|
For this guide, we are using a single content topic: <code>/min-react-js-chat/1/chat/proto</code>.</p>
|
|
<h1 id="setup"><a class="header" href="#setup">Setup</a></h1>
|
|
<p>Create a new React app:</p>
|
|
<pre><code class="language-shell">npx create-react-app min-react-js-chat
|
|
cd min-react-js-chat
|
|
</code></pre>
|
|
<p>Then, install <a href="https://npmjs.com/package/js-waku">js-waku</a>:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
</code></pre>
|
|
<p>Start the dev server and open the dApp in your browser:</p>
|
|
<pre><code class="language-shell">npm run start
|
|
</code></pre>
|
|
<p>Note: We have noticed some <a href="https://github.com/status-im/js-waku/issues/165">issues</a> with React bundling due to <code>npm</code> pulling an old version of babel.
|
|
If you are getting an error about the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">optional chaining (?.)</a>
|
|
character not being valid, try cleaning up and re-installing your dependencies:</p>
|
|
<pre><code class="language-shell">rm -rf node_modules package-lock.json
|
|
npm install
|
|
</code></pre>
|
|
<h1 id="create-waku-instance-2"><a class="header" href="#create-waku-instance-2">Create Waku Instance</a></h1>
|
|
<p>In order to interact with the Waku network, you first need a Waku instance.
|
|
Go to <code>App.js</code> and modify the <code>App</code> function:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
import * as React from 'react';
|
|
|
|
function App() {
|
|
const [waku, setWaku] = React.useState(undefined);
|
|
const [wakuStatus, setWakuStatus] = React.useState('None');
|
|
|
|
// Start Waku
|
|
React.useEffect(() => {
|
|
// 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 !== 'None') return;
|
|
|
|
setWakuStatus('Starting');
|
|
|
|
// Create Waku
|
|
Waku.create({ bootstrap: true }).then((waku) => {
|
|
// Once done, put it in the state
|
|
setWaku(waku);
|
|
// And update the status
|
|
setWakuStatus('Started');
|
|
});
|
|
}, [waku, wakuStatus]);
|
|
|
|
return (
|
|
<div className='App'>
|
|
<header className='App-header'>
|
|
// Display the status on the web page
|
|
<p>{wakuStatus}</p>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
</code></pre>
|
|
<h1 id="wait-to-be-connected-2"><a class="header" href="#wait-to-be-connected-2">Wait to be connected</a></h1>
|
|
<p>When using the <code>bootstrap</code> 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 <code>Waku.waitForConnectedPeer()</code> async function:</p>
|
|
<pre><code class="language-js">React.useEffect(() => {
|
|
if (!!waku) return;
|
|
if (wakuStatus !== 'None') return;
|
|
|
|
setWakuStatus('Starting');
|
|
|
|
Waku.create({ bootstrap: true }).then((waku) => {
|
|
setWaku(waku);
|
|
setWakuStatus('Connecting');
|
|
waku.waitForConnectedPeer().then(() => {
|
|
setWakuStatus('Ready');
|
|
});
|
|
});
|
|
}, [waku, wakuStatus]);
|
|
</code></pre>
|
|
<h1 id="define-message-format"><a class="header" href="#define-message-format">Define Message Format</a></h1>
|
|
<p>To define the Protobuf message format,
|
|
use <a href="https://www.npmjs.com/package/protons">protons</a></p>
|
|
<pre><code class="language-shell">npm install protons
|
|
</code></pre>
|
|
<p>Define <code>SimpleChatMessage</code> with two fields: <code>timestamp</code> and <code>text</code>.</p>
|
|
<pre><code class="language-js">import protons from 'protons';
|
|
|
|
const proto = protons(`
|
|
message SimpleChatMessage {
|
|
uint64 timestamp = 1;
|
|
string text = 2;
|
|
}
|
|
`);
|
|
</code></pre>
|
|
<h1 id="send-messages-2"><a class="header" href="#send-messages-2">Send Messages</a></h1>
|
|
<p>Create a function that takes the Waku instance and a message to send:</p>
|
|
<pre><code class="language-js">import { WakuMessage } from 'js-waku';
|
|
|
|
const ContentTopic = `/min-react-js-chat/1/chat/proto`;
|
|
|
|
function sendMessage(message, timestamp, waku) {
|
|
const time = timestamp.getTime();
|
|
|
|
// Encode to protobuf
|
|
const payload = proto.SimpleChatMessage.encode({
|
|
timestamp: time,
|
|
text: message
|
|
});
|
|
|
|
// Wrap in a Waku Message
|
|
return WakuMessage.fromBytes(payload, ContentTopic).then((wakuMessage) =>
|
|
// Send over Waku Relay
|
|
waku.relay.send(wakuMessage)
|
|
);
|
|
}
|
|
</code></pre>
|
|
<p>Then, add a button to the <code>App</code> function:</p>
|
|
<pre><code class="language-js">function App() {
|
|
const [waku, setWaku] = React.useState(undefined);
|
|
const [wakuStatus, setWakuStatus] = React.useState('None');
|
|
// Using a counter just for the messages to be different
|
|
const [sendCounter, setSendCounter] = React.useState(0);
|
|
|
|
React.useEffect(() => {
|
|
// ... creates Waku
|
|
}, [waku, wakuStatus]);
|
|
|
|
const sendMessageOnClick = () => {
|
|
// Check Waku is started and connected first.
|
|
if (wakuStatus !== 'Ready') return;
|
|
|
|
sendMessage(`Here is message #${sendCounter}`, waku, new Date()).then(() =>
|
|
console.log('Message sent')
|
|
);
|
|
|
|
// For demonstration purposes.
|
|
setSendCounter(sendCounter + 1);
|
|
};
|
|
|
|
return (
|
|
<div className="App">
|
|
<header className="App-header">
|
|
<p>{wakuStatus}</p>
|
|
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}> // Grey the button is Waku is not yet ready.
|
|
Send Message
|
|
</button>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
</code></pre>
|
|
<h1 id="receive-messages-1"><a class="header" href="#receive-messages-1">Receive Messages</a></h1>
|
|
<p>To process incoming messages, you need to register an observer on Waku Relay.
|
|
First, you need to define the observer function.</p>
|
|
<p>You 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 <code>React.useCallback</code>:</p>
|
|
<pre><code class="language-js">const processIncomingMessage = React.useCallback((wakuMessage) => {
|
|
// Empty message?
|
|
if (!wakuMessage.payload) return;
|
|
|
|
// Decode the protobuf payload
|
|
const { timestamp, text } = proto.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}`);
|
|
}, []);
|
|
</code></pre>
|
|
<p>Then, add this observer to Waku Relay.
|
|
Do not forget to delete the observer is the component is being unmounted:</p>
|
|
<pre><code class="language-js">React.useEffect(() => {
|
|
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]);
|
|
</code></pre>
|
|
<h1 id="display-messages"><a class="header" href="#display-messages">Display Messages</a></h1>
|
|
<p>The Waku work is now done.
|
|
Your dApp is able to send and receive messages using Waku.
|
|
For the sake of completeness, let's display received messages on the page.</p>
|
|
<p>First, add incoming messages to the state of the <code>App</code> component:</p>
|
|
<pre><code class="language-js">function App() {
|
|
//...
|
|
|
|
const [messages, setMessages] = React.useState([]);
|
|
|
|
const processIncomingMessage = React.useCallback((wakuMessage) => {
|
|
if (!wakuMessage.payload) return;
|
|
|
|
const { text, timestamp } = proto.SimpleChatMessage.decode(
|
|
wakuMessage.payload
|
|
);
|
|
|
|
const time = new Date();
|
|
time.setTime(timestamp);
|
|
const message = { text, timestamp: time };
|
|
|
|
setMessages((messages) => {
|
|
return [message].concat(messages);
|
|
});
|
|
}, []);
|
|
|
|
// ...
|
|
}
|
|
</code></pre>
|
|
<p>Then, render the messages:</p>
|
|
<pre><code class="language-js">function App() {
|
|
// ...
|
|
|
|
return (
|
|
<div className="App">
|
|
<header className="App-header">
|
|
<p>{wakuStatus}</p>
|
|
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
|
|
Send Message
|
|
</button>
|
|
<ul>
|
|
{messages.map((msg) => {
|
|
return (
|
|
<li>
|
|
<p>
|
|
{msg.timestamp.toString()}: {msg.text}
|
|
</p>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
</code></pre>
|
|
<p>And Voilà! You should now be able to send and receive messages.
|
|
Try out by opening the app from different browsers.</p>
|
|
<p>You can see the complete code in the <a href="https://github.com/status-im/js-waku/tree/main/examples/min-react-js-chat">Minimal ReactJS Chat App</a>.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="retrieve-messages-using-waku-store-with-reactjs"><a class="header" href="#retrieve-messages-using-waku-store-with-reactjs">Retrieve Messages Using Waku Store With ReactJS</a></h1>
|
|
<p>It is easy to use DappConnect with ReactJS.
|
|
In this guide, we will demonstrate how your ReactJS dApp can use Waku Store to retrieve messages.</p>
|
|
<p>DApps running on a phone or in a browser are often offline:
|
|
The browser could be closed or mobile app in the background.</p>
|
|
<p><a href="https://rfc.vac.dev/spec/18/">Waku Relay</a> 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 <strong>not</strong> save messages for later.</p>
|
|
<p>However, <a href="https://rfc.vac.dev/spec/13/">Waku Store</a> 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.</p>
|
|
<p>In this guide, we'll review how you can use Waku Store to retrieve messages.</p>
|
|
<p>Before starting, you need to choose a <em>Content Topic</em> for your dApp.
|
|
Check out the <a href="guides/./choose_content_topic.html">how to choose a content topic guide</a> to learn more about content topics.</p>
|
|
<h1 id="setup-1"><a class="header" href="#setup-1">Setup</a></h1>
|
|
<p>Create a new React app:</p>
|
|
<pre><code class="language-shell">npx create-react-app my-app
|
|
cd my-app
|
|
</code></pre>
|
|
<p>Then, install <a href="https://npmjs.com/package/js-waku">js-waku</a>:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
</code></pre>
|
|
<p>Start the dev server and open the dApp in your browser:</p>
|
|
<pre><code class="language-shell">npm run start
|
|
</code></pre>
|
|
<p>Note: We have noticed some <a href="https://github.com/status-im/js-waku/issues/165">issues</a> with React bundling due to <code>npm</code> pulling an old version of babel.
|
|
If you are getting an error about the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">optional chaining (?.)</a>
|
|
character not being valid, try cleaning up and re-installing your dependencies:</p>
|
|
<pre><code class="language-shell">rm -rf node_modules package-lock.json
|
|
npm install
|
|
</code></pre>
|
|
<h1 id="create-waku-instance-3"><a class="header" href="#create-waku-instance-3">Create Waku Instance</a></h1>
|
|
<p>In order to interact with the Waku network, you first need a Waku instance.
|
|
Go to <code>App.js</code> and modify the <code>App</code> function:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
import * as React from 'react';
|
|
|
|
function App() {
|
|
const [waku, setWaku] = React.useState(undefined);
|
|
const [wakuStatus, setWakuStatus] = React.useState('None');
|
|
|
|
// Start Waku
|
|
React.useEffect(() => {
|
|
// If Waku status not None, it means we are already starting Waku
|
|
if (wakuStatus !== 'None') return;
|
|
|
|
setWakuStatus('Starting');
|
|
|
|
// Create Waku
|
|
Waku.create({ bootstrap: true }).then((waku) => {
|
|
// Once done, put it in the state
|
|
setWaku(waku);
|
|
// And update the status
|
|
setWakuStatus('Connecting');
|
|
});
|
|
}, [waku, wakuStatus]);
|
|
|
|
return (
|
|
<div className='App'>
|
|
<header className='App-header'>
|
|
// Display the status on the web page
|
|
<p>{wakuStatus}</p>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
</code></pre>
|
|
<h1 id="wait-to-be-connected-3"><a class="header" href="#wait-to-be-connected-3">Wait to be connected</a></h1>
|
|
<p>When using the <code>bootstrap</code> 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 <code>Waku.waitForConnectedPeer()</code> async function:</p>
|
|
<pre><code class="language-js">React.useEffect(() => {
|
|
if (!waku) return;
|
|
|
|
if (wakuStatus === 'Connected') return;
|
|
|
|
waku.waitForConnectedPeer().then(() => {
|
|
setWakuStatus('Connected');
|
|
});
|
|
}, [waku, wakuStatus]);
|
|
</code></pre>
|
|
<h1 id="use-protobuf-2"><a class="header" href="#use-protobuf-2">Use Protobuf</a></h1>
|
|
<p>Waku v2 protocols use <a href="https://developers.google.com/protocol-buffers/">protobuf</a> <a href="https://rfc.vac.dev/spec/10/">by default</a>.</p>
|
|
<p>Let's review how you can use protobuf to decode structured data.</p>
|
|
<p>First, define a data structure.
|
|
For this guide, we will use a simple chat message that contains a timestamp, nick and text:</p>
|
|
<pre><code class="language-js">{
|
|
timestamp: Date;
|
|
nick: string;
|
|
text: string;
|
|
}
|
|
</code></pre>
|
|
<p>To encode and decode protobuf payloads, you can use the <a href="https://www.npmjs.com/package/protons">protons</a> package.</p>
|
|
<h2 id="install-protobuf-library-2"><a class="header" href="#install-protobuf-library-2">Install Protobuf Library</a></h2>
|
|
<pre><code class="language-shell">npm install protons
|
|
</code></pre>
|
|
<h2 id="protobuf-definition-2"><a class="header" href="#protobuf-definition-2">Protobuf Definition</a></h2>
|
|
<p>Define the data structure with protons:</p>
|
|
<pre><code class="language-js">import protons from 'protons';
|
|
|
|
const proto = protons(`
|
|
message ChatMessage {
|
|
uint64 timestamp = 1;
|
|
string nick = 2;
|
|
bytes text = 3;
|
|
}
|
|
`);
|
|
</code></pre>
|
|
<p>You can learn about protobuf message definitions here:
|
|
<a href="https://developers.google.com/protocol-buffers/docs/proto">Protocol Buffers Language Guide</a>.</p>
|
|
<h2 id="decode-messages-2"><a class="header" href="#decode-messages-2">Decode Messages</a></h2>
|
|
<p>To decode the messages retrieved from a Waku Store node,
|
|
you need to extract the protobuf payload and decode it using <code>protons</code>.</p>
|
|
<pre><code class="language-js">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('utf-8');
|
|
|
|
return { text: utf8Text, timestamp: time, nick };
|
|
}
|
|
|
|
</code></pre>
|
|
<h2 id="retrieve-messages-1"><a class="header" href="#retrieve-messages-1">Retrieve messages</a></h2>
|
|
<p>You now have all the building blocks to retrieve and decode messages for a store node.</p>
|
|
<p>Note that Waku Store queries are paginated.
|
|
The API provided by <code>js-waku</code> 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 <code>pageDirection</code> option.</p>
|
|
<p>First, define a React state to save the messages:</p>
|
|
<pre><code class="language-js">function App() {
|
|
const [messages, setMessages] = React.useState([]);
|
|
/// [..]
|
|
}
|
|
</code></pre>
|
|
<p>Then, define <code>processMessages</code> to decode and then store messages in the React state.
|
|
You will pass <code>processMessages</code> as a <code>callback</code> option to <code>WakuStore.queryHistory</code>.
|
|
<code>processMessages</code> will be called each time a page is received from the Waku Store.</p>
|
|
<pre><code class="language-js">const processMessages = (retrievedMessages) => {
|
|
const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
|
|
|
|
setMessages((currentMessages) => {
|
|
return currentMessages.concat(messages.reverse());
|
|
});
|
|
};
|
|
</code></pre>
|
|
<p>Finally, pass <code>processMessage</code> in <code>WakuStore.queryHistory</code> as the <code>callback</code> value:</p>
|
|
<pre><code class="language-js">waku.store
|
|
.queryHistory([ContentTopic], { callback: processMessages });
|
|
</code></pre>
|
|
<p>All together, you should now have:</p>
|
|
<pre><code class="language-js">const ContentTopic = '/toy-chat/2/huilong/proto';
|
|
|
|
function App() {
|
|
// [..]
|
|
// Store messages in the state
|
|
const [messages, setMessages] = React.useState([]);
|
|
|
|
React.useEffect(() => {
|
|
if (wakuStatus !== 'Connected') return;
|
|
|
|
const processMessages = (retrievedMessages) => {
|
|
const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
|
|
|
|
setMessages((currentMessages) => {
|
|
return currentMessages.concat(messages.reverse());
|
|
});
|
|
};
|
|
|
|
waku.store
|
|
.queryHistory([ContentTopic], { callback: processMessages })
|
|
.catch((e) => {
|
|
console.log('Failed to retrieve messages', e);
|
|
});
|
|
}, [waku, wakuStatus]);
|
|
|
|
return (
|
|
<div className='App'>
|
|
<header className='App-header'>
|
|
<h2>{wakuStatus}</h2>
|
|
<h3>Messages</h3>
|
|
<ul>
|
|
<Messages messages={messages} />
|
|
</ul>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
</code></pre>
|
|
<p>Note that <code>WakuStore.queryHistory</code> 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.</p>
|
|
<h2 id="filter-messages-by-send-time-1"><a class="header" href="#filter-messages-by-send-time-1">Filter messages by send time</a></h2>
|
|
<p>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.</p>
|
|
<p><a href="https://rfc.vac.dev/spec/14/">Waku Message</a> defines an optional unencrypted <code>timestamp</code> field.
|
|
The timestamp is set by the sender.
|
|
By default, js-waku sets the timestamp of outgoing message to the current time.</p>
|
|
<p>You can filter messages that include a timestamp within given bounds with the <code>timeFilter</code> option.</p>
|
|
<p>Retrieve messages up to a week old:</p>
|
|
<pre><code class="language-js">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: processMessages,
|
|
timeFilter: { startTime, endTime: new Date() }
|
|
});
|
|
</code></pre>
|
|
<h2 id="end-result-1"><a class="header" href="#end-result-1">End result</a></h2>
|
|
<p>You can see the complete code in the <a href="https://github.com/status-im/js-waku/tree/main/examples/store-reactjs-chat">Minimal ReactJS Waku Store App</a>.</p>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="send-messages-using-waku-light-push"><a class="header" href="#send-messages-using-waku-light-push">Send Messages Using Waku Light Push</a></h1>
|
|
<p>Waku Light Push enables a client to receive a confirmation when sending a message.</p>
|
|
<p>The 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.</p>
|
|
<p>Waku 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.</p>
|
|
<p>It 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.</p>
|
|
<p>You can find Waku Light Push's specifications on <a href="https://rfc.vac.dev/spec/19/">Vac RFC</a>.</p>
|
|
<h1 id="content-topic"><a class="header" href="#content-topic">Content Topic</a></h1>
|
|
<p>Before starting, you need to choose a <em>Content Topic</em> for your dApp.
|
|
Check out the <a href="guides/./choose_content_topic.html">how to choose a content topic guide</a> to learn more about content topics.</p>
|
|
<p>For this guide, we are using a single content topic: <code>/light-push-guide/1/guide/proto</code>.</p>
|
|
<h1 id="installation-2"><a class="header" href="#installation-2">Installation</a></h1>
|
|
<p>You can install <a href="https://npmjs.com/package/js-waku">js-waku</a> using your favorite package manager:</p>
|
|
<pre><code class="language-shell">npm install js-waku
|
|
</code></pre>
|
|
<h1 id="create-waku-instance-4"><a class="header" href="#create-waku-instance-4">Create Waku Instance</a></h1>
|
|
<p>In order to interact with the Waku network, you first need a Waku instance:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const wakuNode = await Waku.create({ bootstrap: true });
|
|
</code></pre>
|
|
<p>Passing the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">import { Waku } from 'js-waku';
|
|
|
|
const waku = await Waku.create({
|
|
bootstrap: [
|
|
'/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
|
|
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
|
|
]
|
|
});
|
|
</code></pre>
|
|
<h1 id="wait-to-be-connected-4"><a class="header" href="#wait-to-be-connected-4">Wait to be connected</a></h1>
|
|
<p>When using the <code>bootstrap</code> 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:</p>
|
|
<pre><code class="language-js">await waku.waitForConnectedPeer();
|
|
</code></pre>
|
|
<p>The returned <code>Promise</code> will resolve once you are connected to a Waku peer.</p>
|
|
<h1 id="send-messages-3"><a class="header" href="#send-messages-3">Send messages</a></h1>
|
|
<p>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's connected peers.</p>
|
|
<p>If the dApp is not connected to any light push peer, an error is thrown.</p>
|
|
<pre><code class="language-ts">import {WakuMessage} from 'js-waku';
|
|
|
|
const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/light-push-guide/1/guide/proto`);
|
|
|
|
const ack = await waku.lightPush.push(wakuMessage);
|
|
if (!ack?.isSuccess) {
|
|
// Message was not sent
|
|
}
|
|
</code></pre>
|
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="examples"><a class="header" href="#examples">Examples</a></h1>
|
|
<p>Here is the list of the code examples and the features they demonstrate.
|
|
To run or studies the example, click on the <em>repo</em> links.</p>
|
|
<h2 id="minimal-reactjs-chat-app"><a class="header" href="#minimal-reactjs-chat-app">Minimal ReactJS Chat App</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/min-react-js-chat">min-react-js-chat</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Group chat</li>
|
|
<li>React/JavaScript</li>
|
|
<li>Waku Relay</li>
|
|
<li>Protobuf using <a href="https://www.npmjs.com/package/protons">protons</a></li>
|
|
<li>No async/await syntax</li>
|
|
</ul>
|
|
<h2 id="minimal-reactjs-waku-store-app"><a class="header" href="#minimal-reactjs-waku-store-app">Minimal ReactJS Waku Store App</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/store-reactjs-chat">store-reactjs-chat</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Waku Store</li>
|
|
<li>React/JavaScript</li>
|
|
<li>Protobuf using <a href="https://www.npmjs.com/package/protons">protons</a></li>
|
|
</ul>
|
|
<h2 id="vanilla-javascript-using-minified-library"><a class="header" href="#vanilla-javascript-using-minified-library">Vanilla Javascript Using Minified Library</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/unpkg-js-store">unpkg-js-store</a>.</p>
|
|
<p>Demonstrates: </p>
|
|
<ul>
|
|
<li>How to stop retrieving results from Waku Store on condition</li>
|
|
<li>Use minified bundle from Unpkg.com</li>
|
|
<li>Vanilla JavaScript application</li>
|
|
</ul>
|
|
<h2 id="web-chat-app"><a class="header" href="#web-chat-app">Web Chat App</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/web-chat">web-chat</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Group chat</li>
|
|
<li>React/TypeScript</li>
|
|
<li>Waku Relay</li>
|
|
<li>Waku Store</li>
|
|
<li>Protobuf using .proto files + <a href="https://github.com/bufbuild/buf">bufbuild</a> + <a href="https://www.npmjs.com/package/ts-proto">ts-proto</a></li>
|
|
</ul>
|
|
<h2 id="ethereum-private-message-web-app"><a class="header" href="#ethereum-private-message-web-app">Ethereum Private Message Web App</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/eth-pm">eth-pm</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Private messaging</li>
|
|
<li>React/TypeScript</li>
|
|
<li>Waku Light Push</li>
|
|
<li>Signature with Web3 Wallet</li>
|
|
<li>Asymmetric Encryption</li>
|
|
<li>Symmetric Encryption</li>
|
|
<li>Protobuf using <a href="https://www.npmjs.com/package/protobufjs">protobufjs</a></li>
|
|
</ul>
|
|
<h2 id="ethereum-private-message-using-web3-wallet-encryption-api-web-app"><a class="header" href="#ethereum-private-message-using-web3-wallet-encryption-api-web-app">Ethereum Private Message Using Web3 Wallet Encryption API Web App</a></h2>
|
|
<p>Repo: <a href="https://github.com/status-im/js-waku/tree/main/examples/eth-pm-wallet-encryption">eth-pm-wallet-encryption</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Private Messaging</li>
|
|
<li>React/TypeScript</li>
|
|
<li>Waku Light Push</li>
|
|
<li>Signature with Web3 using EIP-712: <code>eth_signTypedData_v4</code></li>
|
|
<li>Asymmetric Encryption</li>
|
|
<li>Usage of <code>eth_decrypt</code> Web3 Wallet API</li>
|
|
<li>Protobuf using <a href="https://www.npmjs.com/package/protobufjs">protobufjs</a></li>
|
|
</ul>
|
|
<h2 id="uber-like-minimalistic-car-sharing-app-suing-vuejs"><a class="header" href="#uber-like-minimalistic-car-sharing-app-suing-vuejs">Uber-like minimalistic car sharing app suing Vue.js</a></h2>
|
|
<p>Repo: <a href="https://github.com/TheBojda/waku-uber">TheBojda/waku-uber</a>.</p>
|
|
<p>Article: <a href="https://hackernoon.com/decentralized-uber-heres-how-i-built-it-with-statusim-waku-and-vuejs">Decentralized Uber: Here's How I Built It With Status.im, Waku, and Vue.js</a>.</p>
|
|
<p>Demonstrates:</p>
|
|
<ul>
|
|
<li>Vue.js</li>
|
|
<li>Waku Relay</li>
|
|
<li>Protobuf using <a href="https://www.npmjs.com/package/protons">protons</a></li>
|
|
</ul>
|
|
<div style="break-before: page; page-break-before: always;"></div><h2 id="waku-protocol-support"><a class="header" href="#waku-protocol-support">Waku Protocol Support</a></h2>
|
|
<p>You can track progress on the <a href="https://github.com/status-im/js-waku/projects/1">project board</a>.</p>
|
|
<ul>
|
|
<li>✔: Supported</li>
|
|
<li>🚧: Implementation in progress</li>
|
|
<li>⛔: Support is not planned</li>
|
|
</ul>
|
|
<table><thead><tr><th>Spec</th><th>Implementation Status</th></tr></thead><tbody>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/6">6/WAKU1</a></td><td>⛔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/7">7/WAKU-DATA</a></td><td>⛔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/8">8/WAKU-MAIL</a></td><td>⛔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/9">9/WAKU-RPC</a></td><td>⛔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/10">10/WAKU2</a></td><td>🚧</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/11">11/WAKU2-RELAY</a></td><td>✔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/12">12/WAKU2-FILTER</a></td><td></td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/13">13/WAKU2-STORE</a></td><td>✔ (querying node only)</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/14">14/WAKU2-MESSAGE</a></td><td>✔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/15">15/WAKU2-BRIDGE</a></td><td></td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/16">16/WAKU2-RPC</a></td><td>⛔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/17">17/WAKU2-RLNRELAY</a></td><td></td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/18">18/WAKU2-SWAP</a></td><td></td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/19/">19/WAKU2-LIGHTPUSH</a></td><td>✔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/20/">20/TOY-ETH-PM</a></td><td>✔ (as example)</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/21/">21/WAKU2-FTSTORE</a></td><td>✔</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/22/">22/TOY-CHAT</a></td><td>✔ (as example)</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/25/">25/LIBP2P-DNS-DISCOVERY</a></td><td>🚧</td></tr>
|
|
<tr><td><a href="https://rfc.vac.dev/spec/26/">26/WAKU2-PAYLOAD</a></td><td>✔</td></tr>
|
|
</tbody></table>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
|
</nav>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
window.playground_copyable = true;
|
|
</script>
|
|
|
|
|
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
<script type="text/javascript">
|
|
window.addEventListener('load', function() {
|
|
window.setTimeout(window.print, 100);
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|