mirror of
https://github.com/logos-messaging/rln.waku.org.git
synced 2026-01-02 14:13:09 +00:00
chore: show membership management methods
This commit is contained in:
parent
eadc45f57e
commit
8a2b4a4b1f
56
package-lock.json
generated
56
package-lock.json
generated
@ -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"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 (
|
||||
<div className="mt-3 space-y-2 border-t border-terminal-border/40 pt-3 animate-in fade-in-50 duration-300">
|
||||
<div className="flex items-center mb-2">
|
||||
<span className="text-primary font-mono font-medium mr-2">{">"}</span>
|
||||
<h3 className="text-sm font-mono font-semibold text-primary">
|
||||
Membership Details
|
||||
</h3>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center">
|
||||
<span className="text-primary font-mono font-medium mr-2">{">"}</span>
|
||||
<h3 className="text-sm font-mono font-semibold text-primary">
|
||||
Membership Details
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
{isInGracePeriod && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-warning-DEFAULT hover:text-warning-DEFAULT hover:border-warning-DEFAULT flex items-center gap-1"
|
||||
onClick={() => handleAction('extend')}
|
||||
disabled={isLoading.extend}
|
||||
>
|
||||
<Clock className="w-3 h-3" />
|
||||
<span>{isLoading.extend ? 'Extending...' : 'Extend'}</span>
|
||||
</Button>
|
||||
)}
|
||||
{canErase && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive hover:border-destructive flex items-center gap-1"
|
||||
onClick={() => handleAction('erase')}
|
||||
disabled={isLoading.erase}
|
||||
>
|
||||
<Trash2 className="w-3 h-3" />
|
||||
<span>{isLoading.erase ? 'Erasing...' : 'Erase'}</span>
|
||||
</Button>
|
||||
)}
|
||||
{canWithdraw && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-accent hover:text-accent hover:border-accent flex items-center gap-1"
|
||||
onClick={() => handleAction('withdraw')}
|
||||
disabled={isLoading.withdraw}
|
||||
>
|
||||
<Wallet className="w-3 h-3" />
|
||||
<span>{isLoading.withdraw ? 'Withdrawing...' : 'Withdraw'}</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showPasswordInput && (
|
||||
<div className="mb-4 space-y-2 border-b border-terminal-border pb-4">
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={() => handleAction(actionType!)}
|
||||
disabled={!password || isLoading[actionType!]}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setShowPasswordInput(false);
|
||||
setPassword('');
|
||||
setActionType(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2 text-xs font-mono">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Membership State */}
|
||||
|
||||
@ -334,6 +334,7 @@ export function KeystoreManagement() {
|
||||
<MembershipDetails
|
||||
membershipInfo={membershipInfo}
|
||||
copyToClipboard={copyToClipboard}
|
||||
hash={hash}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -54,7 +54,7 @@ export function RLNProvider({ children }: { children: ReactNode }) {
|
||||
const [rateMaxLimit, setRateMaxLimit] = useState<number>(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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user