Skip to content

Commit

Permalink
feat(app): parsing score attestation
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer committed Jul 28, 2023
1 parent adbb526 commit 74d1589
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 42 deletions.
30 changes: 24 additions & 6 deletions app/components/NetworkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Stamp } from "@gitcoin/passport-types";
import { useContext, useEffect, useState } from "react";
import { CeramicContext, AllProvidersState, ProviderState } from "../context/ceramicContext";
import { OnChainContext, OnChainProviderType } from "../context/onChainContext";
import { UserContext } from "../context/userContext";
import { ScorerContext, ScoreStateType } from "../context/scorerContext";
import { SyncToChainButton } from "./SyncToChainButton";

type Chain = {
Expand All @@ -20,22 +20,33 @@ export enum OnChainStatus {
}

type ProviderWithStamp = ProviderState & { stamp: Stamp };

export const checkOnChainStatus = (
allProvidersState: AllProvidersState,
onChainProviders: OnChainProviderType[]
onChainProviders: OnChainProviderType[],
rawScore: number,
scoreState: ScoreStateType,
onChainScore: number
): OnChainStatus => {
if (onChainProviders.length === 0) return OnChainStatus.NOT_MOVED;

if (scoreState === "DONE" && rawScore !== onChainScore) return OnChainStatus.MOVED_OUT_OF_DATE;

const verifiedDbProviders: ProviderWithStamp[] = Object.values(allProvidersState).filter(
(provider): provider is ProviderWithStamp => provider.stamp !== undefined
);

const [equivalentProviders, differentProviders] = verifiedDbProviders.reduce(
([eq, diff], provider): [ProviderWithStamp[], ProviderWithStamp[]] => {
const expirationDateSeconds = Math.floor(new Date(provider.stamp.credential.expirationDate).valueOf() / 1000);
const issuanceDateSeconds = Math.floor(new Date(provider.stamp.credential.issuanceDate).valueOf() / 1000);

const isEquivalent = onChainProviders.some(
(onChainProvider) =>
onChainProvider.providerName === provider.stamp.provider &&
onChainProvider.credentialHash === provider.stamp.credential.credentialSubject?.hash
onChainProvider.credentialHash === provider.stamp.credential.credentialSubject?.hash &&
Math.floor(onChainProvider.expirationDate.valueOf() / 1000) === expirationDateSeconds &&
Math.floor(onChainProvider.issuanceDate.valueOf() / 1000) === issuanceDateSeconds
);
return isEquivalent ? [[...eq, provider], diff] : [eq, [...diff, provider]];
},
Expand All @@ -49,7 +60,8 @@ export const checkOnChainStatus = (

export function NetworkCard({ chain, activeChains }: { chain: Chain; activeChains: string[] }) {
const { allProvidersState } = useContext(CeramicContext);
const { onChainProviders } = useContext(OnChainContext);
const { onChainProviders, onChainScore } = useContext(OnChainContext);
const { rawScore, scoreState } = useContext(ScorerContext);
const [isActive, setIsActive] = useState(false);
const [onChainStatus, setOnChainStatus] = useState<OnChainStatus>(OnChainStatus.NOT_MOVED);

Expand All @@ -60,11 +72,17 @@ export function NetworkCard({ chain, activeChains }: { chain: Chain; activeChain
useEffect(() => {
const checkStatus = async () => {
const savedNetworkProviders = onChainProviders[chain.id] || [];
const stampStatus = checkOnChainStatus(allProvidersState, savedNetworkProviders);
const stampStatus = checkOnChainStatus(
allProvidersState,
savedNetworkProviders,
rawScore,
scoreState,
onChainScore
);
setOnChainStatus(stampStatus);
};
checkStatus();
}, [allProvidersState, chain.id, onChainProviders]);
}, [allProvidersState, chain.id, onChainProviders, onChainScore, rawScore, scoreState]);

return (
<div className="mb-6 border border-accent-2 bg-background-2 p-0">
Expand Down
6 changes: 3 additions & 3 deletions app/components/SyncToChainButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import axios from "axios";
export function getButtonMsg(onChainStatus: OnChainStatus): string {
switch (onChainStatus) {
case OnChainStatus.NOT_MOVED:
return "Up to date";
return "Sync to chain";
case OnChainStatus.MOVED_OUT_OF_DATE:
return "Update";
case OnChainStatus.MOVED_UP_TO_DATE:
Expand Down Expand Up @@ -77,7 +77,7 @@ export type SyncToChainProps = {
export function SyncToChainButton({ onChainStatus, isActive }: SyncToChainProps): JSX.Element {
const { passport } = useContext(CeramicContext);
const { wallet, address } = useContext(UserContext);
const { refreshOnChainProviders } = useContext(OnChainContext);
const { readOnChainData } = useContext(OnChainContext);
const [syncingToChain, setSyncingToChain] = useState(false);
const toast = useToast();

Expand Down Expand Up @@ -161,7 +161,7 @@ export function SyncToChainButton({ onChainStatus, isActive }: SyncToChainProps)
});
await transaction.wait();
const easScanURL = `${process.env.NEXT_PUBLIC_EAS_EXPLORER}/address/${address}`;
await refreshOnChainProviders();
await readOnChainData();
const successSubmit = (
<p>
Passport successfully synced to chain.{" "}
Expand Down
5 changes: 3 additions & 2 deletions app/context/onChainContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { UserContext } from "./userContext";

import { PROVIDER_ID } from "@gitcoin/passport-types";

import { decodeProviderInformation, getAttestationData } from "../utils/onChainStamps";
import { decodeProviderInformation, decodeScoreAttestation, getAttestationData } from "../utils/onChainStamps";

export interface OnChainProviderMap {
[key: string]: OnChainProviderType[];
Expand Down Expand Up @@ -77,7 +77,8 @@ export const OnChainContextProvider = ({ children }: { children: any }) => {
}));

setActiveChainProviders(onChainProviders);
setOnChainScore(1); // TODO

setOnChainScore(await decodeScoreAttestation(passportAttestationData.score));
} catch (e: any) {
datadogLogs.logger.error("Failed to check on-chain status", e);
datadogRum.addError(e);
Expand Down
37 changes: 6 additions & 31 deletions app/utils/onChainStamps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WalletState } from "@web3-onboard/core";
import { BrowserProvider, Contract } from "ethers";
import { BrowserProvider, Contract, formatUnits, BigNumberish } from "ethers";
import { BigNumber } from "@ethersproject/bignumber";
import axios from "axios";
import GitcoinResolver from "../contracts/GitcoinResolver.json";
Expand Down Expand Up @@ -97,38 +97,13 @@ export async function decodeProviderInformation(attestation: Attestation): Promi
}

export async function decodeScoreAttestation(attestation: Attestation): Promise<number> {
const schemaEncoder = new SchemaEncoder(
"uint256[] providers,bytes32[] hashes,uint64[] issuanceDates,uint64[] expirationDates,uint16 providerMapVersion"
);
const schemaEncoder = new SchemaEncoder("uint256 score,uint32 scorer_id,uint8 score_decimals");
const decodedData = schemaEncoder.decodeData(attestation.data);
const providerBitMapInfo = (await axios.get(
`${process.env.NEXT_PUBLIC_PASSPORT_IAM_STATIC_URL}/providerBitMapInfo.json`
)) as {
data: StampBit[];
};

type DecodedProviderInfo = {
providerName: PROVIDER_ID;
providerNumber: number;
};

const providers = decodedData.find((data) => data.name === "providers")?.value.value as BigNumber[];
const issuanceDates = decodedData.find((data) => data.name === "issuanceDates")?.value.value as BigNumber[];
const expirationDates = decodedData.find((data) => data.name === "expirationDates")?.value.value as BigNumber[];
const hashes = decodedData.find((data) => data.name === "hashes")?.value.value as string[];
const score_as_integer = (decodedData.find(({ name }) => name === "score")?.value.value as BigNumber)._hex as string;
const score_decimals = decodedData.find(({ name }) => name === "score_decimals")?.value.value as number;

const onChainProviderInfo: DecodedProviderInfo[] = providerBitMapInfo.data
.map((info) => {
const providerMask = BigNumber.from(1).shl(info.bit);
const currentProvidersBitmap = providers[info.index];
if (currentProvidersBitmap && !providerMask.and(currentProvidersBitmap).eq(BigNumber.from(0))) {
return {
providerName: info.name,
providerNumber: info.index * 256 + info.bit,
};
}
})
.filter((provider): provider is DecodedProviderInfo => provider !== undefined);
const score = parseFloat(formatUnits(score_as_integer, score_decimals));

return { onChainProviderInfo, hashes, issuanceDates, expirationDates };
return score;
}

0 comments on commit 74d1589

Please sign in to comment.