From 5a8f59716cd01b203e30b1339e383f717e2ef58f Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:49:24 +0200 Subject: [PATCH 01/12] Initial logic to send grants to sre --- .../nextjs/app/admin/hooks/useReviewGrant.ts | 2 + .../app/api/grants/[grantId]/review/route.tsx | 46 ++++++++++++++++++- packages/nextjs/utils/eip712.ts | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/app/admin/hooks/useReviewGrant.ts b/packages/nextjs/app/admin/hooks/useReviewGrant.ts index a39f991..1a199e5 100644 --- a/packages/nextjs/app/admin/hooks/useReviewGrant.ts +++ b/packages/nextjs/app/admin/hooks/useReviewGrant.ts @@ -49,6 +49,7 @@ export const useReviewGrant = (grant: GrantData) => { action: action, txHash: txnHash, txChainId: connectedChain.id.toString(), + link: grant.link ?? "", note: note ?? "", }, }); @@ -62,6 +63,7 @@ export const useReviewGrant = (grant: GrantData) => { action: action, txHash: txnHash, txChainId: connectedChain.id.toString(), + link: grant.link ?? "", }, }); } diff --git a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx index 52ae0e6..c60fb8f 100644 --- a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx +++ b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx @@ -13,13 +13,20 @@ type ReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; + link: string; note?: string; isSafeSignature?: boolean; }; export async function POST(req: NextRequest, { params }: { params: { grantId: string } }) { const { grantId } = params; - const { signature, signer, action, txHash, txChainId, note, isSafeSignature } = (await req.json()) as ReqBody; + const { signature, signer, action, txHash, txChainId, note, isSafeSignature, link } = (await req.json()) as ReqBody; + + // Log the incoming payload for debugging + console.log( + "[REVIEW API] Incoming payload:", + JSON.stringify({ grantId, action, txHash, txChainId, link, signature, signer, note }, null, 2), + ); // Validate action is valid const validActions = Object.values(PROPOSAL_STATUS); @@ -28,6 +35,11 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } + // For COMPLETED, require link to be a non-empty string + if (action === PROPOSAL_STATUS.COMPLETED && (typeof link !== "string" || !link.length)) { + return NextResponse.json({ error: "Missing or invalid link for completed grant" }, { status: 400 }); + } + let isValidSignature: boolean; // If action is approved or rejected, include note in signature @@ -41,6 +53,7 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st action: action, txHash, txChainId, + link: link ?? "", note: note ?? "", }, signature, @@ -67,6 +80,7 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st action: action, txHash, txChainId, + link: link ?? "", }, signature, } as const; @@ -104,6 +118,36 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st note, txChainId, }); + + // If the action is COMPLETED, forward the signed payload to SRE + if (action === PROPOSAL_STATUS.COMPLETED) { + // Fetch the grant to get the build link + const grant = await import("~~/services/database/grants").then(m => m.getGrantById(grantId)); + if (grant && grant.link) { + const srePayload = { + grantId, + action, + txHash, + txChainId, + link: grant.link, + signature, + signer, + note, + }; + console.log("[SRE API] Sending payload:", JSON.stringify(srePayload, null, 2)); // Debug log + try { + await fetch(process.env.SRE_API_URL || "https://speedrunethereum/api/grant-completed", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(srePayload), + }); + } catch (sreError) { + console.error("Error forwarding grant completion to SRE", sreError); + } + } else { + console.warn("No build link found for grant, not forwarding to SRE", grantId); + } + } } catch (error) { console.error("Error approving grant", error); return NextResponse.json({ error: "Error approving grant" }, { status: 500 }); diff --git a/packages/nextjs/utils/eip712.ts b/packages/nextjs/utils/eip712.ts index 5f47cd2..bc48bdc 100644 --- a/packages/nextjs/utils/eip712.ts +++ b/packages/nextjs/utils/eip712.ts @@ -26,6 +26,7 @@ export const EIP_712_TYPES__REVIEW_GRANT = { { name: "action", type: "string" }, { name: "txHash", type: "string" }, { name: "txChainId", type: "string" }, + { name: "link", type: "string" }, ], } as const; From 93fc76d0b1e2032bda3dd840b24695b23d4ae34c Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:20:45 +0200 Subject: [PATCH 02/12] Add missing link field --- packages/nextjs/app/admin/hooks/useReviewGrant.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nextjs/app/admin/hooks/useReviewGrant.ts b/packages/nextjs/app/admin/hooks/useReviewGrant.ts index 1a199e5..9a17d33 100644 --- a/packages/nextjs/app/admin/hooks/useReviewGrant.ts +++ b/packages/nextjs/app/admin/hooks/useReviewGrant.ts @@ -16,6 +16,7 @@ type ReqBody = { txChainId: string; note?: string; isSafeSignature?: boolean; + link: string; }; export const useReviewGrant = (grant: GrantData) => { @@ -85,6 +86,7 @@ export const useReviewGrant = (grant: GrantData) => { txChainId: connectedChain.id.toString(), note, isSafeSignature, + link: grant.link ?? "", }); await mutate("/api/grants/review"); notification.remove(notificationId); From 033619cb433c9a9f395eb8f6c79b6ab4a3e12bb9 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:21:20 +0200 Subject: [PATCH 03/12] Add safe signature support and update manual api url fallback --- packages/nextjs/app/api/grants/[grantId]/review/route.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx index c60fb8f..420b192 100644 --- a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx +++ b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx @@ -25,7 +25,7 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st // Log the incoming payload for debugging console.log( "[REVIEW API] Incoming payload:", - JSON.stringify({ grantId, action, txHash, txChainId, link, signature, signer, note }, null, 2), + JSON.stringify({ grantId, action, txHash, txChainId, link, signature, signer, note, isSafeSignature }, null, 2), ); // Validate action is valid @@ -133,10 +133,11 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st signature, signer, note, + isSafeSignature, }; console.log("[SRE API] Sending payload:", JSON.stringify(srePayload, null, 2)); // Debug log try { - await fetch(process.env.SRE_API_URL || "https://speedrunethereum/api/grant-completed", { + await fetch(process.env.SRE_API_URL || "https://speedrunethereum.com/api/builds/grant-completed", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(srePayload), From 8b8a748e5f4c4061f43109d40868ca9f50e51958 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Fri, 8 Aug 2025 01:15:41 +0200 Subject: [PATCH 04/12] Delete logging and redundant comment --- .../nextjs/app/api/grants/[grantId]/review/route.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx index 420b192..f23f843 100644 --- a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx +++ b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx @@ -22,16 +22,9 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st const { grantId } = params; const { signature, signer, action, txHash, txChainId, note, isSafeSignature, link } = (await req.json()) as ReqBody; - // Log the incoming payload for debugging - console.log( - "[REVIEW API] Incoming payload:", - JSON.stringify({ grantId, action, txHash, txChainId, link, signature, signer, note, isSafeSignature }, null, 2), - ); - // Validate action is valid const validActions = Object.values(PROPOSAL_STATUS); if (!validActions.includes(action)) { - console.error("Invalid action", action); return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } @@ -98,7 +91,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st } if (!isValidSignature) { - console.error("Invalid signature", signer); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } @@ -106,7 +98,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st const signerData = await findUserByAddress(signer); if (signerData.data?.role !== "admin") { - console.error("Unauthorized", signer); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } @@ -121,7 +112,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st // If the action is COMPLETED, forward the signed payload to SRE if (action === PROPOSAL_STATUS.COMPLETED) { - // Fetch the grant to get the build link const grant = await import("~~/services/database/grants").then(m => m.getGrantById(grantId)); if (grant && grant.link) { const srePayload = { @@ -135,7 +125,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st note, isSafeSignature, }; - console.log("[SRE API] Sending payload:", JSON.stringify(srePayload, null, 2)); // Debug log try { await fetch(process.env.SRE_API_URL || "https://speedrunethereum.com/api/builds/grant-completed", { method: "POST", @@ -150,7 +139,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st } } } catch (error) { - console.error("Error approving grant", error); return NextResponse.json({ error: "Error approving grant" }, { status: 500 }); } From bbb26e571b621fcb45678e2bd0d97ede8610bf09 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Fri, 8 Aug 2025 01:25:00 +0200 Subject: [PATCH 05/12] Add missing field --- packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts b/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts index 4f2a951..807f3fa 100644 --- a/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts +++ b/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts @@ -15,6 +15,7 @@ type BatchReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; + link: string; }[]; isSafeSignature?: boolean; }; @@ -42,6 +43,7 @@ export const useBatchReviewGrants = () => { action, txHash, txChainId: connectedChain.id.toString(), + link: "", }; }); From 9eca39f0fa1459006c147ab994a3c413e79ec867 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Fri, 8 Aug 2025 01:30:49 +0200 Subject: [PATCH 06/12] Add missing field --- packages/nextjs/app/api/grants/review/route.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nextjs/app/api/grants/review/route.tsx b/packages/nextjs/app/api/grants/review/route.tsx index bb7847f..645c5b1 100644 --- a/packages/nextjs/app/api/grants/review/route.tsx +++ b/packages/nextjs/app/api/grants/review/route.tsx @@ -53,6 +53,7 @@ type BatchReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; + link: string; }[]; isSafeSignature?: boolean; }; From 0b4bba942895c4642c9ab8f53c61ddbae953d8a5 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:23:25 +0200 Subject: [PATCH 07/12] Simplify sre complete grants to use APIkey + buildId --- .../app/admin/hooks/useBatchReviewGrants.ts | 2 - .../nextjs/app/admin/hooks/useReviewGrant.ts | 4 -- .../app/api/grants/[grantId]/review/route.tsx | 39 +-------------- .../nextjs/app/api/grants/review/route.tsx | 1 - packages/nextjs/services/database/grants.ts | 13 +++++ packages/nextjs/services/sre.ts | 48 +++++++++++++++++++ packages/nextjs/utils/eip712.ts | 1 - 7 files changed, 62 insertions(+), 46 deletions(-) create mode 100644 packages/nextjs/services/sre.ts diff --git a/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts b/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts index 807f3fa..4f2a951 100644 --- a/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts +++ b/packages/nextjs/app/admin/hooks/useBatchReviewGrants.ts @@ -15,7 +15,6 @@ type BatchReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; - link: string; }[]; isSafeSignature?: boolean; }; @@ -43,7 +42,6 @@ export const useBatchReviewGrants = () => { action, txHash, txChainId: connectedChain.id.toString(), - link: "", }; }); diff --git a/packages/nextjs/app/admin/hooks/useReviewGrant.ts b/packages/nextjs/app/admin/hooks/useReviewGrant.ts index 9a17d33..a39f991 100644 --- a/packages/nextjs/app/admin/hooks/useReviewGrant.ts +++ b/packages/nextjs/app/admin/hooks/useReviewGrant.ts @@ -16,7 +16,6 @@ type ReqBody = { txChainId: string; note?: string; isSafeSignature?: boolean; - link: string; }; export const useReviewGrant = (grant: GrantData) => { @@ -50,7 +49,6 @@ export const useReviewGrant = (grant: GrantData) => { action: action, txHash: txnHash, txChainId: connectedChain.id.toString(), - link: grant.link ?? "", note: note ?? "", }, }); @@ -64,7 +62,6 @@ export const useReviewGrant = (grant: GrantData) => { action: action, txHash: txnHash, txChainId: connectedChain.id.toString(), - link: grant.link ?? "", }, }); } @@ -86,7 +83,6 @@ export const useReviewGrant = (grant: GrantData) => { txChainId: connectedChain.id.toString(), note, isSafeSignature, - link: grant.link ?? "", }); await mutate("/api/grants/review"); notification.remove(notificationId); diff --git a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx index f23f843..04f981f 100644 --- a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx +++ b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx @@ -13,14 +13,13 @@ type ReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; - link: string; note?: string; isSafeSignature?: boolean; }; export async function POST(req: NextRequest, { params }: { params: { grantId: string } }) { const { grantId } = params; - const { signature, signer, action, txHash, txChainId, note, isSafeSignature, link } = (await req.json()) as ReqBody; + const { signature, signer, action, txHash, txChainId, note, isSafeSignature } = (await req.json()) as ReqBody; // Validate action is valid const validActions = Object.values(PROPOSAL_STATUS); @@ -28,11 +27,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } - // For COMPLETED, require link to be a non-empty string - if (action === PROPOSAL_STATUS.COMPLETED && (typeof link !== "string" || !link.length)) { - return NextResponse.json({ error: "Missing or invalid link for completed grant" }, { status: 400 }); - } - let isValidSignature: boolean; // If action is approved or rejected, include note in signature @@ -46,7 +40,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st action: action, txHash, txChainId, - link: link ?? "", note: note ?? "", }, signature, @@ -73,7 +66,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st action: action, txHash, txChainId, - link: link ?? "", }, signature, } as const; @@ -109,35 +101,6 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st note, txChainId, }); - - // If the action is COMPLETED, forward the signed payload to SRE - if (action === PROPOSAL_STATUS.COMPLETED) { - const grant = await import("~~/services/database/grants").then(m => m.getGrantById(grantId)); - if (grant && grant.link) { - const srePayload = { - grantId, - action, - txHash, - txChainId, - link: grant.link, - signature, - signer, - note, - isSafeSignature, - }; - try { - await fetch(process.env.SRE_API_URL || "https://speedrunethereum.com/api/builds/grant-completed", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(srePayload), - }); - } catch (sreError) { - console.error("Error forwarding grant completion to SRE", sreError); - } - } else { - console.warn("No build link found for grant, not forwarding to SRE", grantId); - } - } } catch (error) { return NextResponse.json({ error: "Error approving grant" }, { status: 500 }); } diff --git a/packages/nextjs/app/api/grants/review/route.tsx b/packages/nextjs/app/api/grants/review/route.tsx index 645c5b1..bb7847f 100644 --- a/packages/nextjs/app/api/grants/review/route.tsx +++ b/packages/nextjs/app/api/grants/review/route.tsx @@ -53,7 +53,6 @@ type BatchReqBody = { action: ProposalStatusType; txHash: string; txChainId: string; - link: string; }[]; isSafeSignature?: boolean; }; diff --git a/packages/nextjs/services/database/grants.ts b/packages/nextjs/services/database/grants.ts index 5021239..da71ce4 100644 --- a/packages/nextjs/services/database/grants.ts +++ b/packages/nextjs/services/database/grants.ts @@ -3,6 +3,7 @@ import { BuilderData, GrantData, GrantDataWithPrivateNote } from "./schema"; import ecosystemGrants from "~~/services/database/ecosystemGrants.json"; import { findUserByAddress } from "~~/services/database/users"; import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants"; +import { sendBuildToSRE, extractBuildId } from "../sre"; const firestoreDB = getFirestoreConnector(); const grantsCollection = firestoreDB.collection("grants"); @@ -177,6 +178,18 @@ export const reviewGrant = async ({ grantId, action, txHash, txChainId, note }: updateData[grantActionTimeStampKey] = grantActionTimeStamp; await grantsCollection.doc(grantId).update(updateData); + + // Notify SpeedRunEthereum once the grant is marked as COMPLETED. + if (action === PROPOSAL_STATUS.COMPLETED) { + const buildLink = grantSnapshot.data()?.link as string | undefined; + const buildId = extractBuildId(buildLink); + if (buildId) { + // we don't want to block the main flow on this third-party request. + void sendBuildToSRE(buildId); + } else { + console.warn("[SRE] Could not derive buildId from link – skipping SRE sync", grantId); + } + } } catch (error) { console.error("Error processing the grant:", error); throw error; diff --git a/packages/nextjs/services/sre.ts b/packages/nextjs/services/sre.ts new file mode 100644 index 0000000..eaafc7b --- /dev/null +++ b/packages/nextjs/services/sre.ts @@ -0,0 +1,48 @@ +export const extractBuildId = (link: string | undefined | null): string | null => { + if (!link) return null; + + try { + const url = new URL(link); + // Only try to extract the buildId from speedrunethereum.com + if (!url.hostname.endsWith("speedrunethereum.com")) { + return null; + } + + const parts = url.pathname.split("/").filter(Boolean); + const id = parts.pop(); + if (parts[parts.length - 1] !== "builds" || !id) { + return null; + } + return id; + } catch { + return null; + } +}; + +export const sendBuildToSRE = async (buildId: string): Promise => { + const apiUrl = process.env.SRE_API_URL; + const apiKey = process.env.SRE_API_KEY; + + if (!apiUrl) { + console.warn("[SRE] SRE_API_URL env var is not set, skipping call to SRE"); + return; + } + if (!apiKey) { + console.warn("[SRE] SRE_API_KEY env var is not set, skipping call to SRE"); + return; + } + if (!buildId) { + console.warn("[SRE] No buildId supplied, nothing to send to SRE"); + return; + } + + try { + await fetch(apiUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ buildId, apiKey }), + }); + } catch (error) { + console.error("[SRE] Failed to send build information to SRE", error); + } +}; diff --git a/packages/nextjs/utils/eip712.ts b/packages/nextjs/utils/eip712.ts index bc48bdc..5f47cd2 100644 --- a/packages/nextjs/utils/eip712.ts +++ b/packages/nextjs/utils/eip712.ts @@ -26,7 +26,6 @@ export const EIP_712_TYPES__REVIEW_GRANT = { { name: "action", type: "string" }, { name: "txHash", type: "string" }, { name: "txChainId", type: "string" }, - { name: "link", type: "string" }, ], } as const; From 2d7fe38cf7cc465dad7c0b5b9d2bff238784bd5b Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Tue, 12 Aug 2025 18:13:27 +0200 Subject: [PATCH 08/12] Add env vars --- packages/nextjs/.env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nextjs/.env.example b/packages/nextjs/.env.example index ededfd7..6904792 100644 --- a/packages/nextjs/.env.example +++ b/packages/nextjs/.env.example @@ -18,3 +18,7 @@ FIREBASE_PROJECT_ID="buidlguidl-v3" # Setup api-key for admin ADMIN_API_KEY="admin-api-key" + +# For Grants-SRE grant completion sync +SRE_API_KEY= +SRE_API_URL= From 02f08577da5095c46b170a40fb6181e14035f67b Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:09:14 +0200 Subject: [PATCH 09/12] Add vercel functions --- packages/nextjs/package.json | 1 + yarn.lock | 34 ++++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 141d485..159f0cb 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -21,6 +21,7 @@ "@safe-global/safe-core-sdk-types": "^5.0.3", "@uniswap/sdk-core": "^4.0.1", "@uniswap/v2-sdk": "^3.0.1", + "@vercel/functions": "^2.0.0", "blo": "^1.0.1", "daisyui": "4.5.0", "firebase-admin": "^11.11.1", diff --git a/yarn.lock b/yarn.lock index c3703de..68eed8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2358,6 +2358,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.39.0 "@uniswap/sdk-core": ^4.0.1 "@uniswap/v2-sdk": ^3.0.1 + "@vercel/functions": ^2.0.0 autoprefixer: ^10.4.12 blo: ^1.0.1 daisyui: 4.5.0 @@ -3701,6 +3702,20 @@ __metadata: languageName: node linkType: hard +"@vercel/functions@npm:^2.0.0": + version: 2.2.8 + resolution: "@vercel/functions@npm:2.2.8" + dependencies: + "@vercel/oidc": 2.0.0 + peerDependencies: + "@aws-sdk/credential-provider-web-identity": "*" + peerDependenciesMeta: + "@aws-sdk/credential-provider-web-identity": + optional: true + checksum: 06e07f83205dc328018e9dcb6c41f3a71ac3ad2d58707748bda8877cf1877044ec2d959b6f50c008b0ef1310c168986023605180dfe108364926bbb15522ba46 + languageName: node + linkType: hard + "@vercel/gatsby-plugin-vercel-analytics@npm:1.0.11": version: 1.0.11 resolution: "@vercel/gatsby-plugin-vercel-analytics@npm:1.0.11" @@ -3798,6 +3813,13 @@ __metadata: languageName: node linkType: hard +"@vercel/oidc@npm:2.0.0": + version: 2.0.0 + resolution: "@vercel/oidc@npm:2.0.0" + checksum: cb93084e1996dd021fff0e3e88ecd7b33e6451a1646279b6d7309188d2bc3c4249031eb8e40af52e4e145065daee086b8d57cf785be4589a285b2a2643baa125 + languageName: node + linkType: hard + "@vercel/python@npm:4.0.2": version: 4.0.2 resolution: "@vercel/python@npm:4.0.2" @@ -9467,12 +9489,12 @@ __metadata: languageName: node linkType: hard -"husky@npm:^8.0.1": - version: 8.0.3 - resolution: "husky@npm:8.0.3" +"husky@npm:~9.1.7": + version: 9.1.7 + resolution: "husky@npm:9.1.7" bin: - husky: lib/bin.js - checksum: 837bc7e4413e58c1f2946d38fb050f5d7324c6f16b0fd66411ffce5703b294bd21429e8ba58711cd331951ee86ed529c5be4f76805959ff668a337dbfa82a1b0 + husky: bin.js + checksum: c2412753f15695db369634ba70f50f5c0b7e5cb13b673d0826c411ec1bd9ddef08c1dad89ea154f57da2521d2605bd64308af748749b27d08c5f563bcd89975f languageName: node linkType: hard @@ -13732,7 +13754,7 @@ __metadata: version: 0.0.0-use.local resolution: "se-2@workspace:." dependencies: - husky: ^8.0.1 + husky: ~9.1.7 lint-staged: ^13.0.3 languageName: unknown linkType: soft From 56d4e4b4e1e3c01cf5a8ba1322059996371a23e4 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:21:49 +0200 Subject: [PATCH 10/12] refactor --- packages/nextjs/services/sre.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/nextjs/services/sre.ts b/packages/nextjs/services/sre.ts index eaafc7b..c755eb0 100644 --- a/packages/nextjs/services/sre.ts +++ b/packages/nextjs/services/sre.ts @@ -23,12 +23,8 @@ export const sendBuildToSRE = async (buildId: string): Promise => { const apiUrl = process.env.SRE_API_URL; const apiKey = process.env.SRE_API_KEY; - if (!apiUrl) { - console.warn("[SRE] SRE_API_URL env var is not set, skipping call to SRE"); - return; - } - if (!apiKey) { - console.warn("[SRE] SRE_API_KEY env var is not set, skipping call to SRE"); + if (!apiUrl || !apiKey) { + console.warn("[SRE] Missing SRE config (SRE_API_URL or SRE_API_KEY); skipping call"); return; } if (!buildId) { @@ -37,11 +33,16 @@ export const sendBuildToSRE = async (buildId: string): Promise => { } try { - await fetch(apiUrl, { + const payload = { buildId, apiKey }; + const response = await fetch(apiUrl, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ buildId, apiKey }), + body: JSON.stringify(payload), }); + + if (!response.ok) { + console.error("[SRE] SRE endpoint returned error status:", response.status); + } } catch (error) { console.error("[SRE] Failed to send build information to SRE", error); } From 5c1f69713737be696a0e7ba04b38a8085fa3cb14 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:26:56 +0200 Subject: [PATCH 11/12] Move the SRE api call to router --- .../app/api/grants/[grantId]/review/route.tsx | 25 ++++++++++++++++++- packages/nextjs/services/database/grants.ts | 13 ---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx index 04f981f..4e2fde4 100644 --- a/packages/nextjs/app/api/grants/[grantId]/review/route.tsx +++ b/packages/nextjs/app/api/grants/[grantId]/review/route.tsx @@ -1,8 +1,10 @@ import { NextRequest, NextResponse } from "next/server"; import { EIP712TypedData } from "@safe-global/safe-core-sdk-types"; +import { waitUntil } from "@vercel/functions"; import { recoverTypedDataAddress } from "viem"; -import { reviewGrant } from "~~/services/database/grants"; +import { getGrantById, reviewGrant } from "~~/services/database/grants"; import { findUserByAddress } from "~~/services/database/users"; +import { extractBuildId, sendBuildToSRE } from "~~/services/sre"; 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"; @@ -105,5 +107,26 @@ export async function POST(req: NextRequest, { params }: { params: { grantId: st return NextResponse.json({ error: "Error approving grant" }, { status: 500 }); } + // Send build to SRE to mark it as bgGrant + if (action === PROPOSAL_STATUS.COMPLETED) { + // Use waitUntil for background processing + waitUntil( + (async () => { + try { + const grant = await getGrantById(grantId); + const buildId = extractBuildId(grant?.link); + if (buildId) { + await sendBuildToSRE(buildId); + } else { + console.warn("[SRE] Could not derive buildId from link – skipping SRE sync", grantId); + } + } catch (sreError) { + // Log SRE errors but don't fail the main operation + console.error("[SRE] Error notifying SpeedRunEthereum:", sreError); + } + })(), + ); + } + return NextResponse.json({ success: true }); } diff --git a/packages/nextjs/services/database/grants.ts b/packages/nextjs/services/database/grants.ts index da71ce4..5021239 100644 --- a/packages/nextjs/services/database/grants.ts +++ b/packages/nextjs/services/database/grants.ts @@ -3,7 +3,6 @@ import { BuilderData, GrantData, GrantDataWithPrivateNote } from "./schema"; import ecosystemGrants from "~~/services/database/ecosystemGrants.json"; import { findUserByAddress } from "~~/services/database/users"; import { PROPOSAL_STATUS, ProposalStatusType } from "~~/utils/grants"; -import { sendBuildToSRE, extractBuildId } from "../sre"; const firestoreDB = getFirestoreConnector(); const grantsCollection = firestoreDB.collection("grants"); @@ -178,18 +177,6 @@ export const reviewGrant = async ({ grantId, action, txHash, txChainId, note }: updateData[grantActionTimeStampKey] = grantActionTimeStamp; await grantsCollection.doc(grantId).update(updateData); - - // Notify SpeedRunEthereum once the grant is marked as COMPLETED. - if (action === PROPOSAL_STATUS.COMPLETED) { - const buildLink = grantSnapshot.data()?.link as string | undefined; - const buildId = extractBuildId(buildLink); - if (buildId) { - // we don't want to block the main flow on this third-party request. - void sendBuildToSRE(buildId); - } else { - console.warn("[SRE] Could not derive buildId from link – skipping SRE sync", grantId); - } - } } catch (error) { console.error("Error processing the grant:", error); throw error; From 4092ac9920cdfd4ffc118768222fe2b3157919d4 Mon Sep 17 00:00:00 2001 From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:39:23 +0200 Subject: [PATCH 12/12] downgrade husky --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 68eed8e..1f36134 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9489,12 +9489,12 @@ __metadata: languageName: node linkType: hard -"husky@npm:~9.1.7": - version: 9.1.7 - resolution: "husky@npm:9.1.7" +"husky@npm:^8.0.1": + version: 8.0.3 + resolution: "husky@npm:8.0.3" bin: - husky: bin.js - checksum: c2412753f15695db369634ba70f50f5c0b7e5cb13b673d0826c411ec1bd9ddef08c1dad89ea154f57da2521d2605bd64308af748749b27d08c5f563bcd89975f + husky: lib/bin.js + checksum: 837bc7e4413e58c1f2946d38fb050f5d7324c6f16b0fd66411ffce5703b294bd21429e8ba58711cd331951ee86ed529c5be4f76805959ff668a337dbfa82a1b0 languageName: node linkType: hard @@ -13754,7 +13754,7 @@ __metadata: version: 0.0.0-use.local resolution: "se-2@workspace:." dependencies: - husky: ~9.1.7 + husky: ^8.0.1 lint-staged: ^13.0.3 languageName: unknown linkType: soft