From 8a2b4a4b1f1e08ea79ee31d6559de549dea17fcf Mon Sep 17 00:00:00 2001 From: Danish Arora Date: Tue, 8 Apr 2025 02:51:41 +0530 Subject: [PATCH] chore: show membership management methods --- package-lock.json | 56 +++---- package.json | 3 +- src/components/MembershipDetails.tsx | 145 +++++++++++++++++- .../Tabs/KeystoreTab/KeystoreManagement.tsx | 1 + src/contexts/rln/RLNContext.tsx | 57 +++---- 5 files changed, 192 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index c831882..c735cb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", - "@waku/rln": "0.1.5-731214b.0", + "@waku/rln": "0.1.5-35b50c3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.6.3", @@ -3384,16 +3384,16 @@ ] }, "node_modules/@waku/core": { - "version": "0.0.35-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-731214b.0.tgz", - "integrity": "sha512-yfNKTZHjjYtGw6/SEuXwzeyAhfhCyBPk5TPBuqgfpnXa9mQZmggBQpK7cHwiX0qYYLQG0sM6SL1qqGnqxJGKwA==", + "version": "0.0.35-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-35b50c3.0.tgz", + "integrity": "sha512-seYdY5sj/5fq7pGLYHHLaXCW9ZFtxcYJPbFiNm+2ZY+WhriUPtOT2ZXQ3SN99fUzfAUa5nZbLt+QHwaMuMqbIw==", "license": "MIT OR Apache-2.0", "dependencies": { "@libp2p/ping": "2.0.1", - "@waku/enr": "0.0.29-731214b.0", - "@waku/interfaces": "0.0.30-731214b.0", - "@waku/proto": "0.0.10-731214b.0", - "@waku/utils": "0.0.23-731214b.0", + "@waku/enr": "0.0.29-35b50c3.0", + "@waku/interfaces": "0.0.30-35b50c3.0", + "@waku/proto": "0.0.10-35b50c3.0", + "@waku/utils": "0.0.23-35b50c3.0", "debug": "^4.3.4", "it-all": "^3.0.4", "it-length-prefixed": "^9.0.4", @@ -3431,9 +3431,9 @@ } }, "node_modules/@waku/enr": { - "version": "0.0.29-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.29-731214b.0.tgz", - "integrity": "sha512-A/rULGlnwC7Dgz21WNsFGQX2Ff5NRQHmrpkY3vnZZmXWngRfm/8QaoZd0USsI3w/K8VMW+9YxTwuKynK4bKLEg==", + "version": "0.0.29-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.29-35b50c3.0.tgz", + "integrity": "sha512-vCtiU+OAFiBFsPWOe+/7fDzngJJeLTjSAwbF37GEaX8yj5eSdxuhgccaHevPiWiRMDPfw9hBfh/C3N+9TYtl/w==", "license": "MIT OR Apache-2.0", "dependencies": { "@ethersproject/rlp": "^5.7.0", @@ -3441,7 +3441,7 @@ "@libp2p/peer-id": "^5.0.1", "@multiformats/multiaddr": "^12.0.0", "@noble/secp256k1": "^1.7.1", - "@waku/utils": "0.0.23-731214b.0", + "@waku/utils": "0.0.23-35b50c3.0", "debug": "^4.3.4", "js-sha3": "^0.9.2" }, @@ -3458,21 +3458,21 @@ } }, "node_modules/@waku/interfaces": { - "version": "0.0.30-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.30-731214b.0.tgz", - "integrity": "sha512-9TnlIAKHmAaT+C9fd/UE7KN8XU3Do/Ma1az8Q2C584heCin3vVPHYSU6KggA3czBpwq8lA34DecjdpRwvpd8Fg==", + "version": "0.0.30-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.30-35b50c3.0.tgz", + "integrity": "sha512-rjLNYuNDU8SgsiT6pJF0qp871CHIS2Y3tNq8fXWz4eY7nIZajcJQdOoacG1SdqsswyTpSjk8eP4ERDAYwVjbAg==", "license": "MIT OR Apache-2.0", "dependencies": { - "@waku/proto": "0.0.10-731214b.0" + "@waku/proto": "0.0.10-35b50c3.0" }, "engines": { "node": ">=20" } }, "node_modules/@waku/proto": { - "version": "0.0.10-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.10-731214b.0.tgz", - "integrity": "sha512-/fwe8Hy6CefvD+t0D5aBuSBIRy70/rNzglNQAnePzSDwooE7ox9Hpe++O/mquKLyR49+x8+B3VrScgMy5u6aFQ==", + "version": "0.0.10-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.10-35b50c3.0.tgz", + "integrity": "sha512-ErSJYWgbVbpDfu5hcXkZBoz/vd6X3H98NEzNHSPjoj0J1RxvbXV7fPExPXh7FjAK8cePS0xi2tOBQDIbMDOkOw==", "license": "MIT OR Apache-2.0", "dependencies": { "protons-runtime": "^5.4.0" @@ -3482,15 +3482,15 @@ } }, "node_modules/@waku/rln": { - "version": "0.1.5-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/rln/-/rln-0.1.5-731214b.0.tgz", - "integrity": "sha512-n7FIsfNN26z+u7xVKoJyVxZvu4N3ZOIP69ptHfW3U5XrkEI8CC9rP1ajgriFOF4LLXR9fNJB9D5aGLOh9vgWhQ==", + "version": "0.1.5-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/rln/-/rln-0.1.5-35b50c3.0.tgz", + "integrity": "sha512-DCg7LIOT7HD0pc7jRBK98k8qitaGONYGOuTFLlYn4pgm3UHce/N90t9ATMyz7YUVObj1D6Z6hfjkwMU8LcZpTw==", "license": "MIT OR Apache-2.0", "dependencies": { "@chainsafe/bls-keystore": "3.0.0", "@noble/hashes": "^1.2.0", - "@waku/core": "0.0.35-731214b.0", - "@waku/utils": "0.0.23-731214b.0", + "@waku/core": "0.0.35-35b50c3.0", + "@waku/utils": "0.0.23-35b50c3.0", "@waku/zerokit-rln-wasm": "^0.0.13", "chai": "^5.1.2", "chai-as-promised": "^8.0.1", @@ -3507,13 +3507,13 @@ } }, "node_modules/@waku/utils": { - "version": "0.0.23-731214b.0", - "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.23-731214b.0.tgz", - "integrity": "sha512-XPxWwkKscYBiDk70jPwet6YR/xH+I0tfdmaj/meWODz03/+taNtTvaKSispnEIBcQ6loLNcM+5B1E72eG7et5g==", + "version": "0.0.23-35b50c3.0", + "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.23-35b50c3.0.tgz", + "integrity": "sha512-0ter2UjLimJ+btXaSD/+NM7P1uzcikBIem5fXZ05RwgOwHrzirlhSORXwmGSjSSf6sGmM612/zrVffzDh83WEw==", "license": "MIT OR Apache-2.0", "dependencies": { "@noble/hashes": "^1.3.2", - "@waku/interfaces": "0.0.30-731214b.0", + "@waku/interfaces": "0.0.30-35b50c3.0", "chai": "^4.3.10", "debug": "^4.3.4", "uint8arrays": "^5.0.1" diff --git a/package.json b/package.json index 1c966a9..ce7fe58 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "dependencies": { "@fontsource-variable/inter": "^5.2.5", "@fontsource-variable/jetbrains-mono": "^5.2.5", - "@next/font": "^14.2.15", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-slider": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", @@ -20,7 +19,7 @@ "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", - "@waku/rln": "0.1.5-731214b.0", + "@waku/rln": "0.1.5-35b50c3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.6.3", diff --git a/src/components/MembershipDetails.tsx b/src/components/MembershipDetails.tsx index a416dd6..5a62d63 100644 --- a/src/components/MembershipDetails.tsx +++ b/src/components/MembershipDetails.tsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Button } from './ui/button'; -import { Copy } from 'lucide-react'; +import { Copy, Clock, Trash2, Wallet } from 'lucide-react'; import { ethers } from 'ethers'; import { MembershipState } from '@waku/rln'; +import { useRLN } from '../contexts/rln/RLNContext'; +import { toast } from 'sonner'; interface MembershipDetailsProps { membershipInfo: { @@ -21,17 +23,144 @@ interface MembershipDetailsProps { token: string; }; copyToClipboard: (text: string) => void; + hash: string; } -export function MembershipDetails({ membershipInfo, copyToClipboard }: MembershipDetailsProps) { +export function MembershipDetails({ membershipInfo, copyToClipboard, hash }: MembershipDetailsProps) { + const { extendMembership, eraseMembership, withdrawDeposit } = useRLN(); + const [isLoading, setIsLoading] = useState<{[key: string]: boolean}>({}); + const [password, setPassword] = useState(''); + const [showPasswordInput, setShowPasswordInput] = useState(false); + const [actionType, setActionType] = useState<'extend' | 'erase' | 'withdraw' | null>(null); + + const handleAction = async (type: 'extend' | 'erase' | 'withdraw') => { + if (!password) { + setActionType(type); + setShowPasswordInput(true); + return; + } + + setIsLoading(prev => ({ ...prev, [type]: true })); + try { + let result; + switch (type) { + case 'extend': + result = await extendMembership(hash, password); + break; + case 'erase': + result = await eraseMembership(hash, password); + break; + case 'withdraw': + result = await withdrawDeposit(hash, password); + break; + } + + if (result.success) { + toast.success(`Successfully ${type}ed membership`); + setPassword(''); + setShowPasswordInput(false); + setActionType(null); + } else { + toast.error(result.error || `Failed to ${type} membership`); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : `Failed to ${type} membership`); + } finally { + setIsLoading(prev => ({ ...prev, [type]: false })); + } + }; + + // Check if membership is in grace period + const isInGracePeriod = membershipInfo.state === MembershipState.GracePeriod; + + // Check if membership is erased and awaiting withdrawal + const canWithdraw = membershipInfo.state === MembershipState.ErasedAwaitsWithdrawal; + + // Check if membership can be erased (Active or GracePeriod) + const canErase = membershipInfo.state === MembershipState.Active || membershipInfo.state === MembershipState.GracePeriod; + return (
-
- {">"} -

- Membership Details -

+
+
+ {">"} +

+ Membership Details +

+
+
+ {isInGracePeriod && ( + + )} + {canErase && ( + + )} + {canWithdraw && ( + + )} +
+ + {showPasswordInput && ( +
+ setPassword(e.target.value)} + placeholder="Enter keystore password" + className="w-full px-3 py-2 border border-terminal-border rounded-md bg-terminal-background text-foreground font-mono focus:ring-1 focus:ring-accent focus:border-accent text-sm" + /> +
+ + +
+
+ )} +
{/* Membership State */} diff --git a/src/components/Tabs/KeystoreTab/KeystoreManagement.tsx b/src/components/Tabs/KeystoreTab/KeystoreManagement.tsx index 97658d5..f395ba1 100644 --- a/src/components/Tabs/KeystoreTab/KeystoreManagement.tsx +++ b/src/components/Tabs/KeystoreTab/KeystoreManagement.tsx @@ -334,6 +334,7 @@ export function KeystoreManagement() {
)} diff --git a/src/contexts/rln/RLNContext.tsx b/src/contexts/rln/RLNContext.tsx index fceb33b..24f6839 100644 --- a/src/contexts/rln/RLNContext.tsx +++ b/src/contexts/rln/RLNContext.tsx @@ -54,7 +54,7 @@ export function RLNProvider({ children }: { children: ReactNode }) { const [rateMaxLimit, setRateMaxLimit] = useState(0); const { saveCredentials: saveToKeystore, getDecryptedCredential } = useKeystore(); - + // Listen for wallet connection useEffect(() => { const checkWallet = async () => { @@ -359,33 +359,31 @@ export function RLNProvider({ children }: { children: ReactNode }) { const getMembershipInfo = async (hash: string, password: string) => { - if (!rln || !rln.contract) { - throw new Error('RLN not initialized or contract not available'); - } + if (!rln || !rln.contract) { + throw new Error('RLN not initialized or contract not available'); + } - const credential = await getDecryptedCredential(hash, password); - if (!credential) { - throw new Error('Could not decrypt credential'); - } + const credential = await getDecryptedCredential(hash, password); + if (!credential) { + throw new Error('Could not decrypt credential'); + } - - try { - const membershipInfo = await rln.contract.getMembershipInfo(credential.identity.IDCommitmentBigInt); - if (!membershipInfo) { - throw new Error('Could not fetch membership info'); - } - return { - ...membershipInfo, - address: rln.contract.address, - chainId: LINEA_SEPOLIA_CONFIG.chainId.toString(), - treeIndex: Number(membershipInfo.index.toString()), - rateLimit: Number(membershipInfo.rateLimit.toString()) - } - } catch (error) { - console.log("error", error); - throw error; + try { + const membershipInfo = await rln.contract.getMembershipInfo(credential.identity.IDCommitmentBigInt); + if (!membershipInfo) { + throw new Error('Could not fetch membership info'); } - + return { + ...membershipInfo, + address: rln.contract.address, + chainId: LINEA_SEPOLIA_CONFIG.chainId.toString(), + treeIndex: Number(membershipInfo.index.toString()), + rateLimit: Number(membershipInfo.rateLimit.toString()) + } + } catch (error) { + console.log("error", error); + throw error; + } }; const extendMembership = async (hash: string, password: string) => { @@ -399,10 +397,7 @@ export function RLNProvider({ children }: { children: ReactNode }) { throw new Error('Could not decrypt credential'); } - // Convert IDCommitment to hex string - const idCommitmentHex = ethers.utils.hexlify(credential.identity.IDCommitment); - - await rln.contract.extendMembership(idCommitmentHex); + await rln.contract.extendMembership(credential.identity.IDCommitmentBigInt); return { success: true }; } catch (err) { console.error('Error extending membership:', err); @@ -424,10 +419,8 @@ export function RLNProvider({ children }: { children: ReactNode }) { throw new Error('Could not decrypt credential'); } - // Convert IDCommitment to hex string - const idCommitmentHex = ethers.utils.hexlify(credential.identity.IDCommitment); - await rln.contract.eraseMembership(idCommitmentHex); + await rln.contract.eraseMembership(credential.identity.IDCommitmentBigInt); return { success: true }; } catch (err) { console.error('Error erasing membership:', err);