Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement permissions #60

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions src/components/shared/CustomTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ const CustomTable: React.FC<CustomTableProps<AccessData>> = ({
ycolumns,
handleGrantOrRevokeAccess,
}) => {
console.log('xcolumns:', xcolumns);
console.log('ycolumns:', ycolumns);

const groupedApplications = ycolumns.reduce(
(acc, application) => {
if (!acc[application.applicationName]) {
Expand Down Expand Up @@ -89,12 +86,16 @@ const CustomTable: React.FC<CustomTableProps<AccessData>> = ({
);
return (
<TableCell key={colIndex} align="center">
<AccessControlButton
hasAccess={application?.hasPermission || false}
onToggleAccess={() =>
handleToggleAccess(application!, platform)
}
/>
{application ? (
<AccessControlButton
hasAccess={application.hasPermission}
onToggleAccess={() =>
handleToggleAccess(application, platform)
}
/>
) : (
<Typography variant="body2">No Data</Typography>
)}
</TableCell>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Identifiers/Attestation/Attestation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export function Attestation() {
data: linkingIdentifierRequest.message.data,
},
signature: linkingIdentifierRequest.signature,
attester: linkingIdentifierRequest.message.attester,
attester: linkingIdentifierRequest.message.attester as string,
deadline: 0n,
};
console.log({ transformedPayload });
Expand Down
281 changes: 143 additions & 138 deletions src/pages/Permissions/Permissions.tsx
Original file line number Diff line number Diff line change
@@ -1,175 +1,180 @@
/* 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';
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useMemo, useState } from 'react';
import { useReadContract, useReadContracts, useWriteContract } from 'wagmi';
import { Address, Abi } from 'viem';
import { Backdrop, CircularProgress, Stack, Typography } from '@mui/material';
import { useGetAttestations } from '../../services/eas/query';
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 from '../../components/shared/CustomTable';

interface IProvider {
uid: string;
provider: string;
id: string;
}
import { decodeAttestationData, IAttestation } from '../../libs/oci';
import CustomTable, {
Platform,
AccessData,
} from '../../components/shared/CustomTable';

export function Permissions() {
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<IProvider[]>([]);
const [uids, setUids] = useState<string[]>([]);

const { data: applications } = useReadContract({
const { writeContract, isPending: isWriting } = useWriteContract();
const { data: attestationsResponse, isLoading: isLoadingAttestations } =
useGetAttestations();
const [applicationsArgs] = useState<[number, number]>([0, 10]);
const [attestations, setAttestations] = useState<
(IAttestation & { provider?: string; id?: string })[]
>([]);
const [permissionsWithUidsAndApps, setPermissionsWithUidsAndApps] = useState<
AccessData[]
>([]);

const { data, isLoading: isLoadingApplications } = useReadContract({
mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved
abi: sepoliaChainAppConctract.appContractABI,
address: sepoliaChainAppConctract.appContractAddress as Address,
functionName: 'getApplications',
args: applicationsArgs,
});

useEffect(() => {
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 applications = useMemo(
() => (data as { name: string; account: string }[]) || [],
[data]
);
mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved

const { data: hasPermissionsOnApp } = useReadContracts({
contracts: contractCalls,
});

console.log({ hasPermissionsOnApp });
useEffect(() => {
const processAttestations = () => {
if (!attestationsResponse) {
return;
}

const permissionsWithUidsAndApps =
hasPermissionsOnApp
?.map((permissionResult, index) => {
const uidIndex = Math.floor(index / applications.length);
const appIndex = index % applications.length;
const attestationsData = attestationsResponse.map((attestation) => {
const decodedData = decodeAttestationData(attestation.data);

if (!permissionResult || typeof permissionResult.result !== 'boolean') {
console.error(
`Unexpected result format for UID: ${uids[uidIndex]} and account: ${applications[appIndex].account}`,
permissionResult
);
return null;
}
const providerData = decodedData.find(
(provider) => provider.name === 'provider'
);

return {
uid: uids[uidIndex],
account: applications[appIndex].account,
applicationName: applications[appIndex].name,
hasPermission: permissionResult.result as boolean,
...attestation,
provider:
typeof providerData?.value.value === 'string'
? providerData.value.value
: undefined,
decodedData,
};
})
.filter(Boolean) || [];
});

console.log({ permissionsWithUidsAndApps });
setAttestations(attestationsData);
};

// const { data: hasPermission } = useReadContract({
// abi: sepoliaChainOidonctract.oidContractAbi,
// address: sepoliaChainOidonctract.oidContractAddress as Address,
// functionName: 'hasPermission',
// args: [uid, account],
// });
processAttestations();
}, [attestationsResponse]);

mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved
// useEffect(() => {
// if (!isConnected) throw new Error('Wallet not connected');
const contractCalls = useMemo(
() =>
attestations.flatMap(
(attestation) =>
applications?.map((application) => ({
abi: sepoliaChainOidonctract.oidContractAbi as Abi,
mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved
address: sepoliaChainOidonctract.oidContractAddress as Address,
functionName: 'hasPermission',
args: [attestation.id, application.account],
})) || []
),
[attestations, applications]
);

// console.log({ hasPermission });
// }, [hasPermission]);
const { data: hasPermissionsOnApp, isLoading: isLoadingPermissions } =
useReadContracts({
contracts: contractCalls,
});

useEffect(() => {
if (isConnected && signer && chainId && litNodeClient) {
createSessionSigs({ signer, chainId, litNodeClient });
if (hasPermissionsOnApp) {
const uids = attestations.map((attestation) => attestation.id);

const permissions =
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) || [];

setPermissionsWithUidsAndApps(
permissions.filter(
(permission): permission is AccessData => permission !== null
)
);
}
}, [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);
}, [hasPermissionsOnApp, attestations, applications]);

setUids(filteredUids);
const providers: Platform[] = attestations.map((attestation) => ({
id: attestation.id || '',
mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved
provider: attestation.provider || 'Unknown',
uid: attestation.id || '',
}));

mehdi-torabiv marked this conversation as resolved.
Show resolved Hide resolved
return attestations.filter(
(attestation) => attestation.revocationTime === 0n
);
const handleGrantOrRevokeAccess = (application: any, platform: any) => {
writeContract({
abi: sepoliaChainOidonctract.oidContractAbi as Abi,
address: sepoliaChainOidonctract.oidContractAddress as Address,
functionName: application.hasPermission
? 'revokePermission'
: 'grantPermission',
args: [platform.uid, application.account],
});
};

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]);

console.log({ providers });
const isLoading =
isWriting ||
isLoadingAttestations ||
isLoadingApplications ||
isLoadingPermissions;

return (
<div>
<div>Permissions</div>
<Backdrop
open={isLoading}
sx={{
zIndex: (theme) => theme.zIndex.drawer + 1,
background: '#fff',
color: 'black',
}}
>
<Stack
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<CircularProgress color="inherit" />
<Typography variant="h6" gutterBottom style={{ marginLeft: '15px' }}>
Loading...
</Typography>
</Stack>
</Backdrop>
<Typography variant="h6" gutterBottom>
Permissions
</Typography>
<CustomTable
xcolumns={providers}
ycolumns={permissionsWithUidsAndApps}
handleGrantOrRevokeAccess={(application, platform) => {
writeContract({
abi: sepoliaChainOidonctract.oidContractAbi as Abi,
address: sepoliaChainOidonctract.oidContractAddress as Address,
functionName: application.hasPermission
? 'revokePermission'
: 'grantPermission',
args: [platform.uid, application.account],
});
}}
handleGrantOrRevokeAccess={handleGrantOrRevokeAccess}
/>
</div>
);
Expand Down
Loading
Loading