mirror of https://github.com/waku-org/waku-lab.git
chore(rln-js): convert to typescript
This commit is contained in:
parent
a7594e60c1
commit
3008f31bdd
File diff suppressed because it is too large
Load Diff
|
@ -7,16 +7,18 @@
|
||||||
"start": "webpack-dev-server"
|
"start": "webpack-dev-server"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/rln": "0.1.2-126bce3",
|
"@waku/rln": "0.1.2-e1679b6",
|
||||||
"@waku/sdk": "^0.0.21",
|
"@waku/sdk": "^0.0.21",
|
||||||
"@waku/utils": "^0.0.14",
|
"@waku/utils": "^0.0.14",
|
||||||
"multiaddr": "^10.0.1",
|
"multiaddr": "^10.0.1",
|
||||||
"protobufjs": "^7.2.5"
|
"protobufjs": "^7.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "13.5.6",
|
"eslint-config-next": "13.5.6",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"ts-loader": "^9.5.1",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
"webpack": "^5.74.0",
|
"webpack": "^5.74.0",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-dev-server": "^4.11.1"
|
"webpack-dev-server": "^4.11.1"
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
import { createRLN, extractMetaMaskSigner } from "@waku/rln";
|
|
||||||
|
|
||||||
export async function initRLN(onStatusChange) {
|
|
||||||
onStatusChange("Initializing RLN...");
|
|
||||||
|
|
||||||
let rln;
|
|
||||||
try {
|
|
||||||
rln = await createRLN();
|
|
||||||
} catch (err) {
|
|
||||||
onStatusChange(`Failed to initialize RLN: ${err}`, "error");
|
|
||||||
throw Error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
onStatusChange("RLN initialized", "success");
|
|
||||||
|
|
||||||
const connectWallet = async () => {
|
|
||||||
let signer;
|
|
||||||
try {
|
|
||||||
onStatusChange("Connecting to wallet...");
|
|
||||||
signer = await extractMetaMaskSigner();
|
|
||||||
} catch (err) {
|
|
||||||
onStatusChange(`Failed to access MetaMask: ${err}`, "error");
|
|
||||||
throw Error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
onStatusChange("Connecting to Ethereum...");
|
|
||||||
await rln.start({ signer });
|
|
||||||
} catch (err) {
|
|
||||||
onStatusChange(`Failed to connect to Ethereum: ${err}`, "error");
|
|
||||||
throw Error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
onStatusChange("RLN started", "success");
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
rln,
|
|
||||||
connectWallet,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { RLNInstance, createRLN, extractMetaMaskSigner } from "@waku/rln";
|
||||||
|
import { Signer } from "ethers";
|
||||||
|
import { StatusChangeArgs } from "./types";
|
||||||
|
|
||||||
|
export async function initRLN(onStatusChange: ({}: StatusChangeArgs) => void) {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: "Initializing RLN..."
|
||||||
|
});
|
||||||
|
|
||||||
|
let rln: RLNInstance;
|
||||||
|
try {
|
||||||
|
console.time("createRLN")
|
||||||
|
rln = await createRLN();
|
||||||
|
console.timeEnd("createRLN")
|
||||||
|
} catch (err) {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: `Failed to initialize RLN: ${err}`,
|
||||||
|
className: "error"
|
||||||
|
});
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: "RLN initialized",
|
||||||
|
className: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
const connectWallet = async () => {
|
||||||
|
let signer: Signer;
|
||||||
|
try {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: "Connecting to wallet..."
|
||||||
|
});
|
||||||
|
signer = await extractMetaMaskSigner();
|
||||||
|
console.log("done")
|
||||||
|
} catch (err) {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: `Failed to access MetaMask: ${err}`,
|
||||||
|
className: "error"
|
||||||
|
});
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: "Connecting to Ethereum..."
|
||||||
|
});
|
||||||
|
await rln.start({ signer });
|
||||||
|
} catch (err) {
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: `Failed to connect to Ethereum: ${err}`,
|
||||||
|
className: "error"
|
||||||
|
});
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChange({
|
||||||
|
newStatus: "RLN started",
|
||||||
|
className: "success"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
rln,
|
||||||
|
connectWallet,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type StatusChangeArgs = {
|
||||||
|
newStatus: string,
|
||||||
|
className?: string
|
||||||
|
}
|
|
@ -1,61 +0,0 @@
|
||||||
const status = document.getElementById("status");
|
|
||||||
const chat = document.getElementById("chat-area");
|
|
||||||
const messages = document.getElementById("messages");
|
|
||||||
|
|
||||||
const nickInput = document.getElementById("nick");
|
|
||||||
const textInput = document.getElementById("text");
|
|
||||||
const sendButton = document.getElementById("send");
|
|
||||||
|
|
||||||
const connectWalletButton = document.getElementById("connect");
|
|
||||||
|
|
||||||
export const initUI = () => {
|
|
||||||
const onStatusChange = (newStatus, className) => {
|
|
||||||
status.innerText = newStatus;
|
|
||||||
status.className = className || "progress";
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoaded = () => {
|
|
||||||
chat.style.display = "block";
|
|
||||||
};
|
|
||||||
|
|
||||||
const _renderMessage = (nick, text, time, validation) => {
|
|
||||||
messages.innerHTML += `
|
|
||||||
<li>
|
|
||||||
(${nick})(${validation})
|
|
||||||
<strong>${text}</strong>
|
|
||||||
<i>[${new Date(time).toISOString()}]</i>
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const registerEvents = (events) => {
|
|
||||||
connectWalletButton.addEventListener("click", async () => {
|
|
||||||
await events.connectWallet();
|
|
||||||
await events.onInitWaku();
|
|
||||||
|
|
||||||
onLoaded();
|
|
||||||
|
|
||||||
events.onSubscribe((nick, text, time, validation) => {
|
|
||||||
_renderMessage(nick, text, time, validation);
|
|
||||||
});
|
|
||||||
|
|
||||||
sendButton.addEventListener("click", async () => {
|
|
||||||
const nick = nickInput.value;
|
|
||||||
const text = textInput.value;
|
|
||||||
|
|
||||||
if (!nick || !text) {
|
|
||||||
console.log("Not sending message: missing nick or text.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await events.onSend(nick, text);
|
|
||||||
textInput.value = "";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerEvents,
|
|
||||||
onStatusChange,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { StatusChangeArgs } from "./types";
|
||||||
|
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const chat = document.getElementById("chat-area");
|
||||||
|
const messages = document.getElementById("messages");
|
||||||
|
|
||||||
|
const nickInput = document.getElementById("nick") as HTMLInputElement;
|
||||||
|
const textInput = document.getElementById("text") as HTMLInputElement;
|
||||||
|
const sendButton = document.getElementById("send") as HTMLInputElement;
|
||||||
|
|
||||||
|
const connectWalletButton = document.getElementById("connect");
|
||||||
|
|
||||||
|
export const initUI = () => {
|
||||||
|
const onStatusChange = ({ newStatus, className }: StatusChangeArgs) => {
|
||||||
|
if (!status) {
|
||||||
|
console.log("Status element not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
status.innerText = newStatus;
|
||||||
|
status.className = className || "progress";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoaded = () => {
|
||||||
|
if (!chat) {
|
||||||
|
console.log("Chat element not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chat.style.display = "block";
|
||||||
|
};
|
||||||
|
|
||||||
|
const _renderMessage = (nick: string, text: string, timestamp: number, proofStatus: string) => {
|
||||||
|
if (!messages) {
|
||||||
|
console.log("Messages element not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messages.innerHTML += `
|
||||||
|
<li>
|
||||||
|
(${nick})(${proofStatus})
|
||||||
|
<strong>${text}</strong>
|
||||||
|
<i>[${new Date(timestamp).toISOString()}]</i>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Events = {
|
||||||
|
connectWallet: () => Promise<void>,
|
||||||
|
onInitWaku: () => Promise<void>,
|
||||||
|
onSubscribe: (cb: (nick: string, text: string, timestamp: number, proofStatus: string) => void) => void,
|
||||||
|
onSend: (nick: string, text: string) => Promise<void>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const registerEvents = (events: Events) => {
|
||||||
|
if (!connectWalletButton) {
|
||||||
|
console.log("Connect wallet button not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendButton) {
|
||||||
|
console.log("Send button not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectWalletButton.addEventListener("click", async () => {
|
||||||
|
await events.connectWallet();
|
||||||
|
await events.onInitWaku();
|
||||||
|
|
||||||
|
onLoaded();
|
||||||
|
|
||||||
|
events.onSubscribe((nick: string, text: string, timestamp: number, proofStatus: string) => {
|
||||||
|
_renderMessage(nick, text, timestamp, proofStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
sendButton.addEventListener("click", async () => {
|
||||||
|
if (!nickInput || !textInput) {
|
||||||
|
console.log("Nick or text input not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!events.onSend) {
|
||||||
|
console.log("onSend event not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nick = nickInput.value;
|
||||||
|
const text = textInput.value;
|
||||||
|
|
||||||
|
if (!nick || !text) {
|
||||||
|
console.log("Not sending message: missing nick or text.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await events.onSend(nick, text);
|
||||||
|
textInput.value = "";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerEvents,
|
||||||
|
onStatusChange,
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import { createLightNode, waitForRemotePeer } from "@waku/sdk";
|
import {type LightNode, createLightNode, waitForRemotePeer, type IFilterSubscription, IDecodedMessage } from "@waku/sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProtoChatMessage,
|
ProtoChatMessage,
|
||||||
|
@ -7,11 +7,24 @@ import {
|
||||||
MEMBERSHIP_HASH,
|
MEMBERSHIP_HASH,
|
||||||
MEMBERSHIP_PASSWORD,
|
MEMBERSHIP_PASSWORD,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
|
import { RLNDecoder, RLNEncoder, RLNInstance } from "@waku/rln";
|
||||||
|
import { StatusChangeArgs } from "./types";
|
||||||
|
|
||||||
export async function initWaku({ rln, onStatusChange }) {
|
type Args = {
|
||||||
let node;
|
rln: RLNInstance,
|
||||||
let encoder, decoder;
|
onStatusChange: (args: StatusChangeArgs) => void,
|
||||||
let subscription;
|
};
|
||||||
|
|
||||||
|
type IChatMessage = {
|
||||||
|
nick: string,
|
||||||
|
text: string,
|
||||||
|
timestamp: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function initWaku({ rln, onStatusChange }: Args) {
|
||||||
|
let node: LightNode;
|
||||||
|
let encoder: RLNEncoder, decoder: RLNDecoder<IDecodedMessage>;
|
||||||
|
let subscription: IFilterSubscription;
|
||||||
|
|
||||||
const onInitWaku = async () => {
|
const onInitWaku = async () => {
|
||||||
encoder = await rln.createEncoder({
|
encoder = await rln.createEncoder({
|
||||||
|
@ -25,16 +38,20 @@ export async function initWaku({ rln, onStatusChange }) {
|
||||||
});
|
});
|
||||||
decoder = rln.createDecoder(CONTENT_TOPIC);
|
decoder = rln.createDecoder(CONTENT_TOPIC);
|
||||||
|
|
||||||
onStatusChange("Initializing Waku...");
|
onStatusChange({
|
||||||
|
newStatus: "Initializing Waku..."
|
||||||
|
});
|
||||||
node = await createLightNode({
|
node = await createLightNode({
|
||||||
defaultBootstrap: true,
|
defaultBootstrap: true,
|
||||||
});
|
});
|
||||||
onStatusChange("Waiting for peers");
|
onStatusChange({
|
||||||
|
newStatus: "Waiting for peers"
|
||||||
|
});
|
||||||
await node.start();
|
await node.start();
|
||||||
await waitForRemotePeer(node);
|
await waitForRemotePeer(node);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSend = async (nick, text) => {
|
const onSend = async (nick: string, text: string) => {
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
const msg = ProtoChatMessage.create({
|
const msg = ProtoChatMessage.create({
|
||||||
text,
|
text,
|
||||||
|
@ -48,21 +65,28 @@ export async function initWaku({ rln, onStatusChange }) {
|
||||||
console.log("Message sent:", res);
|
console.log("Message sent:", res);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubscribe = async (cb) => {
|
const onSubscribe = async (cb: (nick: string, text: string, timestamp: number, proofStatus: string) => void ) => {
|
||||||
onStatusChange("Subscribing to content topic...");
|
onStatusChange({
|
||||||
|
newStatus: "Subscribing to content topic..."
|
||||||
|
});
|
||||||
subscription = await node.filter.createSubscription();
|
subscription = await node.filter.createSubscription();
|
||||||
|
|
||||||
await subscription.subscribe(decoder, (message) => {
|
await subscription.subscribe(decoder, (message) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const { timestamp, nick, text } = ProtoChatMessage.decode(
|
const { timestamp, nick, text } = ProtoChatMessage.decode(
|
||||||
message.payload
|
message.payload
|
||||||
);
|
) as unknown as IChatMessage;
|
||||||
|
|
||||||
let proofStatus = "no proof";
|
let proofStatus = "no proof";
|
||||||
if (message.rateLimitProof) {
|
if (message.rateLimitProof) {
|
||||||
console.log("Proof received: ", message.rateLimitProof);
|
console.log("Proof received: ", message.rateLimitProof);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!rln.contract) {
|
||||||
|
throw new Error("RLN contract not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
console.time("Proof verification took:");
|
console.time("Proof verification took:");
|
||||||
const res = message.verify(rln.contract.roots());
|
const res = message.verify(rln.contract.roots());
|
||||||
console.timeEnd("Proof verification took:");
|
console.timeEnd("Proof verification took:");
|
||||||
|
@ -85,7 +109,10 @@ export async function initWaku({ rln, onStatusChange }) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onStatusChange("Waku initialized", "success");
|
onStatusChange({
|
||||||
|
newStatus: "Waku initialized",
|
||||||
|
className: "success"
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"incremental": true,
|
||||||
|
"target": "es2020",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": false,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"typeRoots": ["node_modules/@types", "src/types"]
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
|
@ -2,15 +2,27 @@ const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: "./src/index.js",
|
entry: "./src/index.ts",
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "build"),
|
path: path.resolve(__dirname, "build"),
|
||||||
filename: "./index.js",
|
filename: "index.js",
|
||||||
},
|
},
|
||||||
experiments: {
|
experiments: {
|
||||||
asyncWebAssembly: true,
|
asyncWebAssembly: true,
|
||||||
},
|
},
|
||||||
mode: "development",
|
mode: "development",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: ["index.html", "favicon.ico", "favicon.png", "manifest.json"],
|
patterns: ["index.html", "favicon.ico", "favicon.png", "manifest.json"],
|
||||||
|
|
Loading…
Reference in New Issue