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

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

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"); 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"],