-
-
Notifications
You must be signed in to change notification settings - Fork 30
Home
framehack-farcaster-hackathon.mp4
FrameTrain is a platform that allows you to upload your own Frame Templates and earn points when they are used.
Incentivizing creators is a huge part of FrameTrain, so we want to make it as easy as possible, all while being greatly rewarded.
- Hooks
- UI Components
- Gating
- Webhooks
- Transactions
- Google Fonts
- Running Code on Client vs Server
- Browser Agent & Scrape
- Image Upload
- Clone the repository
- Create a free Turso account and setup a database.
- Create a free R2 bucket on Cloudflare and make it public. If you're familiar with S3-like storage you can use any provider.
- Add the required
.env
variables-
AUTH_SECRET
: any string will do -
NEYNAR_API_KEY
: you can use the dummy Frog api key ("NEYNAR_FROG_FM") or get a real key from Neynar. -
NEXT_PUBLIC_HOST
: http://localhost:3000 -
NEXT_PUBLIC_DOMAIN
: localhost:3000 -
NEXT_PUBLIC_CDN_HOST
: the public url you got from the R2 bucket, in fullhttps
format -
SCRAPER_URL
: https://tools.farfetched.digital -
TURSO_URL
:libsql://...
-
TURSO_SECRET
: the secret you generated in the Turso dashboard -
S3_BUCKET
: the name of your R2 bucket -
S3_ENDPOINT
: the endpoint of your R2 bucket -
S3_REGION
: auto -
S3_KEY_ID
: the key of your R2 bucket -
S3_SECRET
: the secret of your R2 bucket -
GLIDE_PROJECT_ID
: ea4549f0-4849-4b5a-a972-2a83360a7378
-
- Install dependencies (
npm i
) - Migrate the database (
npm run db:migrate
) - Start the server (
npm run dev
) - Deploy to Vercel using (
npm run deploy
or simply push to GitHub)
Templates live in the @/templates
folder, and this is where you should add yours too.
In order to create a new template, start by cloning the existing starter
template in the @/templates
folder. The index.ts
file has lots of comments to help you get started.
Each template follows a predefined folder structure:
-
handlers
— a collection of 1 or more handlers, used as controllers for displaying theviews
. Must contain at least aninitial
handler that displays the first image/slide. In addition to the validated requestbody
andparams
, Handlers have access to the Frame'sstorage
andconfig
. Under the hood, for eachhandler
in thehandlers
, a new route is created with the same name. -
views
— a collection of 1 or more React components, rendered as the Frames’ image usingsatori
. Must contain at least aCover
view, the initial view that is displayed when you post a Frame. -
Inspector
— a React (NextJSuse client
) component displayed in the Frame Editor. Used to get input from the user, transforming it as needed and saving it as the Frame’s config. The Inspector has access to theconfig
object, and can modify it. This config will be used by yourhandlers
to properly display theviews
. -
Page
— an optional React (NextJSuse client
) component displayed at the/f/${FRAME_ID}/show
route. The Viewer is navigated to this route when clicking on the Frame's image in a client. If the template has noPage.tsx
component, the Viewer is navigated to the URL the User has setup in the Editor (using the "Connect Page" feature), or to the default "Welcome to FrameTrain" page in case no URL has been set as well. -
cover
— a cover image for the template, displayed in the template selection screen. -
icon
— an256x256
icon for the template, displayed in the list of Composer Actions on clients such as Warpcast.
Here's a line-by-line explanation of a template's index.ts
file:
// 1. Import the base types
import type { BaseConfig, BaseStorage, BaseTemplate } from '@/lib/types'
// 2. Import your Inspector component
import Inspector from './Inspector'
// 3. Import your cover image (png, jpg, webp, avif)
import cover from './cover.webp'
// 4. Import your icon (png, jpg, webp, avif)
import icon from './icon.webp'
// 5. Import your handlers
import handlers from './handlers'
// 6. Define your config type, extending the base config
export interface Config extends BaseConfig {
images: []
}
// 7. Define your state type, extending the base state
export interface State extends BaseStorage {}
export default {
// 8. Set the metadata (name, description, your FID and Farcaster username)
name: 'PDF',
description: 'Upload and convert your PDF into a Frame with multiple slides.',
shortDescription: 'Maximum 20 characters',
octicon: 'cog',
icon: icon,
creatorFid: '2',
creatorName: 'Varun',
// 9. Mark the template as visible in the UI
enabled: true,
// 10. Add your previously imported and declared objects
Inspector,
handlers,
initialConfig: {} as Config,
cover,
} satisfies BaseTemplate
Everything else is completely up to you. You can have a hooks
, types
, utils
, or an assets
folder if you want.
1️⃣ The difference between storage
and config
is that for example in a Poll template, you would use the storage
to save incoming votes, and the config
to store the voting options the user wants to display in the Frame and its buttons. You can see this in action in the current Poll template. In general, if you need to save the incoming input from other users Farcaster you use the storage
, if you are saving the configuration of the Frame when the owner/user is editing it on FrameTrain, you use the config
.
2️⃣ — Keep your handlers
as small and quick as possible. In general, try to compute/fetch everything in your Inspector
and save the result in the config
, so you do as little work in your handlers
as possible. If you want to display the avatar of a Twitter user, you can just save the avatar URL to the config
, instead of saving the Twitter username and then fetching the avatar url in your handler
. However, if you need real-time data, for example showing an updated number of Twitter followers or the status of a Sablier stream, you probably need to do it in the handler
on every request.
3️⃣ — Keep it Reactive! You will notice that the Editor saves any changes immediately, so don't include any “save” or “create” buttons in your Inspector
. The “Publish” button in the Editor is used as the “Create/Update” button.
4️⃣ — External APIs that are not well known are not allowed. It's fine to use the OpenSea or 0x API for example, but not your own express app hosted on a VPS.
5️⃣ — Try to minimize the use of any external APIs as much as possible.
6️⃣ — Try to minimize the use of new packages as much as possible. Check the list of installed packages first before installed a new package that does a similar thing. We already have viem
, usehooks-ts
, ms
, dayjs
installed, and separate APIs for image uploads, scraping, and getting farcaster user data.
7️⃣ — Don't use Views
for errors, throw a FrameError
instead.
If you have any questions or suggestions, then you came to the right place!
You can post any thoughts or questions in the /framehack
channel on Farcaster. You will get an answer usually immediately.