Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/nextjs/app/admin/_components/GrantReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import TelegramIcon from "~~/components/assets/TelegramIcon";
import TwitterIcon from "~~/components/assets/TwitterIcon";
import { Address } from "~~/components/scaffold-eth";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";
import { GrantData, GrantDataWithPrivateNote, SocialLinks } from "~~/services/database/schema";
import { SocialLinks } from "~~/services/api/sre/schema";
import { GrantData, GrantDataWithPrivateNote } from "~~/services/database/schema";
import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants";

const BuilderSocials = ({ socialLinks }: { socialLinks?: SocialLinks }) => {
Expand Down Expand Up @@ -163,13 +164,14 @@ export const GrantReview = ({ grant, selected, toggleSelection }: GrantReviewPro
{grant.builderData?.batch?.number && (
<div className="badge badge-outline">Batch #{grant.builderData.batch?.number}</div>
)}
{/* TODO: Should we add to SRE data?
{grant.builderData?.builderCohort?.map(cohort => {
return (
<a href={cohort.url} target="_blank" rel="noreferrer" key={cohort.id} className="link">
<div className="badge badge-secondary">{cohort.name}</div>
</a>
);
})}
})} */}
</div>
</div>
<div className="p-4">
Expand Down
7 changes: 4 additions & 3 deletions packages/nextjs/app/api/admin/signin/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
import { recoverTypedDataAddress } from "viem";
import { findUserByAddress } from "~~/services/database/users";
import { fetchBuilderData } from "~~/services/api/sre/builders";
import { EIP_712_DOMAIN, EIP_712_TYPES__ADMIN_SIGN_IN } from "~~/utils/eip712";
import { validateSafeSignature } from "~~/utils/safe-signature";

Expand All @@ -10,6 +10,7 @@ type AdminSignInBody = {
isSafeSignature?: boolean;
chainId?: number;
};

export async function POST(req: Request) {
try {
const { signer, signature, isSafeSignature, chainId } = (await req.json()) as AdminSignInBody;
Expand All @@ -18,8 +19,8 @@ export async function POST(req: Request) {
return new Response("Missing signer or signature", { status: 400 });
}

const signerData = await findUserByAddress(signer);
if (signerData.data?.role !== "admin") {
const userData = await fetchBuilderData(signer);
if (userData?.role !== "admin") {
console.error("Unauthorized", signer);
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
Expand Down
9 changes: 6 additions & 3 deletions packages/nextjs/app/api/builders/[builderAddress]/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { NextResponse } from "next/server";
import { findUserByAddress } from "~~/services/database/users";
import { fetchBuilderData } from "~~/services/api/sre/builders";

export async function GET(_request: Request, { params }: { params: { builderAddress: string } }) {
try {
const builderAddress = params.builderAddress;
const builderData = await findUserByAddress(builderAddress);
return NextResponse.json(builderData);
const builderData = await fetchBuilderData(builderAddress);
if (!builderData) {
return NextResponse.json({ exists: false });
}
return NextResponse.json({ exists: true, data: builderData });
} catch (error) {
return NextResponse.json(
{ error: "Internal Server Error" },
Expand Down
7 changes: 3 additions & 4 deletions packages/nextjs/app/api/grants/[grantId]/review/route.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
import { EIP712TypedData } from "@safe-global/safe-core-sdk-types";
import { recoverTypedDataAddress } from "viem";
import { fetchBuilderData } from "~~/services/api/sre/builders";
import { reviewGrant } from "~~/services/database/grants";
import { findUserByAddress } from "~~/services/database/users";
import { EIP_712_DOMAIN, EIP_712_TYPES__REVIEW_GRANT, EIP_712_TYPES__REVIEW_GRANT_WITH_NOTE } from "~~/utils/eip712";
import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants";
import { validateSafeSignature } from "~~/utils/safe-signature";
Expand Down Expand Up @@ -89,9 +89,8 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st
}

// Only admins can review grants
const signerData = await findUserByAddress(signer);

if (signerData.data?.role !== "admin") {
const signerData = await fetchBuilderData(signer);
if (signerData?.role !== "admin") {
console.error("Unauthorized", signer);
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
Expand Down
6 changes: 3 additions & 3 deletions packages/nextjs/app/api/grants/[grantId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
import { EIP712TypedData } from "@safe-global/safe-core-sdk-types";
import { recoverTypedDataAddress } from "viem";
import { fetchBuilderData } from "~~/services/api/sre/builders";
import { updateGrant } from "~~/services/database/grants";
import { findUserByAddress } from "~~/services/database/users";
import { EIP_712_DOMAIN, EIP_712_TYPES__EDIT_GRANT } from "~~/utils/eip712";
import { validateSafeSignature } from "~~/utils/safe-signature";

Expand Down Expand Up @@ -55,8 +55,8 @@ export async function PATCH(req: NextRequest, { params }: { params: { grantId: s
}

// Only admins can edit grant
const signerData = await findUserByAddress(signer);
if (signerData.data?.role !== "admin") {
const signerData = await fetchBuilderData(signer);
if (signerData?.role !== "admin") {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
await updateGrant(grantId, { title, description, askAmount }, private_note);
Expand Down
17 changes: 3 additions & 14 deletions packages/nextjs/app/api/grants/new/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NextResponse } from "next/server";
import { recoverTypedDataAddress } from "viem";
import { createGrant } from "~~/services/database/grants";
import { findUserByAddress } from "~~/services/database/users";
import { EIP_712_DOMAIN, EIP_712_TYPES__APPLY_FOR_GRANT } from "~~/utils/eip712";
import { REQUIRED_CHALLENGE_COUNT, fetchAcceptedChallengeCount } from "~~/utils/eligibility-criteria";

Expand All @@ -24,19 +23,9 @@ export async function POST(req: Request) {
return NextResponse.json({ error: "Invalid form details submitted" }, { status: 400 });
}

// Verify if the builder is present (legacy BG check)
const builder = await findUserByAddress(signer);
let eligible = false;
if (builder.exists) {
eligible = true;
} else {
// New SRE challenge check
const completed = await fetchAcceptedChallengeCount(signer);
if (completed >= REQUIRED_CHALLENGE_COUNT) {
eligible = true;
}
}
if (!eligible) {
// Legacy BG builder presence check removed. All eligibility is now based on the new SpeedRunEthereum system. If needed we could try do some kind of ROLE validation and make sure OG BuidlGuidl members that that certain ROLE in new SRE database.
const completed = await fetchAcceptedChallengeCount(signer);
if (completed < REQUIRED_CHALLENGE_COUNT) {
return NextResponse.json(
{
error: `Only builders with at least ${REQUIRED_CHALLENGE_COUNT} accepted SpeedRun Ethereum challenges can submit for grants`,
Expand Down
10 changes: 5 additions & 5 deletions packages/nextjs/app/api/grants/review/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { EIP712TypedData } from "@safe-global/safe-core-sdk-types";
import { recoverTypedDataAddress } from "viem";
import { fetchBuilderData } from "~~/services/api/sre/builders";
import { getAllGrantsForReview, reviewGrant } from "~~/services/database/grants";
import { findUserByAddress } from "~~/services/database/users";
import { EIP_712_DOMAIN, EIP_712_TYPES__REVIEW_GRANT_BATCH } from "~~/utils/eip712";
import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants";
import { validateSafeSignature } from "~~/utils/safe-signature";
Expand All @@ -26,8 +26,8 @@ export async function GET() {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const signerData = await findUserByAddress(address);
if (signerData.data?.role !== "admin") {
const signerData = await fetchBuilderData(address);
if (signerData?.role !== "admin") {
console.error("Unauthorized", address);
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
Expand Down Expand Up @@ -76,8 +76,8 @@ export async function POST(req: NextRequest) {
}

// Only admins can review grants
const signerData = await findUserByAddress(signer);
if (signerData.data?.role !== "admin") {
const signerData = await fetchBuilderData(signer);
if (signerData?.role !== "admin") {
console.error("Unauthorized", signer);
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/hooks/useBGBuilderData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import useSWRImmutable from "swr/immutable";
import { BuilderDataResponse } from "~~/services/database/schema";
import { BuilderDataResponse } from "~~/services/api/sre/schema";

export const useBGBuilderData = (address?: string) => {
const {
Expand Down
24 changes: 24 additions & 0 deletions packages/nextjs/services/api/sre/builders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { BuilderData, SocialLinks } from "./schema";

export async function fetchBuilderData(address: string): Promise<BuilderData | undefined> {
const res = await fetch(`https://speedrunethereum.com/api/users/${address}`);
if (!res.ok) return undefined;
const apiData = await res.json();
const user = apiData.user;
const socialLinks: SocialLinks = {
telegram: user.socialTelegram,
twitter: user.socialX,
github: user.socialGithub,
instagram: user.socialInstagram,
discord: user.socialDiscord,
email: user.socialEmail,
};
return {
id: user.userAddress,
role: user.role ? user.role.toLowerCase() : undefined,
socialLinks,
batch: user.batchId
? { number: String(Number(user.batchId) - 1), status: user.batchStatus }
: undefined,
};
}
20 changes: 20 additions & 0 deletions packages/nextjs/services/api/sre/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type SocialLinks = {
twitter?: string;
github?: string;
discord?: string;
telegram?: string;
instagram?: string;
email?: string;
};

export type BuilderData = {
id: string;
role?: "anonymous" | "user" | "admin";
socialLinks?: SocialLinks;
batch?: { number: string; status: string };
};

export type BuilderDataResponse = {
exists: boolean;
data?: BuilderData;
};
9 changes: 4 additions & 5 deletions packages/nextjs/services/database/grants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getFirestoreConnector } from "./firestoreDB";
import { BuilderData, GrantData, GrantDataWithPrivateNote } from "./schema";
import { GrantData, GrantDataWithPrivateNote } from "./schema";
import { fetchBuilderData } from "../api/sre/builders";
import ecosystemGrants from "~~/services/database/ecosystemGrants.json";
import { findUserByAddress } from "~~/services/database/users";
import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants";

const firestoreDB = getFirestoreConnector();
Expand Down Expand Up @@ -63,13 +63,12 @@ export const getAllGrantsForReview = async () => {
const privateNotes = doc.ref.collection("private_note");
const privateNotesSnapshot = await privateNotes.get();
const private_note = privateNotesSnapshot.empty ? undefined : privateNotesSnapshot.docs[0].data().note;
const builderDataResponse = await findUserByAddress(grantData.builder);

const builderData = await fetchBuilderData(grantData.builder);
return {
id: doc.id,
...grantData,
private_note,
builderData: builderDataResponse.exists ? (builderDataResponse.data as BuilderData) : undefined,
builderData,
} as GrantDataWithPrivateNote;
});

Expand Down
39 changes: 1 addition & 38 deletions packages/nextjs/services/database/schema.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import type { Simplify } from "type-fest";

export type SocialLinks = {
twitter?: string;
github?: string;
discord?: string;
telegram?: string;
instagram?: string;
email?: string;
};
import type { BuilderData } from "../api/sre/schema";

type Build = {
submittedTimestamp: number;
Expand All @@ -24,35 +16,6 @@ type Graduated = {
status: boolean;
};

export type BuilderData = {
id: string;
socialLinks?: SocialLinks;
role?: "anonymous" | "builder" | "admin";
function?: string;
creationTimestamp?: number;
builds?: Build[];
status?: Status;
graduated?: Graduated;
batch?: {
number: string;
status: string;
};
builderCohort?: { id: string; name: string; url: string }[];
stream?: {
balance: string;
cap: string;
frequency: number;
lastContract: number;
lastIndexedBlock: number;
streamAddress: string;
};
};

export type BuilderDataResponse = {
exists: boolean;
data?: BuilderData;
};

export type GrantWithoutTimestamps = {
id: string;
title: string;
Expand Down
21 changes: 0 additions & 21 deletions packages/nextjs/services/database/users.ts

This file was deleted.