Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
32 changes: 32 additions & 0 deletions mini-apps/mini-app-validation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Base Mini App Compatibility Validator

A read-only validation tool that scans mini app codebases to identify patterns that are not supported in Base app.

## What it does

This validator searches your mini app code for specific patterns that work in other environments (like Farcaster frames) but are not supported in Base app, helping ensure compatibility before deployment.

## What it checks for

- **Environment Detection**: `isInMiniApp()` calls
- **Haptics**: Haptic feedback API usage
- **Warpcast Composer URLs**: Direct links to Warpcast compose
- **Token Operations**: `swapToken`, `viewToken`, `sendToken` actions
- **Direct HTML Links**: `<a href>` tags and external links
- **Location Context**: Context location properties
- **SDK Ready Call**: Required `sdk.actions.ready()` implementation

## How it works

1. Scans application code in `/app/`, `/src/`, `/components/`, `/pages/` directories
2. Excludes third-party code (`/node_modules/`, `/.next/`, etc.)
3. Reports exact file locations and line numbers for unsupported patterns
4. Provides compatibility notes for each issue found

## Output

The validator provides a simple report listing any compatibility issues found, or confirms when no unsupported patterns are detected.

---

*This is a read-only validator - it only reports findings and does not modify code or suggest fixes.*
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": [
"next/core-web-vitals",
"next/typescript"
]
}
36 changes: 36 additions & 0 deletions mini-apps/templates/minikit/mini-app-full-demo-minikit/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
27 changes: 27 additions & 0 deletions mini-apps/templates/minikit/mini-app-full-demo-minikit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Base Mini App Demo

A demo [mini app](https://miniapps.farcaster.xyz) to demonstrate the functionality available for mini app developers in [Base App](https://base.app).

Note: this Base Mini App Demo used to live [here](https://github.com/Vicolee/frames-v2-demo) and was recently moved to this `base/demos` repo so it can be more easily found

## Getting Started

This is a [NextJS](https://nextjs.org/) + TypeScript + React app.

To install dependencies:

```bash
$ yarn
```

To run the app:

```bash
$ yarn dev
```

To test your mini app in Farcaster's playground or in Base App, you'll want to use a tunneling tool like [ngrok](https://ngrok.com/)

## Relevant Links
- [Mini Apps in Base App Docs](https://docs.base.org/base-app/introduction/mini-apps)
- [MiniKit Docs](https://docs.base.org/base-app/build-with-minikit/overview)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "~/components",
"utils": "~/lib/utils",
"ui": "~/components/ui",
"lib": "~/lib",
"hooks": "~/hooks"
},
"iconLibrary": "lucide"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
devIndicators: false,
async headers() {
return [
{
source: "/:path*",
headers: [
{
key: "Content-Security-Policy",
value: "frame-ancestors *"
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN"
},
{
key: "Access-Control-Allow-Origin",
value: "*"
}
]
}
];
}
};

export default nextConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "mini-app-full-demo",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "next build --turbo",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@base-org/account": "^2.0.2",
"@base-org/account-ui": "^1.0.1",
"@coinbase/onchainkit": "^1.0.3",
"@farcaster/auth-kit": "^0.8.1",
"@farcaster/miniapp-core": "^0.3.8",
"@farcaster/miniapp-node": "^0.1.8",
"@farcaster/miniapp-sdk": "^0.1.9",
"@farcaster/miniapp-wagmi-connector": "^1.0.0",
"@farcaster/quick-auth": "^0.0.8",
"@radix-ui/react-label": "^2.1.7",
"@tanstack/react-query": "^5.85.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.542.0",
"next": "15.5.2",
"ox": "^0.9.4",
"react": "19.1.1",
"react-dom": "19.1.1",
"siwe": "^3.0.0",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.37.1",
"wagmi": "^2.16.9"
},
"devDependencies": {
"@types/node": "^24.3.0",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.9",
"eruda": "^3.4.3",
"eslint": "^9",
"eslint-config-next": "15.5.2",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"packageManager": "npm@11.5.2"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { METADATA } from "../../../lib/utils";

export async function GET() {
const config = {
accountAssociation: {
header:
"eyJmaWQiOjEyMTQyLCJ0eXBlIjoiY3VzdG9keSIsImtleSI6IjB4MDRlNkYxMTFlQmY2RkQyNTU3NmQ0ODA0ODA5NjI0MzVEYzNhYThEOCJ9",
payload: "eyJkb21haW4iOiJmcmFtZXMtdjItZGVtby1saWxhYy52ZXJjZWwuYXBwIn0",
signature:
"MHg5MGI1YzA0Zjc3MGY1M2I4M2I3OGQzOTMwNTNjMmJjZjUwNmE3ZThjNDViYmEwNDk2OTcwZTM1ZTQ0YzU2MGU1Nzc4Y2Y1ZTJkNDY2YzE1MWQxNGMzYmFjNzM3ZDcxZGEwZDVjYWJmMGMzZTdhYTc2YzRjMmQ5MmE5NDJhYjkyODFj",
},
"frame": {
"version": "1",
"name": METADATA.name,
"iconUrl": METADATA.iconImageUrl,
"homeUrl": METADATA.homeUrl,
"imageUrl": METADATA.bannerImageUrl,
"splashImageUrl": METADATA.iconImageUrl,
"splashBackgroundColor": METADATA.splashBackgroundColor,
"description": METADATA.description,
"ogTitle": METADATA.name,
"ogDescription": METADATA.description,
"ogImageUrl": METADATA.bannerImageUrl,
"requiredCapabilities": [
"actions.ready",
"actions.signIn",
"actions.openMiniApp",
"actions.openUrl",
"actions.sendToken",
"actions.viewToken",
"actions.composeCast",
"actions.viewProfile",
"actions.setPrimaryButton",
"actions.swapToken",
"actions.close",
"actions.viewCast",
"wallet.getEthereumProvider"
],
"requiredChains": [
"eip155:8453",
"eip155:10"
],
"canonicalDomain": "https://fulldemo-minikit.vercel.app",
"noindex": false,
"tags": ["base", "baseapp", "miniapp", "demo", "basepay"]
},
"baseBuilder": {
"allowedAddresses": ["0x8342A48694A74044116F330db5050a267b28dD85"],
}
};

return Response.json(config);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Errors, createClient } from "@farcaster/quick-auth";
import { NextRequest, NextResponse } from "next/server";

const client = createClient();

// Helper function to determine the correct domain for JWT verification
function getUrlHost(request: NextRequest): string {
// First try to get the origin from the Origin header (most reliable for CORS requests)
const origin = request.headers.get("origin");
if (origin) {
try {
const url = new URL(origin);
return url.host;
} catch (error) {
console.warn("Invalid origin header:", origin, error);
}
}

// Fallback to Host header
const host = request.headers.get("host");
if (host) {
return host;
}

// Final fallback to environment variables (your original logic)
let urlValue: string;
if (process.env.VERCEL_ENV === "production") {
urlValue = process.env.NEXT_PUBLIC_URL!;
} else if (process.env.VERCEL_URL) {
urlValue = `https://${process.env.VERCEL_URL}`;
} else {
urlValue = "http://localhost:3000";
}

const url = new URL(urlValue);
return url.host;
}

export async function GET(request: NextRequest) {
// Because we're fetching this endpoint via `sdk.quickAuth.fetch`,
// if we're in a mini app, the request will include the necessary `Authorization` header.
const authorization = request.headers.get("Authorization");

// Here we ensure that we have a valid token.
if (!authorization || !authorization.startsWith("Bearer ")) {
return NextResponse.json({ message: "Missing token" }, { status: 401 });
}

try {
// Now we verify the token. `domain` must match the domain of the request.
// In our case, we're using the `getUrlHost` function to get the domain of the request
// based on the Vercel environment. This will vary depending on your hosting provider.
const payload = await client.verifyJwt({
token: authorization.split(" ")[1] as string,
domain: getUrlHost(request),
});

console.log("payload", payload);

// If the token was valid, `payload.sub` will be the user's Farcaster ID.
const userFid = payload.sub;

// Return user information for your waitlist application
return NextResponse.json({
success: true,
user: {
fid: userFid,
issuedAt: payload.iat,
expiresAt: payload.exp,
},
});

} catch (e) {
if (e instanceof Errors.InvalidTokenError) {
return NextResponse.json({ message: "Invalid token" }, { status: 401 });
}
if (e instanceof Error) {
return NextResponse.json({ message: e.message }, { status: 500 });
}
throw e;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import dynamic from "next/dynamic";
import { useMiniKit, useQuickAuth } from '@coinbase/onchainkit/minikit';
import { useEffect } from 'react';

const Demo = dynamic(() => import("~/components/Demo"), {
ssr: false,
});

interface AuthResponse {
success: boolean;
user?: {
fid: number; // FID is the unique identifier for the user
issuedAt?: number;
expiresAt?: number;
};
message?: string; // Error messages come as 'message' not 'error'
}

export default function App() {
const { isFrameReady, setFrameReady, context } = useMiniKit();

// Initialize the miniapp
useEffect(() => {
if (!isFrameReady) {
setFrameReady();
}
}, [setFrameReady, isFrameReady]);

// If you need to verify the user's identity, you can use the useQuickAuth hook.
// This hook will verify the user's signature and return the user's FID. You can update
// this to meet your needs. See the /app/api/auth/route.ts file for more details.
// Note: If you don't need to verify the user's identity, you can get their FID and other user data
// via `context.user.fid`.
const { data: authData, isLoading: isAuthLoading, error: authError } = useQuickAuth<AuthResponse>(
"/api/auth",
{ method: "GET" }
);

return <Demo />;
}
Binary file not shown.
Loading
Loading