Skip to content

Commit

Permalink
add whitelist (#3725)
Browse files Browse the repository at this point in the history
implement different statuses

fix test

fix icon color

fix collection size (#3723)

* fix collection size

* fix height

* slice string

* add description to collection details

* add cols

* edit slice length

* remove log

added copy functionality to project owner address on ViewProjectDetails page. (#3724)

* added copy functionality

* Update package.json

add verified filter
  • Loading branch information
0xKurt authored Dec 13, 2024
1 parent 8f89823 commit fc33919
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 46 deletions.
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,4 @@ export function isLitUnavailable(chainId: number) {
}

export * from "./chains";
export * from "./programWhitelist";
64 changes: 64 additions & 0 deletions packages/common/src/programWhitelist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
export type WhitelistStatus = "Accepted" | "Rejected" | "Pending";

interface ProgramData {
programId: string;
whitelistStatus: WhitelistStatus;
}

async function fetchProgramsData(): Promise<ProgramData[]> {
try {
const response = await fetch(
"https://docs.google.com/spreadsheets/d/e/2PACX-1vQxC34V_N3ubt3ycs7LvMya_zYeBmAqTxPczt0yDbLSfpI-kMp6o5E08fC0BxQG4uMp7EPV5bxP-64a/pub?gid=0&single=true&output=csv"
);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const csvText = await response.text();

const stringArray = csvText
.split(/\r?\n/)
.filter((line) => line.trim() !== "");

const programsData = stringArray.map((line) => {
const [programId, whitelistStatus] = line.split(",") as [
string,
WhitelistStatus,
];
return { programId, whitelistStatus };
});

return programsData;
} catch (error) {
console.error("Failed to fetch or process the CSV:", error);
return [];
}
}

export async function getWhitelistedPrograms(): Promise<string[]> {
const programsData = await fetchProgramsData();
return programsData
.filter((program) => program.whitelistStatus === "Accepted")
.map((program) => program.programId);
}

export async function isProgramWhitelisted(
programId: string
): Promise<boolean> {
const whitelistedPrograms = await getWhitelistedPrograms();
return whitelistedPrograms.includes(programId);
}

export async function getAllProgramsData(): Promise<ProgramData[]> {
return await fetchProgramsData();
}

export async function getProgramWhitelistStatus(
programId: string
): Promise<WhitelistStatus | null> {
const programsData = await fetchProgramsData();
const program = programsData.find(
(program) => program.programId === programId
);
return program ? program.whitelistStatus : null;
}
13 changes: 10 additions & 3 deletions packages/data-layer/src/data-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -942,18 +942,25 @@ export class DataLayer {
first,
orderBy,
filter,
whitelistedPrograms,
}: {
chainIds: number[];
first: number;
orderBy?: OrderByRounds;
orderDirection?: "asc" | "desc";
filter?: RoundsQueryVariables["filter"];
whitelistedPrograms?: string[];
}): Promise<{ rounds: RoundGetRound[] }> {
return await request(this.gsIndexerEndpoint, getRoundsQuery, {
orderBy: orderBy ?? "NATURAL",
chainIds,
first,
filter,
filter: whitelistedPrograms
? {
...filter,
projectId: { in: whitelistedPrograms },
}
: filter,
});
}

Expand Down Expand Up @@ -1011,8 +1018,8 @@ export class DataLayer {
async getAttestationCount({
attestationChainIds,
}: {
attestationChainIds: number[]
attestationChainIds: number[];
}): Promise<number> {
return this.attestationService.getAttestationCount({attestationChainIds});
return this.attestationService.getAttestationCount({ attestationChainIds });
}
}
16 changes: 12 additions & 4 deletions packages/grant-explorer/src/features/api/rounds.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { getWhitelistedPrograms } from "common";
import useSWR, { SWRResponse } from "swr";
import { createISOTimestamp } from "../discovery/utils/createRoundsStatusFilter";
import { RoundGetRound, RoundsQueryVariables, useDataLayer } from "data-layer";

export const useRounds = (
variables: RoundsQueryVariables,
chainIds: number[]
chainIds: number[],
onlywWhitelistedPrograms = false
): SWRResponse<RoundGetRound[]> => {
const dataLayer = useDataLayer();

const query = useSWR(
// Cache requests on chainIds and variables as keys (when these are the
// same, cache will be used instead of new requests)
["rounds", chainIds, variables],
["rounds", chainIds, variables, onlywWhitelistedPrograms],
async () => {
const whitelistedPrograms = onlywWhitelistedPrograms
? await getWhitelistedPrograms()
: undefined;

const [spamRounds, { rounds }] = await Promise.all([
fetchSpamRounds(),
dataLayer.getRounds({
...variables,
first: 500,
chainIds,
whitelistedPrograms,
}),
]);

Expand Down Expand Up @@ -64,8 +71,9 @@ const OVERRIDE_PRIVATE_ROUND_IDS = [
export const filterOutPrivateRounds = (rounds: RoundGetRound[]) => {
return rounds.filter(
(round) =>
(round.roundMetadata && round.roundMetadata.roundType) &&
round.roundMetadata.roundType !== "private" ||
(round.roundMetadata &&
round.roundMetadata.roundType &&
round.roundMetadata.roundType !== "private") ||
OVERRIDE_PRIVATE_ROUND_IDS.includes(round.id.toLowerCase())
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,23 @@ import { useMemo } from "react";
const ExploreRoundsPage = () => {
const [params] = useSearchParams();
const filter = getRoundSelectionParamsFromUrlParams(params);

// Pass the filter from the search params and build the graphql query
const rounds = useFilterRounds(filter, getEnabledChains());
const rounds = useFilterRounds(
filter,
getEnabledChains(),
filter.status.includes("verified")
);

const publicRounds = useMemo(() => rounds.data?.filter(round => (round.roundMetadata && round.roundMetadata.roundType) && round.roundMetadata.roundType?.toLowerCase() !== "private"), [rounds]);
const publicRounds = useMemo(
() =>
rounds.data?.filter(
(round) =>
round.roundMetadata &&
round.roundMetadata.roundType &&
round.roundMetadata.roundType?.toLowerCase() !== "private"
),
[rounds]
);
rounds.data = publicRounds;

const sectionTitle = getExplorerPageTitle(filter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ export const FILTER_OPTIONS: RoundFilterUiOption[] = [
},
],
},

{
label: "Status",
value: "status",
children: [
{
label: "Verified",
value: "verified",
},
{
label: "Active",
value: RoundStatus.active,
Expand Down Expand Up @@ -75,7 +78,6 @@ export function FilterDropdown() {

const filter = getRoundSelectionParamsFromUrlParams(params);
const { status = "", type = "", network = "" } = filter;

const selected = getFilterLabel({ status, type, network });
return (
<Dropdown
Expand Down Expand Up @@ -133,7 +135,7 @@ export function FilterDropdown() {
>
<Disclosure.Panel
static
className=" mt-1 w-full overflow-auto p-2"
className="mt-1 w-full overflow-auto p-2"
>
{children
?.filter((child) => !child.hide)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import { CollectionsGrid } from "../collections/CollectionsGrid";
const LandingPage = () => {
const activeRounds = useFilterRounds(
ACTIVE_ROUNDS_FILTER,
getEnabledChains()
getEnabledChains(),
true
);
const roundsEndingSoon = useFilterRounds(
ROUNDS_ENDING_SOON_FILTER,
getEnabledChains()
getEnabledChains(),
true
);

const filteredActiveRounds = useMemo(() => {
Expand Down Expand Up @@ -81,7 +83,7 @@ const LandingPage = () => {
<ViewAllLink
to={`/rounds?${toQueryString({
orderBy: ROUNDS_ENDING_SOON_FILTER.orderBy,
status: RoundStatus.active,
status: `${RoundStatus.active},verified`,
})}`}
>
View all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export default function LandingTabs() {
{
to: `/rounds?${toQueryString({
orderBy: "MATCH_AMOUNT_IN_USD_DESC",
status: [RoundStatus.active, RoundStatus.taking_applications].join(","),
status: [
RoundStatus.active,
RoundStatus.taking_applications,
"verified",
].join(","),
})}`,
activeRegExp: /^\/rounds/,
children: isDesktop ? "Explore rounds" : "Rounds",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ vi.mock("common", async () => {
return {
...actual,
renderToPlainText: vi.fn().mockReturnValue((str = "") => str),
getWhitelistedPrograms: vi.fn().mockResolvedValue(undefined),
};
});

Expand Down Expand Up @@ -60,7 +61,6 @@ vi.mock("wagmi", async () => {
});

describe("LandingPage", () => {

it("renders landing page", () => {
renderWithContext(<LandingPage />);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export enum RoundStatus {

export const ACTIVE_ROUNDS_FILTER: RoundSelectionParams = {
orderBy: "MATCH_AMOUNT_IN_USD_DESC",
status: RoundStatus.active,
status: `${RoundStatus.active},verified`,
type: "allov2.DonationVotingMerkleDistributionDirectTransferStrategy,allov1.QF",
network: "",
};
Expand All @@ -69,12 +69,13 @@ export const ROUNDS_ENDING_SOON_FILTER: RoundSelectionParams & {
orderBy: "DONATIONS_END_TIME_ASC",
type: "",
network: "",
status: RoundStatus.ending_soon,
status: `${RoundStatus.ending_soon},verified`,
};

export const useFilterRounds = (
where: RoundSelectionParams,
chains: TChain[]
chains: TChain[],
onlywWhitelistedPrograms?: boolean
): SWRResponse<RoundGetRound[]> => {
const chainIds =
where.network === undefined || where.network.trim() === ""
Expand Down Expand Up @@ -102,7 +103,7 @@ export const useFilterRounds = (
const orderBy =
where.orderBy === undefined ? "CREATED_AT_BLOCK_DESC" : where.orderBy;
const vars = { orderBy, filter };
return useRounds(vars, chainIds);
return useRounds(vars, chainIds, onlywWhitelistedPrograms);
};

const createRoundWhereFilter = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function createRoundsStatusFilter(
): TimeFilterVariables[] {
// Default to all filters
const selectedFilters =
status ||
status.replace(",verified", "") ||
[
RoundStatus.active,
RoundStatus.taking_applications,
Expand Down
6 changes: 6 additions & 0 deletions packages/round-manager/src/assets/explorer-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit fc33919

Please sign in to comment.