├── .gitignore ├── cast-action ├── .gitignore ├── bun.lockb ├── tsconfig.json ├── src │ ├── lib │ │ └── neynarClient.ts │ └── index.tsx ├── package.json └── README.md ├── flask-app ├── .env.example ├── requirements.txt ├── app.py ├── templates │ └── index.html ├── README.md └── .gitignore ├── wownar ├── .eslintrc.json ├── src │ ├── types.d.ts │ ├── app │ │ ├── favicon.ico │ │ ├── globals.scss │ │ ├── page.tsx │ │ ├── Screens │ │ │ ├── Home │ │ │ │ ├── index.module.scss │ │ │ │ └── index.tsx │ │ │ ├── layout.tsx │ │ │ └── Signin │ │ │ │ └── index.tsx │ │ ├── layout.tsx │ │ └── api │ │ │ ├── user │ │ │ └── [fid] │ │ │ │ └── route.ts │ │ │ ├── verify-user │ │ │ └── route.ts │ │ │ └── cast │ │ │ └── route.ts │ ├── window.d.ts │ ├── clients │ │ └── neynar.ts │ ├── components │ │ ├── Button │ │ │ ├── index.module.scss │ │ │ └── index.tsx │ │ └── icons │ │ │ └── Signout │ │ │ └── index.tsx │ ├── utils │ │ └── helpers.ts │ ├── hooks │ │ └── use-local-storage-state.tsx │ └── Context │ │ └── AppContext.tsx ├── postcss.config.js ├── public │ └── logos │ │ ├── powered-by-neynar.png │ │ ├── wownar.svg │ │ ├── wownar-black.svg │ │ └── neynar.svg ├── .env.example ├── next.config.js ├── .gitignore ├── tailwind.config.ts ├── tsconfig.json ├── package.json └── README.md ├── gm-bot ├── .gitignore ├── getApprovedSigner.ts ├── src │ ├── neynarClient.ts │ ├── viemClient.ts │ ├── config.ts │ ├── abi │ │ ├── SignedKeyRequestMetadata.ts │ │ └── keyGateway.ts │ ├── app.ts │ └── utils.ts ├── .env.example ├── ecosystem.config.cjs ├── tsconfig.json ├── package.json └── README.md ├── wownar-react-sdk ├── .eslintrc.json ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── globals.scss │ │ ├── page.tsx │ │ ├── Screens │ │ │ ├── Signin │ │ │ │ └── index.tsx │ │ │ ├── Home │ │ │ │ ├── index.module.scss │ │ │ │ └── index.tsx │ │ │ └── layout.tsx │ │ ├── layout.tsx │ │ └── api │ │ │ └── cast │ │ │ └── route.ts │ ├── clients │ │ └── neynar.ts │ ├── components │ │ └── Button │ │ │ ├── index.module.scss │ │ │ └── index.tsx │ └── Context │ │ ├── NeynarProviderWrapper.tsx │ │ └── AppContext.tsx ├── postcss.config.js ├── .env.example ├── next.config.js ├── .gitignore ├── tailwind.config.ts ├── tsconfig.json ├── package.json ├── README.md └── public │ └── logos │ ├── wownar.svg │ └── wownar-black.svg ├── README.md ├── managed-signers ├── .env.example ├── bun.lockb ├── src │ ├── constants.ts │ ├── lib │ │ └── neynarClient.ts │ ├── app │ │ ├── globals.css │ │ ├── api │ │ │ ├── cast │ │ │ │ └── route.ts │ │ │ ├── user │ │ │ │ └── route.ts │ │ │ └── signer │ │ │ │ └── route.ts │ │ ├── layout.tsx │ │ ├── page.module.css │ │ └── page.tsx │ └── utils │ │ ├── getFid.ts │ │ └── getSignedKey.ts ├── next.config.mjs ├── .gitignore ├── package.json ├── tsconfig.json └── README.md ├── frames-bot ├── bun.lockb ├── utils │ └── neynarClient.ts ├── package.json ├── tsconfig.json ├── index.ts ├── .gitignore └── README.md ├── funding.json ├── wownar-react-native ├── client │ ├── assets │ │ ├── icon.png │ │ ├── favicon.png │ │ ├── splash.png │ │ ├── wownar.png │ │ ├── adaptive-icon.png │ │ └── wownar.svg │ ├── tsconfig.json │ ├── .env.example │ ├── babel.config.js │ ├── constants.ts │ ├── app.config.ts │ ├── src │ │ ├── components │ │ │ ├── Screens │ │ │ │ ├── Loading.tsx │ │ │ │ ├── Signin.tsx │ │ │ │ └── Home.tsx │ │ │ ├── SignoutButton.tsx │ │ │ └── Layout.tsx │ │ ├── AppNavigator.tsx │ │ ├── utils.ts │ │ └── Context │ │ │ └── AppContext.tsx │ ├── app.json │ ├── package.json │ └── App.tsx ├── server │ ├── .env.example │ ├── package.json │ └── index.js ├── .gitignore └── README.md ├── archiver-script ├── package.json ├── index.js ├── README.md └── .gitignore └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | -------------------------------------------------------------------------------- /cast-action/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /flask-app/.env.example: -------------------------------------------------------------------------------- 1 | NEYNAR_API_KEY=YOUR_API_KEY_HERE -------------------------------------------------------------------------------- /wownar/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /gm-bot/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .env.local 3 | node_modules 4 | dist 5 | pm2.log -------------------------------------------------------------------------------- /wownar-react-sdk/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # farcaster-examples 2 | A collection of Farcaster mini-apps powered by Neynar 3 | -------------------------------------------------------------------------------- /managed-signers/.env.example: -------------------------------------------------------------------------------- 1 | NODE_ENV= 2 | NEYNAR_API_KEY= 3 | FARCASTER_DEVELOPER_MNEMONIC= 4 | -------------------------------------------------------------------------------- /gm-bot/getApprovedSigner.ts: -------------------------------------------------------------------------------- 1 | import { getApprovedSigner } from "./src/utils"; 2 | getApprovedSigner(); -------------------------------------------------------------------------------- /cast-action/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/cast-action/bun.lockb -------------------------------------------------------------------------------- /frames-bot/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/frames-bot/bun.lockb -------------------------------------------------------------------------------- /wownar/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface UserInfo { 2 | signerUuid: string; 3 | fid: string; 4 | } 5 | -------------------------------------------------------------------------------- /managed-signers/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/managed-signers/bun.lockb -------------------------------------------------------------------------------- /wownar/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar/src/app/favicon.ico -------------------------------------------------------------------------------- /wownar/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-sdk/src/app/favicon.ico -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x6ad4cefe7290c830541e5477c95554c29d6b95f16e0942bc9d95472052da5802" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /wownar-react-sdk/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /wownar/public/logos/powered-by-neynar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar/public/logos/powered-by-neynar.png -------------------------------------------------------------------------------- /wownar/src/window.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | onSignInSuccess?: (data: any) => void; // Replace 'any' with a more specific type if known 3 | } 4 | -------------------------------------------------------------------------------- /wownar-react-native/client/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-native/client/assets/icon.png -------------------------------------------------------------------------------- /wownar-react-native/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /wownar-react-native/client/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-native/client/assets/favicon.png -------------------------------------------------------------------------------- /wownar-react-native/client/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-native/client/assets/splash.png -------------------------------------------------------------------------------- /wownar-react-native/client/assets/wownar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-native/client/assets/wownar.png -------------------------------------------------------------------------------- /wownar-react-native/client/.env.example: -------------------------------------------------------------------------------- 1 | COMPUTER_IP_ADDRESS=192.168.x.x # => Refer https://www.avg.com/en/signal/find-ip-address to find your computer's IP address -------------------------------------------------------------------------------- /wownar-react-native/client/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katerinasmo/farcaster-examples/HEAD/wownar-react-native/client/assets/adaptive-icon.png -------------------------------------------------------------------------------- /wownar-react-native/client/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /wownar/.env.example: -------------------------------------------------------------------------------- 1 | NEYNAR_API_KEY="key" # Get API Key -> https://neynar.com/. 2 | NEXT_PUBLIC_NEYNAR_CLIENT_ID="" # Get Client ID -> Neynar Developer Portal -> https://dev.neynar.com -------------------------------------------------------------------------------- /cast-action/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "NodeNext", 4 | "strict": true, 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "hono/jsx" 7 | } 8 | } -------------------------------------------------------------------------------- /wownar-react-native/server/.env.example: -------------------------------------------------------------------------------- 1 | NEYNAR_API_KEY="key" # Get API Key -> https://neynar.com/. 2 | NEYNAR_CLIENT_ID="" # Get Client ID -> Neynar Developer Portal -> https://dev.neynar.com -------------------------------------------------------------------------------- /wownar-react-sdk/.env.example: -------------------------------------------------------------------------------- 1 | NEYNAR_API_KEY="key" # Get API Key -> https://neynar.com/. 2 | NEXT_PUBLIC_NEYNAR_CLIENT_ID="" # Get Client ID -> Neynar Developer Portal -> https://dev.neynar.com -------------------------------------------------------------------------------- /wownar/src/clients/neynar.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | 3 | const client = new NeynarAPIClient(process.env.NEYNAR_API_KEY!); 4 | 5 | export default client; 6 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/clients/neynar.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | 3 | const client = new NeynarAPIClient(process.env.NEYNAR_API_KEY!); 4 | 5 | export default client; 6 | -------------------------------------------------------------------------------- /wownar/src/app/globals.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | font-weight: 200; 7 | background-color: #111111; 8 | color: #fff; 9 | } 10 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/globals.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | font-weight: 200; 7 | background-color: #111; 8 | color: #fff; 9 | } 10 | -------------------------------------------------------------------------------- /gm-bot/src/neynarClient.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | import { NEYNAR_API_KEY } from "./config"; 3 | 4 | const neynarClient = new NeynarAPIClient(NEYNAR_API_KEY); 5 | 6 | export default neynarClient; 7 | -------------------------------------------------------------------------------- /gm-bot/src/viemClient.ts: -------------------------------------------------------------------------------- 1 | import { createPublicClient, http } from "viem"; 2 | import { optimism } from "viem/chains"; 3 | 4 | export const viemPublicClient = createPublicClient({ 5 | chain: optimism, 6 | transport: http(), 7 | }); 8 | -------------------------------------------------------------------------------- /wownar-react-native/client/constants.ts: -------------------------------------------------------------------------------- 1 | import Constants from "expo-constants"; 2 | 3 | export const COMPUTER_IP_ADDRESS = Constants.expoConfig!.extra!.COMPUTER_IP_ADDRESS; 4 | 5 | export const API_URL = `http://${COMPUTER_IP_ADDRESS}:5500`; 6 | 7 | -------------------------------------------------------------------------------- /managed-signers/src/constants.ts: -------------------------------------------------------------------------------- 1 | // constants.ts 2 | export const LOCAL_STORAGE_KEYS = { 3 | FARCASTER_USER: "farcasterUser", 4 | }; 5 | 6 | export const DEFAULT_CAST = `gm Farcaster! 7 | 8 | 9 | - Sent from my Neynar App`; 10 | -------------------------------------------------------------------------------- /wownar-react-native/client/app.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | 3 | export default ({ config }: any) => { 4 | return { 5 | ...config, 6 | extra: { 7 | COMPUTER_IP_ADDRESS: process.env.COMPUTER_IP_ADDRESS, 8 | }, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /flask-app/requirements.txt: -------------------------------------------------------------------------------- 1 | blinker==1.7.0 2 | certifi==2023.11.17 3 | charset-normalizer==3.3.2 4 | click==8.1.7 5 | Flask==3.0.0 6 | idna==3.6 7 | itsdangerous==2.1.2 8 | Jinja2==3.1.2 9 | MarkupSafe==2.1.3 10 | requests==2.31.0 11 | urllib3==2.1.0 12 | Werkzeug==3.0.1 13 | -------------------------------------------------------------------------------- /gm-bot/.env.example: -------------------------------------------------------------------------------- 1 | PUBLISH_CAST_TIME # example 08:30 (24 hour time). If not provided will default to 09:00 2 | TIME_ZONE # example "America/New_York". If not provided will default to "UTC" 3 | NEYNAR_API_KEY="key" # Get API Key -> https://neynar.com/. 4 | FARCASTER_BOT_MNEMONIC="MNEMONIC" 5 | -------------------------------------------------------------------------------- /wownar/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: "https", 7 | hostname: "**", 8 | }, 9 | ], 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /wownar-react-sdk/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: "https", 7 | hostname: "**", 8 | }, 9 | ], 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /frames-bot/utils/neynarClient.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | 3 | if (!process.env.NEYNAR_API_KEY) { 4 | throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); 5 | } 6 | 7 | const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY); 8 | 9 | export default neynarClient; 10 | -------------------------------------------------------------------------------- /managed-signers/src/lib/neynarClient.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | 3 | if (!process.env.NEYNAR_API_KEY) { 4 | throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); 5 | } 6 | 7 | const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY); 8 | 9 | export default neynarClient; 10 | -------------------------------------------------------------------------------- /managed-signers/src/app/globals.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | html, 8 | body { 9 | max-width: 100vw; 10 | overflow-x: hidden; 11 | } 12 | 13 | body { 14 | color: #fff; 15 | background: #111111; 16 | } 17 | 18 | a { 19 | color: inherit; 20 | text-decoration: none; 21 | } 22 | -------------------------------------------------------------------------------- /archiver-script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archiver-script", 3 | "module": "index.js", 4 | "type": "module", 5 | "devDependencies": { 6 | "@biomejs/biome": "^1.4.1", 7 | "bun-types": "latest" 8 | }, 9 | "peerDependencies": { 10 | "typescript": "^5.0.0" 11 | }, 12 | "dependencies": { 13 | "@neynar/nodejs-sdk": "^1.1.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gm-bot/ecosystem.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [ 3 | { 4 | name: "neynar-gm-bot", 5 | script: "./dist/app.js", 6 | instances: 1, 7 | exec_mode: "fork", 8 | autorestart: true, 9 | watch: false, 10 | max_memory_restart: "1G", 11 | env: { 12 | NODE_ENV: "production", 13 | }, 14 | }, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /cast-action/src/lib/neynarClient.ts: -------------------------------------------------------------------------------- 1 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 2 | import { config } from "dotenv"; 3 | 4 | config(); 5 | 6 | if (!process.env.NEYNAR_API_KEY) { 7 | throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); 8 | } 9 | 10 | const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY); 11 | 12 | export default neynarClient; 13 | -------------------------------------------------------------------------------- /frames-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "farcaster-bot", 3 | "module": "index.ts", 4 | "type": "module", 5 | "scripts": { 6 | "start": "bun run index.ts" 7 | }, 8 | "devDependencies": { 9 | "bun-types": "latest" 10 | }, 11 | "peerDependencies": { 12 | "typescript": "^5.0.0" 13 | }, 14 | "dependencies": { 15 | "@neynar/nodejs-sdk": "^1.13.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gm-bot/src/config.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | export const FARCASTER_BOT_MNEMONIC = process.env.FARCASTER_BOT_MNEMONIC!; 5 | export const SIGNER_UUID = process.env.SIGNER_UUID!; 6 | export const NEYNAR_API_KEY = process.env.NEYNAR_API_KEY!; 7 | export const PUBLISH_CAST_TIME = process.env.PUBLISH_CAST_TIME || "09:00"; 8 | export const TIME_ZONE = process.env.TIME_ZONE || "UTC"; 9 | -------------------------------------------------------------------------------- /managed-signers/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | images: { 5 | remotePatterns: [ 6 | { 7 | hostname: "*", 8 | protocol: "http", 9 | }, 10 | { 11 | hostname: "*", 12 | protocol: "https", 13 | }, 14 | ], 15 | }, 16 | }; 17 | 18 | export default nextConfig; 19 | -------------------------------------------------------------------------------- /gm-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "allowJs": true, 5 | "checkJs": false, 6 | "outDir": "dist", 7 | "sourceMap": true, 8 | "strict": true, 9 | "alwaysStrict": true, 10 | "target": "es5", 11 | "module": "CommonJS", 12 | "esModuleInterop": true, 13 | "moduleResolution": "node" 14 | }, 15 | "include": ["src/**/*"], 16 | "exclude": ["node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /wownar/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ScreenState, useApp } from "@/Context/AppContext"; 4 | import Signin from "./Screens/Signin"; 5 | import Home from "./Screens/Home"; 6 | 7 | export default function Index() { 8 | const { screen } = useApp(); 9 | 10 | if (screen === ScreenState.Signin) { 11 | return ; 12 | } 13 | 14 | if (screen === ScreenState.Home) { 15 | return ; 16 | } 17 | 18 | return <>; 19 | } 20 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ScreenState, useApp } from "@/Context/AppContext"; 4 | import Signin from "./Screens/Signin"; 5 | import Home from "./Screens/Home"; 6 | 7 | export default function Index() { 8 | const { screen } = useApp(); 9 | 10 | if (screen === ScreenState.Signin) { 11 | return ; 12 | } 13 | 14 | if (screen === ScreenState.Home) { 15 | return ; 16 | } 17 | 18 | return <>; 19 | } 20 | -------------------------------------------------------------------------------- /wownar/src/components/Button/index.module.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | column-gap: 10px; 6 | padding: 12px 20px; 7 | background-color: transparent; 8 | border: 1px solid white; 9 | border-radius: 5px; 10 | color: white; 11 | cursor: pointer; 12 | transition: background-color 0.3s ease, color 0.3s ease; 13 | 14 | &:hover { 15 | background-color: white; 16 | color: black; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cast-action/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cast-action-bun", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "dev": "frog dev", 7 | "serve": "bun run src/index.tsx", 8 | "build": "bun build src/index.tsx" 9 | }, 10 | "dependencies": { 11 | "@neynar/nodejs-sdk": "^1.16.0", 12 | "dotenv": "^16.4.5", 13 | "frog": "latest", 14 | "hono": "^4" 15 | }, 16 | "devDependencies": { 17 | "@types/bun": "latest", 18 | "bun": "latest" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/components/Button/index.module.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | column-gap: 10px; 6 | padding: 12px 20px; 7 | background-color: transparent; 8 | border: 1px solid white; 9 | border-radius: 5px; 10 | color: white; 11 | cursor: pointer; 12 | transition: background-color 0.3s ease, color 0.3s ease; 13 | 14 | &:hover { 15 | background-color: white; 16 | color: black; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wownar-react-native/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon -w index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@neynar/nodejs-sdk": "^1.18.0", 14 | "body-parser": "^1.20.2", 15 | "dotenv": "^16.4.5", 16 | "express": "^4.19.2" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^3.1.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wownar/src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./index.module.scss"; 2 | 3 | interface Props extends React.ButtonHTMLAttributes { 4 | title: string; 5 | rightIcon?: React.ReactNode; 6 | } 7 | 8 | const Button = (props: Props) => { 9 | const { title, rightIcon } = props; 10 | return ( 11 | 15 | ); 16 | }; 17 | 18 | export default Button; 19 | -------------------------------------------------------------------------------- /managed-signers/src/app/api/cast/route.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "@/lib/neynarClient"; 2 | import { NextResponse } from "next/server"; 3 | 4 | export async function POST(req: Request) { 5 | const body = await req.json(); 6 | 7 | try { 8 | const cast = await neynarClient.publishCast(body.signer_uuid, body.text); 9 | 10 | return NextResponse.json(cast, { status: 200 }); 11 | } catch (error) { 12 | console.error(error); 13 | return NextResponse.json({ error: "An error occurred" }, { status: 500 }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./index.module.scss"; 2 | 3 | interface Props extends React.ButtonHTMLAttributes { 4 | title: string; 5 | rightIcon?: React.ReactNode; 6 | } 7 | 8 | const Button = (props: Props) => { 9 | const { title, rightIcon } = props; 10 | return ( 11 | 15 | ); 16 | }; 17 | 18 | export default Button; 19 | -------------------------------------------------------------------------------- /wownar-react-native/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | 11 | # Native 12 | *.orig.* 13 | *.jks 14 | *.p8 15 | *.p12 16 | *.key 17 | *.mobileprovision 18 | 19 | # Metro 20 | .metro-health-check* 21 | 22 | # debug 23 | npm-debug.* 24 | yarn-debug.* 25 | yarn-error.* 26 | 27 | # macOS 28 | .DS_Store 29 | *.pem 30 | 31 | # local env files 32 | .env*.local 33 | .env 34 | 35 | # typescript 36 | *.tsbuildinfo -------------------------------------------------------------------------------- /wownar/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /managed-signers/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /wownar-react-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /managed-signers/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Create Next App", 9 | description: "Generated by create next app", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /wownar/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | content: [ 5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | } 20 | export default config 21 | -------------------------------------------------------------------------------- /wownar-react-sdk/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | content: [ 5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | } 20 | export default config 21 | -------------------------------------------------------------------------------- /frames-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "module": "esnext", 5 | "target": "esnext", 6 | "moduleResolution": "bundler", 7 | "moduleDetection": "force", 8 | "allowImportingTsExtensions": true, 9 | "noEmit": true, 10 | "composite": true, 11 | "strict": true, 12 | "downlevelIteration": true, 13 | "skipLibCheck": true, 14 | "jsx": "react-jsx", 15 | "allowSyntheticDefaultImports": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "allowJs": true, 18 | "types": [ 19 | "bun-types" // add Bun global 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /managed-signers/src/utils/getFid.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "@/lib/neynarClient"; 2 | import { mnemonicToAccount } from "viem/accounts"; 3 | 4 | export const getFid = async () => { 5 | if (!process.env.FARCASTER_DEVELOPER_MNEMONIC) { 6 | throw new Error("FARCASTER_DEVELOPER_MNEMONIC is not set."); 7 | } 8 | 9 | const account = mnemonicToAccount(process.env.FARCASTER_DEVELOPER_MNEMONIC); 10 | 11 | // Lookup user details using the custody address. 12 | const { user: farcasterDeveloper } = 13 | await neynarClient.lookupUserByCustodyAddress(account.address); 14 | 15 | return Number(farcasterDeveloper.fid); 16 | }; 17 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/Screens/Signin/index.tsx: -------------------------------------------------------------------------------- 1 | import ScreenLayout from "../layout"; 2 | import { NeynarAuthButton, SIWN_variant } from "@neynar/react"; 3 | 4 | const Signin = () => { 5 | return ( 6 | 7 |
8 |
9 |

Wowow Farcaster

10 |
11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default Signin; 18 | -------------------------------------------------------------------------------- /managed-signers/src/app/api/user/route.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "@/lib/neynarClient"; 2 | import { NextResponse } from "next/server"; 3 | 4 | export async function GET(req: Request) { 5 | const { searchParams } = new URL(req.url); 6 | const fid = searchParams.get("fid"); 7 | 8 | if (!fid) { 9 | return NextResponse.json({ error: "fid is required" }, { status: 400 }); 10 | } 11 | 12 | try { 13 | const user = await neynarClient.fetchBulkUsers([Number(fid)]); 14 | 15 | return NextResponse.json(user.users[0], { status: 200 }); 16 | } catch (error) { 17 | return NextResponse.json({ error: "An error occurred" }, { status: 500 }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wownar/src/app/Screens/Home/index.module.scss: -------------------------------------------------------------------------------- 1 | .inputContainer { 2 | width: 100%; 3 | max-width: 500px; // Adjust as needed 4 | margin: 0 auto; 5 | position: relative; 6 | text-align: center; 7 | padding: 0px 20px; 8 | } 9 | 10 | .userInput { 11 | width: 100%; 12 | padding: 20px 15px 15px 65px; 13 | margin-bottom: 16px; 14 | background-color: transparent; 15 | border: 1px solid white; 16 | border-radius: 5px; 17 | color: white; 18 | outline: none; 19 | 20 | &::placeholder { 21 | color: white; 22 | opacity: 0.7; // Adjust as needed 23 | } 24 | } 25 | 26 | .profilePic { 27 | position: relative; 28 | top: 50px; 29 | left: 15px; 30 | } 31 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/Screens/Home/index.module.scss: -------------------------------------------------------------------------------- 1 | .inputContainer { 2 | width: 100%; 3 | max-width: 500px; // Adjust as needed 4 | margin: 0 auto; 5 | position: relative; 6 | text-align: center; 7 | padding: 0px 20px; 8 | } 9 | 10 | .userInput { 11 | width: 100%; 12 | padding: 20px 15px 15px 65px; 13 | margin-bottom: 16px; 14 | background-color: transparent; 15 | border: 1px solid white; 16 | border-radius: 5px; 17 | color: white; 18 | outline: none; 19 | 20 | &::placeholder { 21 | color: white; 22 | opacity: 0.7; // Adjust as needed 23 | } 24 | } 25 | 26 | .profilePic { 27 | position: relative; 28 | top: 50px; 29 | left: 15px; 30 | } 31 | -------------------------------------------------------------------------------- /managed-signers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "managed-signers", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@farcaster/hub-nodejs": "^0.11.8", 13 | "@neynar/nodejs-sdk": "^1.16.0", 14 | "axios": "^1.6.8", 15 | "next": "14.1.4", 16 | "qrcode.react": "^3.1.0", 17 | "react": "^18", 18 | "react-dom": "^18", 19 | "viem": "^2.9.15" 20 | }, 21 | "devDependencies": { 22 | "typescript": "^5", 23 | "@types/node": "^20", 24 | "@types/react": "^18", 25 | "@types/react-dom": "^18" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /managed-signers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /wownar-react-native/client/src/components/Screens/Loading.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, Text } from "react-native"; 2 | 3 | const Loading: React.FC = () => { 4 | return ( 5 | 6 | Loading... 7 | 8 | ); 9 | }; 10 | 11 | const styles = StyleSheet.create({ 12 | text: { 13 | fontSize: 20, 14 | color: "white", 15 | marginBottom: 16, 16 | flexDirection: "row", 17 | alignItems: "center", 18 | }, 19 | loadingContainer: { 20 | flex: 1, 21 | backgroundColor: "black", 22 | alignItems: "center", 23 | justifyContent: "center", 24 | padding: 20, 25 | }, 26 | }); 27 | 28 | export default Loading; 29 | -------------------------------------------------------------------------------- /wownar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /wownar-react-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /wownar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Wownar", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev -p 4500", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@neynar/nodejs-sdk": "^1.3.0", 13 | "next": "14.0.3", 14 | "react": "^18", 15 | "react-dom": "^18", 16 | "react-toastify": "^9.1.3", 17 | "sass": "^1.69.5" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^20", 21 | "@types/react": "^18", 22 | "@types/react-dom": "^18", 23 | "autoprefixer": "^10.0.1", 24 | "eslint": "^8", 25 | "eslint-config-next": "14.0.3", 26 | "postcss": "^8", 27 | "tailwindcss": "^3.3.0", 28 | "typescript": "^5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /wownar-react-native/client/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "wownar-native", 4 | "slug": "wownar-native", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": [ 15 | "**/*" 16 | ], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#ffffff" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /wownar/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.scss"; 4 | import { AppProvider } from "@/Context/AppContext"; 5 | import "react-toastify/dist/ReactToastify.css"; 6 | import { ToastContainer } from "react-toastify"; 7 | 8 | const inter = Inter({ subsets: ["latin"] }); 9 | 10 | export const metadata: Metadata = { 11 | title: "Wownar", 12 | description: "A demo app (powered by Neynar) that will help user to cast", 13 | }; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: { 18 | children: React.ReactNode; 19 | }) { 20 | return ( 21 | 22 | 23 | 24 | {children} 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /gm-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gm_bot", 3 | "version": "1.0.0", 4 | "description": "A bot that will cast a 'gm 🪐' message in Warpcast at scheduled time everyday (As long as system is online)", 5 | "main": "./dist/app.js", 6 | "scripts": { 7 | "watch": "tsc --watch", 8 | "build": "rm -rf dist && tsc", 9 | "start": "npm run build && pm2 start ecosystem.config.cjs", 10 | "get-approved-signer": "ts-node getApprovedSigner.ts" 11 | }, 12 | "author": "Neynar", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@neynar/nodejs-sdk": "^0.11.3", 16 | "@types/node": "^20.9.0", 17 | "chatgpt": "^5.2.5", 18 | "dotenv": "^16.3.1", 19 | "node-cron": "^3.0.3", 20 | "typescript": "^5.2.2", 21 | "viem": "^1.19.0" 22 | }, 23 | "devDependencies": { 24 | "@types/node-cron": "^3.0.11", 25 | "ts-node": "^10.9.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /wownar-react-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wownar_react-sdk-implementation", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev -p 4500", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@neynar/nodejs-sdk": "^1.3.0", 13 | "@neynar/react": "^0.2.8", 14 | "@pigment-css/react": "^0.0.9", 15 | "next": "14.0.3", 16 | "react": "^18", 17 | "react-dom": "^18", 18 | "react-toastify": "^9.1.3", 19 | "sass": "^1.69.5" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^20", 23 | "@types/react": "^18", 24 | "@types/react-dom": "^18", 25 | "autoprefixer": "^10.0.1", 26 | "eslint": "^8", 27 | "eslint-config-next": "14.0.3", 28 | "postcss": "^8", 29 | "tailwindcss": "^3.3.0", 30 | "typescript": "^5" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wownar-react-native/client/src/AppNavigator.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createNativeStackNavigator } from "@react-navigation/native-stack"; 3 | import Signin from "./components/Screens/Signin"; 4 | import Home from "./components/Screens/Home"; 5 | import Loading from "./components/Screens/Loading"; 6 | 7 | export type RootStackParamList = { 8 | Home: undefined; 9 | Signin: undefined; 10 | Loading: undefined; 11 | }; 12 | 13 | const Stack = createNativeStackNavigator(); 14 | 15 | const AppNavigator: React.FC = () => { 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | }; 24 | 25 | export default AppNavigator; 26 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Inter } from "next/font/google"; 4 | import "./globals.scss"; 5 | import { AppProvider } from "@/Context/AppContext"; 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import { ToastContainer } from "react-toastify"; 8 | import "@neynar/react/dist/style.css"; 9 | import NeynarProviderWrapper from "@/Context/NeynarProviderWrapper"; 10 | 11 | const inter = Inter({ subsets: ["latin"] }); 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: { 16 | children: React.ReactNode; 17 | }) { 18 | return ( 19 | 20 | 21 | 22 | 23 | {children} 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /wownar/src/app/api/user/[fid]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import neynarClient from "@/clients/neynar"; 3 | import { isApiErrorResponse } from "@neynar/nodejs-sdk"; 4 | 5 | export async function GET( 6 | request: NextRequest, 7 | { params }: { params: { fid: string } } 8 | ) { 9 | try { 10 | const fid = parseInt(params.fid); 11 | const { 12 | result: { user }, 13 | } = await neynarClient.lookupUserByFid(fid); 14 | return NextResponse.json({ user }, { status: 200 }); 15 | } catch (err) { 16 | console.log("/api/user/[fid]", err); 17 | if (isApiErrorResponse(err)) { 18 | return NextResponse.json( 19 | { ...err.response.data }, 20 | { status: err.response.status } 21 | ); 22 | } else 23 | return NextResponse.json( 24 | { message: "Something went wrong" }, 25 | { status: 500 } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wownar-react-native/client/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { ISuccessMessage } from "@neynar/react-native-signin"; 2 | import * as SecureStore from "expo-secure-store"; 3 | 4 | const KEY = "userInfo"; 5 | 6 | export const storeUser = async (data: ISuccessMessage) => { 7 | try { 8 | await SecureStore.setItemAsync(KEY, JSON.stringify(data)); 9 | } catch (error) { 10 | console.log("Error storing credentials", error); 11 | } 12 | }; 13 | 14 | export const retrieveUser = async () => { 15 | try { 16 | const user = await SecureStore.getItemAsync(KEY); 17 | if (!user) return null; 18 | return JSON.parse(user) as ISuccessMessage; 19 | } catch (error) { 20 | console.error(`Error retrieving ${KEY}`, error); 21 | return null; 22 | } 23 | }; 24 | 25 | export const removeUser = async () => { 26 | try { 27 | await SecureStore.deleteItemAsync(KEY); 28 | } catch (error) { 29 | console.error(`Error deleting ${KEY}`, error); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/Context/NeynarProviderWrapper.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { FC, ReactNode } from "react"; 4 | import { ScreenState, useApp } from "./AppContext"; 5 | import { NeynarContextProvider, Theme } from "@neynar/react"; 6 | 7 | interface Props { 8 | children: ReactNode; 9 | } 10 | 11 | const NeynarProviderWrapper: FC = ({ children }) => { 12 | const { setScreen } = useApp(); 13 | 14 | return ( 15 | { 21 | setScreen(ScreenState.Home); 22 | }, 23 | onSignout() { 24 | setScreen(ScreenState.Signin); 25 | }, 26 | }, 27 | }} 28 | > 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default NeynarProviderWrapper; 35 | -------------------------------------------------------------------------------- /gm-bot/src/abi/SignedKeyRequestMetadata.ts: -------------------------------------------------------------------------------- 1 | export const SignedKeyRequestMetadataABI = { 2 | inputs: [ 3 | { 4 | components: [ 5 | { 6 | internalType: "uint256", 7 | name: "requestFid", 8 | type: "uint256", 9 | }, 10 | { 11 | internalType: "address", 12 | name: "requestSigner", 13 | type: "address", 14 | }, 15 | { 16 | internalType: "bytes", 17 | name: "signature", 18 | type: "bytes", 19 | }, 20 | { 21 | internalType: "uint256", 22 | name: "deadline", 23 | type: "uint256", 24 | }, 25 | ], 26 | internalType: "struct SignedKeyRequestValidator.SignedKeyRequestMetadata", 27 | name: "metadata", 28 | type: "tuple", 29 | }, 30 | ], 31 | name: "encodeMetadata", 32 | outputs: [ 33 | { 34 | internalType: "bytes", 35 | name: "", 36 | type: "bytes", 37 | }, 38 | ], 39 | stateMutability: "pure", 40 | type: "function", 41 | }; 42 | -------------------------------------------------------------------------------- /wownar/src/components/icons/Signout/index.tsx: -------------------------------------------------------------------------------- 1 | interface Props { 2 | height?: string; 3 | width?: string; 4 | } 5 | 6 | const Signout = ({ height = "512px", width = "512px" }: Props) => { 7 | return ( 8 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default Signout; 23 | -------------------------------------------------------------------------------- /wownar/src/app/api/verify-user/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import neynarClient from "@/clients/neynar"; 3 | import { isApiErrorResponse } from "@neynar/nodejs-sdk"; 4 | 5 | export async function POST(request: NextRequest) { 6 | const { signerUuid, fid } = (await request.json()) as { 7 | signerUuid: string; 8 | fid: string; 9 | }; 10 | 11 | let isVerifiedUser = false; 12 | try { 13 | const { fid: userFid } = await neynarClient.lookupSigner(signerUuid); 14 | 15 | if (userFid == fid) { 16 | isVerifiedUser = true; 17 | } else isVerifiedUser = false; 18 | return NextResponse.json({ isVerifiedUser }, { status: 200 }); 19 | } catch (err) { 20 | console.log("/api/verify-user", err); 21 | if (isApiErrorResponse(err)) { 22 | return NextResponse.json( 23 | { ...err.response.data }, 24 | { status: err.response.status } 25 | ); 26 | } else 27 | return NextResponse.json( 28 | { message: "Something went wrong" }, 29 | { status: 500 } 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wownar-react-native/client/src/components/SignoutButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, Text, TouchableOpacity } from "react-native"; 3 | import { AntDesign } from "@expo/vector-icons"; 4 | 5 | type SignOutButtonProps = { 6 | onPress: () => void; 7 | }; 8 | 9 | const SignOutButton: React.FC = ({ onPress }) => { 10 | return ( 11 | 12 | Sign Out 13 | 14 | 15 | ); 16 | }; 17 | 18 | const styles = StyleSheet.create({ 19 | button: { 20 | flexDirection: "row", 21 | justifyContent: "center", 22 | alignItems: "center", 23 | paddingVertical: 10, 24 | paddingHorizontal: 20, 25 | borderWidth: 1, 26 | borderColor: "white", 27 | borderRadius: 5, 28 | backgroundColor: "transparent", 29 | }, 30 | text: { 31 | color: "white", 32 | marginRight: 10, 33 | fontSize: 16, 34 | }, 35 | }); 36 | 37 | export default SignOutButton; 38 | -------------------------------------------------------------------------------- /managed-signers/src/app/api/signer/route.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "@/lib/neynarClient"; 2 | import { getSignedKey } from "@/utils/getSignedKey"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function POST() { 6 | try { 7 | const signedKey = await getSignedKey(); 8 | 9 | return NextResponse.json(signedKey, { 10 | status: 200, 11 | }); 12 | } catch (error) { 13 | return NextResponse.json({ error: "An error occurred" }, { status: 500 }); 14 | } 15 | } 16 | 17 | export async function GET(req: Request) { 18 | const { searchParams } = new URL(req.url); 19 | const signer_uuid = searchParams.get("signer_uuid"); 20 | 21 | if (!signer_uuid) { 22 | return NextResponse.json( 23 | { error: "signer_uuid is required" }, 24 | { status: 400 } 25 | ); 26 | } 27 | 28 | try { 29 | const signer = await neynarClient.lookupSigner(signer_uuid); 30 | 31 | return NextResponse.json(signer, { status: 200 }); 32 | } catch (error) { 33 | return NextResponse.json({ error: "An error occurred" }, { status: 500 }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Neynar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wownar/src/app/api/cast/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import neynarClient from "@/clients/neynar"; 3 | import { isApiErrorResponse } from "@neynar/nodejs-sdk"; 4 | 5 | export async function GET(request: NextRequest) { 6 | const fid = (await await request.json()) as { fid: number }; 7 | return NextResponse.json({ fid }, { status: 200 }); 8 | } 9 | 10 | export async function POST(request: NextRequest) { 11 | const { signerUuid, text } = (await request.json()) as { 12 | signerUuid: string; 13 | text: string; 14 | }; 15 | 16 | try { 17 | const { hash } = await neynarClient.publishCast(signerUuid, text); 18 | return NextResponse.json( 19 | { message: `Cast with hash ${hash} published successfully` }, 20 | { status: 200 } 21 | ); 22 | } catch (err) { 23 | console.log("/api/cast", err); 24 | if (isApiErrorResponse(err)) { 25 | return NextResponse.json( 26 | { ...err.response.data }, 27 | { status: err.response.status } 28 | ); 29 | } else 30 | return NextResponse.json( 31 | { message: "Something went wrong" }, 32 | { status: 500 } 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wownar-react-native/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wownar-native", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "@neynar/react-native-signin": "^1.2.0", 13 | "@react-navigation/native": "^6.1.9", 14 | "@react-navigation/native-stack": "^6.9.17", 15 | "@types/ip": "^1.1.3", 16 | "dotenv": "^16.3.1", 17 | "expo": "~49.0.15", 18 | "expo-constants": "~14.4.2", 19 | "expo-secure-store": "~12.3.1", 20 | "expo-status-bar": "~1.6.0", 21 | "react": "18.2.0", 22 | "react-native": "0.72.6", 23 | "react-native-keyboard-aware-scroll-view": "^0.9.5", 24 | "react-native-paper": "^5.12.1", 25 | "react-native-safe-area-context": "4.6.3", 26 | "react-native-screens": "^3.29.0", 27 | "react-native-svg": "^15.2.0", 28 | "react-native-webview": "^13.6.4" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.20.0", 32 | "@types/react": "~18.2.14", 33 | "typescript": "^5.1.3" 34 | }, 35 | "private": true 36 | } 37 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/api/cast/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import neynarClient from "@/clients/neynar"; 3 | import { isApiErrorResponse } from "@neynar/nodejs-sdk"; 4 | 5 | export async function GET(request: NextRequest) { 6 | const fid = (await await request.json()) as { fid: number }; 7 | return NextResponse.json({ fid }, { status: 200 }); 8 | } 9 | 10 | export async function POST(request: NextRequest) { 11 | const { signerUuid, text } = (await request.json()) as { 12 | signerUuid: string; 13 | text: string; 14 | }; 15 | 16 | try { 17 | const { hash } = await neynarClient.publishCast(signerUuid, text); 18 | return NextResponse.json( 19 | { message: `Cast with hash ${hash} published successfully` }, 20 | { status: 200 } 21 | ); 22 | } catch (err) { 23 | console.log("/api/cast", err); 24 | if (isApiErrorResponse(err)) { 25 | return NextResponse.json( 26 | { ...err.response.data }, 27 | { status: err.response.status } 28 | ); 29 | } else 30 | return NextResponse.json( 31 | { message: "Something went wrong" }, 32 | { status: 500 } 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/Context/AppContext.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | useContext, 5 | createContext, 6 | useMemo, 7 | useState, 8 | FC, 9 | ReactNode, 10 | } from "react"; 11 | 12 | type SetState = React.Dispatch>; 13 | 14 | export enum ScreenState { 15 | Signin = "signin", 16 | Home = "home", 17 | } 18 | 19 | interface Props { 20 | children: ReactNode; 21 | } 22 | 23 | interface AppContextInterface { 24 | screen: ScreenState; 25 | setScreen: SetState; 26 | } 27 | 28 | const AppContext = createContext(null); 29 | 30 | export const AppProvider: FC = ({ children }) => { 31 | const [screen, setScreen] = useState(ScreenState.Signin); 32 | 33 | const value: AppContextInterface | null = useMemo( 34 | () => ({ 35 | screen, 36 | setScreen, 37 | }), 38 | [screen] 39 | ); 40 | 41 | return {children}; 42 | }; 43 | 44 | export const useApp = (): AppContextInterface => { 45 | const context = useContext(AppContext); 46 | if (!context) { 47 | throw new Error("AppContext must be used within AppProvider"); 48 | } 49 | return context; 50 | }; 51 | -------------------------------------------------------------------------------- /archiver-script/index.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | 3 | import { NeynarAPIClient } from "@neynar/nodejs-sdk"; 4 | const client = new NeynarAPIClient("YOUR_NEYNAR_API_KEY"); 5 | 6 | const parser = (cast) => { 7 | return { 8 | fid: parseInt(cast.author.fid), 9 | parentFid: parseInt(cast.parentAuthor.fid) 10 | ? parseInt(cast.parentAuthor.fid) 11 | : undefined, 12 | hash: cast.hash || undefined, 13 | threadHash: cast.threadHash || undefined, 14 | parentHash: cast.parentHash || undefined, 15 | parentUrl: cast.parentUrl || undefined, 16 | text: cast.text || undefined, 17 | }; 18 | }; 19 | 20 | // parse and save to file 21 | const dumpCast = (cast) => { 22 | const parsed = parser(cast); 23 | const data = `${JSON.stringify(parsed)}\n`; 24 | fs.appendFileSync("data.ndjson", data); 25 | }; 26 | 27 | const fetchAndDump = async (fid, cursor) => { 28 | const data = await client.fetchAllCastsCreatedByUser(fid, { 29 | limit: 150, 30 | cursor, 31 | }); 32 | data.result.casts.map(dumpCast); 33 | 34 | // If there is no next cursor, we are done 35 | if (data.result.next.cursor === null) return; 36 | await fetchAndDump(fid, data.result.next.cursor); 37 | }; 38 | 39 | // save all @rish.eth's casts in a file called data.ndjson 40 | const fid = 194; 41 | fetchAndDump(fid); 42 | -------------------------------------------------------------------------------- /wownar-react-native/client/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | import { 3 | CommonActions, 4 | NavigationContainer, 5 | NavigationContainerRef, 6 | } from "@react-navigation/native"; 7 | import { AppProvider, useApp } from "./src/Context/AppContext"; 8 | import AppNavigator, { RootStackParamList } from "./src/AppNavigator"; 9 | 10 | const AuthNavigation: React.FC = () => { 11 | const { isAuthenticated } = useApp(); 12 | const navigationRef = 13 | useRef>(null); 14 | 15 | useEffect(() => { 16 | if (isAuthenticated === null) { 17 | navigationRef.current?.navigate("Loading"); 18 | return; 19 | } 20 | 21 | if (isAuthenticated) { 22 | navigationRef.current?.dispatch( 23 | CommonActions.reset({ 24 | index: 0, 25 | routes: [{ name: "Home" }], 26 | }) 27 | ); 28 | } else { 29 | navigationRef.current?.navigate("Signin"); 30 | } 31 | }, [isAuthenticated]); 32 | 33 | return ( 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | const App: React.FC = () => { 41 | return ( 42 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /flask-app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | import requests 3 | import os 4 | 5 | # Initialize the Flask application 6 | app = Flask(__name__) 7 | 8 | 9 | @app.route("/") 10 | def main_app(): 11 | api_key = "YOUR_API_KEY_HERE" 12 | evm_channel = { 13 | "name": "EVM", 14 | "parent_url": "chain://eip155:1/erc721:0x37fb80ef28008704288087831464058a4a3940ae", 15 | "image": "https://warpcast.com/~/channel-images/evm.png", 16 | "channel_id": "evm", 17 | "lead_fid": 3621, 18 | } 19 | 20 | url = "https://api.neynar.com/v2/" 21 | url += "farcaster/feed?feed_type=filter&filter_type=parent_url" 22 | url += f"&parent_url={evm_channel['parent_url']}&limit=25" 23 | headers = {"accept": "application/json", "api_key": api_key} 24 | 25 | # get data from Neynar API, parse, then render template 26 | response = requests.get(url, headers=headers) 27 | data = response.json() 28 | 29 | # parse data 30 | parser = lambda cast: {"fid": cast["author"]["fid"], "text": cast["text"]} 31 | parsed_data = list(map(parser, data["casts"])) 32 | parsed_data = [cast for cast in parsed_data if cast["text"] not in ["", None]] 33 | 34 | return render_template("index.html", data=parsed_data) 35 | 36 | 37 | # Run the application 38 | if __name__ == "__main__": 39 | app.run(debug=True) 40 | -------------------------------------------------------------------------------- /flask-app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | My app! 8 | 49 | 50 | 51 | 52 |
53 |

Casts in EVM channel!

54 | {% for item in data %} 55 |
56 |
fid:{{ item.fid }}
57 |
{{ item.text }}
58 |
59 | {% endfor %} 60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /archiver-script/README.md: -------------------------------------------------------------------------------- 1 | # Neynar Archiver Mini-App 2 | 3 | Welcome to the Neynar Mini-App! This Nodejs script uses Neynar's API to fetch and archive casts of a specific user. Follow these steps to get started: 4 | 5 | ## Steps 6 | 7 | ### Step 1: Clone the Repository 8 | 9 | Clone the Neynar Mini-App repository to your local machine: 10 | 11 | ```sh 12 | git clone https://github.com/neynarxyz/farcaster-examples 13 | ``` 14 | 15 | ### Step 2: Prepare the Application Directory 16 | 17 | Move the Flask app directory to your desired location and remove the cloned repository's extra contents: 18 | 19 | ```sh 20 | mv farcaster-examples/archiver-script . 21 | rm -rf farcaster-examples 22 | cd archiver-script 23 | ``` 24 | 25 | ### Step 3: NPM install 26 | 27 | Install the required Nodejs packages: 28 | 29 | ```sh 30 | npm install 31 | ``` 32 | 33 | ### Step 4: Edit the FID and the API key 34 | 35 | Open `index.js` and replace the `FID` with your own: 36 | 37 | ```javascript 38 | // save all @rish.eth's casts in a file called data.ndjson 39 | const fid = 194; 40 | fetchAndDump(fid); 41 | ``` 42 | 43 | Also don't forget to edit use your API key: 44 | 45 | ```javascript 46 | const client = new NeynarAPIClient("YOUR_NEYNAR_API_KEY"); 47 | ``` 48 | 49 | ### Step 5: Run the Script 50 | 51 | ```sh 52 | node index.js 53 | ``` 54 | 55 | You should now be ready to run the script, saving the casts of the user with the FID you specified to a file called `data.ndjson` in the same directory as `index.js`. 56 | -------------------------------------------------------------------------------- /wownar/README.md: -------------------------------------------------------------------------------- 1 | # wownar-react-sdk 2 | 3 | ## Introduction 4 | 5 | `wownar` is a nextjs app that demonstrates the integration of [SIWN](https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn). 6 | 7 | ## Prerequisites 8 | 9 | - [Node.js](https://nodejs.org/en/): A JavaScript runtime built on Chrome's V8 JavaScript engine. Ensure you have Node.js installed on your system. 10 | 11 | ## Installation and Setup Environment 12 | 13 | 1. **Install Project Dependencies**: Based on the package manager run one of the following commands to install all required dependencies: 14 | 15 | For yarn 16 | 17 | ```bash 18 | yarn install 19 | ``` 20 | 21 | For npm 22 | 23 | ```bash 24 | npm install 25 | ``` 26 | 27 | 2. **Configure Environment Variables** 28 | 29 | - Copy the example environment file: 30 | 31 | ```bash 32 | cp .env.example .env.local 33 | ``` 34 | 35 | - Edit `.env` to add your `NEYNAR_API_KEY` and `NEXT_PUBLIC_NEYNAR_CLIENT_ID`. 36 | 37 | ## Run Application 38 | 39 | - For yarn 40 | 41 | ```bash 42 | yarn dev 43 | ``` 44 | 45 | - For npm 46 | 47 | ```bash 48 | npm run dev 49 | ``` 50 | 51 | ## License 52 | 53 | `wownar-react-sdk` is released under the MIT License. This license permits free use, modification, and distribution of the software, with the requirement that the original copyright and license notice are included in any substantial portion of the work. -------------------------------------------------------------------------------- /flask-app/README.md: -------------------------------------------------------------------------------- 1 | # Neynar Flask Mini-App 2 | 3 | Welcome to the Neynar Mini-App! This Flask application uses Neynar's API to fetch and display casts from the EVM channel. Follow these steps to get started: 4 | 5 | ## Steps 6 | 7 | ### Step 1: Clone the Repository 8 | 9 | Clone the Neynar Mini-App repository to your local machine: 10 | 11 | ```sh 12 | git clone https://github.com/neynarxyz/farcaster-examples 13 | ``` 14 | 15 | ### Step 2: Prepare the Application Directory 16 | 17 | Move the Flask app directory to your desired location and remove the cloned repository's extra contents: 18 | 19 | ```sh 20 | mv farcaster-examples/flask-app . 21 | rm -rf farcaster-examples 22 | cd flask-app 23 | ``` 24 | 25 | ### Step 3: Set Up a Virtual Environment 26 | 27 | Create and activate a virtual environment for the application: 28 | 29 | ```sh 30 | python -m venv venv 31 | # Activate the environment: 32 | # On macOS/Linux: 33 | source venv/bin/activate 34 | # On Windows: 35 | .\venv\Scripts\activate 36 | ``` 37 | 38 | ### Step 4: Install Dependencies 39 | 40 | Install the required Python packages: 41 | 42 | ```sh 43 | pip install -r requirements.txt 44 | ``` 45 | 46 | ### Step 5: Edit the FID and the API key 47 | 48 | Open `app.py` and replace the `api_key` with your own: 49 | 50 | ```python 51 | api_key = "YOUR_API_KEY_HERE" 52 | ``` 53 | 54 | ### Step 6: Run the Application 55 | 56 | Start the Flask app: 57 | 58 | ```sh 59 | python app.py 60 | # Your app will be running on port 5000 61 | ``` 62 | 63 | You should now see the Neynar mini-app running and displaying casts from the EVM channel! 64 | -------------------------------------------------------------------------------- /wownar/src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { toast } from "react-toastify"; 2 | import axios, { AxiosError } from "axios"; 3 | import { ErrorRes } from "@neynar/nodejs-sdk/build/neynar-api/v2"; 4 | 5 | export const welcomeMessages = [ 6 | "Wowow Farcaster", 7 | // "Join the conversation. Sign in to share your story on Warpcast.", 8 | // "Ready to make your mark? Sign in to start casting on Warpcast.", 9 | // "Sign in to cast your thoughts and connect with the Warpcast community.", 10 | // "Be part of the decentralized dialogue. Sign in to cast your first post now.", 11 | // "Let's get your ideas out there. Sign in to start casting your unique perspective.", 12 | // "Elevate your voice. Sign in and amplify your message.", 13 | // "Connect, engage, and influence. Sign in to begin your Warpcast journey.", 14 | // "Make waves with your words. Sign in and cast away!", 15 | // "Sign in and join a new era of social networking.", 16 | ]; 17 | 18 | export const getMessage = (messagesList: string[]) => { 19 | return messagesList[Math.floor(Math.random() * messagesList.length)]; 20 | }; 21 | 22 | export const verifyUser = async (signerUuid: string, fid: string) => { 23 | let _isVerifiedUser = false; 24 | try { 25 | const { 26 | data: { isVerifiedUser }, 27 | } = await axios.post("/api/verify-user", { signerUuid, fid }); 28 | _isVerifiedUser = isVerifiedUser; 29 | } catch (err) { 30 | const { message } = (err as AxiosError).response?.data as ErrorRes; 31 | toast(message, { 32 | type: "error", 33 | theme: "dark", 34 | autoClose: 3000, 35 | position: "bottom-right", 36 | pauseOnHover: true, 37 | }); 38 | } 39 | return _isVerifiedUser; 40 | }; 41 | 42 | export const removeSearchParams = () => { 43 | window.history.replaceState({}, document.title, window.location.pathname); 44 | }; 45 | -------------------------------------------------------------------------------- /frames-bot/index.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "./utils/neynarClient"; 2 | import { NeynarFrameCreationRequest } from "@neynar/nodejs-sdk/build/neynar-api/v2"; 3 | 4 | const server = Bun.serve({ 5 | port: 3000, 6 | async fetch(req) { 7 | try { 8 | const body = await req.text(); 9 | const hookData = JSON.parse(body); 10 | 11 | const creationRequest: NeynarFrameCreationRequest = { 12 | name: `gm ${hookData.data.author.username}`, 13 | pages: [ 14 | { 15 | image: { 16 | url: "https://moralis.io/wp-content/uploads/web3wiki/638-gm/637aeda23eca28502f6d3eae_61QOyzDqTfxekyfVuvH7dO5qeRpU50X-Hs46PiZFReI.jpeg", 17 | aspect_ratio: "1:1", 18 | }, 19 | title: "Page title", 20 | buttons: [], 21 | input: { 22 | text: { 23 | enabled: false, 24 | }, 25 | }, 26 | uuid: "gm", 27 | version: "vNext", 28 | }, 29 | ], 30 | }; 31 | const frame = await neynarClient.publishNeynarFrame(creationRequest); 32 | 33 | if (!process.env.SIGNER_UUID) { 34 | throw new Error("Make sure you set SIGNER_UUID in your .env file"); 35 | } 36 | 37 | const reply = await neynarClient.publishCast( 38 | process.env.SIGNER_UUID, 39 | `gm ${hookData.data.author.username}`, 40 | { 41 | embeds: [ 42 | { 43 | url: frame.link, 44 | }, 45 | ], 46 | replyTo: hookData.data.hash, 47 | } 48 | ); 49 | 50 | return new Response(`Replied to the cast with hash: ${reply.hash}`); 51 | } catch (e: any) { 52 | return new Response(e.message, { status: 500 }); 53 | } 54 | }, 55 | }); 56 | 57 | console.log(`Listening on localhost:${server.port}`); 58 | -------------------------------------------------------------------------------- /wownar-react-sdk/README.md: -------------------------------------------------------------------------------- 1 | # wownar-react-sdk 2 | 3 | ## Introduction 4 | 5 | `wownar-react-sdk` is a nextjs app that demonstrates the integration of [@neynar/react sdk](https://www.npmjs.com/package/@neynar/react) for [SIWN](https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn). `@neynar/`react` makes implementation much simpler by managing the state for you, which wasn't possible in an earlier version of [SIWN](https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn). 6 | 7 | ## Prerequisites 8 | 9 | - [Node.js](https://nodejs.org/en/): A JavaScript runtime built on Chrome's V8 JavaScript engine. Ensure you have Node.js installed on your system. 10 | 11 | ## Installation and Setup Environment 12 | 13 | 1. **Install Project Dependencies**: Based on the package manager run one of the following commands to install all required dependencies: 14 | 15 | For yarn 16 | 17 | ```bash 18 | yarn install 19 | ``` 20 | 21 | For npm 22 | 23 | ```bash 24 | npm install 25 | ``` 26 | 27 | 2. **Configure Environment Variables** 28 | 29 | - Copy the example environment file: 30 | 31 | ```bash 32 | cp .env.example .env.local 33 | ``` 34 | 35 | - Edit `.env` to add your `NEYNAR_API_KEY` and `NEXT_PUBLIC_NEYNAR_CLIENT_ID`. 36 | 37 | ## Run Application 38 | 39 | - For yarn 40 | 41 | ```bash 42 | yarn dev 43 | ``` 44 | 45 | - For npm 46 | 47 | ```bash 48 | npm run dev 49 | ``` 50 | 51 | ## License 52 | 53 | `wownar-react-sdk` is released under the MIT License. This license permits free use, modification, and distribution of the software, with the requirement that the original copyright and license notice are included in any substantial portion of the work. -------------------------------------------------------------------------------- /managed-signers/README.md: -------------------------------------------------------------------------------- 1 | # Write casts with managed signers 2 | 3 | ## Prerequisites 4 | 5 | - Make sure you have Node.js and yarn installed on your machine. 6 | 7 | ## Setup 8 | 9 | 1. **Install Dependencies**: 10 | To ensure that all the required libraries and modules are installed, run: 11 | 12 | ```bash 13 | yarn install 14 | ``` 15 | 16 | 2. **Environment Configuration**: 17 | We use environment variables for various configurations. Start by copying the example environment variables file: 18 | 19 | ```bash 20 | cp .env.example .env.local 21 | ``` 22 | 23 | 3. **Update Environment Variables**: 24 | Open the `.env.local` file in your favorite editor and make sure to replace placeholders with the correct values. 25 | 26 | ```bash 27 | open .env.local 28 | ``` 29 | 30 | 🔔 Notes: 31 | 32 | - NEYNAR_API_KEY: If you need one, sign up on [https://neynar.com](https://neynar.com) 33 | - FARCASTER_DEVELOPER_MNEMONIC: The 12 or 24 word recovery phrase for the Farcaster account. Make sure the value is within single quotes. 34 | - A farcaster developer account is the same as any farcaster account. You can use your personal account as a farcaster developer account e.g. https://warpcast.com/manan or your company / branded developer account like https://warpcast.com/neynar 35 | 36 | 4. **Start the App**: 37 | To start the Sample Farcaster app in development mode, run: 38 | 39 | ```bash 40 | yarn start 41 | ``` 42 | 43 | Your app should now be running on `localhost:3000` (or a specified port in your `.env`). 44 | 45 | ## Troubleshooting 46 | 47 | - If you run into issues with missing packages, make sure to run `yarn install` again. 48 | - Ensure all environment variables in `.env.local` are correctly set. 49 | 50 | ## Feedback 51 | 52 | If you have any feedback or run into issues, please reach out to our team or create an issue on the repository. 53 | 54 | --- 55 | 56 | Happy coding! 🚀 57 | -------------------------------------------------------------------------------- /managed-signers/src/utils/getSignedKey.ts: -------------------------------------------------------------------------------- 1 | import neynarClient from "@/lib/neynarClient"; 2 | import { ViemLocalEip712Signer } from "@farcaster/hub-nodejs"; 3 | import { bytesToHex, hexToBytes } from "viem"; 4 | import { mnemonicToAccount } from "viem/accounts"; 5 | import { getFid } from "./getFid"; 6 | 7 | export const getSignedKey = async () => { 8 | const createSigner = await neynarClient.createSigner(); 9 | const { deadline, signature } = await generate_signature( 10 | createSigner.public_key 11 | ); 12 | 13 | if (deadline === 0 || signature === "") { 14 | throw new Error("Failed to generate signature"); 15 | } 16 | 17 | const fid = await getFid(); 18 | 19 | const signedKey = await neynarClient.registerSignedKey( 20 | createSigner.signer_uuid, 21 | fid, 22 | deadline, 23 | signature 24 | ); 25 | 26 | return signedKey; 27 | }; 28 | 29 | const generate_signature = async function (public_key: string) { 30 | if (typeof process.env.FARCASTER_DEVELOPER_MNEMONIC === "undefined") { 31 | throw new Error("FARCASTER_DEVELOPER_MNEMONIC is not defined"); 32 | } 33 | 34 | const FARCASTER_DEVELOPER_MNEMONIC = process.env.FARCASTER_DEVELOPER_MNEMONIC; 35 | const FID = await getFid(); 36 | 37 | const account = mnemonicToAccount(FARCASTER_DEVELOPER_MNEMONIC); 38 | const appAccountKey = new ViemLocalEip712Signer(account as any); 39 | 40 | // Generates an expiration date for the signature (24 hours from now). 41 | const deadline = Math.floor(Date.now() / 1000) + 86400; 42 | 43 | const uintAddress = hexToBytes(public_key as `0x${string}`); 44 | 45 | const signature = await appAccountKey.signKeyRequest({ 46 | requestFid: BigInt(FID), 47 | key: uintAddress, 48 | deadline: BigInt(deadline), 49 | }); 50 | 51 | if (signature.isErr()) { 52 | return { 53 | deadline, 54 | signature: "", 55 | }; 56 | } 57 | 58 | const sigHex = bytesToHex(signature.value); 59 | 60 | return { deadline, signature: sigHex }; 61 | }; 62 | -------------------------------------------------------------------------------- /wownar/src/hooks/use-local-storage-state.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from "react"; 2 | 3 | type DeserializeFunction = (value: string) => T; 4 | type SerializeFunction = (value: T) => string; 5 | 6 | interface UseLocalStorageStateOptions { 7 | serialize?: SerializeFunction; 8 | deserialize?: DeserializeFunction; 9 | } 10 | 11 | function useLocalStorage( 12 | key: string, 13 | defaultValue: T | (() => T) = "" as T, 14 | { 15 | serialize = JSON.stringify, 16 | deserialize = JSON.parse, 17 | }: UseLocalStorageStateOptions = {} 18 | ): [T, React.Dispatch>, () => void] { 19 | const [state, setState] = useState(() => { 20 | if (typeof window !== "undefined") { 21 | try { 22 | const valueInLocalStorage = window.localStorage.getItem(key); 23 | return valueInLocalStorage 24 | ? deserialize(valueInLocalStorage) 25 | : defaultValue instanceof Function 26 | ? defaultValue() 27 | : defaultValue; 28 | } catch (error) { 29 | console.error("Error reading from localStorage:", error); 30 | return defaultValue instanceof Function ? defaultValue() : defaultValue; 31 | } 32 | } 33 | return defaultValue instanceof Function ? defaultValue() : defaultValue; 34 | }); 35 | 36 | const prevKeyRef = useRef(key); 37 | 38 | useEffect(() => { 39 | const prevKey = prevKeyRef.current; 40 | if (prevKey !== key && typeof window !== "undefined") { 41 | window.localStorage.removeItem(prevKey); 42 | } 43 | prevKeyRef.current = key; 44 | try { 45 | window.localStorage.setItem(key, serialize(state)); 46 | } catch (error) { 47 | console.error("Error writing to localStorage:", error); 48 | } 49 | }, [key, state, serialize]); 50 | 51 | const removeItem = () => { 52 | window.localStorage.removeItem(key); 53 | }; 54 | 55 | return [state, setState, removeItem]; 56 | } 57 | 58 | export default useLocalStorage; 59 | -------------------------------------------------------------------------------- /gm-bot/src/app.ts: -------------------------------------------------------------------------------- 1 | import cron from "node-cron"; 2 | import { MESSAGE } from "./utils"; 3 | import neynarClient from "./neynarClient"; 4 | import { 5 | PUBLISH_CAST_TIME, 6 | SIGNER_UUID, 7 | TIME_ZONE, 8 | NEYNAR_API_KEY, 9 | } from "./config"; 10 | import { isApiErrorResponse } from "@neynar/nodejs-sdk"; 11 | 12 | // Validating necessary environment variables or configurations. 13 | if (!SIGNER_UUID) { 14 | throw new Error("SIGNER_UUID is not defined"); 15 | } 16 | 17 | if (!NEYNAR_API_KEY) { 18 | throw new Error("NEYNAR_API_KEY is not defined"); 19 | } 20 | 21 | /** 22 | * Function to publish a message (cast) using neynarClient. 23 | * @param msg - The message to be published. 24 | */ 25 | const publishCast = async (msg: string) => { 26 | try { 27 | // Using the neynarClient to publish the cast. 28 | await neynarClient.publishCast(SIGNER_UUID, msg); 29 | console.log("Cast published successfully"); 30 | } catch (err) { 31 | // Error handling, checking if it's an API response error. 32 | if (isApiErrorResponse(err)) { 33 | console.log(err.response.data); 34 | } else console.log(err); 35 | } 36 | }; 37 | 38 | // Initial call to publish a motivational message. 39 | publishCast( 40 | `gm! I'm here to brighten your day with daily cheer. Look forward to a warm 'gm' everyday!` 41 | ); 42 | 43 | // Extracting hour and minute from the PUBLISH_CAST_TIME configuration. 44 | const [hour, minute] = PUBLISH_CAST_TIME.split(":"); 45 | 46 | // Scheduling a cron job to publish a message at a specific time every day. 47 | cron.schedule( 48 | `${minute} ${hour} * * *`, // Cron time format 49 | function () { 50 | publishCast(MESSAGE); // Function to execute at the scheduled time. 51 | }, 52 | { 53 | scheduled: true, // Ensure the job is scheduled. 54 | timezone: TIME_ZONE, // Set the timezone for the schedule. 55 | } 56 | ); 57 | 58 | // Logging to inform that the cron job is scheduled. 59 | console.log( 60 | `Cron job scheduled at ${PUBLISH_CAST_TIME} ${TIME_ZONE}, please don't restart your system before the scheduled time.` 61 | ); 62 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/Screens/layout.tsx: -------------------------------------------------------------------------------- 1 | import { ScreenState, useApp } from "@/Context/AppContext"; 2 | import { NeynarAuthButton, useNeynarContext } from "@neynar/react"; 3 | import Image from "next/image"; 4 | import Link from "next/link"; 5 | import { ReactNode, useEffect } from "react"; 6 | 7 | interface Props { 8 | children: ReactNode; 9 | } 10 | 11 | const ScreenLayout = ({ children }: Props) => { 12 | const { setScreen } = useApp(); 13 | const { isAuthenticated } = useNeynarContext(); 14 | 15 | useEffect(() => { 16 | if (isAuthenticated) { 17 | setScreen(ScreenState.Home); 18 | } else { 19 | setScreen(ScreenState.Signin); 20 | } 21 | }, [isAuthenticated, setScreen]); 22 | 23 | return ( 24 |
25 |
26 |
27 | SimpleCaster Logo 33 |

Wownar

34 |
35 | {isAuthenticated && } 36 |
37 | {children} 38 |
39 | {/* 43 | Connect Farcaster accounts for free using  44 | Sign in with Neynar 45 | */} 46 | 50 | Github Repo -> Wownar 51 | 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default ScreenLayout; 58 | -------------------------------------------------------------------------------- /managed-signers/src/app/page.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 6rem; 7 | min-height: 100vh; 8 | text-align: center; 9 | margin-top: 50px; 10 | } 11 | 12 | .btn { 13 | border: none; 14 | border-radius: 5px; 15 | transition: opacity 0.3s ease; 16 | padding: 10px 20px; 17 | font-size: 16px; 18 | } 19 | 20 | .btn:disabled { 21 | opacity: 0.5; 22 | } 23 | 24 | .btn:hover:not(:disabled) { 25 | opacity: 0.8; 26 | } 27 | 28 | .qrContainer { 29 | margin-top: 30px; 30 | display: flex; 31 | flex-direction: column; 32 | align-items: center; 33 | } 34 | 35 | .qr-label { 36 | margin-top: 20px; 37 | font-size: 16px; 38 | } 39 | 40 | .or { 41 | margin-top: 20px; 42 | font-size: 16px; 43 | } 44 | 45 | .castSection { 46 | margin-top: 30px; 47 | display: flex; 48 | flex-direction: column; 49 | align-items: center; 50 | width: 70%; 51 | } 52 | 53 | .link { 54 | color: #007bff; 55 | text-decoration: none; 56 | } 57 | 58 | .toast { 59 | position: fixed; 60 | bottom: 10px; 61 | left: 50%; 62 | transform: translateX(-50%); 63 | background-color: rgba(0, 0, 0, 0.7); 64 | color: white; 65 | padding: 10px 20px; 66 | border-radius: 5px; 67 | } 68 | 69 | .userInfo { 70 | margin-bottom: 30px; 71 | font-size: 20px; 72 | font-weight: bold; 73 | display: flex; 74 | justify-content: center; 75 | align-items: center; 76 | } 77 | 78 | .castContainer { 79 | display: flex; 80 | justify-content: center; 81 | align-items: start; 82 | gap: 10px; 83 | flex-direction: column; 84 | outline: none; 85 | width: 100%; 86 | } 87 | 88 | .castContainer:focus { 89 | outline: none; 90 | border: none; 91 | } 92 | 93 | .castTextarea { 94 | width: 100%; 95 | padding-right: 10px; 96 | } 97 | 98 | .profilePic { 99 | width: 60px; 100 | height: 60px; 101 | border-radius: 50%; 102 | margin-right: 10px; 103 | object-fit: cover; 104 | } 105 | -------------------------------------------------------------------------------- /wownar-react-native/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | NeynarAPIClient, 4 | AuthorizationUrlResponseType, 5 | } = require("@neynar/nodejs-sdk"); 6 | var { json } = require("body-parser"); 7 | require("dotenv").config({ path: ".env" }); 8 | 9 | const app = express(); 10 | 11 | app.use(json()); 12 | 13 | const NEYNAR_API_KEY = process.env.NEYNAR_API_KEY; 14 | const NEYNAR_CLIENT_ID = process.env.NEYNAR_CLIENT_ID; 15 | 16 | const client = new NeynarAPIClient(NEYNAR_API_KEY); 17 | 18 | app.get("/get-auth-url", async (_, res) => { 19 | try { 20 | const { authorization_url } = await client.fetchAuthorizationUrl( 21 | NEYNAR_CLIENT_ID, 22 | AuthorizationUrlResponseType.Code 23 | ); 24 | res.json({ authorization_url }); 25 | } catch (error) { 26 | if (error.isAxiosError) { 27 | console.error("Error:", error); 28 | res.status(error.response.status).json({ error }); 29 | } else { 30 | console.error("Error:", error); 31 | res.status(500).json({ error: "Server error" }); 32 | } 33 | } 34 | }); 35 | 36 | app.get("/user", async (req, res) => { 37 | const { fid } = req.query; 38 | 39 | try { 40 | const { users } = await client.fetchBulkUsers([fid]); 41 | const user = users[0]; 42 | const { display_name, pfp_url } = user; 43 | res.json({ display_name, pfp_url }); 44 | } catch (error) { 45 | if (error.isAxiosError) { 46 | console.error("Error:", error); 47 | res.status(error.response.status).json({ error }); 48 | } else { 49 | console.error("Error:", error); 50 | res.status(500).json({ error: "Server error" }); 51 | } 52 | } 53 | }); 54 | 55 | app.post("/cast", async (req, res) => { 56 | const { signerUuid, text } = req.body; 57 | 58 | try { 59 | const { hash } = await client.publishCast(signerUuid, text); 60 | res.json({ hash }); 61 | } catch (error) { 62 | if (error.isAxiosError) { 63 | console.error("Error:", error); 64 | res.status(error.response.status).json({ error }); 65 | } else { 66 | console.error("Error:", error); 67 | res.status(500).json({ error: "Server error" }); 68 | } 69 | } 70 | }); 71 | 72 | const PORT = 5500; 73 | app.listen(PORT, () => { 74 | console.log(`Server listening on port ${PORT}`); 75 | }); 76 | -------------------------------------------------------------------------------- /wownar/src/app/Screens/layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ScreenState, useApp } from "@/Context/AppContext"; 4 | import Button from "@/components/Button"; 5 | import Signout from "@/components/icons/Signout"; 6 | import useLocalStorage from "@/hooks/use-local-storage-state"; 7 | import { UserInfo } from "@/types"; 8 | import Image from "next/image"; 9 | import Link from "next/link"; 10 | import { ReactNode } from "react"; 11 | 12 | interface Props { 13 | children: ReactNode; 14 | } 15 | 16 | const ScreenLayout = ({ children }: Props) => { 17 | const { screen } = useApp(); 18 | const [_, _1, removeItem] = useLocalStorage("user"); 19 | 20 | const handleSignout = () => { 21 | removeItem(); 22 | window.location.reload(); 23 | }; 24 | 25 | return ( 26 |
27 |
28 |
29 | SimpleCaster Logo 35 |

Wownar

36 |
37 | {screen !== ScreenState.Signin && ( 38 |
39 |
45 | )} 46 |
47 | {children} 48 |
49 | 53 | Connect Farcaster accounts for free using  54 | Sign in with Neynar 55 | 56 | 60 | Github Repo -> Wownar 61 | 62 |
63 |
64 | ); 65 | }; 66 | 67 | export default ScreenLayout; 68 | -------------------------------------------------------------------------------- /flask-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | *.py,cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | db.sqlite3-journal 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | build/docs/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # pipenv 83 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 84 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 85 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 86 | # install all needed dependencies. 87 | #Pipfile.lock 88 | 89 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 90 | __pypackages__/ 91 | 92 | # Celery stuff 93 | celerybeat-schedule 94 | celerybeat.pid 95 | 96 | # SageMath parsed files 97 | *.sage.py 98 | 99 | # Environments 100 | .env 101 | .envrc 102 | .venv 103 | venv/ 104 | ENV/ 105 | env/ 106 | env.bak/ 107 | env.tmp/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | # pytype static type analyzer 128 | .pytype/ 129 | 130 | # Cython debug symbols 131 | cython_debug/ 132 | -------------------------------------------------------------------------------- /cast-action/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Frog, TextInput } from "frog"; 2 | import { devtools } from "frog/dev"; 3 | import { serveStatic } from "frog/serve-static"; 4 | import neynarClient from "./lib/neynarClient.js"; 5 | import { neynar } from "frog/hubs"; 6 | 7 | export const app = new Frog({ 8 | hub: neynar({ apiKey: "NEYNAR_FROG_FM" }), 9 | basePath: "/api", 10 | }); 11 | 12 | const ADD_URL = 13 | "https://warpcast.com/~/add-cast-action?actionType=post&name=Followers&icon=person&postUrl=https%3A%2F%2F05d3-2405-201-800c-6a-70a7-56e4-516c-2d3c.ngrok-free.app%2Fapi%2Ffollowers"; 14 | 15 | app.frame("/", (c) => { 16 | return c.res({ 17 | image: ( 18 |
32 |

44 | gm! Add cast action to view followers count 45 |

46 |
47 | ), 48 | intents: [Add Action], 49 | }); 50 | }); 51 | 52 | app.hono.post("/followers", async (c) => { 53 | try { 54 | const body = await c.req.json(); 55 | const result = await neynarClient.validateFrameAction( 56 | body.trustedData.messageBytes 57 | ); 58 | 59 | const { users } = await neynarClient.fetchBulkUsers([ 60 | Number(result.action.cast.author.fid), 61 | ]); 62 | 63 | if (!users) { 64 | return c.json({ message: "Error. Try Again." }, 500); 65 | } 66 | 67 | let message = `Count:${users[0].follower_count}`; 68 | 69 | return c.json({ message }); 70 | } catch (e) { 71 | return c.json({ message: "Error. Try Again." }, 500); 72 | } 73 | }); 74 | 75 | app.use("/*", serveStatic({ root: "./public" })); 76 | devtools(app, { serveStatic }); 77 | 78 | if (typeof Bun !== "undefined") { 79 | Bun.serve({ 80 | fetch: app.fetch, 81 | port: 3000, 82 | }); 83 | console.log("Server is running on port 3000"); 84 | } 85 | -------------------------------------------------------------------------------- /wownar-react-sdk/src/app/Screens/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import ScreenLayout from "../layout"; 3 | import styles from "./index.module.scss"; 4 | import Button from "@/components/Button"; 5 | import axios, { AxiosError } from "axios"; 6 | import { toast } from "react-toastify"; 7 | import { ErrorRes } from "@neynar/nodejs-sdk/build/neynar-api/v2"; 8 | import { useState } from "react"; 9 | import { useNeynarContext } from "@neynar/react"; 10 | 11 | const Home = () => { 12 | const { user } = useNeynarContext(); 13 | const [text, setText] = useState(""); 14 | 15 | async function handlePublishCast() { 16 | try { 17 | const { 18 | data: { message }, 19 | } = await axios.post<{ message: string }>("/api/cast", { 20 | signerUuid: user?.signer_uuid, 21 | text, 22 | }); 23 | toast(message, { 24 | type: "success", 25 | theme: "dark", 26 | autoClose: 3000, 27 | position: "bottom-right", 28 | pauseOnHover: true, 29 | }); 30 | setText(""); 31 | } catch (err) { 32 | const { message } = (err as AxiosError).response?.data as ErrorRes; 33 | toast(message, { 34 | type: "error", 35 | theme: "dark", 36 | autoClose: 3000, 37 | position: "bottom-right", 38 | pauseOnHover: true, 39 | }); 40 | } 41 | } 42 | 43 | return ( 44 | 45 |
46 | {user ? ( 47 | <> 48 |

49 | Hello {user.display_name} 50 | ... 👋 51 |

52 |
53 | User Profile Picture 60 |