diff --git a/rln-js/index.html b/rln-js/index.html index 63d5642..149a0e4 100644 --- a/rln-js/index.html +++ b/rln-js/index.html @@ -10,6 +10,14 @@

RLN

+

Wallet

+
+
+ +

RLN

+ +
+

You can either generate new credentials:


Or import existing ones:


@@ -34,6 +42,7 @@

Commitment

+
@@ -68,6 +77,8 @@ import {create, MembershipKey, RLNEncoder} from "https://unpkg.com/@waku/rln@0.0.8-964df01/bundle/index.js"; + import {ethers} from "https://unpkg.com/ethers@5.0.7/dist/ethers-all.esm.min.js" + // RLN Elements const generateCredsButton = document.getElementById('generate-credentials') const idDiv = document.getElementById('id'); @@ -77,11 +88,15 @@ const identityKeyInput = document.getElementById('id-key') const commitmentKeyInput = document.getElementById('commitment-key') const importButton = document.getElementById('import-button') + const connectWalletButton = document.getElementById('connect-wallet'); + const retrieveRLNDetailsButton = document.getElementById('retrieve-rln-details') + const registerButton = document.getElementById('register-button'); let rlnInstance; (async () => { rlnInstance = await create() generateCredsButton.disabled = false; + connectWalletButton.disabled = false; })(); // Waku Elements @@ -92,6 +107,7 @@ const textInput = document.getElementById('textInput'); const sendButton = document.getElementById('sendButton'); + const walletDiv = document.getElementById('wallet'); const messagesDiv = document.getElementById('messages') @@ -100,20 +116,25 @@ let encoder; let nodeConnected; let nick; + let retrievedRLNEvents = false; + const updateFields = () => { - if (membershipKey && membershipId) { + if (membershipKey) { keyDiv.innerHTML = utils.bytesToHex(membershipKey.IDKey) commitmentDiv.innerHTML = utils.bytesToHex(membershipKey.IDCommitment) - idDiv.innerHTML = membershipId + idDiv.innerHTML = membershipId || "-" - encoder = new RLNEncoder( - new EncoderV0(ContentTopic), - rlnInstance, - membershipId, - membershipKey - ); + + if (membershipId) { + encoder = new RLNEncoder( + new EncoderV0(ContentTopic), + rlnInstance, + membershipId, + membershipKey + ); + } } - + importButton.disabled = !(membershipIdInput.value && identityKeyInput.value && commitmentKeyInput.value); @@ -127,7 +148,7 @@ generateCredsButton.onclick = () => { membershipKey = rlnInstance.generateMembershipKey() - updateFields() + updateFields(); } membershipIdInput.onchange = updateFields; @@ -138,7 +159,7 @@ const idKey = utils.hexToBytes(identityKeyInput.value) const idCommitment = utils.hexToBytes(commitmentKeyInput.value) membershipKey = new MembershipKey(idKey, idCommitment) - membershipId = membershipIdInput.value; + membershipId = membershipIdInput.value ; updateFields() } @@ -176,6 +197,120 @@ updateMessages(messages, messagesDiv) } + const checkChain = async (chainId) => { + retrieveRLNDetailsButton.disabled = retrievedRLNEvents || chainId != 5; + registerButton.disabled = !(chainId == 5 && retrievedRLNEvents); + if(chainId != 5){ + alert("Switch to Goerli") + } + } + + const checkCurrentChain = async () => { + const network = await provider.getNetwork(); + checkChain(network.chainId); + } + + const rlnDeployBlk = 7677135; + const rlnAddress = "0x367F3e869cF2E1DD28644E308b42652cb6fcDA72"; + const rlnAbi = [ + "function MEMBERSHIP_DEPOSIT() public view returns(uint256)", + "function register(uint256 pubkey) external payable", + "function withdraw(uint256 secret, uint256 _pubkeyIndex, address payable receiver) external", + "event MemberRegistered(uint256 pubkey, uint256 index)", + "event MemberWithdrawn(uint256 pubkey, uint256 index)" + ]; + + const provider = new ethers.providers.Web3Provider(window.ethereum, "any"); + + checkCurrentChain(); + + let accounts; + let rlnContract; + + const handleMembership = (pubkey, index, event) => { + try { + const idCommitment = ethers.utils.zeroPad(ethers.utils.arrayify(pubkey), 32); + rlnInstance.insertMember(idCommitment); + console.debug("IDCommitment registered in tree", idCommitment, index.toNumber()); + } catch(err){ + alert(err); // TODO: the merkle tree can be in a wrong state. The app should be disabled + } + } + + const setAccounts = acc => { + accounts = acc; + walletDiv.innerHTML = accounts.length ? accounts[0] : ""; + } + + connectWalletButton.onclick = async () => { + try { + accounts = await provider.send("eth_requestAccounts", []); + setAccounts(accounts); + checkCurrentChain(); + } catch (e) { + ("No web3 provider available", e); + } + }; + + retrieveRLNDetailsButton.onclick = async () => { + rlnContract = new ethers.Contract(rlnAddress, rlnAbi, provider); + + const filter = rlnContract.filters.MemberRegistered() + + // populating merkle tree: + const alreadyRegisteredMembers = await rlnContract.queryFilter(filter, rlnDeployBlk) + alreadyRegisteredMembers.forEach(event => { + handleMembership(event.args.pubkey, event.args.index, event); + }); + + alert("Loaded all events from contract"); + retrievedRLNEvents = true; + // TODO: at this point it should be possible to register a credential and send messages + // TODO: emit an event to enable message form and register button + + retrieveRLNDetailsButton.disabled = true; + registerButton.disabled = false; + + // reacting to new registrations + rlnContract.on(filter, (pubkey, index, event) => { + handleMembership(event.args.pubkey, event.args.index, event); + }); + } + + window.ethereum.on('accountsChanged', setAccounts); + window.ethereum.on('chainChanged', chainId => { + checkChain(parseInt(chainId, 16)); + }); + + registerButton.onclick = async () => { + try { + registerButton.disabled = true; + + const pubkey = ethers.BigNumber.from(membershipKey.IDCommitment); + const price = await rlnContract.MEMBERSHIP_DEPOSIT(); + + const signer = provider.getSigner() + const rlnContractWithSigner = rlnContract.connect(signer); + + const txResponse = await rlnContractWithSigner.register(pubkey, {value: price}); + console.log("Transaction broadcasted:", txResponse); + + const txReceipt = await txResponse.wait(); + + console.log("Transaction receipt", txReceipt); + + // Update membershipId + membershipId = txReceipt.events[0].args.index.toNumber(); + console.log("Obtained index for current membership credentials", membershipId); + updateFields(); + registerButton.disabled = false; + } catch(err) { + alert(err); + registerButton.disabled = false; + } + + } + let node; (async () => { statusDiv.innerHTML = '

Creating Waku node.

';