Skip to content

Commit 75eadc4

Browse files
committed
feat: matching estimate prototype
1 parent 55bdceb commit 75eadc4

File tree

5 files changed

+433
-171
lines changed

5 files changed

+433
-171
lines changed

packages/grant-explorer/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"msw": "^0.47.4",
7878
"node-fetch": "^3.3.1",
7979
"os-browserify": "^0.3.0",
80+
"pluralistic": "github:gitcoinco/pluralistic.js",
8081
"process": "^0.11.10",
8182
"react": "^18.1.0",
8283
"react-datetime": "^3.1.1",
@@ -93,9 +94,10 @@
9394
"stream-browserify": "^3.0.0",
9495
"stream-http": "^3.2.0",
9596
"swr": "^2.0.0",
96-
"typescript": "^4.6.0",
97+
"typescript": "^5.1.3",
9798
"url": "^0.11.0",
9899
"util": "^0.12.4",
100+
"viem": "^1.1.4",
99101
"wagmi": "0.6.8",
100102
"web-vitals": "^2.1.0",
101103
"webpack": ">=2"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { useMemo } from "react";
2+
import { Client } from "allo-indexer-client";
3+
import { useWallet } from "./Auth";
4+
import { Contribution, linearQF } from "pluralistic";
5+
import useSWR from "swr";
6+
import { Round } from "allo-indexer-client";
7+
import { useAccount, useNetwork } from "wagmi";
8+
import { getAddress, Hex, zeroAddress } from "viem";
9+
10+
export function useAlloIndexerClient(): Client {
11+
const { chain } = useNetwork();
12+
13+
return useMemo(() => {
14+
return new Client(
15+
fetch.bind(window),
16+
process.env.REACT_APP_ALLO_API_URL ?? "",
17+
chain?.id ?? 1
18+
);
19+
}, [chain?.id]);
20+
}
21+
22+
export function useProjectContributions(roundId: string, projectId: string) {
23+
const client = useAlloIndexerClient();
24+
return useSWR([roundId, "/applications"], ([roundId]) => {
25+
return client.getVotes(roundId);
26+
});
27+
}
28+
29+
export function useRound(roundId: string) {
30+
const client = useAlloIndexerClient();
31+
return useSWR([roundId, "/stats"], ([roundId]) => {
32+
return client.getRoundBy("id", roundId);
33+
});
34+
}
35+
36+
/**
37+
* Pseudocode
38+
* - get matching amount without user contribution
39+
* for each donation amount:
40+
* - get amount after user contribution by running linearQF with the added vote
41+
* - deduct match amount w/o user contribution to get new matching
42+
* - return new matching
43+
* */
44+
export function MatchingEstimations({
45+
roundId,
46+
projectId,
47+
recipient,
48+
}: {
49+
projectId: string;
50+
roundId: string;
51+
recipient: string;
52+
}) {
53+
const { address } = useAccount();
54+
const { data: round } = useRound(getAddress(roundId));
55+
const { data: votes } = useProjectContributions(
56+
getAddress(roundId),
57+
projectId
58+
);
59+
60+
if (!round || !votes) {
61+
return null;
62+
}
63+
const rawContributions: Contribution[] = (votes ?? []).map((vote) => ({
64+
amount: BigInt(Math.round(vote.amountUSD)),
65+
recipient: vote.grantAddress,
66+
contributor: vote.voter,
67+
}));
68+
69+
const amountMatchedBeforeUserContribution = linearQF(
70+
rawContributions,
71+
BigInt(Math.round(round?.matchAmountUSD) ?? 0),
72+
BigInt(18)
73+
// TODO: fetch additional options for round
74+
);
75+
76+
const matchingBeforeUserContribution =
77+
amountMatchedBeforeUserContribution[recipient]?.matched;
78+
79+
const matchingPredictions = [0, 1, 10, 100, 1000]
80+
.map((donationAmount) => {
81+
const contributions: Contribution[] = [
82+
...rawContributions,
83+
{
84+
amount: BigInt(Math.round(donationAmount)),
85+
contributor: address ?? zeroAddress,
86+
recipient: recipient,
87+
},
88+
];
89+
const amountMatchedAfterUserContribution = linearQF(
90+
contributions,
91+
BigInt(Math.round(round?.matchAmountUSD) ?? 0),
92+
BigInt(18)
93+
// TODO: fetch additional options for round
94+
)[recipient]?.matched;
95+
96+
return (
97+
amountMatchedAfterUserContribution - matchingBeforeUserContribution
98+
);
99+
})
100+
.map((bigint) => Number(bigint));
101+
return <div>{JSON.stringify(matchingPredictions)}</div>;
102+
}

packages/grant-explorer/src/features/round/ViewProjectDetails.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import PassportBanner from "../common/PassportBanner";
3232
import { ProjectBanner } from "../common/ProjectBanner";
3333
import RoundEndedBanner from "../common/RoundEndedBanner";
3434
import Breadcrumb, { BreadcrumbItem } from "../common/Breadcrumb";
35+
import { linearQF } from "pluralistic";
36+
import { MatchingEstimations } from "../common/MatchingEstimations";
3537

3638
const CalendarIcon = (props: React.SVGProps<SVGSVGElement>) => {
3739
return (
@@ -547,6 +549,13 @@ export function ProjectStats() {
547549
"rounded bg-gray-50 mb-4 p-4 gap-4 grid grid-cols-3 md:flex md:flex-col"
548550
}
549551
>
552+
{projectToRender && (
553+
<MatchingEstimations
554+
projectId={projectToRender.grantApplicationId}
555+
roundId={roundId as string}
556+
recipient={projectToRender.recipient}
557+
/>
558+
)}
550559
<div>
551560
<h3>${application?.amountUSD.toFixed(2) ?? "-"}</h3>
552561
<p>funding received in current round</p>

packages/grant-explorer/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "es5",
3+
"target": "es2020",
44
"lib": ["dom", "dom.iterable", "esnext"],
55
"allowJs": true,
66
"skipLibCheck": true,

0 commit comments

Comments
 (0)