mirror of
https://github.com/logos-messaging/logos-messaging-frontend.git
synced 2026-01-30 19:43:11 +00:00
167 lines
4.7 KiB
TypeScript
167 lines
4.7 KiB
TypeScript
import { ethers } from "ethers";
|
|
import {
|
|
create,
|
|
Keystore,
|
|
RLNContract,
|
|
SEPOLIA_CONTRACT,
|
|
RLNInstance,
|
|
} from "@waku/rln";
|
|
import { isBrowserProviderValid } from "@/utils/ethereum";
|
|
|
|
export enum RLNEventsNames {
|
|
Status = "status",
|
|
Keystore = "keystore-changed",
|
|
}
|
|
|
|
export enum StatusEventPayload {
|
|
WASM_LOADING = "WASM Blob download in progress...",
|
|
WASM_LOADED = "WASM Blob downloaded",
|
|
WASM_FAILED = "Failed to download WASM, check console",
|
|
CONTRACT_LOADING = "Connecting to RLN contract",
|
|
CONTRACT_FAILED = "Failed to connect to RLN contract",
|
|
RLN_INITIALIZED = "RLN dependencies initialized",
|
|
KEYSTORE_LOCAL = "Keystore initialized from localStore",
|
|
KEYSTORE_NEW = "New Keystore was initialized",
|
|
CREDENTIALS_REGISTERING = "Registering credentials...",
|
|
CREDENTIALS_REGISTERED = "Registered credentials",
|
|
CREDENTIALS_FAILURE = "Failed to register credentials, check console",
|
|
}
|
|
|
|
type EventListener = (event: CustomEvent) => void;
|
|
|
|
type IRLN = {
|
|
saveKeystore: () => void;
|
|
addEventListener: (name: RLNEventsNames, fn: EventListener) => void;
|
|
removeEventListener: (name: RLNEventsNames, fn: EventListener) => void;
|
|
};
|
|
|
|
export class RLN implements IRLN {
|
|
private readonly emitter = new EventTarget();
|
|
public ethProvider: ethers.providers.Web3Provider | undefined;
|
|
|
|
public rlnInstance: undefined | RLNInstance;
|
|
public rlnContract: undefined | RLNContract;
|
|
public keystore: Keystore;
|
|
|
|
private initialized = false;
|
|
private initializing = false;
|
|
|
|
public constructor() {
|
|
this.keystore = this.initKeystore();
|
|
}
|
|
|
|
public async init(): Promise<void> {
|
|
if (this.initialized || this.initializing) {
|
|
return;
|
|
}
|
|
|
|
this.initializing = true;
|
|
|
|
this.initProvider();
|
|
await this.initRLNWasm();
|
|
|
|
// emit keystore keys once app is ready
|
|
this.emitKeystoreKeys();
|
|
|
|
this.initialized = true;
|
|
this.initializing = false;
|
|
}
|
|
|
|
private initProvider() {
|
|
if (typeof window === "undefined") {
|
|
return;
|
|
}
|
|
|
|
const ethereum =
|
|
window.ethereum as unknown as ethers.providers.ExternalProvider;
|
|
if (!isBrowserProviderValid(ethereum)) {
|
|
throw Error(
|
|
"Invalid Ethereum provider present on the page. Check if MetaMask is connected."
|
|
);
|
|
}
|
|
this.ethProvider = new ethers.providers.Web3Provider(ethereum, "any");
|
|
}
|
|
|
|
private async initRLNWasm(): Promise<void> {
|
|
this.emitStatusEvent(StatusEventPayload.WASM_LOADING);
|
|
try {
|
|
this.rlnInstance = await create();
|
|
} catch (error) {
|
|
console.error(
|
|
"Failed at fetching WASM and creating RLN instance: ",
|
|
error
|
|
);
|
|
this.emitStatusEvent(StatusEventPayload.WASM_FAILED);
|
|
throw error;
|
|
}
|
|
this.emitStatusEvent(StatusEventPayload.WASM_LOADED);
|
|
}
|
|
|
|
public async initRLNContract(rlnInstance: RLNInstance): Promise<void> {
|
|
if (this.rlnContract || !this.ethProvider) {
|
|
return;
|
|
}
|
|
|
|
this.emitStatusEvent(StatusEventPayload.CONTRACT_LOADING);
|
|
try {
|
|
this.rlnContract = await RLNContract.init(rlnInstance, {
|
|
registryAddress: SEPOLIA_CONTRACT.address,
|
|
provider: this.ethProvider.getSigner(),
|
|
});
|
|
} catch (error) {
|
|
console.error("Failed to connect to RLN contract: ", error);
|
|
this.emitStatusEvent(StatusEventPayload.CONTRACT_FAILED);
|
|
throw error;
|
|
}
|
|
this.emitStatusEvent(StatusEventPayload.RLN_INITIALIZED);
|
|
}
|
|
|
|
private initKeystore(): Keystore {
|
|
const localKeystoreString = localStorage.getItem("keystore");
|
|
|
|
if (!localKeystoreString) {
|
|
return Keystore.create();
|
|
}
|
|
|
|
try {
|
|
return Keystore.fromString(localKeystoreString || "") || Keystore.create();
|
|
} catch(error) {
|
|
return Keystore.create();
|
|
}
|
|
}
|
|
|
|
public addEventListener(name: RLNEventsNames, fn: EventListener) {
|
|
return this.emitter.addEventListener(name, fn as any);
|
|
}
|
|
|
|
public removeEventListener(name: RLNEventsNames, fn: EventListener) {
|
|
return this.emitter.removeEventListener(name, fn as any);
|
|
}
|
|
|
|
private emitStatusEvent(payload: StatusEventPayload) {
|
|
this.emitter.dispatchEvent(
|
|
new CustomEvent(RLNEventsNames.Status, { detail: payload })
|
|
);
|
|
}
|
|
|
|
private emitKeystoreKeys() {
|
|
const credentials = Object.keys(this.keystore.toObject().credentials || {});
|
|
this.emitter.dispatchEvent(
|
|
new CustomEvent(RLNEventsNames.Keystore, { detail: credentials })
|
|
);
|
|
}
|
|
|
|
public async saveKeystore() {
|
|
localStorage.setItem("keystore", this.keystore.toString());
|
|
this.emitKeystoreKeys();
|
|
}
|
|
|
|
public importKeystore(value: string) {
|
|
this.keystore = Keystore.fromString(value) || Keystore.create();
|
|
this.saveKeystore();
|
|
}
|
|
}
|
|
|
|
// Next.js sometimes executes code in server env where there is no window object
|
|
export const rln = typeof window === "undefined" ? undefined : new RLN();
|