@@ -60,7 +117,7 @@ export function Identifiers() {
primary={
{identifier.verified && (
-
+
)}
{identifier.name}
diff --git a/src/pages/Permissions/Permissions.tsx b/src/pages/Permissions/Permissions.tsx
index b61af7e..b919617 100644
--- a/src/pages/Permissions/Permissions.tsx
+++ b/src/pages/Permissions/Permissions.tsx
@@ -1,57 +1,178 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import {
+ useAccount,
+ useReadContract,
+ useReadContracts,
+ useWriteContract,
+} from 'wagmi';
+import { Abi, Address } from 'viem';
import { useEffect, useState } from 'react';
-import { FaDiscord, FaGoogle } from 'react-icons/fa';
-import { useReadContract } from 'wagmi';
-import { Address } from 'viem';
-import sepoliaChain from '../../utils/contracts/app/sepoliaChain.json';
+import sepoliaChainAppConctract from '../../utils/contracts/app/sepoliaChain.json';
+import sepoliaChainOidonctract from '../../utils/contracts/oid/sepoliaChain.json';
+import useSessionSigs from '../../hooks/useSessionSigs';
+import useLit from '../../hooks/LitProvider';
+import { useSigner } from '../../utils/eas-wagmi-utils';
+import { decryptAttestation, getAttestations } from '../../libs/oci';
import CustomTable, {
AccessData,
Platform,
} from '../../components/shared/CustomTable';
-const xcolumns: Platform[] = [
- { name: 'Discord', icon: },
- { name: 'Google', icon: },
-];
+interface IProvider {
+ uid: string;
+ provider: string;
+ id: string;
+}
export function Permissions() {
- const [applications, setApplications] = useState([]);
-
- const {
- data: contractData,
- isLoading,
- isError,
- } = useReadContract({
- abi: sepoliaChain.appContractABI,
- address: sepoliaChain.appContractAddress as Address,
+ const signer = useSigner();
+ const { litNodeClient } = useLit();
+ const { sessionSigs, createSessionSigs } = useSessionSigs();
+
+ const { isConnected, address, chainId } = useAccount();
+ const [applicationsArgs, setApplicationsArgs] = useState<[number, number]>([
+ 0, 10,
+ ]);
+ const { writeContract } = useWriteContract();
+
+ const [providers, setProviders] = useState([]);
+ const [uids, setUids] = useState([]);
+
+ const { data: applications } = useReadContract({
+ abi: sepoliaChainAppConctract.appContractABI,
+ address: sepoliaChainAppConctract.appContractAddress as Address,
functionName: 'getApplications',
- args: [0, 10],
+ args: applicationsArgs,
});
useEffect(() => {
- if (contractData) {
- console.log({ contractData });
-
- // Map the contract data to match your AccessData structure
- const mappedApplications: AccessData[] = contractData.map(
- (app: { name: string; account: string }) => ({
- application: app.name,
- Discord: false,
- Google: false,
- })
- );
- setApplications(mappedApplications);
+ if (!isConnected) throw new Error('Wallet not connected');
+ }, [isConnected, applications]);
+
+ const contractCalls = uids.flatMap((uid) =>
+ applications.map((application) => ({
+ abi: sepoliaChainOidonctract.oidContractAbi as Abi,
+ address: sepoliaChainOidonctract.oidContractAddress as Address,
+ functionName: 'hasPermission',
+ args: [uid, application.account],
+ }))
+ );
+
+ const { data: hasPermissionsOnApp } = useReadContracts({
+ contracts: contractCalls,
+ });
+
+ console.log({ hasPermissionsOnApp });
+
+ const permissionsWithUidsAndApps =
+ hasPermissionsOnApp
+ ?.map((permissionResult, index) => {
+ const uidIndex = Math.floor(index / applications.length);
+ const appIndex = index % applications.length;
+
+ if (!permissionResult || typeof permissionResult.result !== 'boolean') {
+ console.error(
+ `Unexpected result format for UID: ${uids[uidIndex]} and account: ${applications[appIndex].account}`,
+ permissionResult
+ );
+ return null;
+ }
+
+ return {
+ uid: uids[uidIndex],
+ account: applications[appIndex].account,
+ applicationName: applications[appIndex].name,
+ hasPermission: permissionResult.result as boolean,
+ };
+ })
+ .filter(Boolean) || [];
+
+ console.log({ permissionsWithUidsAndApps });
+
+ // const { data: hasPermission } = useReadContract({
+ // abi: sepoliaChainOidonctract.oidContractAbi,
+ // address: sepoliaChainOidonctract.oidContractAddress as Address,
+ // functionName: 'hasPermission',
+ // args: [uid, account],
+ // });
+
+ // useEffect(() => {
+ // if (!isConnected) throw new Error('Wallet not connected');
+
+ // console.log({ hasPermission });
+ // }, [hasPermission]);
+
+ useEffect(() => {
+ if (isConnected && signer && chainId && litNodeClient) {
+ createSessionSigs({ signer, chainId, litNodeClient });
}
- }, [contractData]);
+ }, [signer, isConnected, litNodeClient, chainId, createSessionSigs]);
+
+ const fetchAttestations = async () => {
+ if (!address) throw new Error('No address found');
+
+ const attestations = await getAttestations(address as `0x${string}`);
+
+ const filteredUids = attestations
+ .filter((attestation) => attestation.revocationTime === 0n)
+ .map((attestation) => attestation.uid);
+
+ setUids(filteredUids);
+
+ return attestations.filter(
+ (attestation) => attestation.revocationTime === 0n
+ );
+ };
+
+ useEffect(() => {
+ const decrypt = async () => {
+ try {
+ if (!sessionSigs || !litNodeClient)
+ throw new Error('No sessionSigs found');
+
+ const attestations = await fetchAttestations();
+
+ const decryptedProviders = (await Promise.all(
+ attestations.map((attestation) =>
+ decryptAttestation(litNodeClient, attestation, sessionSigs)
+ .then((result) => ({
+ ...result,
+ uid: attestation.uid,
+ }))
+ .catch(() => null)
+ )
+ ).then((results) =>
+ results.filter((value) => value !== null)
+ )) as IProvider[];
+
+ setProviders(decryptedProviders);
+ } catch (e) {
+ console.log(e);
+ }
+ };
+
+ decrypt();
+ }, [sessionSigs, litNodeClient]);
- if (isLoading) return Loading...
;
- if (isError) return Error loading data
;
+ console.log({ providers });
return (
+
Permissions
{
+ writeContract({
+ abi: sepoliaChainOidonctract.oidContractAbi as Abi,
+ address: sepoliaChainOidonctract.oidContractAddress as Address,
+ functionName: application.hasPermission
+ ? 'revokePermission'
+ : 'grantPermission',
+ args: [platform.uid, application.account],
+ });
+ }}
/>
);
diff --git a/src/utils/contracts/eas/sepoliaChain.json b/src/utils/contracts/eas/sepoliaChain.json
index 1616853..6ec0c4e 100644
--- a/src/utils/contracts/eas/sepoliaChain.json
+++ b/src/utils/contracts/eas/sepoliaChain.json
@@ -1,5 +1,6 @@
{
"chainId": 11155111,
+ "easSchemaUUID": "0x85e90e3e16d319578888790af3284fea8bca549305071531e7478e3e0b5e7d6d",
"easContractAddress": "0xC2679fBD37d54388Ce493F1DB75320D236e1815e",
"easContractAbi": [
{
diff --git a/src/utils/contracts/oid/sepoliaChain.json b/src/utils/contracts/oid/sepoliaChain.json
new file mode 100644
index 0000000..d92d563
--- /dev/null
+++ b/src/utils/contracts/oid/sepoliaChain.json
@@ -0,0 +1,86 @@
+{
+ "chainId": 11155111,
+ "oidContractAddress": "0x787aeDd9Fb3e16EeF5b00C0F35f105daD2A1aA15",
+ "oidContractAbi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "uid",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "bool",
+ "name": "granted",
+ "type": "bool"
+ }
+ ],
+ "name": "PermissionDeleted",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "uid",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "bool",
+ "name": "granted",
+ "type": "bool"
+ }
+ ],
+ "name": "PermissionUpdated",
+ "type": "event"
+ },
+ {
+ "inputs": [
+ { "internalType": "bytes32", "name": "uid", "type": "bytes32" },
+ { "internalType": "address", "name": "account", "type": "address" }
+ ],
+ "name": "grantPermission",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bytes32", "name": "uid", "type": "bytes32" },
+ { "internalType": "address", "name": "account", "type": "address" }
+ ],
+ "name": "hasPermission",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bytes32", "name": "uid", "type": "bytes32" },
+ { "internalType": "address", "name": "account", "type": "address" }
+ ],
+ "name": "revokePermission",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+ ]
+}
diff --git a/src/utils/eas-wagmi-utils.ts b/src/utils/eas-wagmi-utils.ts
new file mode 100644
index 0000000..ae14a79
--- /dev/null
+++ b/src/utils/eas-wagmi-utils.ts
@@ -0,0 +1,84 @@
+// import { type PublicClient, type WalletClient } from '@wagmi/core';
+import {
+ BrowserProvider,
+ FallbackProvider,
+ JsonRpcProvider,
+ JsonRpcSigner,
+} from 'ethers';
+import { PublicClient, WalletClient, type HttpTransport } from 'viem';
+import { useEffect, useState } from 'react';
+// import type { JsonRpcProvider, JsonRpcSigner } from '@ethersproject/providers';
+import { usePublicClient, useWalletClient } from 'wagmi';
+
+export function publicClientToProvider(publicClient: PublicClient) {
+ const { chain, transport } = publicClient;
+
+ if (!chain) throw new Error('Chain not found');
+
+ const network = {
+ chainId: chain.id,
+ name: chain.name,
+ ensAddress: chain.contracts?.ensRegistry?.address,
+ };
+ if (transport.type === 'fallback')
+ return new FallbackProvider(
+ (transport.transports as ReturnType[]).map(
+ ({ value }) => new JsonRpcProvider(value?.url, network)
+ )
+ );
+ return new JsonRpcProvider(transport.url, network);
+}
+
+export function walletClientToSigner(walletClient: WalletClient) {
+ const { account, chain, transport } = walletClient;
+
+ if (!chain) throw new Error('Chain not found');
+
+ const network = {
+ chainId: chain.id,
+ name: chain.name,
+ ensAddress: chain.contracts?.ensRegistry?.address,
+ };
+ const provider = new BrowserProvider(transport, network);
+ const signer = provider.getSigner(account?.address);
+
+ return signer;
+}
+
+export function useSigner() {
+ const { data: walletClient } = useWalletClient();
+
+ const [signer, setSigner] = useState(undefined);
+ useEffect(() => {
+ async function getSigner() {
+ if (!walletClient) return;
+
+ const tmpSigner = walletClientToSigner(walletClient);
+
+ setSigner(await tmpSigner);
+ }
+
+ getSigner();
+ }, [walletClient]);
+ return signer;
+}
+
+export function useProvider() {
+ const publicClient = usePublicClient();
+
+ const [provider, setProvider] = useState(
+ undefined
+ );
+ useEffect(() => {
+ async function getSigner() {
+ if (!publicClient) return;
+
+ const tmpProvider = publicClientToProvider(publicClient);
+
+ setProvider(tmpProvider as JsonRpcProvider);
+ }
+
+ getSigner();
+ }, [publicClient]);
+ return provider;
+}
diff --git a/vite.config.ts b/vite.config.ts
index baf21db..9856648 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -2,11 +2,12 @@
///
import { defineConfig } from 'vite';
+import { nodePolyfills } from 'vite-plugin-node-polyfills';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()],
+ plugins: [react(), nodePolyfills()],
test: {
globals: true,
environment: 'jsdom',