Skip to content

Commit f10db35

Browse files
Merge pull request #3571 from gitcoinco/builder-direct-donations
Show direct donations on builder
2 parents 6a31447 + 3144929 commit f10db35

File tree

7 files changed

+154
-48
lines changed

7 files changed

+154
-48
lines changed

packages/builder/src/components/grants/stats/StatCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default function StatCard({
1919
p={3}
2020
className={`${
2121
bg ? `bg-${bg}` : ""
22-
} border-grey-100 mx-2 mt-2 sm:table-row md:table-cell`}
22+
} border-grey-100 mx-2 sm:table-row md:table-cell`}
2323
borderWidth={border ? "1px" : "0px"}
2424
borderRadius="md"
2525
minWidth="193px"

packages/builder/src/components/grants/stats/Stats.tsx

Lines changed: 102 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { Spinner } from "@chakra-ui/react";
2+
import { BaseDonorValues, useDataLayer } from "data-layer";
23
import { useEffect, useState } from "react";
34
import { useSelector } from "react-redux";
45
import { useParams } from "react-router-dom";
56
import { RootState } from "../../../reducers";
67
import { ProjectStats } from "../../../reducers/projects";
78
import RoundDetailsCard from "./RoundDetailsCard";
89
import StatCard from "./StatCard";
10+
import { allChains } from "../../../utils/wagmi";
911

1012
export const slugify = (input: string): string =>
1113
input
@@ -19,9 +21,12 @@ export default function RoundStats() {
1921
const NAText = "N/A";
2022

2123
const params = useParams();
24+
const dataLayer = useDataLayer();
2225
const [details, setDetails] = useState<any>([]);
2326
const [allTimeStats, setAllTimeStats] = useState<any>({
2427
allTimeReceived: 0,
28+
totalCrowdFunding: 0,
29+
totalDirectDonations: 0,
2530
allTimeContributions: 0,
2631
roundsLength: 0,
2732
});
@@ -41,57 +46,101 @@ export default function RoundStats() {
4146
});
4247

4348
useEffect(() => {
44-
const detailsTmp: any[] = [];
45-
let allTime = {
46-
allTimeReceived: 0,
47-
allTimeContributions: 0,
48-
roundsLength: 0,
49-
};
49+
const fetch = async () => {
50+
const detailsTmp: any[] = [];
51+
let allTime = {
52+
allTimeReceived: 0,
53+
totalCrowdFunding: 0,
54+
totalDirectDonations: 0,
55+
allTimeContributions: 0,
56+
roundsLength: 0,
57+
};
58+
59+
let totalDirectDonations = 0;
60+
let totalDirectDonationCount = 0;
5061

51-
if (props.stats?.length > 0) {
52-
props.stats.forEach((stat) => {
53-
allTime = {
54-
allTimeReceived:
55-
allTime.allTimeReceived + (stat.success ? stat.fundingReceived : 0),
56-
allTimeContributions:
57-
allTime.allTimeContributions +
58-
(stat.success ? stat.totalContributions : 0),
59-
roundsLength: props.stats.length,
60-
};
61-
62-
const newStat = { ...stat };
63-
if (newStat.uniqueContributors > 0 || newStat.totalContributions > 0) {
64-
if (newStat.fundingReceived === 0) newStat.fundingReceived = NA;
65-
if (newStat.avgContribution === 0) newStat.avgContribution = NA;
66-
}
67-
68-
if (props.rounds[stat.roundId]?.round?.programName) {
69-
detailsTmp.push({
70-
round: props.rounds[stat.roundId].round,
71-
stats: { ...newStat },
62+
try {
63+
const directDonations: BaseDonorValues[] =
64+
await dataLayer.getDirectDonationsByProjectId({
65+
projectId: props.projectID,
66+
chainIds: allChains.map((chain) => chain.id),
7267
});
73-
}
74-
});
75-
}
7668

77-
setAllTimeStats(allTime);
78-
setDetails(detailsTmp);
79-
}, [props.stats, props.rounds]);
69+
directDonations.forEach((donation) => {
70+
totalDirectDonations += donation.totalAmountDonatedInUsd;
71+
totalDirectDonationCount += donation.totalDonationsCount;
72+
});
73+
} catch (e) {
74+
console.error("Error fetching direct donations", e);
75+
}
76+
77+
if (props.stats?.length > 0) {
78+
props.stats.forEach((stat) => {
79+
allTime = {
80+
...allTime,
81+
totalCrowdFunding:
82+
allTime.totalCrowdFunding +
83+
(stat.success ? stat.fundingReceived : 0),
84+
allTimeContributions:
85+
allTime.allTimeContributions +
86+
(stat.success ? stat.totalContributions : 0),
87+
roundsLength: props.stats.length,
88+
};
89+
90+
const newStat = { ...stat };
91+
if (
92+
newStat.uniqueContributors > 0 ||
93+
newStat.totalContributions > 0
94+
) {
95+
if (newStat.fundingReceived === 0) newStat.fundingReceived = NA;
96+
if (newStat.avgContribution === 0) newStat.avgContribution = NA;
97+
}
98+
99+
if (props.rounds[stat.roundId]?.round?.programName) {
100+
detailsTmp.push({
101+
round: props.rounds[stat.roundId].round,
102+
stats: { ...newStat },
103+
});
104+
}
105+
});
106+
}
107+
108+
allTime.totalDirectDonations = totalDirectDonations;
109+
allTime.allTimeReceived =
110+
allTime.totalCrowdFunding + totalDirectDonations;
111+
allTime.allTimeContributions += totalDirectDonationCount;
112+
113+
setAllTimeStats(allTime);
114+
setDetails(detailsTmp);
115+
};
116+
if (props.projectID) fetch();
117+
}, [props.stats, props.rounds, props.projectID, dataLayer]);
80118

81119
const section = (
82120
description: any,
83121
container: any,
84122
pt: boolean,
85-
key: string
123+
key: string,
124+
shortRow?: boolean
86125
) => (
87126
<div
88127
key={key}
89-
className={`grid md:grid-cols-7 sm:grid-cols-1 border-b border-gitcoin-grey-100 pb-10 ${
90-
pt && "pt-10"
91-
}`}
128+
className={`grid ${
129+
shortRow ? "md:grid-cols-8" : "md:grid-cols-7"
130+
} sm:grid-cols-1 border-b border-gitcoin-grey-100 pb-10 ${pt && "pt-10"}`}
92131
>
93-
<div className="md:col-span-2">{description}</div>
94-
<div className="md:col-span-4 sm:col-span-1 md:flex space-between">
132+
<div
133+
className={`${
134+
shortRow ? "md:col-span-2" : "md:col-span-1"
135+
} flex items-center justify-start`}
136+
>
137+
{description}
138+
</div>
139+
<div
140+
className={`${
141+
shortRow ? "md:col-span-5" : "md:col-span-6"
142+
} sm:col-span-1 md:flex space-between`}
143+
>
95144
{container}
96145
</div>
97146
<div className="md:col-span-1 sm:col-span-1" />
@@ -131,6 +180,18 @@ export default function RoundStats() {
131180
bg="gitcoin-violet-100"
132181
tooltip="The estimated funding received by this project. This number is not final and may change based on updated data."
133182
/>
183+
<StatCard
184+
heading="Est. Crowdfunding Received"
185+
value={`$${allTimeStats.totalCrowdFunding.toFixed(2)}`}
186+
bg="gitcoin-violet-100"
187+
tooltip="The number of rounds this project has participated in."
188+
/>
189+
<StatCard
190+
heading="Total Direct Donations"
191+
value={`$${allTimeStats.totalDirectDonations.toFixed(2)}`}
192+
bg="gitcoin-violet-100"
193+
tooltip="The amount of contributions this project has received as direct donations."
194+
/>
134195
<StatCard
135196
heading="No. of Contributions"
136197
value={allTimeStats.allTimeContributions}
@@ -193,7 +254,8 @@ export default function RoundStats() {
193254
/>
194255
</>,
195256
true,
196-
`details-${index}`
257+
`details-${index}`,
258+
true
197259
)
198260
)}
199261
</>

packages/builder/src/utils/wagmi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { providers } from "ethers";
88
import { type Account, type Chain, type Client, type Transport } from "viem";
99
import { Connector } from "wagmi";
1010

11-
const allChains: RChain[] =
11+
export const allChains: RChain[] =
1212
process.env.REACT_APP_ENV === "development" ? allNetworks : mainnetNetworks;
1313

1414
/* TODO: remove hardcoded value once we have environment variables validation */

packages/common/src/allo/backends/allo-v2.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,14 @@ export function getDirectAllocationPoolId(chainId: number) {
9797
return 36;
9898
case 43114:
9999
return 15;
100+
case 534352:
100101
case 534353:
101102
return 22;
102103
case 250:
103104
return 4;
104105
case 1:
105106
return 11;
107+
case 1329:
106108
case 808:
107109
return 8;
108110
case 42:
@@ -130,12 +132,16 @@ export function getDirectAllocationStrategyAddress(chainId: number) {
130132
return "0x86b4329E7CB8674b015477C81356420D79c71A53";
131133
case 534353:
132134
return "0x56662F9c0174cD6ae14b214fC52Bd6Eb6B6eA602";
135+
case 534352:
136+
return "0x9da0a7978b7bd826e06800427cbf1ec1200393e3";
133137
case 250:
134138
return "0x1E18cdce56B3754c4Dca34CB3a7439C24E8363de";
135139
case 1:
136140
return "0x56662F9c0174cD6ae14b214fC52Bd6Eb6B6eA602";
137141
case 808:
138142
return "0x1cfa7A687cd18b99D255bFc25930d3a0b05EB00F";
143+
case 1329:
144+
return "0x7836f59bd6dc1d87a45df8b9a74eefcdf25bc8a9";
139145
case 42:
140146
return "0xeB6325d9daCD1E46A20C02F46E41d4CAE45C0980";
141147
case 1088:

packages/data-layer/src/data-layer.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
ExpandedApplicationRef,
2727
RoundApplicationPayout,
2828
ProjectApplicationWithRoundAndProgram,
29+
BaseDonorValues,
2930
} from "./data.types";
3031
import {
3132
ApplicationSummary,
@@ -56,6 +57,7 @@ import {
5657
getPaginatedProjects,
5758
getProjectsBySearchTerm,
5859
getRoundsForManagerByAddress,
60+
getDirectDonationsByProjectId,
5961
} from "./queries";
6062
import { mergeCanonicalAndLinkedProjects } from "./utils";
6163

@@ -959,4 +961,19 @@ export class DataLayer {
959961
): Promise<SearchBasedProjectCategory | null> {
960962
return await categories.getSearchBasedCategoryById(id);
961963
}
964+
965+
async getDirectDonationsByProjectId({
966+
projectId,
967+
chainIds,
968+
}: {
969+
projectId: string;
970+
chainIds: number[];
971+
}): Promise<BaseDonorValues[]> {
972+
const response: { rounds: BaseDonorValues[] } = await request(
973+
this.gsIndexerEndpoint,
974+
getDirectDonationsByProjectId,
975+
{ projectId, chainIds },
976+
);
977+
return response.rounds;
978+
}
962979
}

packages/data-layer/src/data.types.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ export type v2Project = {
211211
* The linked chains to the canonical project
212212
*/
213213
linkedChains?: number[];
214-
qfRounds? : string[];
215-
dgRounds? : string[];
214+
qfRounds?: string[];
215+
dgRounds?: string[];
216216
};
217217

218218
/**
@@ -257,6 +257,12 @@ export type RoundForExplorer = Omit<RoundGetRound, "applications"> & {
257257
uniqueDonorsCount?: number;
258258
};
259259

260+
export type BaseDonorValues = {
261+
totalAmountDonatedInUsd: number;
262+
totalDonationsCount: number;
263+
uniqueDonorsCount: number;
264+
};
265+
260266
/**
261267
* The project application type for v2
262268
*
@@ -269,11 +275,8 @@ export type ProjectApplication = {
269275
status: ApplicationStatus;
270276
metadataCid: string;
271277
metadata: ProjectApplicationMetadata;
272-
totalDonationsCount: number;
273-
totalAmountDonatedInUsd: number;
274-
uniqueDonorsCount: number;
275278
distributionTransaction: string | null;
276-
};
279+
} & BaseDonorValues;
277280

278281
export type ProjectApplicationForManager = ProjectApplication & {
279282
anchorAddress: Address;

packages/data-layer/src/queries.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,21 @@ export const getPayoutsByChainIdRoundIdProjectId = gql`
834834
}
835835
}
836836
`;
837+
838+
// 0x4cd0051913234cdd7d165b208851240d334786d6e5afbb4d0eec203515a9c6f3 == DirectDonationsStrategy Id
839+
export const getDirectDonationsByProjectId = gql`
840+
query getDirectDonationsByProjectId($projectId: String!, $chainIds: [Int!]!) {
841+
rounds(
842+
filter: {
843+
strategyId: { equalTo: "0x4cd0051913234cdd7d165b208851240d334786d6e5afbb4d0eec203515a9c6f3" }
844+
chainId: { in: $chainIds }
845+
donations: { every: { projectId: { equalTo: $projectId } } }
846+
}
847+
) {
848+
id
849+
uniqueDonorsCount
850+
totalAmountDonatedInUsd
851+
totalDonationsCount
852+
}
853+
}
854+
`;

0 commit comments

Comments
 (0)