mirror of
https://github.com/logos-messaging/lab.waku.org.git
synced 2026-01-06 07:43:07 +00:00
chore(keystore-management): upgrade to new API (#125)
This commit is contained in:
parent
1700af13b0
commit
b3b261fc26
56
examples/keystore-management/package-lock.json
generated
56
examples/keystore-management/package-lock.json
generated
@ -8,7 +8,7 @@
|
|||||||
"name": "waku-keystore-management",
|
"name": "waku-keystore-management",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/rln": "0.1.5-ad0e277.0",
|
"@waku/rln": "0.1.5-6997987.0",
|
||||||
"next": "15.1.7",
|
"next": "15.1.7",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
@ -2309,16 +2309,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/core": {
|
"node_modules/@waku/core": {
|
||||||
"version": "0.0.35-ad0e277.0",
|
"version": "0.0.35-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-6997987.0.tgz",
|
||||||
"integrity": "sha512-IfgWE/Kc8jpcmO6PGsLzySHAWwoDQtIY9gsxWtoqPnll1cE9ylxfGVh0je4o58E5F0XGw+wm9TdD149hq30cJQ==",
|
"integrity": "sha512-rdTBFpaoJr9PClZWcnazRu2bydIjj1MnG31espjWNe072vepEAMZln48ZtPqtjIn1pfr5Dn0QOL9blVDdGIAkw==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libp2p/ping": "2.0.1",
|
"@libp2p/ping": "2.0.1",
|
||||||
"@waku/enr": "0.0.29-ad0e277.0",
|
"@waku/enr": "0.0.29-6997987.0",
|
||||||
"@waku/interfaces": "0.0.30-ad0e277.0",
|
"@waku/interfaces": "0.0.30-6997987.0",
|
||||||
"@waku/proto": "0.0.10-ad0e277.0",
|
"@waku/proto": "0.0.10-6997987.0",
|
||||||
"@waku/utils": "0.0.23-ad0e277.0",
|
"@waku/utils": "0.0.23-6997987.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"it-all": "^3.0.4",
|
"it-all": "^3.0.4",
|
||||||
"it-length-prefixed": "^9.0.4",
|
"it-length-prefixed": "^9.0.4",
|
||||||
@ -2356,9 +2356,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/enr": {
|
"node_modules/@waku/enr": {
|
||||||
"version": "0.0.29-ad0e277.0",
|
"version": "0.0.29-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.29-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.29-6997987.0.tgz",
|
||||||
"integrity": "sha512-3IoFVU3XX7rEge3VFMl5r3wc0XVnZTvheRuFYSm0HgPrMls23PmAd7IdFLOMq5e9CGWAbWx0fdT3StzIdN2PbQ==",
|
"integrity": "sha512-w3ECNF1LuZ1Jwki3aeu9VJv2oGW0xXsgn8pgszGjFk0ydHPlJY9gbuvTK2xx+o+vedRaiAXWBXwOY5g7GRc9lg==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/rlp": "^5.7.0",
|
"@ethersproject/rlp": "^5.7.0",
|
||||||
@ -2366,7 +2366,7 @@
|
|||||||
"@libp2p/peer-id": "^5.0.1",
|
"@libp2p/peer-id": "^5.0.1",
|
||||||
"@multiformats/multiaddr": "^12.0.0",
|
"@multiformats/multiaddr": "^12.0.0",
|
||||||
"@noble/secp256k1": "^1.7.1",
|
"@noble/secp256k1": "^1.7.1",
|
||||||
"@waku/utils": "0.0.23-ad0e277.0",
|
"@waku/utils": "0.0.23-6997987.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"js-sha3": "^0.9.2"
|
"js-sha3": "^0.9.2"
|
||||||
},
|
},
|
||||||
@ -2383,21 +2383,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/interfaces": {
|
"node_modules/@waku/interfaces": {
|
||||||
"version": "0.0.30-ad0e277.0",
|
"version": "0.0.30-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.30-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.30-6997987.0.tgz",
|
||||||
"integrity": "sha512-Xg0vupz9y+PGNKVsJ3AodpcxWLkGafLPser4H/0SYvwiBKR0doEs08wfRdXY83WID1GZPVkA3jcIm0rAnPZWSw==",
|
"integrity": "sha512-df526iM92hkW2Fj2zrn+iDQp+xfhDx2EqGbUwaK/G52oK5cyh77a/AtaBEFIOE301i1Ur0TAi4KEivryfKYfwg==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/proto": "0.0.10-ad0e277.0"
|
"@waku/proto": "0.0.10-6997987.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/proto": {
|
"node_modules/@waku/proto": {
|
||||||
"version": "0.0.10-ad0e277.0",
|
"version": "0.0.10-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.10-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.10-6997987.0.tgz",
|
||||||
"integrity": "sha512-F4RKTcdX3v+E3/TxdINPWpsQEjst1VXlslJdNXC1LXPRqd87S7r2e47PXafM7TGBdOitIIPw8Xf4MxbPFGa1Gg==",
|
"integrity": "sha512-OtM6cCFNlc8fcSj4t7Wcz2HEAcqDHh3cXZI4TLsvXk7Xm0/bJ5AR6KdPvmFdWNUWvMOGLkw6rVkXMsFTsgcN3A==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"protons-runtime": "^5.4.0"
|
"protons-runtime": "^5.4.0"
|
||||||
@ -2407,15 +2407,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/rln": {
|
"node_modules/@waku/rln": {
|
||||||
"version": "0.1.5-ad0e277.0",
|
"version": "0.1.5-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/rln/-/rln-0.1.5-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/rln/-/rln-0.1.5-6997987.0.tgz",
|
||||||
"integrity": "sha512-2QxvEhZoZjsP0J8wg6BAv7GTvDMeOzD7a46WR6M3Gld39TNf+2hQMzED5I91QxjcoAvMMbphD8FiH4X5tqzM5g==",
|
"integrity": "sha512-lEoVyR2LdNIjo/VFeoBFKgDHspAoBQTdECLTIu9oOZ9iJLvT5VWX/PVGAoQ55yNteTu56Oivl9fkzDmITfIMbg==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chainsafe/bls-keystore": "3.0.0",
|
"@chainsafe/bls-keystore": "3.0.0",
|
||||||
"@noble/hashes": "^1.2.0",
|
"@noble/hashes": "^1.2.0",
|
||||||
"@waku/core": "0.0.35-ad0e277.0",
|
"@waku/core": "0.0.35-6997987.0",
|
||||||
"@waku/utils": "0.0.23-ad0e277.0",
|
"@waku/utils": "0.0.23-6997987.0",
|
||||||
"@waku/zerokit-rln-wasm": "^0.0.13",
|
"@waku/zerokit-rln-wasm": "^0.0.13",
|
||||||
"chai": "^5.1.2",
|
"chai": "^5.1.2",
|
||||||
"chai-as-promised": "^8.0.1",
|
"chai-as-promised": "^8.0.1",
|
||||||
@ -2432,13 +2432,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@waku/utils": {
|
"node_modules/@waku/utils": {
|
||||||
"version": "0.0.23-ad0e277.0",
|
"version": "0.0.23-6997987.0",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.23-ad0e277.0.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.23-6997987.0.tgz",
|
||||||
"integrity": "sha512-r3nef/L4fZx2GA7byUAOFLSjhVXuH22FFgmEp7+/jzbAjKAcVIaCfG2M09SnNDbxc5uViKeEE6ODIfw+k+2rZw==",
|
"integrity": "sha512-ExVLIpaBnjbGNjOl6JLTWMj0rBvqvK0kW/MhqtPue4i6l+iKVytdBAsq7KDT9V/DN1MyXpSVd8kptOPh5+y8iA==",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.3.2",
|
"@noble/hashes": "^1.3.2",
|
||||||
"@waku/interfaces": "0.0.30-ad0e277.0",
|
"@waku/interfaces": "0.0.30-6997987.0",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.3.10",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"uint8arrays": "^5.0.1"
|
"uint8arrays": "^5.0.1"
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/rln": "0.1.5-ad0e277.0",
|
"@waku/rln": "0.1.5-6997987.0",
|
||||||
"next": "15.1.7",
|
"next": "15.1.7",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useKeystore } from '../contexts/KeystoreContext';
|
|
||||||
import { saveKeystoreToFile, readKeystoreFromFile } from '../utils/fileUtils';
|
|
||||||
|
|
||||||
export default function KeystoreManager() {
|
|
||||||
const {
|
|
||||||
isInitialized: isKeystoreInitialized,
|
|
||||||
hasStoredCredentials,
|
|
||||||
storedCredentialsHashes,
|
|
||||||
exportKeystore,
|
|
||||||
importKeystore
|
|
||||||
} = useKeystore();
|
|
||||||
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const handleExport = () => {
|
|
||||||
try {
|
|
||||||
const keystoreJson = exportKeystore();
|
|
||||||
saveKeystoreToFile(keystoreJson);
|
|
||||||
setSuccessMessage('Keystore exported successfully');
|
|
||||||
setTimeout(() => setSuccessMessage(null), 3000);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : 'Failed to export keystore');
|
|
||||||
setTimeout(() => setError(null), 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleImport = async () => {
|
|
||||||
try {
|
|
||||||
const keystoreJson = await readKeystoreFromFile();
|
|
||||||
const success = importKeystore(keystoreJson);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
setSuccessMessage('Keystore imported successfully');
|
|
||||||
} else {
|
|
||||||
setError('Failed to import keystore');
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setSuccessMessage(null);
|
|
||||||
setError(null);
|
|
||||||
}, 3000);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : 'Failed to import keystore');
|
|
||||||
setTimeout(() => setError(null), 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isKeystoreInitialized) {
|
|
||||||
return (
|
|
||||||
<div className="p-4 bg-gray-100 dark:bg-gray-700 rounded-lg">
|
|
||||||
<p className="text-gray-700 dark:text-gray-300">Initializing keystore...</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
||||||
<h2 className="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Keystore Management</h2>
|
|
||||||
|
|
||||||
{/* Status */}
|
|
||||||
<div className="mb-4 p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
|
||||||
<p className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
<span className="font-semibold">Status:</span> {hasStoredCredentials ? 'Credentials found' : 'No credentials stored'}
|
|
||||||
</p>
|
|
||||||
{hasStoredCredentials && (
|
|
||||||
<p className="text-sm text-gray-700 dark:text-gray-300 mt-1">
|
|
||||||
<span className="font-semibold">Stored credentials:</span> {storedCredentialsHashes.length}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Notifications */}
|
|
||||||
{error && (
|
|
||||||
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900 rounded-lg">
|
|
||||||
<p className="text-sm text-red-700 dark:text-red-300">{error}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{successMessage && (
|
|
||||||
<div className="mb-4 p-3 bg-green-50 dark:bg-green-900 rounded-lg">
|
|
||||||
<p className="text-sm text-green-700 dark:text-green-300">{successMessage}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Import/Export Buttons */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* Export Keystore */}
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={handleExport}
|
|
||||||
disabled={!hasStoredCredentials}
|
|
||||||
className={`w-full py-2 px-4 rounded ${
|
|
||||||
!hasStoredCredentials
|
|
||||||
? 'bg-gray-300 text-gray-500 cursor-not-allowed dark:bg-gray-600 dark:text-gray-400'
|
|
||||||
: 'bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800 dark:bg-blue-700 dark:hover:bg-blue-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Export Keystore
|
|
||||||
</button>
|
|
||||||
{!hasStoredCredentials && (
|
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
|
||||||
No credentials to export
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Import Keystore */}
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={handleImport}
|
|
||||||
className="w-full py-2 px-4 bg-green-600 text-white rounded hover:bg-green-700 active:bg-green-800 dark:bg-green-700 dark:hover:bg-green-800"
|
|
||||||
>
|
|
||||||
Import Keystore
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { Children, isValidElement } from 'react';
|
import React, { Children, isValidElement } from 'react';
|
||||||
import { TabNavigation, TabItem } from './Tabs/TabNavigation';
|
import { TabItem, TabNavigation } from './Tabs/TabNavigation';
|
||||||
import { useAppState } from '../contexts/AppStateContext';
|
import { useAppState } from '@/contexts/AppStateContext';
|
||||||
|
|
||||||
const tabs: TabItem[] = [
|
const tabs: TabItem [] = [
|
||||||
{
|
{
|
||||||
id: 'membership',
|
id: 'membership',
|
||||||
label: 'Membership Registration',
|
label: 'Membership Registration',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useRLNImplementation } from '@/contexts';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRLNImplementation } from '../contexts/rln';
|
|
||||||
|
|
||||||
export function RLNImplementationToggle() {
|
export function RLNImplementationToggle() {
|
||||||
const { implementation, setImplementation } = useRLNImplementation();
|
const { implementation, setImplementation } = useRLNImplementation();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useRLN } from '@/contexts';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRLN } from '../contexts/rln';
|
|
||||||
|
|
||||||
export function RLNInitButton() {
|
export function RLNInitButton() {
|
||||||
const { initializeRLN, isInitialized, isStarted, error, isLoading } = useRLN();
|
const { initializeRLN, isInitialized, isStarted, error, isLoading } = useRLN();
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useKeystore } from '@/contexts/keystore';
|
import { useKeystore } from '@/contexts/keystore';
|
||||||
import { useRLN } from '@/contexts/rln';
|
import { useRLN } from '@/contexts/rln';
|
||||||
import { saveKeystoreToFile, readKeystoreFromFile } from '../../../utils/fileUtils';
|
import { saveKeystoreToFile, readKeystoreFromFile } from '../../../utils/file';
|
||||||
import { KeystoreEntity } from '@waku/rln';
|
import { DecryptedCredentials } from '@waku/rln';
|
||||||
import { useAppState } from '@/contexts/AppStateContext';
|
import { useAppState } from '@/contexts/AppStateContext';
|
||||||
|
|
||||||
export function KeystoreManagement() {
|
export function KeystoreManagement() {
|
||||||
@ -23,7 +23,7 @@ export function KeystoreManagement() {
|
|||||||
const [selectedCredential, setSelectedCredential] = useState<string | null>(null);
|
const [selectedCredential, setSelectedCredential] = useState<string | null>(null);
|
||||||
const [viewPassword, setViewPassword] = useState<string>('');
|
const [viewPassword, setViewPassword] = useState<string>('');
|
||||||
const [viewingCredential, setViewingCredential] = useState<string | null>(null);
|
const [viewingCredential, setViewingCredential] = useState<string | null>(null);
|
||||||
const [decryptedInfo, setDecryptedInfo] = useState<KeystoreEntity | null>(null);
|
const [decryptedInfo, setDecryptedInfo] = useState<DecryptedCredentials | null>(null);
|
||||||
const [isDecrypting, setIsDecrypting] = useState(false);
|
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -206,7 +206,38 @@ export function KeystoreManagement() {
|
|||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<pre className="text-xs text-gray-600 dark:text-gray-400 whitespace-pre-wrap bg-gray-100 dark:bg-gray-800 p-2 rounded overflow-auto max-h-60">
|
<pre className="text-xs text-gray-600 dark:text-gray-400 whitespace-pre-wrap bg-gray-100 dark:bg-gray-800 p-2 rounded overflow-auto max-h-60">
|
||||||
{JSON.stringify(decryptedInfo, null, 2)}
|
<div className="grid grid-cols-1 gap-1">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">ID Commitment:</span>
|
||||||
|
<span className="break-all">{decryptedInfo.identity.IDCommitment}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">ID Nullifier:</span>
|
||||||
|
<span className="break-all">{decryptedInfo.identity.IDNullifier}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">ID Secret Hash:</span>
|
||||||
|
<span className="break-all">{decryptedInfo.identity.IDSecretHash}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">Membership Address:</span>
|
||||||
|
<span className="break-all">{decryptedInfo.membership.address}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">Chain ID:</span>
|
||||||
|
<span>{decryptedInfo.membership.chainId}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">Tree Index:</span>
|
||||||
|
<span>{decryptedInfo.membership.treeIndex}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold">Rate Limit:</span>
|
||||||
|
<span>{decryptedInfo.membership.rateLimit}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* {JSON.stringify(decryptedInfo, null, 2)} */}
|
||||||
</pre>
|
</pre>
|
||||||
<button
|
<button
|
||||||
onClick={() => setDecryptedInfo(null)}
|
onClick={() => setDecryptedInfo(null)}
|
||||||
|
|||||||
@ -1,15 +1,3 @@
|
|||||||
// Re-export wallet context
|
|
||||||
export { WalletProvider, useWallet } from './wallet';
|
export { WalletProvider, useWallet } from './wallet';
|
||||||
|
|
||||||
// Re-export keystore context
|
|
||||||
export { KeystoreProvider, useKeystore } from './keystore';
|
export { KeystoreProvider, useKeystore } from './keystore';
|
||||||
|
export { RLNImplementationProvider, useRLNImplementation, type RLNImplementationType, RLNProvider, useRLN } from './rln';
|
||||||
// Re-export RLN contexts
|
|
||||||
export {
|
|
||||||
RLNImplementationProvider,
|
|
||||||
useRLNImplementation,
|
|
||||||
type RLNImplementationType,
|
|
||||||
RLNProvider,
|
|
||||||
type UnifiedRLNInstance,
|
|
||||||
useRLN
|
|
||||||
} from './rln';
|
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||||
import { Keystore, KeystoreEntity } from '@waku/rln';
|
import { Keystore, KeystoreEntity } from '@waku/rln';
|
||||||
|
|
||||||
// Define types for the context
|
export const LOCAL_STORAGE_KEYSTORE_KEY = 'waku-rln-keystore';
|
||||||
|
|
||||||
interface KeystoreContextType {
|
interface KeystoreContextType {
|
||||||
keystore: Keystore | null;
|
keystore: Keystore | null;
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
@ -17,10 +18,8 @@ interface KeystoreContextType {
|
|||||||
getDecryptedCredential: (hash: string, password: string) => Promise<KeystoreEntity | null>;
|
getDecryptedCredential: (hash: string, password: string) => Promise<KeystoreEntity | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context
|
|
||||||
const KeystoreContext = createContext<KeystoreContextType | undefined>(undefined);
|
const KeystoreContext = createContext<KeystoreContextType | undefined>(undefined);
|
||||||
|
|
||||||
// Provider component
|
|
||||||
export function KeystoreProvider({ children }: { children: ReactNode }) {
|
export function KeystoreProvider({ children }: { children: ReactNode }) {
|
||||||
const [keystore, setKeystore] = useState<Keystore | null>(null);
|
const [keystore, setKeystore] = useState<Keystore | null>(null);
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
@ -30,7 +29,7 @@ export function KeystoreProvider({ children }: { children: ReactNode }) {
|
|||||||
// Initialize keystore
|
// Initialize keystore
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const storedKeystore = localStorage.getItem('waku-rln-keystore');
|
const storedKeystore = localStorage.getItem(LOCAL_STORAGE_KEYSTORE_KEY);
|
||||||
let keystoreInstance: Keystore;
|
let keystoreInstance: Keystore;
|
||||||
|
|
||||||
if (storedKeystore) {
|
if (storedKeystore) {
|
||||||
@ -57,7 +56,7 @@ export function KeystoreProvider({ children }: { children: ReactNode }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (keystore && isInitialized) {
|
if (keystore && isInitialized) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('waku-rln-keystore', keystore.toString());
|
localStorage.setItem(LOCAL_STORAGE_KEYSTORE_KEY, keystore.toString());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Could not save keystore to localStorage:", err);
|
console.warn("Could not save keystore to localStorage:", err);
|
||||||
}
|
}
|
||||||
@ -72,7 +71,7 @@ export function KeystoreProvider({ children }: { children: ReactNode }) {
|
|||||||
try {
|
try {
|
||||||
const hash = await keystore.addCredential(credentials, password);
|
const hash = await keystore.addCredential(credentials, password);
|
||||||
|
|
||||||
localStorage.setItem('waku-rln-keystore', keystore.toString());
|
localStorage.setItem(LOCAL_STORAGE_KEYSTORE_KEY, keystore.toString());
|
||||||
|
|
||||||
setStoredCredentialsHashes(keystore.keys());
|
setStoredCredentialsHashes(keystore.keys());
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ export function KeystoreProvider({ children }: { children: ReactNode }) {
|
|||||||
if (imported) {
|
if (imported) {
|
||||||
setKeystore(imported);
|
setKeystore(imported);
|
||||||
setStoredCredentialsHashes(imported.keys());
|
setStoredCredentialsHashes(imported.keys());
|
||||||
localStorage.setItem('waku-rln-keystore', keystoreJson);
|
localStorage.setItem(LOCAL_STORAGE_KEYSTORE_KEY, keystoreJson);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -142,7 +141,7 @@ export function KeystoreProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
keystore.removeCredential(hash);
|
keystore.removeCredential(hash);
|
||||||
setStoredCredentialsHashes(keystore.keys());
|
setStoredCredentialsHashes(keystore.keys());
|
||||||
localStorage.setItem('waku-rln-keystore', keystore.toString());
|
localStorage.setItem(LOCAL_STORAGE_KEYSTORE_KEY, keystore.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextValue: KeystoreContextType = {
|
const contextValue: KeystoreContextType = {
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
||||||
import { KeystoreEntity } from '@waku/rln';
|
import { KeystoreEntity, RLNCredentialsManager } from '@waku/rln';
|
||||||
import { createRLNImplementation, UnifiedRLNInstance } from './implementations';
|
import { createRLNImplementation } from './implementations';
|
||||||
import { useRLNImplementation } from './RLNImplementationContext';
|
import { useRLNImplementation } from './RLNImplementationContext';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { useKeystore } from '../keystore';
|
import { useKeystore } from '../keystore';
|
||||||
import { ERC20_ABI, LINEA_SEPOLIA_CONFIG, ensureLineaSepoliaNetwork } from './utils/network';
|
import { ERC20_ABI, LINEA_SEPOLIA_CONFIG, ensureLineaSepoliaNetwork } from '../../utils/network';
|
||||||
|
|
||||||
interface RLNContextType {
|
interface RLNContextType {
|
||||||
rln: UnifiedRLNInstance | null;
|
rln: RLNCredentialsManager | null;
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
isStarted: boolean;
|
isStarted: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
@ -32,7 +32,7 @@ const RLNContext = createContext<RLNContextType | undefined>(undefined);
|
|||||||
|
|
||||||
export function RLNProvider({ children }: { children: ReactNode }) {
|
export function RLNProvider({ children }: { children: ReactNode }) {
|
||||||
const { implementation } = useRLNImplementation();
|
const { implementation } = useRLNImplementation();
|
||||||
const [rln, setRln] = useState<UnifiedRLNInstance | null>(null);
|
const [rln, setRln] = useState<RLNCredentialsManager | null>(null);
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
const [isStarted, setIsStarted] = useState(false);
|
const [isStarted, setIsStarted] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -130,11 +130,15 @@ export function RLNProvider({ children }: { children: ReactNode }) {
|
|||||||
console.log("RLN started successfully, isStarted set to true");
|
console.log("RLN started successfully, isStarted set to true");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const minLimit = await rln.contract.getMinRateLimit();
|
const minLimit = await rln.contract?.getMinRateLimit();
|
||||||
const maxLimit = await rln.contract.getMaxRateLimit();
|
const maxLimit = await rln.contract?.getMaxRateLimit();
|
||||||
setRateMinLimit(minLimit);
|
if (minLimit !== undefined && maxLimit !== undefined) {
|
||||||
setRateMaxLimit(maxLimit);
|
setRateMinLimit(minLimit);
|
||||||
console.log("Rate limits fetched:", { min: minLimit, max: maxLimit });
|
setRateMaxLimit(maxLimit);
|
||||||
|
console.log("Rate limits fetched:", { min: minLimit, max: maxLimit });
|
||||||
|
} else {
|
||||||
|
throw new Error("Rate limits not available");
|
||||||
|
}
|
||||||
} catch (limitErr) {
|
} catch (limitErr) {
|
||||||
console.warn("Could not fetch rate limits:", limitErr);
|
console.warn("Could not fetch rate limits:", limitErr);
|
||||||
}
|
}
|
||||||
@ -192,12 +196,15 @@ export function RLNProvider({ children }: { children: ReactNode }) {
|
|||||||
error: 'RLN not initialized or not started'
|
error: 'RLN not initialized or not started'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const minLimit = await rln.contract.getMinRateLimit();
|
const minLimit = await rln.contract?.getMinRateLimit();
|
||||||
const maxLimit = await rln.contract.getMaxRateLimit();
|
const maxLimit = await rln.contract?.getMaxRateLimit();
|
||||||
|
if (minLimit !== undefined && maxLimit !== undefined) {
|
||||||
// Update state
|
// Update state
|
||||||
setRateMinLimit(minLimit);
|
setRateMinLimit(minLimit);
|
||||||
setRateMaxLimit(maxLimit);
|
setRateMaxLimit(maxLimit);
|
||||||
|
} else {
|
||||||
|
throw new Error("Rate limits not available");
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -242,7 +249,7 @@ export function RLNProvider({ children }: { children: ReactNode }) {
|
|||||||
error: `Rate limit must be between ${rateMinLimit} and ${rateMaxLimit}`
|
error: `Rate limit must be between ${rateMinLimit} and ${rateMaxLimit}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await rln.contract.setRateLimit(rateLimit);
|
await rln.contract?.setRateLimit(rateLimit);
|
||||||
|
|
||||||
// Ensure we're on the correct network
|
// Ensure we're on the correct network
|
||||||
const isOnLineaSepolia = await ensureLineaSepoliaNetwork(signer);
|
const isOnLineaSepolia = await ensureLineaSepoliaNetwork(signer);
|
||||||
|
|||||||
@ -1,33 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createRLN, MembershipInfo, RLNLightInstance } from '@waku/rln';
|
import { createRLN, RLNCredentialsManager } from '@waku/rln';
|
||||||
import { ethers } from 'ethers';
|
|
||||||
|
|
||||||
export interface UnifiedRLNInstance {
|
export async function createRLNImplementation(type: 'standard' | 'light' = 'light') {
|
||||||
contract: {
|
|
||||||
address: string;
|
|
||||||
membershipFee?: () => Promise<ethers.BigNumber>;
|
|
||||||
getRateLimit: () => number;
|
|
||||||
getMinRateLimit: () => Promise<number>;
|
|
||||||
getMaxRateLimit: () => Promise<number>;
|
|
||||||
getMaxTotalRateLimit: () => Promise<number>;
|
|
||||||
getCurrentTotalRateLimit: () => Promise<number>;
|
|
||||||
getRemainingTotalRateLimit: () => Promise<number>;
|
|
||||||
setRateLimit: (newRateLimit: number) => Promise<void>;
|
|
||||||
getRemainingMessages: (membershipId: number) => Promise<number>;
|
|
||||||
getMembershipInfo: (idCommitment: string) => Promise<MembershipInfo | undefined>;
|
|
||||||
extendMembership: (idCommitment: string) => Promise<ethers.ContractTransaction>;
|
|
||||||
eraseMembership: (idCommitment: string, eraseFromMembershipSet?: boolean) => Promise<ethers.ContractTransaction>;
|
|
||||||
registerMembership: (idCommitment: string, rateLimit?: number) => Promise<ethers.ContractTransaction>;
|
|
||||||
};
|
|
||||||
start: (options: { signer: ethers.Signer }) => Promise<void>;
|
|
||||||
registerMembership: (options: { signature: string }) => Promise<Record<string, unknown>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createRLNImplementation(type: 'standard' | 'light' = 'light'): Promise<UnifiedRLNInstance> {
|
|
||||||
if (type === 'standard') {
|
if (type === 'standard') {
|
||||||
return await createRLN() as unknown as UnifiedRLNInstance;
|
return await createRLN();
|
||||||
} else {
|
} else {
|
||||||
return new RLNLightInstance() as unknown as UnifiedRLNInstance;
|
return new RLNCredentialsManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export { RLNProvider as StandardRLNProvider, useRLN as useStandardRLN } from './standard';
|
export { RLNProvider as StandardRLNProvider, useRLN as useStandardRLN } from './standard';
|
||||||
export { RLNProvider as LightRLNProvider, useRLN as useLightRLN } from './light';
|
export { RLNProvider as LightRLNProvider, useRLN as useLightRLN } from './light';
|
||||||
export { createRLNImplementation, type UnifiedRLNInstance } from './factory';
|
export { createRLNImplementation } from './factory';
|
||||||
@ -1,27 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
||||||
import { DecryptedCredentials, RLNInstance, RLNLightInstance } from '@waku/rln';
|
import { RLNCredentialsManager } from '@waku/rln';
|
||||||
import { useWallet } from '../../wallet';
|
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { ensureLineaSepoliaNetwork, ERC20_ABI, SIGNATURE_MESSAGE } from '../utils/network';
|
import { ensureLineaSepoliaNetwork, ERC20_ABI, SIGNATURE_MESSAGE } from '../../../utils/network';
|
||||||
|
import { useWallet } from '@/contexts';
|
||||||
interface RLNContextType {
|
import { RLNContextType } from './types';
|
||||||
rln: RLNLightInstance | RLNInstance | null;
|
|
||||||
isInitialized: boolean;
|
|
||||||
isStarted: boolean;
|
|
||||||
error: string | null;
|
|
||||||
initializeRLN: () => Promise<void>;
|
|
||||||
registerMembership: (rateLimit: number) => Promise<{ success: boolean; error?: string; credentials?: DecryptedCredentials }>;
|
|
||||||
rateMinLimit: number;
|
|
||||||
rateMaxLimit: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RLNContext = createContext<RLNContextType | undefined>(undefined);
|
const RLNContext = createContext<RLNContextType | undefined>(undefined);
|
||||||
|
|
||||||
export function RLNProvider({ children }: { children: ReactNode }) {
|
export function RLNProvider({ children }: { children: ReactNode }) {
|
||||||
const { isConnected, signer } = useWallet();
|
const { isConnected, signer } = useWallet();
|
||||||
const [rln, setRln] = useState<RLNLightInstance | RLNInstance | null>(null);
|
const [rln, setRln] = useState<RLNCredentialsManager | null>(null);
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
const [isStarted, setIsStarted] = useState(false);
|
const [isStarted, setIsStarted] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -38,7 +28,7 @@ export function RLNProvider({ children }: { children: ReactNode }) {
|
|||||||
console.log("Creating RLN instance...");
|
console.log("Creating RLN instance...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rlnInstance = new RLNLightInstance();
|
const rlnInstance = new RLNCredentialsManager();
|
||||||
|
|
||||||
console.log("RLN instance created successfully:", !!rlnInstance);
|
console.log("RLN instance created successfully:", !!rlnInstance);
|
||||||
setRln(rlnInstance);
|
setRln(rlnInstance);
|
||||||
|
|||||||
@ -1,21 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createContext, useContext, useState, ReactNode } from 'react';
|
import { createContext, useContext, useState, ReactNode } from 'react';
|
||||||
import { createRLN, DecryptedCredentials, LINEA_CONTRACT, RLNInstance } from '@waku/rln';
|
import { createRLN, LINEA_CONTRACT, RLNInstance } from '@waku/rln';
|
||||||
import { useWallet } from '../../wallet';
|
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { ensureLineaSepoliaNetwork, ERC20_ABI, SIGNATURE_MESSAGE } from '../utils/network';
|
import { ensureLineaSepoliaNetwork, ERC20_ABI, SIGNATURE_MESSAGE } from '../../../utils/network';
|
||||||
|
import { RLNContextType } from './types';
|
||||||
|
import { useWallet } from '@/contexts';
|
||||||
|
|
||||||
|
|
||||||
interface RLNContextType {
|
|
||||||
rln: RLNInstance | null;
|
|
||||||
isInitialized: boolean;
|
|
||||||
isStarted: boolean;
|
|
||||||
error: string | null;
|
|
||||||
initializeRLN: () => Promise<void>;
|
|
||||||
registerMembership: (rateLimit: number) => Promise<{ success: boolean; error?: string; credentials?: DecryptedCredentials }>;
|
|
||||||
rateMinLimit: number;
|
|
||||||
rateMaxLimit: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RLNContext = createContext<RLNContextType | undefined>(undefined);
|
const RLNContext = createContext<RLNContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { DecryptedCredentials, RLNCredentialsManager, RLNInstance } from "@waku/rln";
|
||||||
|
|
||||||
|
export interface RLNContextType {
|
||||||
|
rln: RLNInstance | RLNCredentialsManager | null;
|
||||||
|
isInitialized: boolean;
|
||||||
|
isStarted: boolean;
|
||||||
|
error: string | null;
|
||||||
|
initializeRLN: () => Promise<void>;
|
||||||
|
registerMembership: (rateLimit: number) => Promise<{ success: boolean; error?: string; credentials?: DecryptedCredentials }>;
|
||||||
|
rateMinLimit: number;
|
||||||
|
rateMaxLimit: number;
|
||||||
|
}
|
||||||
@ -1,3 +1,2 @@
|
|||||||
export { RLNProvider, useRLN } from './RLNContext';
|
export { RLNProvider, useRLN } from './RLNContext';
|
||||||
export { RLNImplementationProvider, useRLNImplementation, type RLNImplementationType } from './RLNImplementationContext';
|
export { RLNImplementationProvider, useRLNImplementation, type RLNImplementationType } from './RLNImplementationContext';
|
||||||
export type { UnifiedRLNInstance } from './implementations';
|
|
||||||
@ -3,17 +3,9 @@
|
|||||||
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
||||||
import { extractMetaMaskSigner } from '@waku/rln';
|
import { extractMetaMaskSigner } from '@waku/rln';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
|
import { WalletContextType } from './types';
|
||||||
|
|
||||||
|
|
||||||
interface WalletContextType {
|
|
||||||
isConnected: boolean;
|
|
||||||
address: string | null;
|
|
||||||
signer: ethers.Signer | null;
|
|
||||||
balance: string | null;
|
|
||||||
chainId: number | null;
|
|
||||||
connectWallet: () => Promise<void>;
|
|
||||||
disconnectWallet: () => void;
|
|
||||||
error: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const WalletContext = createContext<WalletContextType | undefined>(undefined);
|
const WalletContext = createContext<WalletContextType | undefined>(undefined);
|
||||||
|
|
||||||
@ -146,19 +138,3 @@ export function useWallet() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
ethereum?: {
|
|
||||||
isMetaMask?: boolean;
|
|
||||||
isConnected?: boolean;
|
|
||||||
selectedAddress?: string;
|
|
||||||
request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;
|
|
||||||
on(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
|
|
||||||
on(event: 'chainChanged', listener: (chainId: string) => void): void;
|
|
||||||
on(event: string, listener: (...args: unknown[]) => void): void;
|
|
||||||
removeListener(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
|
|
||||||
removeListener(event: 'chainChanged', listener: (chainId: string) => void): void;
|
|
||||||
removeListener(event: string, listener: (...args: unknown[]) => void): void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
29
examples/keystore-management/src/contexts/wallet/types.ts
Normal file
29
examples/keystore-management/src/contexts/wallet/types.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ethers } from 'ethers';
|
||||||
|
|
||||||
|
export interface WalletContextType {
|
||||||
|
isConnected: boolean;
|
||||||
|
address: string | null;
|
||||||
|
signer: ethers.Signer | null;
|
||||||
|
balance: string | null;
|
||||||
|
chainId: number | null;
|
||||||
|
connectWallet: () => Promise<void>;
|
||||||
|
disconnectWallet: () => void;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
ethereum?: {
|
||||||
|
isMetaMask?: boolean;
|
||||||
|
isConnected?: boolean;
|
||||||
|
selectedAddress?: string;
|
||||||
|
request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;
|
||||||
|
on(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
|
||||||
|
on(event: 'chainChanged', listener: (chainId: string) => void): void;
|
||||||
|
on(event: string, listener: (...args: unknown[]) => void): void;
|
||||||
|
removeListener(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
|
||||||
|
removeListener(event: 'chainChanged', listener: (chainId: string) => void): void;
|
||||||
|
removeListener(event: string, listener: (...args: unknown[]) => void): void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user