Run prettier (#29)

This commit is contained in:
Franck R 2022-01-24 12:17:00 +11:00 committed by GitHub
parent 23ba3e4829
commit 41bc0a9d80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 765 additions and 675 deletions

View File

@ -3,11 +3,11 @@ title: Cryptographic Libraries
date: 2021-12-09T14:00:00+01:00
weight: 50
---
# Cryptographic Libraries
A note on the cryptographic libraries used as it is a not a straightforward affair.
## Asymmetric encryption
Uses [ecies-geth](https://github.com/cyrildever/ecies-geth/)
@ -15,7 +15,6 @@ which in turns uses [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/
[secp256k1](https://www.npmjs.com/package/secp256k1) (native binding for node)
or [elliptic](https://www.npmjs.com/package/elliptic) (pure JS if none of the other libraries are available).
## Symmetric encryption
Uses [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) Web API (browser)

View File

@ -3,6 +3,7 @@ title: Examples
date: 2021-12-09T14:00:00+01:00
weight: 40
---
# Examples
Here is the list of the code examples and the features they demonstrate.
@ -34,7 +35,7 @@ Demonstrates:
Repo: [unpkg-js-store](https://github.com/status-im/js-waku/tree/main/examples/unpkg-js-store).
Demonstrates:
Demonstrates:
- How to stop retrieving results from Waku Store on condition
- Use minified bundle from Unpkg.com

View File

@ -3,6 +3,7 @@ title: Receive and Send Messages Using Waku Relay
date: 2021-12-09T14:00:00+01:00
weight: 2
---
# Receive and Send Messages Using Waku Relay
Waku Relay is a gossip protocol that enables you to send and receive messages.
@ -26,22 +27,22 @@ npm install js-waku
In order to interact with the Waku network, you first need a Waku instance:
```js
import {Waku} from 'js-waku';
import { Waku } from "js-waku";
const waku = await Waku.create({bootstrap: {default: true}});
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:
```js
import { Waku } from 'js-waku';
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'
]
"/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",
],
});
```
@ -67,7 +68,7 @@ const processIncomingMessage = (wakuMessage) => {
console.log(`Message Received: ${wakuMessage.payloadAsUtf8}`);
};
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
waku.relay.addObserver(processIncomingMessage, ["/relay-guide/1/chat/proto"]);
```
# Send Messages
@ -79,9 +80,12 @@ To 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:
```js
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/relay-guide/1/chat/proto`);
const wakuMessage = await WakuMessage.fromUtf8String(
"Here is a message",
`/relay-guide/1/chat/proto`
);
```
Then, use the `relay` module to send the message to our peers,
@ -124,7 +128,7 @@ npm install protons
Then define the simple chat message:
```js
import protons from 'protons';
import protons from "protons";
const proto = protons(`
message SimpleChatMessage {
@ -147,7 +151,7 @@ First, encode the object:
```js
const payload = proto.SimpleChatMessage.encode({
timestamp: Date.now(),
text: 'Here is a message'
text: "Here is a message",
});
```
@ -184,7 +188,7 @@ const processIncomingMessage = (wakuMessage) => {
Like before, add this callback as an observer to Waku Relay:
```js
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
waku.relay.addObserver(processIncomingMessage, ["/relay-guide/1/chat/proto"]);
```
# Conclusion
@ -194,8 +198,8 @@ That is it! Now, you know how to send and receive messages over Waku using the W
Here is the final code:
```js
import { getBootstrapNodes, Waku, WakuMessage } from 'js-waku';
import protons from 'protons';
import { getBootstrapNodes, Waku, WakuMessage } from "js-waku";
import protons from "protons";
const proto = protons(`
message SimpleChatMessage {
@ -220,11 +224,11 @@ const processIncomingMessage = (wakuMessage) => {
console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`);
};
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
waku.relay.addObserver(processIncomingMessage, ["/relay-guide/1/chat/proto"]);
const payload = proto.SimpleChatMessage.encode({
timestamp: Date.now(),
text: 'Here is a message'
text: "Here is a message",
});
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
await waku.relay.send(wakuMessage);

View File

@ -3,6 +3,7 @@ title: Retrieve Messages Using Waku Store
date: 2021-12-09T14:00:00+01:00
weight: 3
---
# Retrieve Messages Using Waku Store
DApps running on a phone or in a browser are often offline:
@ -40,22 +41,22 @@ npm install js-waku
In order to interact with the Waku network, you first need a Waku instance:
```js
import {Waku} from 'js-waku';
import { Waku } from "js-waku";
const wakuNode = await Waku.create({bootstrap: {default: true}});
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:
```js
import { Waku } from 'js-waku';
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'
]
"/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",
],
});
```
@ -103,7 +104,7 @@ npm install protons
Then specify the data structure:
```js
import protons from 'protons';
import protons from "protons";
const proto = protons(`
message ArticleMessage {
@ -150,7 +151,7 @@ The `WakuStore.queryHistory` API automatically query all the pages in a sequenti
To process messages as soon as they received (page by page), use the `callback` option:
```js
const ContentTopic = '/store-guide/1/news/proto';
const ContentTopic = "/store-guide/1/news/proto";
const callback = (retrievedMessages) => {
const articles = retrievedMessages
@ -160,12 +161,10 @@ const callback = (retrievedMessages) => {
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);
});
waku.store.queryHistory([ContentTopic], { callback }).catch((e) => {
// Catch any potential error
console.log("Failed to retrieve messages from store", e);
});
```
Note that `WakuStore.queryHistory` select an available store node for you.
@ -195,10 +194,10 @@ startTime.setTime(startTime.getTime() - 7 * 24 * 60 * 60 * 1000);
waku.store
.queryHistory([ContentTopic], {
callback,
timeFilter: { startTime, endTime: new Date() }
timeFilter: { startTime, endTime: new Date() },
})
.catch((e) => {
console.log('Failed to retrieve messages from store', e);
console.log("Failed to retrieve messages from store", e);
});
```

View File

@ -3,6 +3,7 @@ title: Encrypt Messages Using Waku Message Version 1
date: 2021-12-09T14:00:00+01:00
weight: 4
---
# Encrypt Messages Using Waku Message Version 1
The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption.
@ -71,7 +72,7 @@ To use symmetric encryption, you first need to generate a key.
Use `generateSymmetricKey` for secure key generation:
```js
import { generateSymmetricKey } from 'js-waku';
import { generateSymmetricKey } from "js-waku";
const symmetricKey = generateSymmetricKey();
```
@ -86,10 +87,10 @@ Same as Waku Messages version 0 (unencrypted),
See [Receive and Send Messages Using Waku Relay](/docs/guides/02_relay_receive_send_messages/) for details.
```js
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const message = await WakuMessage.fromBytes(payload, contentTopic, {
symKey: symmetricKey
symKey: symmetricKey,
});
```
@ -108,10 +109,11 @@ add the symmetric key as a decryption key to your Waku instance.
```js
waku.addDecryptionKey(symmetricKey);
```
Alternatively, you can pass the key when creating the instance:
```js
import { Waku } from 'js-waku';
import { Waku } from "js-waku";
const waku = Waku.create({ decryptionKeys: [symmetricKey] });
```
@ -131,7 +133,7 @@ To use asymmetric encryption, you first need to generate a private key and calcu
Use `generatePrivateKey` for secure key generation:
```js
import { generatePrivateKey, getPublicKey } from 'js-waku';
import { generatePrivateKey, getPublicKey } from "js-waku";
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
@ -153,10 +155,10 @@ Same as clear Waku Messages,
See [Receive and Send Messages Using Waku Relay](/docs/guides/02_relay_receive_send_messages/) for details.
```js
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const message = await WakuMessage.fromBytes(payload, contentTopic, {
encPublicKey: publicKey
encPublicKey: publicKey,
});
```
@ -177,10 +179,11 @@ add the private key as a decryption key to your Waku instance.
```js
waku.addDecryptionKey(privateKey);
```
Alternatively, you can pass the key when creating the instance:
```js
import { Waku } from 'js-waku';
import { Waku } from "js-waku";
const waku = Waku.create({ decryptionKeys: [privateKey] });
```
@ -199,10 +202,10 @@ the payload gets encrypted.
Which means that `wakuMessage.payload` returns an encrypted payload:
```js
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const message = await WakuMessage.fromBytes(payload, contentTopic, {
encPublicKey: publicKey
encPublicKey: publicKey,
});
console.log(message.payload); // This is encrypted
@ -216,7 +219,7 @@ If a message was not successfully decrypted, then it will be dropped from the re
Which means that `WakuMessage` instances returned by `WakuRelay` and `WakuStore` always have a clear payload (in regard to Waku Message version 1):
```js
import { Waku } from 'js-waku';
import { Waku } from "js-waku";
const waku = Waku.create({ decryptionKeys: [privateKey] });
@ -226,12 +229,14 @@ 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]);
waku.relay.addObserver(
(message) => {
console.log(message.payload); // This payload is decrypted
},
[contentTopic]
);
```
## Code Example
The [Eth-PM](https://github.com/status-im/js-waku/tree/main/examples/eth-pm) Web App example demonstrates both the use of symmetric and asymmetric encryption.

View File

@ -3,6 +3,7 @@ title: Sign Messages Using Waku Message Version 1
date: 2021-12-09T14:00:00+01:00
weight: 5
---
# Sign Messages Using Waku Message Version 1
The Waku Message format provides an easy way to sign messages using elliptic curve cryptography.
@ -19,7 +20,7 @@ See [Cryptographic Libraries](/docs/crypto_libraries/) for more details on the c
Generate a new keypair to sign your messages:
```ts
import { generatePrivateKey, getPublicKey } from 'js-waku';
import { generatePrivateKey, getPublicKey } from "js-waku";
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
@ -37,11 +38,11 @@ You can learn more about encryption at [Encrypt Messages Using Waku Message Vers
Given `symKey` the symmetric key used for encryption:
```ts
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const message = await WakuMessage.fromBytes(payload, myAppContentTopic, {
encPublicKey: symKey,
sigPrivKey: privateKey
sigPrivKey: privateKey,
});
```
@ -49,12 +50,10 @@ If encryption is not needed for your use case,
then you can create a symmetric key from the content topic:
```ts
import { hexToBuf } from 'js-waku/lib/utils';
import { keccak256 } from 'ethers/lib/utils';
import { hexToBuf } from "js-waku/lib/utils";
import { keccak256 } from "ethers/lib/utils";
const symKey = hexToBuf(
keccak256(Buffer.from(myAppContentTopic, 'utf-8'))
);
const symKey = hexToBuf(keccak256(Buffer.from(myAppContentTopic, "utf-8")));
```
`symKey` can then be used to encrypt and decrypt messages on `myAppContentTopic` content topic.
@ -62,14 +61,14 @@ Read [How to Choose a Content Topic](/docs/guides/01_choose_content_topic/) to l
### Using asymmetric encryption
Given `recipientPublicKey` the public key of the message's recipient:
Given `recipientPublicKey` the public key of the message's recipient:
```ts
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const message = await WakuMessage.fromBytes(payload, myAppContentTopic, {
encPublicKey: recipientPublicKey,
sigPrivKey: privateKey
sigPrivKey: privateKey,
});
```
@ -86,13 +85,13 @@ As comparing hex string can lead to issues (is the `0x` prefix present?),
simply use helper function `equalByteArrays`.
```ts
import { equalByteArrays } from 'js-waku/lib/utils';
import { equalByteArrays } from "js-waku/lib/utils";
const sigPubKey = wakuMessage.signaturePublicKey;
const isSignedByAlice = sigPubKey && equalByteArrays(sigPubKey, alicePublicKey);
if (!isSignedByAlice) {
// Message is not signed by Alice
// Message is not signed by Alice
}
```

View File

@ -3,6 +3,7 @@ title: Send Messages Using Waku Light Push
date: 2021-12-09T14:00:00+01:00
weight: 6
---
# Send Messages Using Waku Light Push
Waku Light Push enables a client to receive a confirmation when sending a message.
@ -40,22 +41,22 @@ npm install js-waku
In order to interact with the Waku network, you first need a Waku instance:
```js
import {Waku} from 'js-waku';
import { Waku } from "js-waku";
const wakuNode = await Waku.create({bootstrap: {default: true}});
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:
```js
import { Waku } from 'js-waku';
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'
]
"/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",
],
});
```
@ -80,12 +81,15 @@ The peer is selected among the dApp's connected peers.
If the dApp is not connected to any light push peer, an error is thrown.
```ts
import {WakuMessage} from 'js-waku';
import { WakuMessage } from "js-waku";
const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/light-push-guide/1/guide/proto`);
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
// Message was not sent
}
```

View File

@ -3,10 +3,11 @@ title: Receive and Send Messages Using Waku Relay With ReactJS
date: 2021-12-09T14:00:00+01:00
weight: 7
---
# Receive and Send Messages Using Waku Relay With ReactJS
It is easy to use WakuConnect with ReactJS.
In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages.
In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages.
Before starting, you need to choose a _Content Topic_ for your dApp.
Check out the [how to choose a content topic guide](/docs/guides/01_choose_content_topic/) to learn more about content topics.
@ -76,65 +77,65 @@ npm install -D cra-webpack-rewired
Create a `config/webpack.extend.js` file at the root of your app:
```js
const webpack = require('webpack');
const webpack = require("webpack");
module.exports = {
dev: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve('buffer'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
});
dev: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve("buffer"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
});
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
'process.env.ENV': JSON.stringify('dev'),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser.js',
Buffer: ['buffer', 'Buffer'],
})
);
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
"process.env.ENV": JSON.stringify("dev"),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: "process/browser.js",
Buffer: ["buffer", "Buffer"],
})
);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
return config;
},
prod: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve('buffer'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
});
return config;
},
prod: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve("buffer"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
});
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
'process.env.ENV': JSON.stringify('prod'),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser.js',
Buffer: ['buffer', 'Buffer'],
})
);
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
"process.env.ENV": JSON.stringify("prod"),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: "process/browser.js",
Buffer: ["buffer", "Buffer"],
})
);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
return config;
},
return config;
},
};
```
@ -171,38 +172,38 @@ In order to interact with the Waku network, you first need a Waku instance.
Go to `App.js` and modify the `App` function:
```js
import {Waku} from 'js-waku';
import * as React from 'react';
import { Waku } from "js-waku";
import * as React from "react";
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
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;
// 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');
setWakuStatus("Starting");
// Create Waku
Waku.create({bootstrap: {default: true}}).then((waku) => {
// Once done, put it in the state
setWaku(waku);
// And update the status
setWakuStatus('Started');
});
}, [waku, wakuStatus]);
// Create Waku
Waku.create({ bootstrap: { default: 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'>
<p>Waku node's status: {wakuStatus}</p>
</header>
</div>
);
return (
<div className="App">
<header className="App-header">
<p>Waku node's status: {wakuStatus}</p>
</header>
</div>
);
}
export default App;
@ -216,18 +217,18 @@ use the `Waku.waitForConnectedPeer()` async function:
```js
React.useEffect(() => {
if (!!waku) return;
if (wakuStatus !== 'None') return;
if (!!waku) return;
if (wakuStatus !== "None") return;
setWakuStatus('Starting');
setWakuStatus("Starting");
Waku.create({bootstrap: {default: true}}).then((waku) => {
setWaku(waku);
setWakuStatus('Connecting');
waku.waitForConnectedPeer().then(() => {
setWakuStatus('Ready');
});
Waku.create({ bootstrap: { default: true } }).then((waku) => {
setWaku(waku);
setWakuStatus("Connecting");
waku.waitForConnectedPeer().then(() => {
setWakuStatus("Ready");
});
});
}, [waku, wakuStatus]);
```
@ -243,7 +244,7 @@ npm install protons
Define `SimpleChatMessage` with two fields: `timestamp` and `text`.
```js
import protons from 'protons';
import protons from "protons";
const proto = protons(`
message SimpleChatMessage {
@ -258,7 +259,7 @@ message SimpleChatMessage {
Create a function that takes the Waku instance and a message to send:
```js
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const ContentTopic = `/relay-reactjs-chat/1/chat/proto`;
@ -268,7 +269,7 @@ function sendMessage(message, waku, timestamp) {
// Encode to protobuf
const payload = proto.SimpleChatMessage.encode({
timestamp: time,
text: message
text: message,
});
// Wrap in a Waku Message
@ -284,20 +285,20 @@ Then, add a button to the `App` function:
```js
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
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;
if (wakuStatus !== "Ready") return;
sendMessage(`Here is message #${sendCounter}`, waku, new Date()).then(() =>
console.log('Message sent')
console.log("Message sent")
);
// For demonstration purposes.
@ -308,7 +309,7 @@ function App() {
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== "Ready"}>
Send Message
</button>
</header>
@ -393,6 +394,7 @@ function App() {
// ...
}
```
Then, render the messages:
```js
@ -403,7 +405,7 @@ function App() {
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== "Ready"}>
Send Message
</button>
<ul>

View File

@ -3,6 +3,7 @@ title: Retrieve Messages Using Waku Store With ReactJS
date: 2021-12-09T14:00:00+01:00
weight: 8
---
# Retrieve Messages Using Waku Store With ReactJS
It is easy to use WakuConnect with ReactJS.
@ -92,65 +93,65 @@ npm install -D cra-webpack-rewired
Create a `config/webpack.extend.js` file at the root of your app:
```js
const webpack = require('webpack');
const webpack = require("webpack");
module.exports = {
dev: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve('buffer'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
});
dev: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve("buffer"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
});
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
'process.env.ENV': JSON.stringify('dev'),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser.js',
Buffer: ['buffer', 'Buffer'],
})
);
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
"process.env.ENV": JSON.stringify("dev"),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: "process/browser.js",
Buffer: ["buffer", "Buffer"],
})
);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
return config;
},
prod: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve('buffer'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
});
return config;
},
prod: (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve("buffer"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
});
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
'process.env.ENV': JSON.stringify('prod'),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser.js',
Buffer: ['buffer', 'Buffer'],
})
);
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.DefinePlugin({
"process.env.ENV": JSON.stringify("prod"),
})
);
config.plugins.push(
new webpack.ProvidePlugin({
process: "process/browser.js",
Buffer: ["buffer", "Buffer"],
})
);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
if (!config.ignoreWarnings) config.ignoreWarnings = [];
config.ignoreWarnings.push(/Failed to parse source map/);
return config;
},
return config;
},
};
```
@ -190,6 +191,7 @@ character not being valid, try cleaning up and re-installing your dependencies:
rm -rf node_modules package-lock.json
npm install
```
{{< /hint >}}
# Create Waku Instance
@ -198,36 +200,36 @@ In order to interact with the Waku network, you first need a Waku instance.
Go to `App.js` and modify the `App` function:
```js
import {Waku} from 'js-waku';
import * as React from 'react';
import { Waku } from "js-waku";
import * as React from "react";
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
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;
// Start Waku
React.useEffect(() => {
// If Waku status not None, it means we are already starting Waku
if (wakuStatus !== "None") return;
setWakuStatus('Starting');
setWakuStatus("Starting");
// Create Waku
Waku.create({bootstrap: {default: true}}).then((waku) => {
// Once done, put it in the state
setWaku(waku);
// And update the status
setWakuStatus('Connecting');
});
}, [waku, wakuStatus]);
// Create Waku
Waku.create({ bootstrap: { default: 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'>
<p>{wakuStatus}</p>
</header>
</div>
);
return (
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
</header>
</div>
);
}
export default App;
@ -243,10 +245,10 @@ use the `Waku.waitForConnectedPeer()` async function:
React.useEffect(() => {
if (!waku) return;
if (wakuStatus === 'Connected') return;
if (wakuStatus === "Connected") return;
waku.waitForConnectedPeer().then(() => {
setWakuStatus('Connected');
setWakuStatus("Connected");
});
}, [waku, wakuStatus]);
```
@ -281,7 +283,7 @@ npm install protons
Define the data structure with protons:
```js
import protons from 'protons';
import protons from "protons";
const proto = protons(`
message ChatMessage {
@ -314,11 +316,10 @@ function decodeMessage(wakuMessage) {
const time = new Date();
time.setTime(timestamp);
const utf8Text = Buffer.from(text).toString('utf-8');
const utf8Text = Buffer.from(text).toString("utf-8");
return { text: utf8Text, timestamp: time, nick };
}
```
## Retrieve messages
@ -355,32 +356,31 @@ const processMessages = (retrievedMessages) => {
Pass `processMessage` in `WakuStore.queryHistory` as the `callback` value:
```js
waku.store
.queryHistory([ContentTopic], { callback: processMessages });
waku.store.queryHistory([ContentTopic], { callback: processMessages });
```
Finally, create a `Messages` component to render the messages:
```tsx
function Messages(props) {
return props.messages.map(({ text, timestamp, nick }) => {
return (
<li>
({formatDate(timestamp)}) {nick}: {text}
</li>
);
});
return props.messages.map(({ text, timestamp, nick }) => {
return (
<li>
({formatDate(timestamp)}) {nick}: {text}
</li>
);
});
}
function formatDate(timestamp) {
return timestamp.toLocaleString([], {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: false,
});
return timestamp.toLocaleString([], {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
}
```
@ -391,8 +391,8 @@ function App() {
// [..]
return (
<div className='App'>
<header className='App-header'>
<div className="App">
<header className="App-header">
<h2>{wakuStatus}</h2>
<h3>Messages</h3>
<ul>
@ -407,11 +407,11 @@ function App() {
All together, you should now have:
```js
import {Waku} from 'js-waku';
import * as React from 'react';
import protons from 'protons';
import { Waku } from "js-waku";
import * as React from "react";
import protons from "protons";
const ContentTopic = '/toy-chat/2/huilong/proto';
const ContentTopic = "/toy-chat/2/huilong/proto";
const proto = protons(`
message ChatMessage {
@ -422,107 +422,106 @@ message ChatMessage {
`);
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
const [messages, setMessages] = React.useState([]);
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState("None");
const [messages, setMessages] = React.useState([]);
// Start Waku
React.useEffect(() => {
// If Waku status not None, it means we are already starting Waku
if (wakuStatus !== 'None') return;
// Start Waku
React.useEffect(() => {
// If Waku status not None, it means we are already starting Waku
if (wakuStatus !== "None") return;
setWakuStatus('Starting');
setWakuStatus("Starting");
// Create Waku
Waku.create({bootstrap: {default: true}}).then((waku) => {
// Once done, put it in the state
setWaku(waku);
// And update the status
setWakuStatus('Connecting');
});
}, [waku, wakuStatus]);
// Create Waku
Waku.create({ bootstrap: { default: true } }).then((waku) => {
// Once done, put it in the state
setWaku(waku);
// And update the status
setWakuStatus("Connecting");
});
}, [waku, wakuStatus]);
React.useEffect(() => {
if (!waku) return;
React.useEffect(() => {
if (!waku) return;
if (wakuStatus === "Connected") return;
if (wakuStatus === 'Connected') return;
waku.waitForConnectedPeer().then(() => {
setWakuStatus("Connected");
});
}, [waku, wakuStatus]);
waku.waitForConnectedPeer().then(() => {
setWakuStatus('Connected');
});
}, [waku, wakuStatus]);
React.useEffect(() => {
if (wakuStatus !== "Connected") return;
React.useEffect(() => {
if (wakuStatus !== 'Connected') return;
const processMessages = (retrievedMessages) => {
const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
const processMessages = (retrievedMessages) => {
const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
setMessages((currentMessages) => {
return currentMessages.concat(messages.reverse());
});
};
setMessages((currentMessages) => {
return currentMessages.concat(messages.reverse());
});
};
waku.store
.queryHistory([ContentTopic], { callback: processMessages })
.catch((e) => {
console.log("Failed to retrieve messages", e);
});
}, [waku, wakuStatus]);
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>
);
return (
<div className="App">
<header className="App-header">
<h2>{wakuStatus}</h2>
<h3>Messages</h3>
<ul>
<Messages messages={messages} />
</ul>
</header>
</div>
);
}
export default App;
function decodeMessage(wakuMessage) {
if (!wakuMessage.payload) return;
if (!wakuMessage.payload) return;
const {timestamp, nick, text} = proto.ChatMessage.decode(
wakuMessage.payload
);
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;
// All fields in protobuf are optional so be sure to check
if (!timestamp || !text || !nick) return;
const time = new Date();
time.setTime(timestamp);
const time = new Date();
time.setTime(timestamp);
const utf8Text = Buffer.from(text).toString('utf-8');
const utf8Text = Buffer.from(text).toString("utf-8");
return {text: utf8Text, timestamp: time, nick};
return { text: utf8Text, timestamp: time, nick };
}
function Messages(props) {
return props.messages.map(({text, timestamp, nick}) => {
return (
<li>
({formatDate(timestamp)}) {nick}: {text}
</li>
);
});
return props.messages.map(({ text, timestamp, nick }) => {
return (
<li>
({formatDate(timestamp)}) {nick}: {text}
</li>
);
});
}
function formatDate(timestamp) {
return timestamp.toLocaleString([], {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: false,
});
return timestamp.toLocaleString([], {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
}
```
@ -552,11 +551,10 @@ 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() }
});
waku.store.queryHistory([ContentTopic], {
callback: processMessages,
timeFilter: { startTime, endTime: new Date() },
});
```
## End result

View File

@ -1,6 +1,7 @@
---
weight: 30
---
# Guides
## Waku Concepts

View File

@ -1,6 +1,7 @@
---
weight: 100
---
# WakuConnect Vote & Poll SDK
The WakuConnect Vote & Poll SDK enables developers to add Waku powered polling and voting features to their dApp.
@ -42,7 +43,7 @@ Only the party that starts an election and submit the end results need to intera
For example, it can be used by a DAO to manage proposals
where proposal creation and vote results must be committed to the blockchain.
With WakuConnect Vote SDK, the DAO could be the one spending gas when creating the proposal and committing the votes,
With WakuConnect Vote SDK, the DAO could be the one spending gas when creating the proposal and committing the votes,
whereas the token holders do not spend gas when voting.
### Documentation

View File

@ -13,7 +13,6 @@ Install the Waku Poll SDK packages.
In this guide, we use [useDApp](https://usedapp.io/) to access the blockchain.
```shell
yarn create react-app poll-dapp-ts --template typescript
cd poll-dapp-ts
@ -64,29 +63,28 @@ yarn add -D react-app-rewired
Create a `config-overrides.js` file at the root of your app:
```js
const webpack = require('webpack');
const webpack = require("webpack");
module.exports = (config) => {
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
buffer: require.resolve("buffer"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
assert: require.resolve("assert"),
});
// Override webpack 5 config from react-scripts to load polyfills
if (!config.resolve) config.resolve = {};
if (!config.resolve.fallback) config.resolve.fallback = {};
Object.assign(config.resolve.fallback, {
"buffer": require.resolve("buffer"),
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert")
}
)
if (!config.plugins) config.plugins = [];
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
})
);
if (!config.plugins) config.plugins = []
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}));
return config;
}
return config;
};
```
Use `react-app-rewired` in the `package.json`, instead of `react-scripts`:

View File

@ -25,31 +25,31 @@ The component uses `ethers` to connect to the user's wallet:
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
const { account, library, activateBrowserWallet, deactivate } = useEthers();
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined);
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
useEffect(() => {
if (account) {
setSigner(library?.getSigner());
} else {
// Deactivate signer if signed out
setSigner(undefined);
}
}, [account]);
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
)
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={"Poll dApp"}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
);
}
```
@ -60,25 +60,27 @@ export function PollPage() {
Create a `config` variable that contains the Ethereum network parameters:
```tsx
import {ChainId, DAppProvider, useEthers} from '@usedapp/core';
import {DEFAULT_CONFIG} from "@usedapp/core/dist/cjs/src/model/config/default";
import { ChainId, DAppProvider, useEthers } from "@usedapp/core";
import { DEFAULT_CONFIG } from "@usedapp/core/dist/cjs/src/model/config/default";
const config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/your-infura-token',
},
multicallAddresses: {
1: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
3: '0x53c43764255c17bd724f74c4ef150724ac50a3ed',
1337: process.env.GANACHE_MULTICALL_CONTRACT ?? '0x0000000000000000000000000000000000000000',
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
}
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: "https://mainnet.infura.io/v3/your-infura-token",
},
multicallAddresses: {
1: "0xeefba1e63905ef1d7acba5a8513c70307c1ce441",
3: "0x53c43764255c17bd724f74c4ef150724ac50a3ed",
1337:
process.env.GANACHE_MULTICALL_CONTRACT ??
"0x0000000000000000000000000000000000000000",
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
};
```
Replace `your-infura-token` with your [Infura API token](https://infura.io/docs/ethereum).
@ -89,12 +91,12 @@ Replace `your-infura-token` with your [Infura API token](https://infura.io/docs/
Create a `Wrapper` variable to use in the page component:
```tsx
import styled from 'styled-components'
import styled from "styled-components";
const Wrapper = styled.div`
height: 100%;
width: 100%;
`
`;
```
### Render
@ -103,97 +105,99 @@ Finally, create the `App` component:
```tsx
export function App() {
return (
<Wrapper>
<GlobalStyle/>
<DAppProvider config={config}>
<PollPage/>
</DAppProvider>
</Wrapper>
)
return (
<Wrapper>
<GlobalStyle />
<DAppProvider config={config}>
<PollPage />
</DAppProvider>
</Wrapper>
);
}
```
Your `index.tsx` should now be:
```tsx
import {ChainId, DAppProvider, useEthers} from '@usedapp/core';
import {GlobalStyle, TopBar} from '@waku/vote-poll-sdk-react-components';
import React, {useEffect, useState} from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import {JsonRpcSigner} from "@ethersproject/providers";
import {orangeTheme} from "@waku/vote-poll-sdk-react-components/dist/cjs/src/style/themes";
import {DEFAULT_CONFIG} from "@usedapp/core/dist/cjs/src/model/config/default";
import styled from 'styled-components'
import { ChainId, DAppProvider, useEthers } from "@usedapp/core";
import { GlobalStyle, TopBar } from "@waku/vote-poll-sdk-react-components";
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import { JsonRpcSigner } from "@ethersproject/providers";
import { orangeTheme } from "@waku/vote-poll-sdk-react-components/dist/cjs/src/style/themes";
import { DEFAULT_CONFIG } from "@usedapp/core/dist/cjs/src/model/config/default";
import styled from "styled-components";
const config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/b4451d780cc64a078ccf2181e872cfcf',
},
multicallAddresses: {
1: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
3: '0x53c43764255c17bd724f74c4ef150724ac50a3ed',
1337: process.env.GANACHE_MULTICALL_CONTRACT ?? '0x0000000000000000000000000000000000000000',
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
}
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]:
"https://mainnet.infura.io/v3/b4451d780cc64a078ccf2181e872cfcf",
},
multicallAddresses: {
1: "0xeefba1e63905ef1d7acba5a8513c70307c1ce441",
3: "0x53c43764255c17bd724f74c4ef150724ac50a3ed",
1337:
process.env.GANACHE_MULTICALL_CONTRACT ??
"0x0000000000000000000000000000000000000000",
},
supportedChains: [...DEFAULT_CONFIG.supportedChains, 1337],
notifications: {
checkInterval: 500,
expirationPeriod: 50000,
},
};
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
const { account, library, activateBrowserWallet, deactivate } = useEthers();
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined);
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
useEffect(() => {
if (account) {
setSigner(library?.getSigner());
} else {
// Deactivate signer if signed out
setSigner(undefined);
}
}, [account]);
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
)
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={"Poll dApp"}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
</div>
);
}
export function App() {
return (
<Wrapper>
<GlobalStyle/>
<DAppProvider config={config}>
<PollPage/>
</DAppProvider>
</Wrapper>
)
return (
<Wrapper>
<GlobalStyle />
<DAppProvider config={config}>
<PollPage />
</DAppProvider>
</Wrapper>
);
}
const Wrapper = styled.div`
height: 100%;
width: 100%;
`
`;
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
```

View File

@ -20,7 +20,7 @@ touch components/Poll.tsx
Again, create a `Wrapper` for styling:
```tsx
import styled from 'styled-components'
import styled from "styled-components";
const Wrapper = styled.div`
display: flex;
@ -40,7 +40,7 @@ const Wrapper = styled.div`
@media (max-width: 425px) {
padding: 96px 16px 84px;
}
`
`;
```
## Button
@ -56,67 +56,74 @@ Upon clicking the button, we set `showPollCreation` to true.
`showPollCreation` will control when to render the poll creation modal.
`components/Poll.tsx`:
```tsx
import {useState} from 'react'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
import { useState } from "react";
import { JsonRpcSigner, Web3Provider } from "@ethersproject/providers";
import { CreateButton } from "@waku/vote-poll-sdk-react-components";
import { Theme } from "@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes";
type PollProps = {
signer: JsonRpcSigner | undefined
theme: Theme
}
signer: JsonRpcSigner | undefined;
theme: Theme;
};
export function Poll({signer, theme}: PollProps) {
const [showPollCreation, setShowPollCreation] = useState(false)
export function Poll({ signer, theme }: PollProps) {
const [showPollCreation, setShowPollCreation] = useState(false);
const disabled = !signer;
const disabled = !signer;
return (
<Wrapper>
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
</Wrapper>
)
return (
<Wrapper>
{
<CreateButton
style={{
backgroundColor: disabled ? "lightgrey" : theme.primaryColor,
}}
theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}
>
Create a poll
</CreateButton>
}
</Wrapper>
);
}
```
Now update the `PollPage` component to render the new `Poll` component:
`index.tsx`:
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
const { account, library, activateBrowserWallet, deactivate } = useEthers();
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined);
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
useEffect(() => {
if (account) {
setSigner(library?.getSigner());
} else {
// Deactivate signer if signed out
setSigner(undefined);
}
}, [account]);
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll theme={orangeTheme} signer={signer}/>
</div>
)
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={"Poll dApp"}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll theme={orangeTheme} signer={signer} />
</div>
);
}
```

View File

@ -10,8 +10,9 @@ The Poll SDK provides an off-the-shelf component to create a new poll: `PollCrea
It takes in a `WakuPolling` hook that can created with `useWakuPolling`.
`useWakuPolling` takes:
- `appName`: Your app name.
It is used to generate a unique content topic for your polls.
It is used to generate a unique content topic for your polls.
See [How to Choose a Content Topic](/docs/guides/01_choose_content_topic/) for more information.
- `tokenAddress`: The address of your ERC-20 token.
Only token holders can create and answer polls.
@ -21,46 +22,68 @@ It takes in a `WakuPolling` hook that can created with `useWakuPolling`.
Add these parameters to `PollProps` and call `useWakuPolling`.
`components/Poll.tsx`
```tsx
import {useState} from 'react'
import {useConfig} from '@usedapp/core'
import {PollCreation} from '@waku/poll-sdk-react-components'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {useWakuPolling} from '@waku/poll-sdk-react-hooks'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
import {ChainId} from "@usedapp/core/src/constants";
import { useState } from "react";
import { useConfig } from "@usedapp/core";
import { PollCreation } from "@waku/poll-sdk-react-components";
import { JsonRpcSigner, Web3Provider } from "@ethersproject/providers";
import { useWakuPolling } from "@waku/poll-sdk-react-hooks";
import { CreateButton } from "@waku/vote-poll-sdk-react-components";
import { Theme } from "@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes";
import { ChainId } from "@usedapp/core/src/constants";
type PollProps = {
appName: string
library: Web3Provider | undefined
signer: JsonRpcSigner | undefined
chainId: ChainId | undefined
theme: Theme
tokenAddress: string
}
appName: string;
library: Web3Provider | undefined;
signer: JsonRpcSigner | undefined;
chainId: ChainId | undefined;
theme: Theme;
tokenAddress: string;
};
export function Poll({appName, library, signer, chainId, theme, tokenAddress}: PollProps) {
const config = useConfig()
const [showPollCreation, setShowPollCreation] = useState(false)
const wakuPolling = useWakuPolling(appName, tokenAddress, library, config?.multicallAddresses?.[chainId ?? 1337])
export function Poll({
appName,
library,
signer,
chainId,
theme,
tokenAddress,
}: PollProps) {
const config = useConfig();
const [showPollCreation, setShowPollCreation] = useState(false);
const wakuPolling = useWakuPolling(
appName,
tokenAddress,
library,
config?.multicallAddresses?.[chainId ?? 1337]
);
const disabled = !signer;
const disabled = !signer;
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme}/>
)}
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
</Wrapper>
)
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation
wakuPolling={wakuPolling}
setShowPollCreation={setShowPollCreation}
theme={theme}
/>
)}
{
<CreateButton
style={{
backgroundColor: disabled ? "lightgrey" : theme.primaryColor,
}}
theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}
>
Create a poll
</CreateButton>
}
</Wrapper>
);
}
```
@ -70,35 +93,43 @@ In this example, we use `demo-poll-dapp` for the app name and the mainnet SNT to
Replace those with your own.
`index.tsx`
```tsx
export function PollPage() {
const {account, library, activateBrowserWallet, deactivate, chainId} = useEthers()
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined)
const { account, library, activateBrowserWallet, deactivate, chainId } =
useEthers();
const [signer, setSigner] = useState<undefined | JsonRpcSigner>(undefined);
useEffect(() => {
if (account) {
setSigner(library?.getSigner())
} else {
// Deactivate signer if signed out
setSigner(undefined)
}
}, [account])
useEffect(() => {
if (account) {
setSigner(library?.getSigner());
} else {
// Deactivate signer if signed out
setSigner(undefined);
}
}, [account]);
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={'Poll dApp'}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll theme={orangeTheme} appName={'demo-poll-dapp'} library={library} signer={signer} chainId={chainId}
tokenAddress={'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'}/>
</div>
)
return (
<div>
<TopBar
logo={""}
logoWidth={84}
title={"Poll dApp"}
theme={orangeTheme}
activate={activateBrowserWallet}
account={account}
deactivate={deactivate}
/>
<Poll
theme={orangeTheme}
appName={"demo-poll-dapp"}
library={library}
signer={signer}
chainId={chainId}
tokenAddress={"0x744d70FDBE2Ba4CF95131626614a1763DF805B9E"}
/>
</div>
);
}
```

View File

@ -12,56 +12,86 @@ Simply add it to the `Poll` function to render it.
It needs the `account` variable that can be passed as a property to `Poll`:
`components/Poll.tsx`:
```tsx
import {useState} from 'react'
import {useConfig} from '@usedapp/core'
import {PollCreation, PollList} from '@waku/poll-sdk-react-components'
import {JsonRpcSigner, Web3Provider} from '@ethersproject/providers'
import {useWakuPolling} from '@waku/poll-sdk-react-hooks'
import {CreateButton} from '@waku/vote-poll-sdk-react-components'
import {Theme} from '@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes'
import {ChainId} from "@usedapp/core/src/constants";
import { useState } from "react";
import { useConfig } from "@usedapp/core";
import { PollCreation, PollList } from "@waku/poll-sdk-react-components";
import { JsonRpcSigner, Web3Provider } from "@ethersproject/providers";
import { useWakuPolling } from "@waku/poll-sdk-react-hooks";
import { CreateButton } from "@waku/vote-poll-sdk-react-components";
import { Theme } from "@waku/vote-poll-sdk-react-components/dist/esm/src/style/themes";
import { ChainId } from "@usedapp/core/src/constants";
type PollProps = {
appName: string
library: Web3Provider | undefined
signer: JsonRpcSigner | undefined
chainId: ChainId | undefined
account: string | null | undefined
theme: Theme
tokenAddress: string
}
appName: string;
library: Web3Provider | undefined;
signer: JsonRpcSigner | undefined;
chainId: ChainId | undefined;
account: string | null | undefined;
theme: Theme;
tokenAddress: string;
};
export function Poll({appName, library, signer, chainId, account, theme, tokenAddress}: PollProps) {
const config = useConfig()
const [showPollCreation, setShowPollCreation] = useState(false)
const wakuPolling = useWakuPolling(appName, tokenAddress, library, config?.multicallAddresses?.[chainId ?? 1337])
export function Poll({
appName,
library,
signer,
chainId,
account,
theme,
tokenAddress,
}: PollProps) {
const config = useConfig();
const [showPollCreation, setShowPollCreation] = useState(false);
const wakuPolling = useWakuPolling(
appName,
tokenAddress,
library,
config?.multicallAddresses?.[chainId ?? 1337]
);
const disabled = !signer;
const disabled = !signer;
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation wakuPolling={wakuPolling} setShowPollCreation={setShowPollCreation} theme={theme}/>
)}
{
<CreateButton style={{backgroundColor: disabled ? "lightgrey" : theme.primaryColor}} theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}>
Create a poll
</CreateButton>
}
<PollList wakuPolling={wakuPolling} account={account} theme={theme} />
</Wrapper>
)
return (
<Wrapper>
{showPollCreation && signer && (
<PollCreation
wakuPolling={wakuPolling}
setShowPollCreation={setShowPollCreation}
theme={theme}
/>
)}
{
<CreateButton
style={{
backgroundColor: disabled ? "lightgrey" : theme.primaryColor,
}}
theme={theme}
disabled={disabled}
onClick={() => setShowPollCreation(true)}
>
Create a poll
</CreateButton>
}
<PollList wakuPolling={wakuPolling} account={account} theme={theme} />
</Wrapper>
);
}
```
Pass the `account` to `Poll` in `index.tsx`:
```tsx
<Poll theme={orangeTheme} appName={'demo-poll-dapp'} library={library} signer={signer} chainId={chainId}
account={account}
tokenAddress={'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'}/>
<Poll
theme={orangeTheme}
appName={"demo-poll-dapp"}
library={library}
signer={signer}
chainId={chainId}
account={account}
tokenAddress={"0x744d70FDBE2Ba4CF95131626614a1763DF805B9E"}
/>
```
Et voila!
@ -77,12 +107,11 @@ You can find the resulting code in the [examples folder](https://github.com/stat
{{< hint info >}}
The example above uses webpack 5 instead of react-app-rewired.
It also allows passing a token contract address in the url, as described in the [README](https://github.com/status-im/wakuconnect-vote-poll-sdk/blob/main/examples/mainnet-poll/README.md).
It also allows passing a token contract address in the url, as described in the [README](https://github.com/status-im/wakuconnect-vote-poll-sdk/blob/main/examples/mainnet-poll/README.md).
{{< /hint >}}
The final gif:
![Poll demo](/assets/poll_sdk/wakuconnect-poll-demo.gif)
{{< button relref="./04_poll_creation" >}}Back{{< /button >}}

View File

@ -3,6 +3,7 @@ title: Introduction
date: 2021-12-09T14:00:00+01:00
weight: 10
---
# WakuConnect Docs
WakuConnect is a suite of libraries, SDKs and documentations to help you use Waku in your dApp.
@ -28,7 +29,7 @@ and how it can be used with popular web frameworks.
The js-waku repository also holds a number of [examples](https://github.com/status-im/js-waku/tree/main/examples).
The examples are working Proof-of-Concepts that demonstrate how to use js-waku.
Check out the [example list](/docs/examples/) to see what usage each example demonstrates.
Check out the [example list](/docs/examples/) to see what usage each example demonstrates.
Finally, if you want to learn how Waku works under the hoods, check the specs at [rfc.vac.dev](https://rfc.vac.dev/).

View File

@ -3,6 +3,7 @@ title: Presentations & Videos
date: 2022-01-12T01:00:00+01:00
weight: 22
---
# Presentations & Videos
## 17 Sep 2021 - EthOnline

View File

@ -3,6 +3,7 @@ title: Quick Start
date: 2021-12-09T14:00:00+01:00
weight: 20
---
# Quick Start
In this section you will learn how to receive and send messages using Waku Relay.
@ -22,9 +23,9 @@ yarn add js-waku
### Start a waku node
```ts
import {Waku} from 'js-waku';
import { Waku } from "js-waku";
const waku = await Waku.create({bootstrap: {default: true}});
const waku = await Waku.create({ bootstrap: { default: true } });
```
### Listen for messages
@ -38,9 +39,12 @@ For example, if you were to use a new `contentTopic` such as `/my-cool-app/1/my-
here is how to listen to new messages received via [Waku v2 Relay](https://rfc.vac.dev/spec/11/):
```ts
waku.relay.addObserver((msg) => {
console.log("Message received:", msg.payloadAsUtf8)
}, ["/my-cool-app/1/my-use-case/proto"]);
waku.relay.addObserver(
(msg) => {
console.log("Message received:", msg.payloadAsUtf8);
},
["/my-cool-app/1/my-use-case/proto"]
);
```
### Send messages
@ -48,12 +52,15 @@ waku.relay.addObserver((msg) => {
Messages are wrapped in a `WakuMessage` envelop.
```ts
import { WakuMessage } from 'js-waku';
import { WakuMessage } from "js-waku";
const msg = await WakuMessage.fromUtf8String("Here is a message!", "/my-cool-app/1/my-use-case/proto")
const msg = await WakuMessage.fromUtf8String(
"Here is a message!",
"/my-cool-app/1/my-use-case/proto"
);
await waku.relay.send(msg);
```
### Building an app
Check out the [ReactJS Waku Relay guide](/docs/guides/07_reactjs_relay/) to learn how you can use the code above in a React app.
Check out the [ReactJS Waku Relay guide](/docs/guides/07_reactjs_relay/) to learn how you can use the code above in a React app.

View File

@ -15,11 +15,13 @@ If we are aware of other projects using js-waku and other use cases that could b
feel free to open a [PR](https://github.com/vacp2p/docs.wakuconnect.dev).
Legend:
- _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.
---
{{< columns >}}
@ -27,7 +29,7 @@ Legend:
## Chat Messenger
| _Work In Progress_ |
|--------------------|
| ------------------ |
Waku can be used as the communication layer to a private, decentralized, censorship-resistant messenger.
@ -38,7 +40,7 @@ Waku can be used as the communication layer to a private, decentralized, censors
## Polls
| _SDK Available_ |
|-----------------|
| --------------- |
Create, answer and view polls which are censorship-resistant.
@ -51,7 +53,7 @@ Create, answer and view polls which are censorship-resistant.
## 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.
@ -67,7 +69,7 @@ Add a social media layer, allowing NFT owners to like, comments, etc.
## State Channels
| _Idea_ |
|--------|
| ------ |
Use Waku to enable two parties to setup and maintain a state channel.
@ -78,7 +80,7 @@ Use Waku to enable two parties to setup and maintain a state channel.
## Voting and Proposals
| _SDK Available_ |
|-----------------|
| --------------- |
For proposals submitted on the blockchain,
exchange votes over Waku to save gas.
@ -95,7 +97,7 @@ Create, answer and view polls which are censorship-resistant.
## 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 & censorship-resistant manner to approve transactions.
@ -109,7 +111,7 @@ private & 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.
@ -121,7 +123,7 @@ Remove the need of a centralized infrastructure for gameplay communications.
## dApp to Wallet Communication
| _Live_ |
|--------|
| ------ |
Communication between a user'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),
@ -135,7 +137,7 @@ or for a dApp to request transaction signature to the wallet.
## Layer 2 Communication
| _Idea_ |
|--------|
| ------ |
Use Waku as an existing communication network to broadcast and aggregate layer 2 transactions.
Possibly increasing privacy, anonymity and resilience.
@ -149,7 +151,7 @@ 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.
@ -163,7 +165,7 @@ to create a ride-sharing or tradings apps.
## Social Media Platform
| _Idea_ |
|--------|
| ------ |
[Chat Messenger](#chat-messenger) is one form of social media that can be empowered by Waku to be decentralized
and censorship-resistant.

View File

@ -3,6 +3,7 @@ title: Implemented Waku Protocols
date: 2021-12-09T14:00:00+01:00
weight: 60
---
## Waku Protocol Support
You can track progress on the [project board](https://github.com/status-im/js-waku/projects/1).
@ -11,24 +12,24 @@ You can track progress on the [project board](https://github.com/status-im/js-wa
- 🚧: Implementation in progress
- ⛔: Support is not planned
| Spec | Implementation Status |
| ---- | -------------- |
|[6/WAKU1](https://rfc.vac.dev/spec/6)|⛔|
|[7/WAKU-DATA](https://rfc.vac.dev/spec/7)|⛔|
|[8/WAKU-MAIL](https://rfc.vac.dev/spec/8)|⛔|
|[9/WAKU-RPC](https://rfc.vac.dev/spec/9)|⛔|
|[10/WAKU2](https://rfc.vac.dev/spec/10)|🚧|
|[11/WAKU2-RELAY](https://rfc.vac.dev/spec/11)|✔|
|[12/WAKU2-FILTER](https://rfc.vac.dev/spec/12)||
|[13/WAKU2-STORE](https://rfc.vac.dev/spec/13)|✔ (querying node only)|
|[14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14)|✔|
|[15/WAKU2-BRIDGE](https://rfc.vac.dev/spec/15)||
|[16/WAKU2-RPC](https://rfc.vac.dev/spec/16)|⛔|
|[17/WAKU2-RLNRELAY](https://rfc.vac.dev/spec/17)||
|[18/WAKU2-SWAP](https://rfc.vac.dev/spec/18)||
|[19/WAKU2-LIGHTPUSH](https://rfc.vac.dev/spec/19/)|✔|
|[20/TOY-ETH-PM](https://rfc.vac.dev/spec/20/)|✔ (as example)|
|[21/WAKU2-FTSTORE](https://rfc.vac.dev/spec/21/)|✔|
|[22/TOY-CHAT](https://rfc.vac.dev/spec/22/)|✔ (as example)|
|[25/LIBP2P-DNS-DISCOVERY](https://rfc.vac.dev/spec/25/)|🚧|
|[26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26/)|✔|
| Spec | Implementation Status |
| ------------------------------------------------------- | ---------------------- |
| [6/WAKU1](https://rfc.vac.dev/spec/6) | |
| [7/WAKU-DATA](https://rfc.vac.dev/spec/7) | |
| [8/WAKU-MAIL](https://rfc.vac.dev/spec/8) | |
| [9/WAKU-RPC](https://rfc.vac.dev/spec/9) | |
| [10/WAKU2](https://rfc.vac.dev/spec/10) | 🚧 |
| [11/WAKU2-RELAY](https://rfc.vac.dev/spec/11) | |
| [12/WAKU2-FILTER](https://rfc.vac.dev/spec/12) | |
| [13/WAKU2-STORE](https://rfc.vac.dev/spec/13) | ✔ (querying node only) |
| [14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14) | |
| [15/WAKU2-BRIDGE](https://rfc.vac.dev/spec/15) | |
| [16/WAKU2-RPC](https://rfc.vac.dev/spec/16) | |
| [17/WAKU2-RLNRELAY](https://rfc.vac.dev/spec/17) | |
| [18/WAKU2-SWAP](https://rfc.vac.dev/spec/18) | |
| [19/WAKU2-LIGHTPUSH](https://rfc.vac.dev/spec/19/) | |
| [20/TOY-ETH-PM](https://rfc.vac.dev/spec/20/) | ✔ (as example) |
| [21/WAKU2-FTSTORE](https://rfc.vac.dev/spec/21/) | |
| [22/TOY-CHAT](https://rfc.vac.dev/spec/22/) | ✔ (as example) |
| [25/LIBP2P-DNS-DISCOVERY](https://rfc.vac.dev/spec/25/) | 🚧 |
| [26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26/) | |

View File

@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1