diff --git a/airdrops/.gitignore b/airdrops/.gitignore
new file mode 100644
index 00000000000..7ef847c931f
--- /dev/null
+++ b/airdrops/.gitignore
@@ -0,0 +1,37 @@
+# dependencies
+/node_modules
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# next builds
+/src/.next
+.next
+/out
+.next/cache/**
+next-env.d.ts
+scripts/paywallURL.ts
+scripts/index.html
+
+# Sentry
+.sentryclirc
+.vercel
+
+certificates
+
+# tsbuild logs
+tsconfig.tsbuildinfo
\ No newline at end of file
diff --git a/airdrops/app/globals.css b/airdrops/app/globals.css
new file mode 100644
index 00000000000..7bae7731ead
--- /dev/null
+++ b/airdrops/app/globals.css
@@ -0,0 +1,7 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+body {
+ @apply bg-ui-secondary-200;
+}
diff --git a/airdrops/app/layout.tsx b/airdrops/app/layout.tsx
new file mode 100644
index 00000000000..9c3c50fb6cf
--- /dev/null
+++ b/airdrops/app/layout.tsx
@@ -0,0 +1,35 @@
+import { Inter } from 'next/font/google'
+import './globals.css'
+import { Metadata } from 'next'
+import Providers from '../components/providers'
+
+const inter = Inter({
+ subsets: ['latin'],
+ style: ['normal'],
+ display: 'swap',
+ weight: ['400', '500', '600', '700'],
+})
+
+export const metadata: Metadata = {
+ icons: {
+ icon: '/favicon.ico',
+ },
+}
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/airdrops/app/page.tsx b/airdrops/app/page.tsx
new file mode 100644
index 00000000000..8a5d3176b7b
--- /dev/null
+++ b/airdrops/app/page.tsx
@@ -0,0 +1,3 @@
+export default function Home() {
+ return
Unlock Airdrops
+}
diff --git a/airdrops/components/providers.tsx b/airdrops/components/providers.tsx
new file mode 100644
index 00000000000..b36166abbf9
--- /dev/null
+++ b/airdrops/components/providers.tsx
@@ -0,0 +1,18 @@
+'use client'
+
+import { PrivyProvider } from '@privy-io/react-auth'
+
+const Providers = ({ children }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export default Providers
diff --git a/airdrops/eslint.config.js b/airdrops/eslint.config.js
new file mode 100755
index 00000000000..d7368cace16
--- /dev/null
+++ b/airdrops/eslint.config.js
@@ -0,0 +1,13 @@
+const unlockConfig = require('@unlock-protocol/eslint-config/next')
+module.exports = [
+ ...unlockConfig,
+ {
+ ignores: ['src/**/*.typegen.ts', 'tsconfig.json'],
+ },
+ {
+ rules: {
+ 'react/no-children-prop': 'off',
+ 'no-constant-binary-expression': 'warn',
+ },
+ },
+]
diff --git a/airdrops/next.config.js b/airdrops/next.config.js
new file mode 100644
index 00000000000..8ff106bd994
--- /dev/null
+++ b/airdrops/next.config.js
@@ -0,0 +1,9 @@
+/** @type {import('next').NextConfig} */
+const config = {
+ images: {
+ unoptimized: true,
+ },
+ output: 'standalone',
+}
+
+module.exports = config
diff --git a/airdrops/package.json b/airdrops/package.json
new file mode 100644
index 00000000000..068e6ba4aa9
--- /dev/null
+++ b/airdrops/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "@unlock-protocol/airdrops",
+ "version": "0.1.0",
+ "private": true,
+ "peerDependencies": {
+ "react": "18.3.1",
+ "react-dom": "18.3.1"
+ },
+ "dependencies": {
+ "@headlessui/react": "2.1.9",
+ "@privy-io/react-auth": "2.2.1",
+ "@sentry/nextjs": "8.54.0",
+ "@tanstack/react-query": "5.59.19",
+ "@tw-classed/react": "1.7.0",
+ "@unlock-protocol/core": "workspace:./packages/core",
+ "@unlock-protocol/crypto-icon": "workspace:./packages/crypto-icon",
+ "@unlock-protocol/eslint-config": "workspace:./packages/eslint-config",
+ "@unlock-protocol/networks": "workspace:./packages/networks",
+ "@unlock-protocol/ui": "workspace:./packages/ui",
+ "@unlock-protocol/unlock-js": "workspace:./packages/unlock-js",
+ "@vercel/og": "0.6.5",
+ "@vercel/speed-insights": "1.0.14",
+ "dayjs": "1.11.13",
+ "embla-carousel-react": "8.5.2",
+ "eslint": "9.11.1",
+ "next": "14.2.21",
+ "react-hot-toast": "2.4.1",
+ "tailwind-merge": "3.0.1",
+ "typescript": "5.6.3"
+ },
+ "devDependencies": {
+ "@types/react": "18.3.18",
+ "@unlock-protocol/tsconfig": "workspace:./packages/tsconfig",
+ "@vitejs/plugin-react": "4.3.4",
+ "autoprefixer": "10.4.20",
+ "jsdom": "26.0.0",
+ "postcss": "8.4.49",
+ "tailwindcss": "3.4.17",
+ "vitest": "2.1.9"
+ },
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build --no-lint",
+ "deploy": "yarn build && next export -o out",
+ "start": "yarn build && NODE_ENV=production next start",
+ "test": "UNLOCK_ENV=test vitest run --coverage --environment=jsdom",
+ "lint": "eslint",
+ "ci": "yarn test && yarn lint && yarn build"
+ },
+ "browserslist": [
+ "defaults",
+ "not IE 11",
+ "maintained node versions"
+ ]
+}
diff --git a/airdrops/postcss.config.js b/airdrops/postcss.config.js
new file mode 100644
index 00000000000..33ad091d26d
--- /dev/null
+++ b/airdrops/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/airdrops/public/favicon.ico b/airdrops/public/favicon.ico
new file mode 100644
index 00000000000..3c136c0b2e1
Binary files /dev/null and b/airdrops/public/favicon.ico differ
diff --git a/airdrops/public/fonts/inter-400.woff b/airdrops/public/fonts/inter-400.woff
new file mode 100644
index 00000000000..9b73a33be96
Binary files /dev/null and b/airdrops/public/fonts/inter-400.woff differ
diff --git a/airdrops/public/fonts/inter-700.woff b/airdrops/public/fonts/inter-700.woff
new file mode 100644
index 00000000000..a1280240e01
Binary files /dev/null and b/airdrops/public/fonts/inter-700.woff differ
diff --git a/airdrops/public/images/svg/unlock-logo.svg b/airdrops/public/images/svg/unlock-logo.svg
new file mode 100644
index 00000000000..34221b76a0e
--- /dev/null
+++ b/airdrops/public/images/svg/unlock-logo.svg
@@ -0,0 +1,8 @@
+
diff --git a/airdrops/public/robots.txt b/airdrops/public/robots.txt
new file mode 100644
index 00000000000..eb0536286f3
--- /dev/null
+++ b/airdrops/public/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/airdrops/tailwind.config.js b/airdrops/tailwind.config.js
new file mode 100644
index 00000000000..00be2632984
--- /dev/null
+++ b/airdrops/tailwind.config.js
@@ -0,0 +1,10 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ './src/**/*.{tsx,ts,jsx,js,html,mdx}',
+ './app/**/*.{tsx,ts,jsx,js,html,mdx}',
+ './components/**/*.{tsx,ts,jsx,js,html,mdx}',
+ './../node_modules/@unlock-protocol/ui/dist/*.{js,css}',
+ ],
+ presets: [require('@unlock-protocol/ui/dist/unlock-tailwind-preset')],
+}
diff --git a/airdrops/tsconfig.json b/airdrops/tsconfig.json
new file mode 100644
index 00000000000..ab3441b7019
--- /dev/null
+++ b/airdrops/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "extends": "@unlock-protocol/tsconfig",
+ "compilerOptions": {
+ "allowJs": true,
+ "jsx": "preserve",
+ "skipLibCheck": true,
+ "strict": false,
+ "noEmit": true,
+ "incremental": true,
+ "lib": [
+ "dom"
+ ],
+ "resolveJsonModule": true,
+ "moduleResolution": "node",
+ "isolatedModules": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": [
+ "next-env.d.ts",
+ ".next/types/**/*.ts",
+ "**/*.ts",
+ "**/*.tsx"
+ ]
+}
diff --git a/package.json b/package.json
index 2ad26d0b52c..488bc2e4b2a 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,8 @@
"wedlocks",
"provider",
"docs",
- "graph-service"
+ "graph-service",
+ "airdrops"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
diff --git a/yarn.lock b/yarn.lock
index cb5da50035d..cb78e9ec8f9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -18567,6 +18567,44 @@ __metadata:
languageName: node
linkType: hard
+"@unlock-protocol/airdrops@workspace:airdrops":
+ version: 0.0.0-use.local
+ resolution: "@unlock-protocol/airdrops@workspace:airdrops"
+ dependencies:
+ "@headlessui/react": "npm:2.1.9"
+ "@privy-io/react-auth": "npm:2.2.1"
+ "@sentry/nextjs": "npm:8.54.0"
+ "@tanstack/react-query": "npm:5.59.19"
+ "@tw-classed/react": "npm:1.7.0"
+ "@types/react": "npm:18.3.18"
+ "@unlock-protocol/core": "workspace:./packages/core"
+ "@unlock-protocol/crypto-icon": "workspace:./packages/crypto-icon"
+ "@unlock-protocol/eslint-config": "workspace:./packages/eslint-config"
+ "@unlock-protocol/networks": "workspace:./packages/networks"
+ "@unlock-protocol/tsconfig": "workspace:./packages/tsconfig"
+ "@unlock-protocol/ui": "workspace:./packages/ui"
+ "@unlock-protocol/unlock-js": "workspace:./packages/unlock-js"
+ "@vercel/og": "npm:0.6.5"
+ "@vercel/speed-insights": "npm:1.0.14"
+ "@vitejs/plugin-react": "npm:4.3.4"
+ autoprefixer: "npm:10.4.20"
+ dayjs: "npm:1.11.13"
+ embla-carousel-react: "npm:8.5.2"
+ eslint: "npm:9.11.1"
+ jsdom: "npm:26.0.0"
+ next: "npm:14.2.21"
+ postcss: "npm:8.4.49"
+ react-hot-toast: "npm:2.4.1"
+ tailwind-merge: "npm:3.0.1"
+ tailwindcss: "npm:3.4.17"
+ typescript: "npm:5.6.3"
+ vitest: "npm:2.1.9"
+ peerDependencies:
+ react: 18.3.1
+ react-dom: 18.3.1
+ languageName: unknown
+ linkType: soft
+
"@unlock-protocol/contracts@workspace:./packages/contracts, @unlock-protocol/contracts@workspace:^, @unlock-protocol/contracts@workspace:packages/contracts":
version: 0.0.0-use.local
resolution: "@unlock-protocol/contracts@workspace:packages/contracts"