diff --git a/apps/api/index.ts b/apps/api/index.ts index e791ed7..ddaf2ba 100644 --- a/apps/api/index.ts +++ b/apps/api/index.ts @@ -1,9 +1,10 @@ import express from "express" import { authMiddleware } from "./middleware"; import { prismaClient } from "db/client"; +import {Prisma} from "db/client"; import cors from "cors"; -import { Transaction, SystemProgram, Connection } from "@solana/web3.js"; - +import { Transaction, SystemProgram, Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js"; +const privateKey=process.env.PRIVATE_KEY; const connection = new Connection("https://api.mainnet-beta.solana.com"); const app = express(); @@ -42,7 +43,9 @@ app.get("/api/v1/website/status", authMiddleware, async (req, res) => { } }) - res.json(data) + res.json({ + data + }); }) @@ -84,7 +87,94 @@ app.delete("/api/v1/website/", authMiddleware, async (req, res) => { }) app.post("/api/v1/payout/:validatorId", async (req, res) => { - + const validatorId=req.params.validatorId; + + const txn=await prismaClient.$transaction(async (prisma)=>{ + const validator=await prisma.validator.findUnique({ + where:{ + id: validatorId + }, + select:{ + id: true, + pendingPayouts: true, + publicKey: true, + lockedAt: true + } + }) + + if (!validator){ + res.status(404).json({ + message: "Validator not found" + }); + return; + } + + if (validator.lockedAt){ + res.json({ + message: "Payout is still in process" + }); + return; + } + + if (validator.pendingPayouts===0){ + res.json({ + message: "No payout left" + }); + return; + } + + await prisma.validator.update({ + where:{ + id: validatorId + }, + data:{ + lockedAt: new Date() + } + }); + return validator; + }); + if (!txn) return; + + try{ + const fromKeypair=Keypair.fromSecretKey(Uint8Array.from(JSON.parse(privateKey!))); + const toPublicKey= new PublicKey(txn.publicKey); + const amount=txn.pendingPayouts * 1000000; + + const transaction=new Transaction().add( + SystemProgram.transfer({ + fromPubkey: fromKeypair.publicKey, + toPubkey: toPublicKey, + lamports: amount + }) + ); + + const signature=await sendAndConfirmTransaction(connection, transaction, [fromKeypair]); + await prismaClient.validator.update({ + where:{id: validatorId}, + data:{ + pendingPayouts: 0, + isPaidOut: true, + lockedAt: null, + transactions:{ + create:{ + amount: amount, + signature: signature, + createdAt: new Date + } as Prisma.TransactionsCreateWithoutValidatorInput + } + } + }); + + res.json({ + message: "Payout Successful with signature: ", signature + }) + }catch(e){ + console.log("Error processing payout", e); + res.status(500).json({ + messsage: "Error processing payout" + }); + } + }) app.listen(8080); diff --git a/apps/api/middleware.ts b/apps/api/middleware.ts index 5217ce0..ba9e0ff 100644 --- a/apps/api/middleware.ts +++ b/apps/api/middleware.ts @@ -5,16 +5,21 @@ import { JWT_PUBLIC_KEY } from "./config"; export function authMiddleware(req: Request, res: Response, next: NextFunction) { const token = req.headers['authorization']; if (!token) { - return res.status(401).json({ error: 'Unauthorized' }); + res.status(401).json({ error: 'Unauthorized' }); + return; } - - const decoded = jwt.verify(token, JWT_PUBLIC_KEY); - console.log(decoded); - if (!decoded || !decoded.sub) { - return res.status(401).json({ error: 'Unauthorized' }); + try{ + const decoded = jwt.verify(token, JWT_PUBLIC_KEY); + console.log(decoded); + if (!decoded || !decoded.sub) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + req.userId = decoded.sub as string; + next(); + }catch(e){ + res.status(401).json({ + error: 'Invalid Token' + }) } - - req.userId = decoded.sub as string; - - next() } \ No newline at end of file diff --git a/apps/api/types.d.ts b/apps/api/types.d.ts index 9bdaea1..59f6419 100644 --- a/apps/api/types.d.ts +++ b/apps/api/types.d.ts @@ -1,4 +1,3 @@ - declare namespace Express { interface Request { userId?: string diff --git a/apps/frontend/.gitignore b/apps/frontend/.gitignore index 5ef6a52..e2764f9 100644 --- a/apps/frontend/.gitignore +++ b/apps/frontend/.gitignore @@ -39,3 +39,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/apps/validator/index.ts b/apps/validator/index.ts index 1736c7e..7f6b1d3 100644 --- a/apps/validator/index.ts +++ b/apps/validator/index.ts @@ -92,6 +92,4 @@ async function signMessage(message: string, keypair: Keypair) { main(); -setInterval(async () => { - -}, 10000); \ No newline at end of file +setInterval(async () => {}, 10000); \ No newline at end of file diff --git a/bun.lock b/bun.lock index 1b01488..a9b67e1 100644 --- a/bun.lock +++ b/bun.lock @@ -105,6 +105,7 @@ "packages/db": { "name": "db", "dependencies": { + "@prisma/client": "6.5.0", "prisma": "^6.5.0", }, "devDependencies": { @@ -327,6 +328,8 @@ "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + "@prisma/client": ["@prisma/client@6.5.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw=="], + "@prisma/config": ["@prisma/config@6.5.0", "", { "dependencies": { "esbuild": ">=0.12 <1", "esbuild-register": "3.6.0" } }, "sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw=="], "@prisma/debug": ["@prisma/debug@6.5.0", "", {}, "sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ=="], diff --git a/packages/db/package.json b/packages/db/package.json index 833e905..9d56099 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -12,6 +12,7 @@ "typescript": "^5.0.0" }, "dependencies": { + "@prisma/client": "6.5.0", "prisma": "^6.5.0" }, "prisma": { diff --git a/packages/db/prisma/migrations/20250323143423_update_validator_and_transactions_model/migration.sql b/packages/db/prisma/migrations/20250323143423_update_validator_and_transactions_model/migration.sql new file mode 100644 index 0000000..d1909dc --- /dev/null +++ b/packages/db/prisma/migrations/20250323143423_update_validator_and_transactions_model/migration.sql @@ -0,0 +1,17 @@ +-- AlterTable +ALTER TABLE "Validator" ADD COLUMN "isPaidOut" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "lockedAt" TIMESTAMP(3); + +-- CreateTable +CREATE TABLE "Transactions" ( + "id" TEXT NOT NULL, + "amount" INTEGER NOT NULL, + "signature" TEXT NOT NULL, + "validatorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Transactions_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_validatorId_fkey" FOREIGN KEY ("validatorId") REFERENCES "Validator"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index a3b9521..4b7c767 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -14,36 +14,48 @@ datasource db { } model User { - id String @id @default(uuid()) + id String @id @default(uuid()) email String } model Website { - id String @id @default(uuid()) - url String - userId String - ticks WebsiteTick[] - disabled Boolean @default(false) + id String @id @default(uuid()) + url String + userId String + ticks WebsiteTick[] + disabled Boolean @default(false) } model Validator { - id String @id @default(uuid()) - publicKey String - location String - ip String - pendingPayouts Int @default(0) - ticks WebsiteTick[] + id String @id @default(uuid()) + publicKey String + location String + ip String + pendingPayouts Int @default(0) + ticks WebsiteTick[] + transactions Transactions[] + isPaidOut Boolean @default(false) + lockedAt DateTime? } model WebsiteTick { - id String @id @default(uuid()) + id String @id @default(uuid()) websiteId String validatorId String createdAt DateTime status WebsiteStatus latency Float - website Website @relation(fields: [websiteId], references: [id]) - validator Validator @relation(fields: [validatorId], references: [id]) + website Website @relation(fields: [websiteId], references: [id]) + validator Validator @relation(fields: [validatorId], references: [id]) +} + +model Transactions{ + id String @id @default(uuid()) + amount Int + signature String + validatorId String + validator Validator @relation(fields: [validatorId], references: [id]) + createdAt DateTime } enum WebsiteStatus { diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 7b50121..1427342 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -1,5 +1,4 @@ import { PrismaClient } from "@prisma/client" +export { Prisma } from "@prisma/client" export const prismaClient = new PrismaClient() - -