Skip to content
FTCHD edited this page Oct 1, 2024 · 10 revisions
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.

For this we have prepared guides for each part of the FrameTrain SDK:

How to Get Started

  • 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 full https 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)

Anatomy of a Template

Farcaster Frames Templates

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 the views. Must contain at least an initial handler that displays the first image/slide. In addition to the validated request body and params, Handlers have access to the Frame's storage and config. Under the hood, for each handler in the handlers, a new route is created with the same name.
  • views — a collection of 1 or more React components, rendered as the Frames’ image using satori. Must contain at least a Cover view, the initial view that is displayed when you post a Frame.
  • Inspector — a React (NextJS use 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 the config object, and can modify it. This config will be used by your handlers to properly display the views.
  • Page — an optional React (NextJS use 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 no Page.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 — an 256x256 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.

Good Tips

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.

Support

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.