Skip to content

Commit

Permalink
Merge pull request #4 from sasatech-gk/devin/1735276554-nft-verse-apis
Browse files Browse the repository at this point in the history
feat: implement Web3-powered Waka poetry creation service
  • Loading branch information
DevRyuki authored Dec 27, 2024
2 parents 8ec8398 + feb5512 commit 8003b26
Show file tree
Hide file tree
Showing 20 changed files with 7,212 additions and 774 deletions.
16 changes: 16 additions & 0 deletions apps/web/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
25 changes: 17 additions & 8 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,28 @@
"lint": "next lint"
},
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"ethers": "^6.13.4",
"lucide-react": "^0.469.0",
"nanoid": "^5.0.9",
"next": "15.1.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.1.3"
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"@eslint/eslintrc": "^3",
"@types/node": "^20.17.10",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"@types/react-is": "^19.0.0",
"eslint": "^9",
"eslint-config-next": "15.1.3",
"@eslint/eslintrc": "^3"
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
83 changes: 69 additions & 14 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,76 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--background: #ffffff;
--foreground: #171717;
}

@media (prefers-color-scheme: dark) {

@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;

--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;

--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;

--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;

--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;

--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;

--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;

--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;

--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;

--radius: 0.5rem;
}

.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;

--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;

--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;

--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;

--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;

--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;

--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;

--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;

--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
54 changes: 54 additions & 0 deletions apps/web/src/features/waka/api/create-lower-verse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { NextRequest, NextResponse } from "next/server";
import { WakaSchema } from "../schema/waka-schema";
import { addLowerVerse, verifySignature, getVerse } from "../utils/web3";
import { ethers } from "ethers";
import { getProvider } from "../utils/web3";

export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validate the request body against the schema
const { tokenId, lowerVerse, signature, signerAddress } = WakaSchema.lowerVerse.parse(body);

// Verify the signature
const message = `Add lower verse to ${tokenId}: ${lowerVerse}`;
if (!verifySignature(message, signature, signerAddress)) {
return NextResponse.json(
{ error: "Invalid signature" },
{ status: 400 }
);
}

// Get the verse information to verify it exists and is incomplete
const provider = getProvider();
const verseInfo = await getVerse(provider, tokenId);

if (!verseInfo || verseInfo.isComplete) {
return NextResponse.json(
{ error: "Verse not found or already completed" },
{ status: 400 }
);
}

// Create wallet from signature for contract interaction
const wallet = new ethers.Wallet(signature);

// Add lower verse and mint NFT
const { txHash } = await addLowerVerse(wallet, tokenId, lowerVerse);

return NextResponse.json({
tokenId,
txHash,
lowerVerse,
signerAddress,
status: "completed",
message: "Lower verse added successfully"
}, { status: 201 });
} catch (error) {
console.error("Error creating lower verse:", error);
return NextResponse.json(
{ error: "Failed to create lower verse" },
{ status: 400 }
);
}
}
41 changes: 41 additions & 0 deletions apps/web/src/features/waka/api/create-upper-verse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { NextRequest, NextResponse } from "next/server";
import { WakaSchema } from "../schema/waka-schema";
import { createUpperVerse, verifySignature } from "../utils/web3";
import { ethers } from "ethers";

export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validate the request body against the schema
const { upperVerse, signature, signerAddress } = WakaSchema.upperVerse.parse(body);

// Verify the signature
const message = `Create upper verse: ${upperVerse}`;
if (!verifySignature(message, signature, signerAddress)) {
return NextResponse.json(
{ error: "Invalid signature" },
{ status: 400 }
);
}

// Create wallet from signature for contract interaction
const wallet = new ethers.Wallet(signature);

// Create upper verse on-chain
const { tokenId, txHash } = await createUpperVerse(wallet, upperVerse);

return NextResponse.json({
tokenId,
txHash,
upperVerse,
signerAddress,
collaborationUrl: `/waka/${tokenId}/complete`
}, { status: 201 });
} catch (error) {
console.error("Error creating upper verse:", error);
return NextResponse.json(
{ error: "Failed to create upper verse" },
{ status: 400 }
);
}
}
40 changes: 40 additions & 0 deletions apps/web/src/features/waka/schema/waka-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { z } from "zod";

export const WakaSchema = {
upperVerse: z.object({
upperVerse: z.string()
.min(1, "Upper verse is required")
.max(100, "Upper verse must be less than 100 characters"),
signature: z.string()
.min(1, "Signature is required")
.describe("Ethereum signature of the upper verse"),
signerAddress: z.string()
.min(1, "Signer address is required")
.describe("Ethereum address of the signer"),
}),

lowerVerse: z.object({
tokenId: z.string()
.min(1, "Token ID is required"),
lowerVerse: z.string()
.min(1, "Lower verse is required")
.max(100, "Lower verse must be less than 100 characters"),
signature: z.string()
.min(1, "Signature is required")
.describe("Ethereum signature of the lower verse"),
signerAddress: z.string()
.min(1, "Signer address is required")
.describe("Ethereum address of the signer"),
}),

completeWaka: z.object({
tokenId: z.string(),
upperVerse: z.string(),
lowerVerse: z.string(),
upperCreator: z.string(),
lowerCreator: z.string(),
txHash: z.string(),
createdAt: z.string(),
completedAt: z.string(),
}),
};
8 changes: 8 additions & 0 deletions apps/web/src/features/waka/utils/generate-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { customAlphabet } from "nanoid";

// Create a custom ID generator with a specific alphabet
const nanoid = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 16);

export function generateId(): string {
return nanoid();
}
Loading

0 comments on commit 8003b26

Please sign in to comment.