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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@waku/rln": "0.1.2-126bce3",
|
||||
"@waku/rln": "0.1.2-e1679b6",
|
||||
"@waku/sdk": "^0.0.21",
|
||||
"@waku/utils": "^0.0.14",
|
||||
"multiaddr": "^10.0.1",
|
||||
"protobufjs": "^7.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"eslint": "^8",
|
||||
"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-cli": "^4.10.0",
|
||||
"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 {
|
||||
ProtoChatMessage,
|
||||
|
@ -7,11 +7,24 @@ import {
|
|||
MEMBERSHIP_HASH,
|
||||
MEMBERSHIP_PASSWORD,
|
||||
} from "./const";
|
||||
import { RLNDecoder, RLNEncoder, RLNInstance } from "@waku/rln";
|
||||
import { StatusChangeArgs } from "./types";
|
||||
|
||||
export async function initWaku({ rln, onStatusChange }) {
|
||||
let node;
|
||||
let encoder, decoder;
|
||||
let subscription;
|
||||
type Args = {
|
||||
rln: RLNInstance,
|
||||
onStatusChange: (args: StatusChangeArgs) => void,
|
||||
};
|
||||
|
||||
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 () => {
|
||||
encoder = await rln.createEncoder({
|
||||
|
@ -25,16 +38,20 @@ export async function initWaku({ rln, onStatusChange }) {
|
|||
});
|
||||
decoder = rln.createDecoder(CONTENT_TOPIC);
|
||||
|
||||
onStatusChange("Initializing Waku...");
|
||||
onStatusChange({
|
||||
newStatus: "Initializing Waku..."
|
||||
});
|
||||
node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
});
|
||||
onStatusChange("Waiting for peers");
|
||||
onStatusChange({
|
||||
newStatus: "Waiting for peers"
|
||||
});
|
||||
await node.start();
|
||||
await waitForRemotePeer(node);
|
||||
};
|
||||
|
||||
const onSend = async (nick, text) => {
|
||||
const onSend = async (nick: string, text: string) => {
|
||||
const timestamp = new Date();
|
||||
const msg = ProtoChatMessage.create({
|
||||
text,
|
||||
|
@ -48,21 +65,28 @@ export async function initWaku({ rln, onStatusChange }) {
|
|||
console.log("Message sent:", res);
|
||||
};
|
||||
|
||||
const onSubscribe = async (cb) => {
|
||||
onStatusChange("Subscribing to content topic...");
|
||||
const onSubscribe = async (cb: (nick: string, text: string, timestamp: number, proofStatus: string) => void ) => {
|
||||
onStatusChange({
|
||||
newStatus: "Subscribing to content topic..."
|
||||
});
|
||||
subscription = await node.filter.createSubscription();
|
||||
|
||||
await subscription.subscribe(decoder, (message) => {
|
||||
try {
|
||||
|
||||
const { timestamp, nick, text } = ProtoChatMessage.decode(
|
||||
message.payload
|
||||
);
|
||||
) as unknown as IChatMessage;
|
||||
|
||||
let proofStatus = "no proof";
|
||||
if (message.rateLimitProof) {
|
||||
console.log("Proof received: ", message.rateLimitProof);
|
||||
|
||||
try {
|
||||
if (!rln.contract) {
|
||||
throw new Error("RLN contract not initialized");
|
||||
}
|
||||
|
||||
console.time("Proof verification took:");
|
||||
const res = message.verify(rln.contract.roots());
|
||||
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 {
|
|
@ -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");
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index.js",
|
||||
entry: "./src/index.ts",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build"),
|
||||
filename: "./index.js",
|
||||
filename: "index.js",
|
||||
},
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
mode: "development",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: ["index.html", "favicon.ico", "favicon.png", "manifest.json"],
|
||||
|
|
Loading…
Reference in New Issue