-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0f95078
commit 2816245
Showing
11 changed files
with
895 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Button } from "gitcoin-ui"; | ||
|
||
interface MessageProps { | ||
title: string; | ||
message: string; | ||
action?: { | ||
label: string; | ||
onClick: () => void; | ||
}; | ||
className?: string; | ||
} | ||
|
||
export function MessagePage({ title, message, action, className = "" }: MessageProps) { | ||
return ( | ||
<div className={`flex min-h-[50vh] flex-col items-center justify-center p-4 ${className}`}> | ||
<div className="mx-auto max-w-md text-center"> | ||
<h1 className="text-gray-900 mb-3 text-2xl font-bold">{title}</h1> | ||
|
||
<p className="text-gray-600 mb-8">{message}</p> | ||
|
||
{action && <Button onClick={action.onClick} value={action.label} />} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./usePerformEvaluation"; | ||
export * from "./utils"; |
65 changes: 65 additions & 0 deletions
65
apps/admin/src/hooks/checker/usePerformEvaluation/usePerformEvaluation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { useState, useEffect } from "react"; | ||
import { useMutation } from "@tanstack/react-query"; | ||
import { EvaluationBody } from "gitcoin-ui/checker"; | ||
import { Hex } from "viem"; | ||
import { useAccount, useWalletClient } from "wagmi"; | ||
import { deterministicKeccakHash, submitEvaluation } from "./utils"; | ||
|
||
export const usePerformEvaluation = () => { | ||
const [evaluationBody, setEvaluationBody] = useState<EvaluationBody | null>(null); | ||
const { address } = useAccount(); | ||
const { data: walletClient } = useWalletClient(); | ||
|
||
const handleSetEvaluationBody = (data: EvaluationBody) => { | ||
setEvaluationBody(data); | ||
}; | ||
|
||
const signEvaluationBody = async (): Promise<Hex> => { | ||
if (!walletClient) { | ||
throw new Error("No wallet client found"); | ||
} | ||
|
||
if (!evaluationBody) { | ||
throw new Error("No evaluation body found"); | ||
} | ||
|
||
const hash = await deterministicKeccakHash({ | ||
chainId: evaluationBody.chainId, | ||
alloPoolId: evaluationBody.alloPoolId, | ||
alloApplicationId: evaluationBody.alloApplicationId, | ||
cid: evaluationBody.cid, | ||
evaluator: address, | ||
summaryInput: evaluationBody.summaryInput, | ||
evaluationStatus: evaluationBody.evaluationStatus, | ||
}); | ||
|
||
const signature = await walletClient.signMessage({ message: hash }); | ||
|
||
return signature; | ||
}; | ||
|
||
// Evaluation mutation | ||
const evaluationMutation = useMutation({ | ||
mutationFn: async (data: EvaluationBody) => { | ||
if (!address) { | ||
throw new Error("No address found"); | ||
} | ||
const signature = await signEvaluationBody(); | ||
await submitEvaluation({ ...data, signature, evaluator: address }); | ||
}, | ||
}); | ||
|
||
// Trigger the signing mutation when evaluationBody is set | ||
useEffect(() => { | ||
if (evaluationBody) { | ||
evaluationMutation.mutateAsync(evaluationBody); | ||
} | ||
}, [evaluationBody]); | ||
|
||
return { | ||
setEvaluationBody: handleSetEvaluationBody, | ||
isEvaluating: evaluationMutation.isPending, | ||
isError: evaluationMutation.isError, | ||
isSuccess: evaluationMutation.isSuccess, | ||
}; | ||
}; |
65 changes: 65 additions & 0 deletions
65
apps/admin/src/hooks/checker/usePerformEvaluation/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// CHECKER | ||
import { EvaluationBody, SyncPoolBody } from "gitcoin-ui/checker"; | ||
import stringify from "json-stringify-deterministic"; | ||
import { type Hex, keccak256, toHex } from "viem"; | ||
|
||
export const CHECKER_ENDPOINT = "https://api.checker.gitcoin.co"; | ||
|
||
export async function submitEvaluation( | ||
evaluationBody: EvaluationBody, | ||
): Promise<{ evaluationId: string }> { | ||
const url = `${CHECKER_ENDPOINT}/api/evaluate`; | ||
|
||
try { | ||
const response = await fetch(url, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ ...evaluationBody, evaluatorType: "human" }), | ||
}); | ||
|
||
if (!response.ok) { | ||
const errorData = await response.json(); | ||
throw new Error(`Error: ${response.status} - ${errorData.message || "Unknown error"}`); | ||
} | ||
|
||
const data = await response.json(); | ||
return data.evaluationId; | ||
} catch (error) { | ||
console.error("Error submitting evaluation:", error); | ||
throw error; | ||
} | ||
} | ||
|
||
export async function syncPool(syncPoolBody: SyncPoolBody): Promise<boolean> { | ||
const url = `${CHECKER_ENDPOINT}/api/pools`; | ||
|
||
try { | ||
const response = await fetch(url, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
...syncPoolBody, | ||
skipEvaluation: false, | ||
}), | ||
}); | ||
|
||
if (!response.ok) { | ||
const errorData = await response.json(); | ||
throw new Error(`Error: ${response.status} - ${errorData.message || "Unknown error"}`); | ||
} | ||
|
||
return true; | ||
} catch (error) { | ||
console.error("Error syncing pool:", error); | ||
throw error; | ||
} | ||
} | ||
|
||
export async function deterministicKeccakHash<T>(obj: T): Promise<Hex> { | ||
const deterministicString = stringify(obj); | ||
return keccak256(toHex(deterministicString)); | ||
} |
2 changes: 2 additions & 0 deletions
2
apps/admin/src/hooks/checker/usePerformOnChainReview/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./usePerformOnChainReview"; | ||
export * from "./utils"; |
160 changes: 160 additions & 0 deletions
160
apps/admin/src/hooks/checker/usePerformOnChainReview/usePerformOnChainReview.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { useState, useEffect, useMemo } from "react"; | ||
import { useMutation } from "@tanstack/react-query"; | ||
import { ReviewBody } from "gitcoin-ui/checker"; | ||
import { ProgressStatus } from "gitcoin-ui/types"; | ||
import { Abi, createPublicClient, encodeFunctionData, http } from "viem"; | ||
import { useWalletClient } from "wagmi"; | ||
import { | ||
waitUntilIndexerSynced, | ||
applicationStatusToNumber, | ||
buildUpdatedRowsOfApplicationStatuses, | ||
getStrategyInstance, | ||
getOnchainEvaluationProgressSteps, | ||
} from "."; | ||
|
||
export const usePerformOnChainReview = () => { | ||
const [reviewBody, setReviewBody] = useState<ReviewBody | null>(null); | ||
const [contractUpdatingStatus, setContractUpdatingStatus] = useState<ProgressStatus>( | ||
ProgressStatus.NOT_STARTED, | ||
); | ||
const [indexingStatus, setIndexingStatus] = useState<ProgressStatus>(ProgressStatus.NOT_STARTED); | ||
const [finishingStatus, setFinishingStatus] = useState<ProgressStatus>( | ||
ProgressStatus.NOT_STARTED, | ||
); | ||
|
||
const { data: walletClient } = useWalletClient(); | ||
|
||
const handleSetReviewBody = (reviewBody: ReviewBody | null) => { | ||
setReviewBody(reviewBody); | ||
}; | ||
|
||
const evaluationMutation = useMutation({ | ||
mutationFn: async (data: ReviewBody) => { | ||
if (!walletClient) { | ||
throw new Error("WalletClient is undefined"); | ||
} | ||
|
||
try { | ||
// Reset statuses before starting | ||
setContractUpdatingStatus(ProgressStatus.IN_PROGRESS); | ||
|
||
// Prepare the strategy instance based on the strategy type. | ||
const { strategyInstance, strategyInstanceAbi } = getStrategyInstance( | ||
data.strategyAddress, | ||
walletClient.chain.id, | ||
data.roundId, | ||
data.strategy, | ||
); | ||
|
||
// Try to get total applications | ||
let totalApplications = BigInt(0); | ||
try { | ||
totalApplications = await strategyInstance.recipientsCounter(); | ||
} catch (error) { | ||
totalApplications = BigInt(data.currentApplications.length + 1); | ||
} | ||
|
||
// Build updated rows of application statuses | ||
const rows = buildUpdatedRowsOfApplicationStatuses({ | ||
applicationsToUpdate: data.applicationsToUpdate, | ||
currentApplications: data.currentApplications, | ||
statusToNumber: applicationStatusToNumber, | ||
bitsPerStatus: 4, | ||
}); | ||
|
||
// Send transaction | ||
const account = walletClient.account; | ||
if (!account) { | ||
throw new Error("WalletClient account is undefined"); | ||
} | ||
|
||
const publicClient = createPublicClient({ | ||
chain: walletClient.chain, | ||
transport: http(), | ||
}); | ||
|
||
let txHash; | ||
let receipt; | ||
try { | ||
txHash = await walletClient.sendTransaction({ | ||
account: account, | ||
to: data.strategyAddress, | ||
data: encodeFunctionData({ | ||
abi: strategyInstanceAbi as Abi, | ||
functionName: "reviewRecipients", | ||
args: [rows, totalApplications], | ||
}), | ||
}); | ||
|
||
receipt = await publicClient.waitForTransactionReceipt({ | ||
hash: txHash, | ||
confirmations: 1, | ||
}); | ||
} catch (sendError) { | ||
setContractUpdatingStatus(ProgressStatus.IS_ERROR); | ||
throw sendError; | ||
} | ||
|
||
if (!receipt.status) { | ||
setContractUpdatingStatus(ProgressStatus.IS_ERROR); | ||
throw new Error("Failed to update application status"); | ||
} | ||
|
||
// Transaction sent successfully | ||
setContractUpdatingStatus(ProgressStatus.IS_SUCCESS); | ||
setIndexingStatus(ProgressStatus.IN_PROGRESS); | ||
|
||
// Wait until indexer is synced | ||
try { | ||
await waitUntilIndexerSynced({ | ||
chainId: walletClient.chain.id, | ||
blockNumber: receipt.blockNumber, | ||
}); | ||
} catch (e) { | ||
setIndexingStatus(ProgressStatus.IS_ERROR); | ||
} | ||
|
||
setIndexingStatus(ProgressStatus.IS_SUCCESS); | ||
|
||
// Finishing up | ||
setFinishingStatus(ProgressStatus.IN_PROGRESS); | ||
// Any finishing steps can be added here | ||
setFinishingStatus(ProgressStatus.IS_SUCCESS); | ||
} catch (error) { | ||
throw error; | ||
} | ||
}, | ||
}); | ||
|
||
useEffect(() => { | ||
if (reviewBody) { | ||
evaluationMutation.mutate(reviewBody); | ||
} | ||
}, [reviewBody]); | ||
|
||
useEffect(() => { | ||
if (evaluationMutation.isSuccess) { | ||
setContractUpdatingStatus(ProgressStatus.NOT_STARTED); | ||
setIndexingStatus(ProgressStatus.NOT_STARTED); | ||
setFinishingStatus(ProgressStatus.NOT_STARTED); | ||
evaluationMutation.reset(); | ||
} | ||
}, [evaluationMutation]); | ||
|
||
const steps = useMemo(() => { | ||
return getOnchainEvaluationProgressSteps({ | ||
contractUpdatingStatus, | ||
indexingStatus, | ||
finishingStatus, | ||
}); | ||
}, [contractUpdatingStatus, indexingStatus, finishingStatus]); | ||
|
||
return { | ||
setReviewBody: handleSetReviewBody, | ||
steps, | ||
isReviewing: evaluationMutation.isPending, | ||
isError: evaluationMutation.isError, | ||
isSuccess: evaluationMutation.isSuccess, | ||
error: evaluationMutation.error, | ||
}; | ||
}; |
Oops, something went wrong.