Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 24 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"extends": [
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "react", "prettier"],
"rules": {
"prettier/prettier": ["error"],
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": ["warn"],
"@typescript-eslint/no-explicit-any": "warn",
"no-console": ["warn", { "allow": ["warn", "error"] }]
},
"settings": {
"react": {
"version": "detect"
}
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ yarn-debug.log*
yarn-error.log*

# env files (can opt-in for commiting if needed)
!.env.example
.env*

# vercel
Expand Down
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"]
}
63 changes: 44 additions & 19 deletions app/api/faucet/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,80 @@ const akaveChain = {
nativeCurrency: {
name: 'AKVT',
symbol: 'AKVT',
decimals: 18
decimals: 18,
},
rpcUrls: {
default: {
http: ['https://n1-us.akave.ai/ext/bc/2JMWNmZbYvWcJRPPy1siaDBZaDGTDAaqXoY5UBKh4YrhNFzEce/rpc']
http: [
'https://n1-us.akave.ai/ext/bc/2JMWNmZbYvWcJRPPy1siaDBZaDGTDAaqXoY5UBKh4YrhNFzEce/rpc',
],
},
public: {
http: ['https://n1-us.akave.ai/ext/bc/2JMWNmZbYvWcJRPPy1siaDBZaDGTDAaqXoY5UBKh4YrhNFzEce/rpc']
}
}
http: [
'https://n1-us.akave.ai/ext/bc/2JMWNmZbYvWcJRPPy1siaDBZaDGTDAaqXoY5UBKh4YrhNFzEce/rpc',
],
},
},
}

const publicClient = createPublicClient({
chain: akaveChain,
transport: http()
transport: http(),
})

async function verifyRecaptcha(token: string) {
try {
const response = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`,
})

const data = await response.json()
return data.success
} catch (error) {
console.error('Error verifying reCAPTCHA:', error)
return false
}
}

export async function POST(request: Request) {
try {
const { address, email } = await request.json()
const { address, email, recaptchaToken } = await request.json()

if (!address) {
return NextResponse.json({ error: 'Address is required' }, { status: 400 })
}

if (!recaptchaToken) {
return NextResponse.json({ error: 'reCAPTCHA verification is required' }, { status: 400 })
}

const isValidRecaptcha = await verifyRecaptcha(recaptchaToken)
if (!isValidRecaptcha) {
return NextResponse.json({ error: 'Invalid reCAPTCHA verification' }, { status: 400 })
}

// Check user's balance
const balance = await publicClient.getBalance({ address })

if (balance >= parseEther('10')) {
return NextResponse.json(
{ error: 'Address already has more than 10 AKVT' },
{ status: 400 }
)
return NextResponse.json({ error: 'Address already has more than 10 AKVT' }, { status: 400 })
}

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const walletClient = createWalletClient({
account,
chain: akaveChain,
transport: http()
transport: http(),
})

// Send transaction
const hash = await walletClient.sendTransaction({
to: address,
value: parseEther('10')
value: parseEther('10'),
})

// Store email if provided
Expand All @@ -72,9 +100,6 @@ export async function POST(request: Request) {
return NextResponse.json({ hash })
} catch (error) {
console.error(error)
return NextResponse.json(
{ error: 'Failed to process request' },
{ status: 500 }
)
return NextResponse.json({ error: 'Failed to process request' }, { status: 500 })
}
}
32 changes: 16 additions & 16 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import Script from 'next/script'

const inter = Inter({ subsets: ["latin"] });
const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: "Akave Faucet",
description: "Claim AKVF tokens",
title: 'Akave Faucet',
description: 'Claim AKVF tokens',
icons: {
icon: [
{
url: "/favicon-32x32.png",
sizes: "32x32",
type: "image/png",
url: '/favicon-32x32.png',
sizes: '32x32',
type: 'image/png',
},
],
},
};
}

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Script src={`https://www.google.com/recaptcha/api.js?render=explicit`} async defer />
</head>
<body className={inter.className}>{children}</body>
</html>
);
)
}
Loading