chore(rln-js): convert to typescript

This commit is contained in:
danisharora099 2024-04-07 13:59:52 +03:00
parent a7594e60c1
commit 3008f31bdd
No known key found for this signature in database
GPG Key ID: FBD2BF500037F135
12 changed files with 2700 additions and 2206 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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,
};
}

View File

@ -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,
};
}

View File

@ -0,0 +1,4 @@
export type StatusChangeArgs = {
newStatus: string,
className?: string
}

View File

@ -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,
};
};

102
examples/rln-js/src/ui.ts Normal file
View File

@ -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,
};
};

View File

@ -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 {

View File

@ -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"]
}

View File

@ -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"],