mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-12-11 11:52:35 -05:00
Compare commits
9 Commits
arthur/ney
...
veganbeef/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bf80740c0 | ||
|
|
39437af6f0 | ||
|
|
7b87bc4dfa | ||
|
|
5420b8fca0 | ||
|
|
9cd07170b9 | ||
|
|
3e1acfce42 | ||
|
|
0b139f184d | ||
|
|
16a3e69a43 | ||
|
|
7df556740d |
29
bin/init.js
29
bin/init.js
@@ -76,7 +76,7 @@ export async function init() {
|
|||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'useNeynar',
|
name: 'useNeynar',
|
||||||
message: '🪐 Neynar is an API that makes it easy to build on Farcaster.\n\n' +
|
message: `🪐 ${purple}${bright}${italic}Neynar is an API that makes it easy to build on Farcaster.${reset}\n\n` +
|
||||||
'Benefits of using Neynar in your mini app:\n' +
|
'Benefits of using Neynar in your mini app:\n' +
|
||||||
'- Pre-configured webhook handling (no setup required)\n' +
|
'- Pre-configured webhook handling (no setup required)\n' +
|
||||||
'- Automatic mini app analytics in your dev portal\n' +
|
'- Automatic mini app analytics in your dev portal\n' +
|
||||||
@@ -252,6 +252,17 @@ export async function init() {
|
|||||||
]);
|
]);
|
||||||
answers.useTunnel = hostingAnswer.useTunnel;
|
answers.useTunnel = hostingAnswer.useTunnel;
|
||||||
|
|
||||||
|
// Ask about analytics opt-out
|
||||||
|
const analyticsAnswer = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'enableAnalytics',
|
||||||
|
message: 'Would you like to help improve Neynar products by sharing usage data from your mini app?',
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
answers.enableAnalytics = analyticsAnswer.enableAnalytics;
|
||||||
|
|
||||||
const projectName = answers.projectName;
|
const projectName = answers.projectName;
|
||||||
const projectDirName = projectName.replace(/\s+/g, '-').toLowerCase();
|
const projectDirName = projectName.replace(/\s+/g, '-').toLowerCase();
|
||||||
const projectPath = path.join(process.cwd(), projectDirName);
|
const projectPath = path.join(process.cwd(), projectDirName);
|
||||||
@@ -318,6 +329,7 @@ export async function init() {
|
|||||||
"@farcaster/frame-sdk": ">=0.0.31 <1.0.0",
|
"@farcaster/frame-sdk": ">=0.0.31 <1.0.0",
|
||||||
"@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0",
|
"@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0",
|
||||||
"@farcaster/mini-app-solana": "^0.0.5",
|
"@farcaster/mini-app-solana": "^0.0.5",
|
||||||
|
"@neynar/react": "^1.2.2",
|
||||||
"@radix-ui/react-label": "^2.1.1",
|
"@radix-ui/react-label": "^2.1.1",
|
||||||
"@solana/wallet-adapter-react": "^0.15.38",
|
"@solana/wallet-adapter-react": "^0.15.38",
|
||||||
"@tanstack/react-query": "^5.61.0",
|
"@tanstack/react-query": "^5.61.0",
|
||||||
@@ -327,10 +339,10 @@ export async function init() {
|
|||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"lucide-react": "^0.469.0",
|
"lucide-react": "^0.469.0",
|
||||||
"mipd": "^0.0.7",
|
"mipd": "^0.0.7",
|
||||||
"next": "15.0.3",
|
"next": "^15",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"react": "^18",
|
"react": "^19",
|
||||||
"react-dom": "^18",
|
"react-dom": "^19",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"viem": "^2.23.6",
|
"viem": "^2.23.6",
|
||||||
@@ -340,8 +352,8 @@ export async function init() {
|
|||||||
|
|
||||||
packageJson.devDependencies = {
|
packageJson.devDependencies = {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^19",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "15.0.3",
|
"eslint-config-next": "15.0.3",
|
||||||
@@ -352,10 +364,9 @@ export async function init() {
|
|||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add Neynar dependencies if selected
|
// Add Neynar SDK if selected
|
||||||
if (useNeynar) {
|
if (useNeynar) {
|
||||||
packageJson.dependencies['@neynar/nodejs-sdk'] = '^2.19.0';
|
packageJson.dependencies['@neynar/nodejs-sdk'] = '^2.19.0';
|
||||||
packageJson.dependencies['@neynar/react'] = '^0.9.7';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
||||||
@@ -376,6 +387,8 @@ export async function init() {
|
|||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${answers.primaryCategory}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${answers.primaryCategory}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_TAGS="${answers.tags.join(',')}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_TAGS="${answers.tags.join(',')}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
||||||
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_ANALYTICS_ENABLED="${answers.enableAnalytics}"`);
|
||||||
|
|
||||||
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
||||||
if (useNeynar && neynarApiKey && neynarClientId) {
|
if (useNeynar && neynarApiKey && neynarClientId) {
|
||||||
fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${neynarApiKey}"`);
|
fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${neynarApiKey}"`);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@neynar/create-farcaster-mini-app",
|
"name": "@neynar/create-farcaster-mini-app",
|
||||||
"version": "1.2.28",
|
"version": "1.3.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": false,
|
"private": false,
|
||||||
"access": "public",
|
"access": "public",
|
||||||
|
|||||||
@@ -355,6 +355,9 @@ async function main() {
|
|||||||
`NEXT_PUBLIC_FRAME_TAGS="${process.env.NEXT_PUBLIC_FRAME_TAGS || ''}"`,
|
`NEXT_PUBLIC_FRAME_TAGS="${process.env.NEXT_PUBLIC_FRAME_TAGS || ''}"`,
|
||||||
`NEXT_PUBLIC_FRAME_BUTTON_TEXT="${buttonText}"`,
|
`NEXT_PUBLIC_FRAME_BUTTON_TEXT="${buttonText}"`,
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
`NEXT_PUBLIC_ANALYTICS_ENABLED="${process.env.NEXT_PUBLIC_ANALYTICS_ENABLED || 'false'}"`,
|
||||||
|
|
||||||
// Neynar configuration (if it exists in current env)
|
// Neynar configuration (if it exists in current env)
|
||||||
...(process.env.NEYNAR_API_KEY ?
|
...(process.env.NEYNAR_API_KEY ?
|
||||||
[`NEYNAR_API_KEY="${process.env.NEYNAR_API_KEY}"`] : []),
|
[`NEYNAR_API_KEY="${process.env.NEYNAR_API_KEY}"`] : []),
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ async function loadEnvLocal() {
|
|||||||
'NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY',
|
'NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY',
|
||||||
'NEXT_PUBLIC_FRAME_TAGS',
|
'NEXT_PUBLIC_FRAME_TAGS',
|
||||||
'NEXT_PUBLIC_FRAME_BUTTON_TEXT',
|
'NEXT_PUBLIC_FRAME_BUTTON_TEXT',
|
||||||
|
'NEXT_PUBLIC_ANALYTICS_ENABLED',
|
||||||
'NEYNAR_API_KEY',
|
'NEYNAR_API_KEY',
|
||||||
'NEYNAR_CLIENT_ID'
|
'NEYNAR_CLIENT_ID'
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import type { Session } from "next-auth"
|
import type { Session } from "next-auth";
|
||||||
import { SessionProvider } from "next-auth/react"
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { FrameProvider } from "~/components/providers/FrameProvider";
|
import { MiniAppProvider } from "@neynar/react";
|
||||||
import { SafeFarcasterSolanaProvider } from "~/components/providers/SafeFarcasterSolanaProvider";
|
import { SafeFarcasterSolanaProvider } from "~/components/providers/SafeFarcasterSolanaProvider";
|
||||||
|
|
||||||
const WagmiProvider = dynamic(
|
const WagmiProvider = dynamic(
|
||||||
@@ -18,11 +18,11 @@ export function Providers({ session, children }: { session: Session | null, chil
|
|||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<WagmiProvider>
|
<WagmiProvider>
|
||||||
<FrameProvider>
|
<MiniAppProvider analyticsEnabled={true}>
|
||||||
<SafeFarcasterSolanaProvider endpoint={solanaEndpoint}>
|
<SafeFarcasterSolanaProvider endpoint={solanaEndpoint}>
|
||||||
{children}
|
{children}
|
||||||
</SafeFarcasterSolanaProvider>
|
</SafeFarcasterSolanaProvider>
|
||||||
</FrameProvider>
|
</MiniAppProvider>
|
||||||
</WagmiProvider>
|
</WagmiProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { APP_URL, APP_NAME, APP_DESCRIPTION } from "~/lib/constants";
|
import { APP_URL, APP_NAME, APP_DESCRIPTION } from "~/lib/constants";
|
||||||
import { getFrameEmbedMetadata } from "~/lib/utils";
|
import { getFrameEmbedMetadata } from "~/lib/utils";
|
||||||
@@ -7,8 +7,12 @@ export const revalidate = 300;
|
|||||||
// This is an example of how to generate a dynamically generated share page based on fid:
|
// This is an example of how to generate a dynamically generated share page based on fid:
|
||||||
// Sharing this route e.g. exmaple.com/share/123 will generate a share page for fid 123,
|
// Sharing this route e.g. exmaple.com/share/123 will generate a share page for fid 123,
|
||||||
// with the image dynamically generated by the opengraph-image API route.
|
// with the image dynamically generated by the opengraph-image API route.
|
||||||
export async function generateMetadata({ params }: { params: { fid: string } }): Promise<Metadata> {
|
export async function generateMetadata({
|
||||||
const fid = params.fid;
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ fid: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { fid } = await params;
|
||||||
const imageUrl = `${APP_URL}/api/opengraph-image?fid=${fid}`;
|
const imageUrl = `${APP_URL}/api/opengraph-image?fid=${fid}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -27,4 +31,4 @@ export async function generateMetadata({ params }: { params: { fid: string } }):
|
|||||||
export default function SharePage() {
|
export default function SharePage() {
|
||||||
// redirect to home page
|
// redirect to home page
|
||||||
redirect("/");
|
redirect("/");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,24 @@ import { truncateAddress } from "~/lib/truncateAddress";
|
|||||||
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
|
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
|
||||||
import { BaseError, UserRejectedRequestError } from "viem";
|
import { BaseError, UserRejectedRequestError } from "viem";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
|
import { useMiniApp } from "@neynar/react";
|
||||||
import { Label } from "~/components/ui/label";
|
import { Label } from "~/components/ui/label";
|
||||||
import { useFrame } from "~/components/providers/FrameProvider";
|
|
||||||
import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
|
import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
|
||||||
|
|
||||||
export default function Demo(
|
export default function Demo(
|
||||||
{ title }: { title?: string } = { title: "Frames v2 Demo" }
|
{ title }: { title?: string } = { title: "Frames v2 Demo" }
|
||||||
) {
|
) {
|
||||||
const { isSDKLoaded, context, added, notificationDetails, lastEvent, addFrame, addFrameResult, openUrl, close } = useFrame();
|
const {
|
||||||
|
isSDKLoaded,
|
||||||
|
context,
|
||||||
|
added,
|
||||||
|
notificationDetails,
|
||||||
|
lastEvent,
|
||||||
|
addFrame,
|
||||||
|
addFrameResult,
|
||||||
|
openUrl,
|
||||||
|
close,
|
||||||
|
} = useMiniApp();
|
||||||
const [isContextOpen, setIsContextOpen] = useState(false);
|
const [isContextOpen, setIsContextOpen] = useState(false);
|
||||||
const [txHash, setTxHash] = useState<string | null>(null);
|
const [txHash, setTxHash] = useState<string | null>(null);
|
||||||
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from "react";
|
|
||||||
import sdk, { type Context, type FrameNotificationDetails, AddMiniApp } from "@farcaster/frame-sdk";
|
|
||||||
import { createStore } from "mipd";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
interface FrameContextType {
|
|
||||||
isSDKLoaded: boolean;
|
|
||||||
context: Context.FrameContext | undefined;
|
|
||||||
openUrl: (url: string) => Promise<void>;
|
|
||||||
close: () => Promise<void>;
|
|
||||||
added: boolean;
|
|
||||||
notificationDetails: FrameNotificationDetails | null;
|
|
||||||
lastEvent: string;
|
|
||||||
addFrame: () => Promise<void>;
|
|
||||||
addFrameResult: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FrameContext = React.createContext<FrameContextType | undefined>(undefined);
|
|
||||||
|
|
||||||
export function useFrame() {
|
|
||||||
const [isSDKLoaded, setIsSDKLoaded] = useState(false);
|
|
||||||
const [context, setContext] = useState<Context.FrameContext>();
|
|
||||||
const [added, setAdded] = useState(false);
|
|
||||||
const [notificationDetails, setNotificationDetails] = useState<FrameNotificationDetails | null>(null);
|
|
||||||
const [lastEvent, setLastEvent] = useState("");
|
|
||||||
const [addFrameResult, setAddFrameResult] = useState("");
|
|
||||||
|
|
||||||
// SDK actions only work in mini app clients, so this pattern supports browser actions as well
|
|
||||||
const openUrl = useCallback(async (url: string) => {
|
|
||||||
if (context) {
|
|
||||||
await sdk.actions.openUrl(url);
|
|
||||||
} else {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
}
|
|
||||||
}, [context]);
|
|
||||||
|
|
||||||
const close = useCallback(async () => {
|
|
||||||
if (context) {
|
|
||||||
await sdk.actions.close();
|
|
||||||
} else {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}, [context]);
|
|
||||||
|
|
||||||
const addFrame = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setNotificationDetails(null);
|
|
||||||
const result = await sdk.actions.addFrame();
|
|
||||||
|
|
||||||
if (result.notificationDetails) {
|
|
||||||
setNotificationDetails(result.notificationDetails);
|
|
||||||
}
|
|
||||||
setAddFrameResult(
|
|
||||||
result.notificationDetails
|
|
||||||
? `Added, got notificaton token ${result.notificationDetails.token} and url ${result.notificationDetails.url}`
|
|
||||||
: "Added, got no notification details"
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AddMiniApp.RejectedByUser || error instanceof AddMiniApp.InvalidDomainManifest) {
|
|
||||||
setAddFrameResult(`Not added: ${error.message}`);
|
|
||||||
}else {
|
|
||||||
setAddFrameResult(`Error: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const load = async () => {
|
|
||||||
const context = await sdk.context;
|
|
||||||
setContext(context);
|
|
||||||
setIsSDKLoaded(true);
|
|
||||||
|
|
||||||
// Set up event listeners
|
|
||||||
sdk.on("frameAdded", ({ notificationDetails }) => {
|
|
||||||
console.log("Frame added", notificationDetails);
|
|
||||||
setAdded(true);
|
|
||||||
setNotificationDetails(notificationDetails ?? null);
|
|
||||||
setLastEvent("Frame added");
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("frameAddRejected", ({ reason }) => {
|
|
||||||
console.log("Frame add rejected", reason);
|
|
||||||
setAdded(false);
|
|
||||||
setLastEvent(`Frame add rejected: ${reason}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("frameRemoved", () => {
|
|
||||||
console.log("Frame removed");
|
|
||||||
setAdded(false);
|
|
||||||
setLastEvent("Frame removed");
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("notificationsEnabled", ({ notificationDetails }) => {
|
|
||||||
console.log("Notifications enabled", notificationDetails);
|
|
||||||
setNotificationDetails(notificationDetails ?? null);
|
|
||||||
setLastEvent("Notifications enabled");
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("notificationsDisabled", () => {
|
|
||||||
console.log("Notifications disabled");
|
|
||||||
setNotificationDetails(null);
|
|
||||||
setLastEvent("Notifications disabled");
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("primaryButtonClicked", () => {
|
|
||||||
console.log("Primary button clicked");
|
|
||||||
setLastEvent("Primary button clicked");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Call ready action
|
|
||||||
console.log("Calling ready");
|
|
||||||
sdk.actions.ready({});
|
|
||||||
|
|
||||||
// Set up MIPD Store
|
|
||||||
const store = createStore();
|
|
||||||
store.subscribe((providerDetails) => {
|
|
||||||
console.log("PROVIDER DETAILS", providerDetails);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (sdk && !isSDKLoaded) {
|
|
||||||
console.log("Calling load");
|
|
||||||
setIsSDKLoaded(true);
|
|
||||||
load();
|
|
||||||
return () => {
|
|
||||||
sdk.removeAllListeners();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [isSDKLoaded]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isSDKLoaded,
|
|
||||||
context,
|
|
||||||
added,
|
|
||||||
notificationDetails,
|
|
||||||
lastEvent,
|
|
||||||
addFrame,
|
|
||||||
addFrameResult,
|
|
||||||
openUrl,
|
|
||||||
close,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FrameProvider({ children }: { children: React.ReactNode }) {
|
|
||||||
const frameContext = useFrame();
|
|
||||||
|
|
||||||
if (!frameContext.isSDKLoaded) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FrameContext.Provider value={frameContext}>
|
|
||||||
{children}
|
|
||||||
</FrameContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createConfig, http, WagmiProvider } from "wagmi";
|
import { createConfig, http, WagmiProvider } from "wagmi";
|
||||||
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
|
import { base, degen, mainnet, optimism, unichain, celo } from "wagmi/chains";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { farcasterFrame } from "@farcaster/frame-wagmi-connector";
|
import { farcasterFrame } from "@farcaster/frame-wagmi-connector";
|
||||||
import { coinbaseWallet, metaMask } from 'wagmi/connectors';
|
import { coinbaseWallet, metaMask } from 'wagmi/connectors';
|
||||||
@@ -42,13 +42,14 @@ function useCoinbaseWalletAutoConnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const config = createConfig({
|
export const config = createConfig({
|
||||||
chains: [base, optimism, mainnet, degen, unichain],
|
chains: [base, optimism, mainnet, degen, unichain, celo],
|
||||||
transports: {
|
transports: {
|
||||||
[base.id]: http(),
|
[base.id]: http(),
|
||||||
[optimism.id]: http(),
|
[optimism.id]: http(),
|
||||||
[mainnet.id]: http(),
|
[mainnet.id]: http(),
|
||||||
[degen.id]: http(),
|
[degen.id]: http(),
|
||||||
[unichain.id]: http(),
|
[unichain.id]: http(),
|
||||||
|
[celo.id]: http(),
|
||||||
},
|
},
|
||||||
connectors: [
|
connectors: [
|
||||||
farcasterFrame(),
|
farcasterFrame(),
|
||||||
|
|||||||
Reference in New Issue
Block a user