# dtech.vision Documentation --- --- Title: Awesome Farcaster Open Source --- # Awesome Farcaster Open Source >A collection of the most amazing Farcaster Open Source Projects, Code Snippets and Tutorials. >Everything ranging from small utilities over tools and bots to full blown clients. Only requirement: must be OpenSource Other Awesome Farcaster Lists: - [Awesome Frames by @df](https://github.com/davidfurlong/awesome-frames) - [Awesome Farcaster by @a16zcrypto](https://github.com/a16z/awesome-farcaster) ## Clients - [Quikcast - Expo Mobile, NextJS Web](https://github.com/farcasterxyz/quikcast/) - [Herocast - NextJS Web](https://github.com/hellno/herocast) - [Litecast - Expo Mobile](https://github.com/dylsteck/litecast/) - [Litecast Web - NextJS Web](https://github.com/dylsteck/litecast-web) - [Opencast - NextJS Web](https://github.com/stephancill/opencast) - [Farcaster Photo Client - NextJS Web](https://github.com/PinataCloud/farcaster-photo-client-template) ## Frames ### Development Kits - [Coinbase Onchainkit - Typescript Function based](https://github.com/coinbase/build-onchain-apps) - [Frames.js - Typescript React JSX based](https://framesjs.org) - [Frog.fm - Typescript Hono ServerSide Routes](https://frog.fm/) - [Airstack Onchainkit for Frames](https://docs.airstack.xyz/airstack-docs-and-faqs/guides/farcaster/airstack-onchain-kit-for-farcaster-frames) - [Pinata FDK - First class IPFS support, Typescript](https://docs.pinata.cloud/farcaster/fdk) - [Neynar Frame Studio - NoCode Frames](https://neynar.com) - [Python Frames with flask](https://github.com/devinaconley/python-frames) ### Frame Sample Github Repos - [Frames with Express.js](https://github.com/SamBroner/farcaster-frames-express-template) - [Account Abstraction Frames with premissionless.js](https://github.com/pimlicolabs/smart-account-frame-template) ## Misc - [Casterscan Farcaster chain explorer - NextJS Web](https://github.com/dylsteck/casterscan) - [Farcaster Search - NextJS Web](https://github.com/gskril/searchcaster) - [SpeedRunEthereum - Smart Contract Quickstart](https://speedrunethereum.com/) - [SpeedRunEthereum - Smart Contract Quickstart Videos](https://www.youtube.com/playlist?list=PLJz1HruEnenAf80uOfDwBPqaliJkjKg69) - [Smart Contract Auditor Quickstart Course from updraft.cyfrin.io](https://github.com/Cyfrin/security-and-auditing-full-course-s23) --- --- Title: Write amazing development specifications Description: Development Specifications are key to evaluate the effort needed to pull of a project and to have a written description of what has to be done for both parties to formally agree and be on the same page. It also aids in being able to go back to a reference document during testing and evaluation. --- # Write amazing development specifications **Development Specifications** are key to evaluate the effort needed to pull of a project and to have a written description of what has to be done for both parties to formally agree and be on the same page. It also aids in being able to go back to a reference document during testing and evaluation. Though they should be written for projects of all sizes the project complexity and people involved determine the amount of detail needed in the actual document. It is generally a good idea to have the specification be a little bit too detailed than to be to vague, but some teams may just be so magically connected that they beam detailled thoughts to each other communicating full features and designs in bullet points only. >**Assume you are not one of these teams. - write a spec** The general practice of **having UI be present** as a rough wireframe (e.g. sketched on paper or via excalidraw) and if larger in scope as full design (e.g. Figma) and defining how each function, button and page should behave in clear language to someone that has no context of the system to be built at all is key. If the specification can be read by a highschool student **without any previous knowledge** who may just need to lookup some industry standard terms (better if not), then any developer will be able to know what to do and you will be able to hold him to the document. It goes both ways in that developers need to know **what to build** and tester neeed to know **how to evaluate** what has been built. Looking at the reasons of project delays and budget explosions should also be reason alone to write specifications if the above mindset didn't resonate cost and time will. ![](../../../assets/images/docs/guides/developmentspec/delayIndustrialized.png) ![](../../../assets/images/docs/guides/developmentspec/delayDeveloping.png) [Source: ](https://doi.org/10.1139/cjce-2020-0527) ## How to structure your development specification Generally there is only two things each specification needs to accomplish: 1) Nail down what is to be built so anyone is on the same page 2) Provide a detailled construction plan so the house is built properly therefore it is important to have an increcible sharp overview (summary) at the start and build into the details as one reads on. A good outline we are following is - 1) General Vision - 2) Case Study - 3) How to get there? (Milestones) - 3.1) M1: description of first milestone - 3.2) M2: description of another milestone - 4) Estimate - 4.1) Timeline - 4.2) Cost ## The process of writing a specification Starting out by writing it from top to bottom and delivering different versions of the specification to clients as fast as possible to get feedback. ### General vision The general vision is very important to get right as it's the direction you all go with. That needs to be perfect for all people. Generally you can get the vision through conversations (meetings). In these conversations don't take everything said word for word, but think about the end user, the development journey and the customer. The end user will likely appreciate something different then the customer is saying they want built so your job is to make sure that there is no big difference and the general vision is good of the end user. If the customer does not agree argue with them and learn their position. These conversations will improve the precision of the general vision, be open minded when writing and discussing with the client. If you have the time and budget, doing user interviews or reading user reviews may be a good idea for the general vision and for larger milestones. Though usually applying common sense from the end user point of view works very well. >Never forget: Common sense makes sense. The user's intuition when using the solution is always right. ### Case Study The case study is a simple review of how similar solutions already work, whether that is competitors or other applications in the space the end users may be familiar with. It could also include multiple samples and generally the feature working somewhere totally different and then transferring the learnings to the solution being specified for the customer. The goal is to promote vision and dreaming by showing actual data and relevant examples. After reading the vision and case study one should go "oh that's what you want to build? yeah and that direction could work". The vision and relevant case study complementing each other to form that picture. ### How to get there? (Milestones) Each individual milestone is a step to take. Be precise in defining your steps. Many clear small steps are easier to take then one big step. >Define many clear, detailled small steps. Within the definition of each step (milestone) include the vision of what to do, potential designs (user interface) needed and technical details. Make sure that the description is written so the business person knows what will get done, the builder knows the direction of how to do it and the customer knows how to evaluate if it was successful or not based on users using it. The more precise you can be the better, but be mindful that requirements may be changed and multiple options need to be considered. If you need to consider multiple options define the testing of multiple options in the milestone so there is no dispute about changing and adapting requirements during the process which will shift budget and time expectations on all sides. Describe the relevant documents and not only the clear outcome. If it is a software project relevant documents include code, and the end user documentation as well as developer documentation. The end user documentation describes how a user is supposed to interact with the software (for small features that are simple enough this may be omitted), while the developer documentation should never be omitted and consists of the code being documented itself and a write up of the philosophy behind the code, data flow and any things to look out for as well as a getting started. ### Estimate Every single client will want to know how much it is going to cost before you solve the small little problems that are unpredictable. Even if one says pay me per hour and I don't know how long it is going to take, they will want to have a fixed number. Why? because they need to know "if it's worth it" and if they can afford it. The second cost is time, if you are fast and the customer values it, it is worth more money. Also time needs to be planned and scheduled as releases on customer side depend on it as does their business which also has other stakeholders. That is why estimates are so important and why it is better to overestimate then to underestimate. If there are big quesion marks note them down and define a potential solution in simple language that anyone understands. - Example: If Milestone 3, which is scheduled for 1 day, takes more than 2 days the extra time will be charged at X rate. - Example: If delivery material Y by the client is delayed by more than 3 days the extra time will be added to all following deadlines. This doesn't just apply to individual milestones but can be extended to the overall project if it makes sense. And they can also be used to provide guarantees to the client. - Example: If Milestone 4 takes more than 8 hours, the milestone is dropped (or reduced to scope XYZ). Talk about conditions and rather have something defined and not need it then to have arguments about something that could be clear beforehand. #### Timeline Geting the timing right is always hardest, but also most important. Why? Because how long something takes decides if it makes sense to do it and also due to human cost is a large factor in budget. So how can we make sure we get the timing right? Luckily this is a development specification and not an investment thesis so you don't lose money by overestimating the timing and always having enough time at the end. If we overestimate the time needed we get two benefits: - Customer is happy because we deliver (way) before the proclaimed delivery date - If complications arise we have enough space to solve them without budget discussions and due date shenanigans So how do you esimate a timeline? Usually you have an idea on how fast you have done something in the past or how fast you can get something done based on experience and the milestone description at hand. That is why the milestone description and the vision need to be so clear, else your estimate here is worthless. By the way dear customer this also includes you changing the requirements mid project, the previous estimates become worthless once we always change the spec. >**Rule of thumb:** take the first time that comes to mind and multiply times 3 or 4 to get a realistic estimate. -> think it takes a day -> say 4 days. If it needs to get done faster, reduce the scope or state assumptions you are making to enable the faster delivery clearly in the estimate also mentioning the long estimate. That way both options are clearly communicated and there one can point to the specification in case of any discussions. #### Cost There are two parts of a cost estimate - Human time (how many hours and how fast?) - Third party cost (partners, infrastructure, assets) How fast is important because that means you need to prioritize the proposal over other projects which needs to be paid for, otherwise it doesn't make sense. Then human cost usually is calculated by estimated time x cost per unit of time. Third party cost are a list of all costs also associated with fullfilling the specification like freelancer estimates, infrastructure bills and other things you need to buy. Most development projects have API and server bills and some UI/UX designer cost to provide the high detail user interface to be implemented. ## How detailed does the spec need to be? The level of detail required depends on the size of the project. Generally it makes sense to be as precise as possible, but sometimes that just means having a clear outline while other times the project is large (multi month +) which requires of course requires more detail as any change will propagate with more consequences in terms of time and cost. For a simple 1 day to 1 week project there are likely 2-3 milestones where each one is clear and you just need to write it out while having some conversation to make sure what you wrote down fits and is understandable by all stakeholders. For larger projects which have lots of functionality or more unkowns it gets trickier and you should go into more detail and split the milestones smaller, because that provides flexibility and detail in the estimate as well as in what you are actually going to want to have delivered. And if you have to implement UI either state that you are the one providing a sample UI or have a full design delivered before you spend much time on the design as it is often chaning and will need small details like loading animations, position changes and the likes to be implemented after the fact. These changes all add up to in the end be the largest time sink of the whole project if not clearly defined before. When the design is defined beforehand, the estimate can be accurate and the small changes are not many so one can just do them within the time and cost estimate. ## Do I really need to have the UI defined? When the UI is defined beforehand, the estimate can be accurate and the small changes are not many so one can just do them within the time and cost estimate. Therefore if you can DO IT. If the UI will evolve later have the specification be a scratch design that illustrates where each button would be and how it would feel, but leave it out. Then once you get to touch something and have a feel design the user interface and add to the specification or make it a quick follow up job. Most customers will not like that, but they will love it in the end because there is no discussions about UI going over budget, taking too long and the likes. All parties will be happy. ## Sample Specifications - Coming Soon --- --- Title: Complete Guide: Publishing NPM Packages with Changesets Description: Master the complete workflow for publishing and versioning NPM packages using Changesets - from setup to automated releases using Github Actions with comprehensive testing and troubleshooting. --- # Complete Guide: Publishing NPM Packages with Changesets **Changesets** help you manage versions and releases in packages, monorepos and multi-package projects. This comprehensive guide walks you through the complete setup, workflow, and automation for publishing NPM packages with nice release notes, and automated version incrementation. No need to manually bump version in package.json files. Changesets handle versioning automatically. > **Why Changesets?** Traditional versioning is manual, error-prone, and doesn't scale. Changesets make versioning **automatic**, **predictable**, and **collaborative**. ## Project Setup & Architecture ### Repository Structure Your project will follow this rough folder structure for changeset workflows: ```bash my-packages-project/ ├── .changeset/ │ ├── config.json # Changeset configuration │ └── README.md # Generated changeset docs, you don't need to do anything here ├── packages/ │ ├── package-a/ # Your individual packages e.g. your-project-core │ ├── package-b/ # Your individual packages e.g. your-project-solidjs │ └── shared-utils/ ├── .github/ │ └── workflows/ │ └── release.yml # Automated release workflow ├── package.json # Root package.json └── bunfig.toml # Workspace configuration, may also be pnpm-workspace.yaml ``` We'll use bun in the following guide, though you can ask your favorite LLM to adapt everything to the package manager of your choice. ### Initial Setup Commands ```bash # Initialize your monorepo mkdir my-packages-project && cd my-packages-project git init # Install changeset CLI bun add -D @changesets/cli # Initialize changesets bun changeset init ``` **Expected Result:** Creates `.changeset/config.json` and `.changeset/README.md` ## Configuration ### Changeset Configuration Edit `.changeset/config.json` for your specific needs: >**Note**: Really make sure to configure the correct `baseBranch` for your repository. Check if it's master or main! >You also want to set the correct Github repository. Make sure it's not the full https://... url but org/repo! ```json { "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json", "changelog": [ "@changesets/cli/changelog", { "repo": "dtechvision/my-packages-project" } ], "commit": false, "fixed": [], "linked": [], "access": "public", "baseBranch": "master", "updateInternalDependencies": "patch", "ignore": [] } ``` **Key Configuration Options:** - `"commit": true` - Auto-commit changeset files - `"access": "restricted"` - For private packages - `"linked": [["package-a", "package-b"]]` - Release packages together - `"fixed": [["ui-*"]]` - Keep versions in sync - `"ignore": ["scratchpad"]` - Exclude paths from changeset tracking and releases ### Package.json Requirements Each package needs proper NPM configuration, which you can do in your package.json of that respective package. In our repo structure example that would be package-a for example so `packages/package-a/package.json` is what we'd be editing here to ensure **LICENSE**, **publishConfig**, **repository**, **description**, and others are set. ```json { "name": "@dtechvision/package-a", "version": "0.0.0", "license": "MIT", "type": "module", "description": "The core typescript package for embed APIs.", "repository": { "type": "git", "url": "https://github.com/ZKAI-Network/embed-sdk/", "directory": "packages/embed-typescript" }, "publishConfig": { "access": "public", "directory": "dist" }, "scripts": { ... } ``` ### Root Package.json Setup Your root `package.json`, which is the one in the main folder so for our sample it is `my-packages-project/package.json` should include the scripts needed to make changesets and more work. Ensure that **private** is set to true as well! We don't want to publish the main folder structure, just the individual packages or the one package we have. ```json { "private": true, "type": "module", "license": "MIT", "packageManager": "bun@1.1.34", "workspaces": [ "packages/*" ], "scripts": { "changeset-version": "changeset version && node scripts/version.mjs", "changeset-publish": "bun run build && TEST_DIST= bun test && changeset publish" ... your other scripts ... }, ``` Why don't we have a publish script? In case you don't want to automate you could use a publish script to write directly to the npm registry. `npm publish` would be what you're looking for. Though we are automating the process with release management with changesets. Our setup will publish each package that we changed with each change described in a changeet. This allows nice versioning and overviews of changes for anyone watching for package updates and trying to debug something. ### Bunfig.toml The Bunfig holds bun specific configuration options. We require it here since we're managing a workspace (monorepo). Our bun config in the root folder `my-packages-project/bunfig.toml` looks as follows: ```toml [workspaces] # Enable workspace support enabled = true [install] # Use exact versions for more predictable builds exact = true # Enable auto-install for missing dependencies auto = true [test] # Use vitest as the test runner runner = "vitest" [build] # Enable TypeScript support typescript = true # Package manager settings [package-manager] # Use bun as the package manager default = "bun" ``` ## Development Workflow ### Step 1: Making Changes ```bash # Create feature branch git checkout -b feature/new-authentication-api # Make your changes to packages echo "export const best = 'dTech';" >> packages/package-a/src/main.ts # this is an example change, but you'd generally want to develop something # Build and test your changes bun run build bun run test ``` ### Step 2: Creating Changesets ```bash # Generate changeset bun run changeset # Interactive prompts will ask: # 1. Which packages changed? (select with space, confirm with enter) # 2. Major, minor, or patch? (for each selected package) # 3. Summary of changes? (write clear description) ``` **Example Changeset Output:** ```markdown --- "dtechvision/package-a": minor "dtechvision/package-b": patch --- Described the best Development Boutique's name. Ensuring it's mentioned in main! ``` ### Step 3: Commit and Create PR ```bash # Commit changeset with your changes git add . git commit -m "feat: Described the best Development Boutique's name." git push origin feature/description # Create pull request gh pr create --title "feat: described the best boutique" --body "Described the best Development Boutique's name." # you can also do this on the website of Github ``` Now let's make sure we have a release for our change. This would automatically release when we merge the PR once the automation is set up, lets do that now. ## Automated Release Setup ### GitHub Repository Permissions Before setting up automation, configure these settings: 1. **Repository Settings → Actions → General** 2. Set **Workflow permissions** to "Read and write permissions" 3. Check **"Allow GitHub Actions to create and approve pull requests"** > **Important:** If this option is grayed out, your organization admin needs to enable it at the organization level first. If that won't be done you need to manually release via `npm publish`. Now create `.github/workflows/release.yml` so that it can be triggered by a push to the `master` (or `main`) branch. ```yaml name: Release on: push: branches: [master] concurrency: group: ${{ github.workflow }}-${{ github.ref }} jobs: release: name: Release runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: write id-token: write pull-requests: write steps: - uses: actions/checkout@v4 - name: Install dependencies uses: ./.github/actions/setup - name: Create Release Pull Request or Publish uses: changesets/action@v1 with: version: bun run changeset-version publish: bun run changeset-publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} ``` To validate and bump version before we are releasing we will use `.github/workflows/pr-check.yml`. This ensures all PR's to master will have changesets. ```yaml name: PR Check on: pull_request: branches: [master] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read pull-requests: write jobs: changeset-check: name: Changeset Check runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Install dependencies run: bun install --frozen-lockfile - name: Check for changesets run: bunx changeset status env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` ### NPM Token Setup ```bash # Create automation token on npmjs.com # Go to: https://www.npmjs.com/settings/tokens # Create new token → Automation → Copy the token # Add to GitHub Secrets: # Repository Settings → Secrets and variables → Actions → New repository secret # Name: NPM_TOKEN # Value: npm_xxxxxxxxxxxxxxxxxxxx ``` ## Testing & Validation ### Test 1: Changeset Enforcement Create a PR without a changeset to verify the bot works: ```bash # Test PR without changeset (should be blocked) git checkout -b test-no-changeset echo "// test change without changeset" >> packages/core/src/index.ts git add . && git commit -m "test: change without changeset" git push origin test-no-changeset # Create PR gh pr create --title "test: no changeset" --body "Testing changeset enforcement - this should be blocked" ``` **Expected Result:** Changeset bot adds comment requesting changeset and creates failing status check. ### **Test 2: Valid Changeset Flow** Test the complete happy path: ```bash # Create test branch with proper changeset git checkout main && git pull git checkout -b test-with-changeset echo "export const testFunction = () => 'hello world';" >> packages/core/src/test.ts # Create changeset pnpm changeset # Select: core package, patch version # Description: "Add test function for validation" # Commit and push git add . && git commit -m "feat: add test function with changeset" git push origin test-with-changeset # Create PR gh pr create --title "feat: test function" --body "Testing complete changeset flow with proper changeset file" ``` **Expected Result:** All checks pass, PR shows green status, ready to merge. ### Test 3: Release Process Verification ```bash # After merging PR with changeset to main, check for release PR gh pr list --label "changeset-release/main" # You should see a PR titled "chore: release packages" # Review the PR to see version bumps and changelog updates # Merge the release PR to trigger NPM publication gh pr merge --squash ``` **Expected Result:** Packages published to NPM with correct version numbers. ## Monitoring & Verification Commands ### Status Check Commands ```bash # Check what packages will be released pnpm changeset status # Preview version bumps without applying them pnpm changeset version --dry-run # List all pending changesets ls .changeset/ # Check current package versions pnpm list --depth=0 --json | jq '.dependencies' ``` ### Release Verification ```bash # Verify NPM publication npm view dtechvision/package-a # Check specific version in registry npm view dtechvision/package-a version # Check all versions npm view dtechvision/package-a versions --json # Test installation in clean environment mkdir temp-test && cd temp-test npm init -y npm install dtechvision/package-a node -e "console.log(require('dtechvision/package-a'))" ``` ## 🔧 Advanced Configuration Patterns ### Custom Changelog Generation Install and configure GitHub-integrated changelogs: ```bash # Install GitHub changelog plugin pnpm add -D @changesets/changelog-github # Update .changeset/config.json { "changelog": [ "@changesets/changelog-github", { "repo": "yourorg/yourrepo", "skipCI": false } ] } ``` **Result:** Changelogs include links to PRs and commits, plus contributor attribution. ## ⚠️ Troubleshooting Common Issues ### Issue: GitHub Actions Permission Error ``` Error: GitHub Actions is not permitted to create or approve pull requests ``` **Solutions:** 1. Go to Repository Settings → Actions → General 2. Set "Workflow permissions" to "Read and write permissions" 3. Check "Allow GitHub Actions to create and approve pull requests" 4. If grayed out, ask organization admin to enable at org level ### Issue: NPM Publish Fails with 403 ``` npm ERR! 403 Forbidden - PUT https://registry.npmjs.org/@yourorg%2fpackage ``` **Solutions:** 1. Verify `NPM_TOKEN` exists in GitHub Secrets 2. Check token has publish permissions: `npm token list` 3. Verify package.json `publishConfig.access` matches your intent 4. Ensure you're a member of the NPM organization 5. Check if package name is already taken ## 🚀 Production Best Practices ### Branch Protection Rules Configure in GitHub Settings → Branches → Add rule for `master` (or `main`): - **Require status checks to pass before merging** - **Require branches to be up to date before merging** - **Restrict pushes that create files in `.changeset/`** - **Require pull request reviews before merging** ### NPM Token Security Use granular access tokens with minimal permissions: ```bash # Create automation token (not classic token) # Go to npmjs.com → Access Tokens → Generate New Token # Select: Automation # Packages: Select only your packages # IP Allowlist: GitHub Actions IPs (optional) ``` ### Monorepo Performance Optimization ```json { "updateInternalDependencies": "patch", "ignore": ["@yourorg/dev-tools", "@yourorg/examples"], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true, "useCalculatedVersionForSnapshots": true } } ``` ### Semantic Versioning Guidelines **Patch (0.0.X):** Bug fixes, documentation updates, internal refactoring **Minor (0.X.0):** New features, non-breaking API additions **Major (X.0.0):** Breaking changes, API removals, major refactoring > **Golden Rule:** When in doubt, err on the side of a larger version bump. It's better to be conservative with breaking changes. --- --- Title: Farcaster Architecture --- # Farcaster Architecture >How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. >We link out to detailed project documentation where used.
![farcaster architecture overview](../../../../assets/images/docs/farcaster/farcasterArchitecture.png) --- --- Title: Farcaster Bots --- # Farcaster Bots >How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. >We link out to detailed project documentation where used. ![farcaster architecture overview](../../../../assets/images/docs/farcaster/farcasterArchitecture.png) --- --- Title: Farcaster Clients and other applications --- # Farcaster Clients and other applications >How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. >We link out to detailed project documentation where used. ![farcaster architecture overview](../../../../assets/images/docs/farcaster/farcasterArchitecture.png) --- --- Title: Frames V1: Astro Starlight Farcaster Frames --- # Frames V1: Astro Starlight Farcaster Frames >This guide assumes you know what [Farcaster Frames](/farcaster/frames/) are, but even if you don't you can follow the guide to implement one for authentication to your app. We will setup Astro Starlight to provide two links clickable in Farcaster Frames defined in Astro YAML Frontmatter or using our default frame for the whole webpage. This will not handle any post requests though you can also do that of course by adding a post_url and handling the incoming request making your Astro Frame we define in the YAML Frontmatter serve as the inital Frame that hands over to your Server. The Frame we will be creating looks like the one shown in the image below: ![Sample Image for the Astro Frame](../../../../assets/images/docs/farcaster/frames/astroFrameSample.png) >To learn debugging Frames and more check the [Quickstart Guide](/farcaster/frames/quickstart-farcasterframes) ## Setup Astro Starlight for Farcaster Frames We just need to adjust two Files for our Astro Starlight Webpage/Documentation to support Farcaster Frames. This method can also be extended to any custom html head or html metatag like OpenGraph or OpenFrames in Astro Starlight. Firstly we will create a config that contains the extended Frontmatter configuration so we don't need to write html or javascript over and over again for our Frames. Simply open/create your config file and paste the contents **src/content/config.ts** ```typescript export const collections = { docs: defineCollection({ schema: docsSchema({ extend: z.object({ // Add Farcaster Frames to the schema // Farcaster Frame tags will also be used for OpenFrames fcFrameImage: z.string().optional(), fcFrameAspectRatio: z.string().optional(), fcFrameInputText: z.string().optional(), fcFrameState: z.string().optional(), fcFramePostUrl: z.string().optional(), fcFrameButton1Text: z.string().optional(), fcFrameButton1Action: z.string().optional(), fcFrameButton1Target: z.string().optional(), fcFrameButton2Text: z.string().optional(), fcFrameButton2Action: z.string().optional(), fcFrameButton2Target: z.string().optional(), fcFrameButton3Text: z.string().optional(), fcFrameButton3Action: z.string().optional(), fcFrameButton3Target: z.string().optional(), fcFrameButton4Text: z.string().optional(), fcFrameButton4Action: z.string().optional(), fcFrameButton4Target: z.string().optional(), }), }), }), }; ``` Then create a custom Head component or edit yours to include the Farcaster Frames Tags that are adjusted based on the Frontmatter of our Document. If a value like Button3 is not set in our File, we will only return our default buttons. In the sample below we show two buttons. You could use this to have a link to page and promotion button next to it, our show link to blog post and link to youtube video from the same URL! Just create the file at the right path and copy the contents below. Adjust to your liking if needed. **src/components/Head.astro** ```astro --- --- {/** You can define what you need in your YAML Frontmatter of each page using the components fcFrameImage ... check /src/content/config.ts for details * Huge shoutout to the solution being proposed here on Github * https://github.com/withastro/starlight/discussions/1106#discussioncomment-8837253 * to generate yaml head frontmatter: https://starlight-head-generator.vercel.app/ * code: https://github.com/HiDeoo/starlight-head-generator */} { Astro.props.entry.data.fcFramePostUrl ? : {}} { Astro.props.entry.data.fcFrameState ? : {}} { Astro.props.entry.data.fcFrameButton3Text ? : {}} { Astro.props.entry.data.fcFrameButton3Action ? : {}} { Astro.props.entry.data.fcFrameButton3Target ? : {}} { Astro.props.entry.data.fcFrameButton4Text ? : {}} { Astro.props.entry.data.fcFrameButton4Action ? : {}} { Astro.props.entry.data.fcFrameButton4Target ? : {}} ``` ## Using the Frame in our Astro Website We will use a simple markdown page here, but this also works in .mdx and .astro pages as it just leverages the YAML Frontmatter! ```markdown --- title: "Quickstart Farcaster Frames 101" linkTitle: "Farcaster Frames 101" author: Samuel Huber ([@samuellhuber](https://farcaster.xyz/samuellhuber)) # Define Farcaster Frame displayed when sharing this page fcFrameImage: "https://dtech.vision/frame.png" fcFrameAspectRatio: "1:1" fcFrameButton1Text: Quickstart with Frames fcFrameButton1Action: link fcFrameButton1Target: https://dtech.vision/farcaster/frames/quickstart-farcasterframes --- # Ship Frames as fast as possible! ``` ## Final Remarks Make sure that your image is accessible and the paths are correct! Astro will make filenames be lower case so FramesQuickstart.md will be /framesquickstart in the link! Make sure you set that correctly in the frame. For Images I usually just use the public folder and access images there so public/frame.png will be at https://dtech.vision/frame.png if our webpage is deployed at https://dtech.vision If you want to learn more about Frames and build custom frames that handle Request and use a Frame Server instead of just linking out check [our quickstart with Frames guide](/farcaster/frames/quickstart-farcasterframes)! --- --- Title: Frames V1: Sign In to NextAuth from Farcaster Frames Button presses with Farcaster AuthKit --- # Frames V1: Sign In to NextAuth from Farcaster Frames Button presses with Farcaster AuthKit >This guide assumes you know what [Farcaster Frames](/farcaster/frames/) are, but even if you don't you can follow the guide to implement one for authentication to your app. There is an incredible [AuthKit](https://github.com/farcasterxyz/auth-monorepo) available that already did the work for our WebPage to setup NextAuth, Sign In With Farcaster and provides it as a sample. We'll use this sample to get up and running and add Sign In as well as user creation via Farcaster Frames using the signed message each Frame interaction gives us. To start simply run the following in your terminal which will set you up with a fresh example. ```bash git clone https://github.com/farcasterxyz/auth-monorepo.git && rm -rf auth-monorepo/.git && mv auth-monorepo/examples/with-next-auth authkit-nextauth && rm -rf auth-monorepo && cd authkit-nextauth && git init . && npm install && npm run dev ``` You can already see the sample webpage in your browser. Go sign in with Warpcast! ![](../../../../assets/images/docs/farcaster/frames/FarcasterAuthKit-NextAuthSample.gif) Make sure your .env is setup with the correct values for the following ``` # $ openssl rand -base64 32 NEXTAUTH_SECRET='760...' NEXTAUTH_URL='http://localhost:3000' NEYNAR_API_KEY='' ``` You can get a Neynar API Key at [the Neynar Webpage](https://neynar.com). The Next Auth parameters are documented [in the NextAuth docs](https://next-auth.js.org/configuration/options). You won't need a NEYNAR API Key if you use another API or Hub. For this tutorial we will use Neynar.
## Building the actual Frames Authentication What we want to build is a Frame that contains a link. When the user clicks that button and is linked to our webpage we want to know that the user is real and log them in. If we already have user data on them we can easily use that as well as we will know who is logging in. But how is it done? We are using the Farcaster Clients that will actually query our server for the link! That way we can show unique links to each user! ![](../../../../assets/images/docs/farcaster/frames/authLinksFromFrames.png) Well okay so why does it need to be unique and why is it secure? We need unique links because if the link is the same how can we know what user it is? We are not able to set cookies as the user will not visit the webpage before clicking the link to the webpage. We need to have the data in the link! We can use URL parameters for that! And security? well each Farcaster client has a Signer. That means they can sign cryptographically which demonstrates that the user is actually the user! Okay and that helps us? Yes because we can verify that signature and if it matches the user, we know it is them accessing the page and log them in. It is like sending a password reset link to a user that will log them in to reset the password. Just that we use the authenticity and security of Farcaster Signers and don't need to setup anything ourselves besides validation! How cool is that? We also know each user is unique since each has their own FID (userId). So how do we get it done? ### Add a Custom Credentials Provider In NextAuth to login users we need Authentication Providers and we will use [Credential Providers](https://next-auth.js.org/configuration/providers/credentials) we create on ourselves that only takes the Farcaster Signed Message we recieve from the Farcaster client when the user clicks the button in the Frame. ![](../../../../assets/images/docs/farcaster/frames/authLinksFromFramesDetailedValidation.png) To get it done we need to be able to verify that the signedMessage we get in the Magic Link is valid. That is a NextAuth Specific part and you can use the same logic we are building now for any authentication provider to login users from Farcaster Frames. In NextAuth we have our authentification specific code in `pages/api/auth/[...nextauth].ts` where all our configuration for authentification lives. We can see that our login via Farcaster in the Quickstart worked because there already is a CredentialsProvider called "Sign in with Farcaster". This is exactly what we did and what logged us in. Feel free to check the code as what we are building is really similar just that we will not verify SIWF messages, but the signedMessageBytes from a Farcaster Frame. ![](../../../../assets/images/docs/farcaster/frames/authLinksFromFramesExistingCredentialsProvider.png) Start by creating a new CredentialsProvider and give it the id `farcasterframe`. We will need this id to tell our authentication backend what provider we want to sign the user in with later. We will also give our Credentials Provider a name. ```typescript // sign in with Farcaster Frame // used with link like: `https://dtech.vision/?signedmessagebytes=${signedmessagebytes}` CredentialsProvider({ id: 'farcasterframe', name: 'Sign in with Farcaster Frame Message', }), ``` Now we have our CredentialsProvider setup, but it doesn't know what to login with so we need to tell it that we are passing a credential of type text that is called signedMessageBytes which we name it to match what Farcaster gives us. ```typescript // sign in with Farcaster Frame // used with link like: `https://dtech.vision/?signedmessagebytes=${signedmessagebytes}` CredentialsProvider({ id: 'farcasterframe', name: 'Sign in with Farcaster Frame Message', credentials: { signedmessagebytes: { label: "Farcaster Signed Message Bytes", type: "text" }, }, }), ``` We are able to pass the signedmessagebytes from the Farcaster Frame to our Backend which handles authentification now. But that is not enough. Simply passing the signed Message doesn't mean we verified it and doesn't log the user in. So we need to tell our Backend how to do that. We can override the `authorize` method of our CredentialsProvider and implement our custom authentification logic there. >**CAREFUL** You are now handling Authentication. If you screw up you will have unauthorized access, attack vectors or users not being able to log in. > Check twice and talk to others to check that what you are doing really is secure and makes sense. Test the intended flow and edge cases! To validate the signedMessage we need validate the Frame Action against a Farcaster Hub. You could use any Hub out there to do it or you can use an API Provider like Neynar that does it for you so you don't need to run Hubs yourself. What is going to happen? We will send the signedmessagebytes out and get a validation response that tells us if it is valid and what the signed data is. The signed data format can be viewed in the [Farcaster Specification](https://docs.farcaster.xyz/reference/frames/spec#frame-signature-packet) and contains important information like the userId (FID) of the interacting user as well as a timestamp of when the message was signed (meaning when the interaction happened). When looking at the specification of the Frame Signature Packet you will see that the untrustedData is the data format contained in the signed message. Nice bro, but how to do auth? Simply use the Neynar API, verify the signature and return a user object if successful. Code? Here is our authorize function that we override and takes in the credentials we defined above. To install the [Neynar SDK](https://github.com/neynarxyz/nodejs-sdk/) we run ```npm install @neynar/nodejs-sdk``` and add the `. Now we are ready to send the signedMessageBytes off and get the result back. ```typescript // sign in with Farcaster Frame // used with link like: `https://dtech.vision/?signedmessagebytes=${signedmessagebytes}` CredentialsProvider({ id: 'farcasterframe', name: 'Sign in with Farcaster Frame Message', credentials: { signedmessagebytes: { label: "Farcaster Signed Message Bytes", type: "text" }, }, authorize: async (credentials) => { try{ const neynar = new NeynarAPIClient(process.env.NEYNAR_API_KEY!!); const response = await neynar.validateFrameAction( credentials?.signedmessagebytes as string, { castReactionContext: false, followContext: false, signerContext: false } ); // shorten response types from Neynar (https://docs.neynar.com/reference/validate-frame) const action = response.action; const interactor = action?.interactor; return { id: interactor?.fid.toString(), name: interactor?.username, image: interactor?.pfp_url, }; } catch (error) { console.error(`Error in authorize farcasterframe: ${error}`); return null; } }, }), ``` What we do here is return the same information that the Sign in with Farcaster CredentialsProvider does to stay compliant with the sample. You may and likely want to adjust this to your customer user type. It makes sense to use Farcaster ID's as a unique mapping from FID < > User in your application. Do not use the username (fname e.g. samuellhuber) as a user can change that but will never change their FID. Also be reminded that with the above setup one could replay the signedMessage and be signed in. We will [discuss security in a dedicated section](#security-and-other-considerations-for-authentication). Now that we have a method to log users in we need to generate the authentication links, add them to our Frames and then handle the login in the Frontend. ### Add a Frames Endpoint to generate Authentication Links What is a Frames Endpoint? Simply anything that handles a POST request and returns something according to the Frames Specification. In our case we want to recieve Frame Action with it's signedMessageBytes and return a redirect link. >**Important** Make sure to return 302! Do not use .redirect() in NextJS as that will override the Status Code to 307 or 308 but we need 302! So what we will do is create a POST request handler and return our redirect link. The scaffolding of that we will create under our auth API directory and call the route createAuthLink. pages/api/auth/createAuthLink.ts ```typescript export default async function handler ( req: NextApiRequest, res: NextApiResponse ) { if(req.method !== "POST") { res.status(405).json({ error: "Method not allowed" }); } try { // handle post } catch (error) { // handle error } } ``` What we need to be mindful of here is that our Frame will expect a response in any case! Meaning if our computation throws an error we absolutely need to make sure we still return a redirect! Else the user will not get a link and think our Frame is broken. I'd much rather have the user login on the webpage manually then not have them at the webpage at all. Best of course is sign them in which reduces friction. Okay so what do we do? You already know! We'll use the FrameAction and create a link that has the signedMessageBytes as URL Parameters. Why that? Doesn't that mean they are public? Yes. Also following Kerkhoffs Principle any security system should be secure if the mechanism is public. No issue there, we will add defence against replay attacks reusing the signedMessageBytes from the link in [the security section](#security-and-other-considerations-for-authentication). the logic to handle the post will extract the signedMessage byte and any user input as well. Input is useful if you frame has input enabling you to add context to each action. You could also have the different buttons mean different pages on the webpage like Settings, Homescreen or a Profile. That custom return could also be handled here by running a case distinction (if statements) over the button index used. For details check the [Quickstart with Farcaster Frames Tutorial](/farcaster/frames/quickstart-farcasterframes/). Let's handle the error case first. In this case we'll just link to our webpage (Home). Guaranteeing that the user will always land on our page! ```typescript try { // handle post } catch (error) { console.error(`Error in createAuthLink: ${error}`) res.writeHead(302, { Location: `${process.env.NEXTAUTH_URL}/` }); res.end(); } ``` Notice the explicit use of `writeHead(302, ...)` to guarantee the status code of our response is a 302 redirect! We are using the NEXTAUTH_URL as a Location since we will need that set in our environment variables anyway to make NEXTAUTH work properly! Lets return a URL that contains our signedmessagebytes and potential user input as link to the user! For that we will simply take what is sent and not do verification. Why? Because verification happens at the auth level anyway. No need to redo, we can't persist that login here anyway since it's a simple API request and we can't set session cookies in the user's browser. ```typescript if(req.method !== "POST") { res.status(405).json({ error: "Method not allowed" }); } try { const frameRequest = req.body as FrameRequest; const input = frameRequest.untrustedData.inputText; const signedmessagebytes = frameRequest.trustedData.messageBytes; res.writeHead(302, { Location: `${process.env.NEXTAUTH_URL}/?input=${input}&signedmessagebytes=${signedmessagebytes}` }); res.end(); } catch (error) { console.error(`Error in createAuthLink: ${error}`) res.writeHead(302, { Location: `${process.env.NEXTAUTH_URL}/` }); res.end(); } ``` Our handler now return a 302 redirect in all cases and has input and signedmessagebytes in the successful case, while logging errors and just redirecting to the mainpage otherwise. If you would love the types check the [types in Coinbase OnchainKit](https://github.com/coinbase/onchainkit/blob/main/src/frame/types.ts). So now we are able to return a URL to our user! We just need to build a frame that calls our endpoint and handle the URL parameters for signin on the application side. ### Build our Frame to Link to the Webpage with SignIn The Frame building is easy. We only need one button that pings our server so we can return the link and the user gets prompted to go there. Read and work through the [Quickstart with Farcaster Frames Tutorial](/farcaster/frames/quickstart-farcasterframes/) if you want to learn how to build more complex Frames and learn how Farcaster Frames work in general. In our `pages/index.tsx` we are returning some HTML via the JSX and can add our Frame there, remember Frames are just HTML Metadata. and what we want is the Farcaster Client to send a POST request to our Server so we respond with a 302 redirect and location to go to. This can be done by making a post_redirect action button. So we set `fc:frame` as metadata because that says we build a frame. Then we also set an image and a text input with placeholder `channelid` because we want to get user input too. Then we build our button. post_url will be the URL we set so ```${process.env.NEXTAUTH_URL}/api/auth/createAuthLink``` which I stored in a redirectUrl variable. After specifying that we set the button text/label (content) and tell the button what it is via the action tag, namely a 'post_redirect'. pages/index.tsx ``` export default function Home() { return ( <> Farcaster AuthKit + NextAuth Demo
); } ``` For the image I used a jpg that I called frame.jpg and placed in the `public/` folder. You can use any image that is 1.91:1 aspect ratio or 1:1 aspect ratio as that is what will be rendered and if you do 1:1 set the aspect ratio parameter. ``` ``` So our index.tsx now defines the Frame, some content we show which we can leave as is to our sample rendering the top right profile component once logged in and some text. ![](../../../../assets/images/docs/farcaster/frames/authLinksFromFramesIndexTSXFrame.png) ### Handle Login on our Webpage Now we created a Frame that we can use to get the login link with we need to handle the login on the webpage as well. How do we do? We check for the URL parameters and then just try to log the user in. To handle the signIn luckily we can use the provided React Hooks to SignIn. If we just hijack the Profile component we can do it all there. To check existance of any url parameter (input and signedmessagebytes) we can use the included Router. ```typescript const router = useRouter(); useEffect(() => { // if one uses a one click login link via farcaster frame -> sign in if (router.query.signedmessagebytes) { console.log('signedmessagebytes found!'); } }, [router]); ``` Why is there that weird `[router]` after our function? ask react devs. Thank you. From the types it is the dependency list meaning that if anything in that list changes the effect will be executed. Since we check for existence of our signedmessagebytes query parameter that we set in the createAuthLink API route we use to get it to the user, we know it's present if the user wants to login. It could also be present if someone replays the login, but we'll discuss security [here](#security-and-other-considerations-for-authentication). For now let's actually use the sign in hook to sign a user in. ```typescript const router = useRouter(); useEffect(() => { // if one uses a one click login link via farcaster frame -> sign in if (router.query.signedmessagebytes) { signIn("farcasterframe", { signedmessagebytes, redirect: false }); } }, [router, signIn]); ``` What we are now doing is using the provided signIn Hook to call our auth backend and sign a user in using our custom CredentialsProvider. This is why we set the id to *farcasterframe*. Because now we can tell it to specifically use the one we want. We are passing the signedmessagebytes and like we programmed before the user is logged in if the signed data is valid. Why redirect false? Well if we redirect the default is to redirect to the same page and then the query parameters (url/?signedmessagebytes=...) still exist so we are looping. We can also handle that by creating custom redirect links or removing the url parameters after use. Let's reset the url parameters once we used them and also add some error handling. ```typescript const router = useRouter(); useEffect(() => { // if one uses a one click login link via farcaster frame -> sign in if (router.query.signedmessagebytes) { const signedmessagebytes = router.query.signedmessagebytes as string; if (signedmessagebytes) { try { // redirect is set to false to leave us on current page vs being looped around signIn("farcasterframe", { signedmessagebytes, redirect: false }); router.replace(router.pathname, undefined, { shallow: true }); } catch (error) { console.error("Failed to sign in with Farcaster Frame"); } } } }, [router.query.signedmessagebytes, session, router]); ``` Let's also handle our input and make sure that there is an input variable able to be used by our frontend that we set if it's available. Note that this [input parameter may not be 100% authentic](#security-and-other-considerations-for-authentication). ```typescript const router = useRouter(); const [input, setInput] = useState(""); useEffect(() => { // if one uses a one click login link via farcaster frame -> sign in if (router.query.signedmessagebytes) { const signedmessagebytes = router.query.signedmessagebytes as string; if (router.query.input) setInput(router.query.input as string); if (signedmessagebytes) { try { // redirect is set to false to leave us on current page vs being looped around signIn("farcasterframe", { signedmessagebytes, redirect: false }); router.replace(router.pathname, undefined, { shallow: true }); } catch (error) { console.error("Failed to sign in with Farcaster Frame"); } } } }, [router.query.signedmessagebytes, signIn, session, router]); ``` Now you have signed in a user from a Farcaster frame. You can try it by pinging your example with a local debugger or the warpcast tool. For more on Frames [check the quickstart](/farcaster/frames/quickstart-farcasterframes/). ### Security and other considerations for Authentication With what we have so far on our authorize() function for the CredentialsProvider we do not check when the signature was created so anyone that at somepoint managed to get a signature for a user will be logged in. We don't want that so we can check the timestamp present in the signed data and compare it to current time. Checking timestamps and making sure we only allow a certain time to be passed reduced the attack surface for replay attacks. To make sure that we use the correct time (since Farcaster Time is Farcaster Epoch not Unix Timestamp), we will create a helper file and use functions from there. ```typescript export const FARCASTER_EPOCH = 1609459200000; // January 1, 2021 UTC /** * Get the current Farcaster time. * @returns seconds since the Farcaster Epoch */ export const getFarcasterTime = () => { return toFarcasterTime(Date.now()); }; /** * Converts from a Unix to Farcaster timestamp. * @param time unix milliseconds * @returns seconds since the Farcaster Epoch */ export const toFarcasterTime = (time: number) => { if (time < FARCASTER_EPOCH) { throw Error(("bad_request.invalid_param: time must be after Farcaster epoch (01/01/2022)")); } const secondsSinceEpoch = Math.round((time - FARCASTER_EPOCH) / 1000); if (secondsSinceEpoch > 2 ** 32 - 1) { throw new Error("bad_request.invalid_param: time too far in future"); } return secondsSinceEpoch; }; /** * Converts from a Farcaster to Unix timestamp. * @param time seconds since the Farcaster Epoch * @returns unix milliseconds */ export const fromFarcasterTime = (time: number) => { return time * 1000 + FARCASTER_EPOCH; }; ``` These helpers allow us to convert the Frame Message timestamp to Unix time and check if the signature is expired. ```typescript // ... // shorten response types from Neynar (https://docs.neynar.com/reference/validate-frame) const action = response.action; const interactor = action?.interactor; const actionTimestamp = fromFarcasterTime(message.data.timestamp); // Farcaster timestamp is seconds since Farcaster epoch, while Date.now is milliseconds since Unix epoch // Farcaster Epoch is Jan 1, 2021 00:00:00 UTC (unix timestamp: 1609455600) -> https://docs.farcaster.xyz/learn/what-is-farcaster/messages#timestamps if ((actionTimestamp + (1000 * 60 * 5)) < Date.now().valueOf()) { // don't allow messages older than 5 minutes, .valueOf() for UTC return null; } // ... ``` The Input variable in the frame example is used from untrusted data and from URL Parameters as is. One would need to verify the signature again and extract input from it to guarantee the input is actually the input the user signed. After verification you could use the return from verification to use this input variable. This is omitted for the sake of simplicity of the concept, but might be critical in a production system. --- --- Title: How do Frames work? --- # How do Frames work? >We assume you already know what Frames are, [else learn what Frames are here](/farcaster/frames/). >Additionally it's assumed you know that HTML is the Web programming language, though no programming knowledge is assumed. ## Frame User Perspective Look at the user all the way on the right, they only see a Frame in the App, interact and then after a while see a new Frame or Error. While in the background the App (e.g. Farcaster Client) that renders the Frame roundtrips with the Frame Server defined by the Developer to playout the interaction to display everything properly for the User. ![Frames from a User perspective visualized](../../../../assets/images/docs/farcaster/frames/frameInteractionsVisualized.png) These roundtrips are why Frames may appear high latency, low frame rate. This is because the developer doesn't return the next Frame fast enough from their server and the internet connection may not be optimal. Additionally the logic the Frame Server runs to return the next Frame or Error is allowed up to 5 seconds. So from the user interaction until the response is there 5 seconds are allowed, which leaves the dev around 4.5 seconds of compute (0.5s being the roundtrip). ## How do Frames work? - programming They are literally OpenGraph tags. What? They are simple text fields you can insert into your webpage (or server response). That text follows the Farcaster Frames specification so anyone knows how to interpret it. With your text description that is being read, one can render the Frame and allow the user to interact with it. Basically you write a little description and that is all. If we just wanted to have a Frame that renders an image in square aspect ratio the following three lines are just that! ```html ``` Well okay so you want a button so anyone knows to press it to get to your webpage? Sure that means we add a button description that links to our webpage! Notice "link" and the button text in the following frame code that is just an extension of the above with a button! ```html ``` If we let a webserver serve just the above HTML, any Farcaster client will render it as a Frame pointing to this webpage with our custom image + button!(notice how I added the html and head tags on the second sample to make it copyable?) Now since for complexer logic we don't want to just write HTML or return HTML and luckily there are [many Frameworks](/farcaster/frames/whichframesframework/) available. Though never forget! You don't need to use Frameworks, just return HTML! And if you want to return specific messages to the user after they click a button in your frame simple return content-type application/json with error code 4XX (400 something) while the json is the following ```json { message: "" } ``` then each client will show the user the message (see above: ``````). Want to develop your first Frame? Learn with our [Quickstart Farcaster Frames](/farcaster/frames/quickstart-farcasterframes/) --- --- Title: Frames V1: How to do a custom starting Image per User in our Frame? --- # Frames V1: How to do a custom starting Image per User in our Frame? > We assume you already know what Frames are and how to develop them, [else learn what Frames are here](/farcaster/frames/). # How to do a custom starting Image per User when sharing our Frame? What we want to achieve is that when sharing it loads a new frame and the background image with the pfp and number from the api. Where we can use any data to customize the start frame. You do not need to use an API but likely you want to show some statistics that are customized to the user, so we'll do that. We will go through how to customise the frame so when people share it with a share button it creates a customized image for that user and shares that to their cast (post). You can think of this as 'share frame' button with custom frame input. Additionally we will walk you through how to post that image you get as new frame upon recasting it. ## Warpcast Cast Intents to let users share your Frame in the feed To get a user to share a post we need to let the Farcaster client know. This can be done with deep links. Warpcast calls these Intents and they are documented [here](https://docs.farcaster.xyz/reference/warpcast/cast-composer-intents). We will use these Cast Intents to create a link that will share our Frame. Let's say our frame link is `https://dtech.vision/` and the text is "Hey @samuellhuber look!", then we can create a share link for our Frame with the predefined text to be shared as Cast Intent: >Note: Make sure you URL encode the text. This means a ' ' becomes %20 and so on. ``` https://farcaster.xyz/~/compose?text=Hey%20@samuellhuber%20look!&embeds[]=https://dtech.vision/ ``` Want to try if this works? click [here](https://farcaster.xyz/~/compose?text=Hey%20@samuellhuber%20look!&embeds[]=https://dtech.vision/) and it should look like the below screenshot. ![Warpcast Cast Intent Preview](../../../../assets/images/docs/farcaster/frames/castIntentComposerScreenshot.png) ## How to build the 'share frame' button with custom frame input to get a per user customized frame image shared Now to build the share button we can add a button to our Frame that will have **action type "link"** and contain the Url we created as [Cast Intent](#warpcast-cast-intents-to-let-users-share-your-frame-in-the-feed). As Frames are pure HTML Meta Tags, we can achieve it by providing these. Here is the above share button implemented as HTML metatag in a Farcaster Frame. If you are using a [Frames Framework](/farcaster/frames/whichframesframework/) will need to recreate it in the Framework (Button of type Link with content of your Cast Intent URL). ```html ``` Now one thing to remember though is that the link we just used will share our Frame, but it is not customized! If a user shares this frame link we will render the default start screen. To be able to customize the starting frame image, we want to pass additional info to your Frame server! We can do this by adding url parameters. We will add an fid parameter, that clarifies which FID is the one sharing. Let's use 1 then our new link is ``` https://farcaster.xyz/~/compose?text=Hey%20@samuellhuber%20look!&embeds[]=https://dtech.vision/?fid=1 ``` Notice the **?fid=1** at the end of the link? this is our url parameter that the server can read and use to not return the default image, but return a custom image! On your server where you build the share button link, you can customize that parameter, it doesn't need to be an FID. Just make sure when building the return frame metadata (aka returning the frame response) that you include the parameter and fill it. ## How to share the custom frame image on the server side when building the frame Now that we have the parameter in the URL, we can use it to customize the frame image. We can use the parameter to get the user's profile picture and number from an API and use that to create a custom image. The general idea is when handling the request, read the url parameter and return the customized frame image in a frame. In Pseudo code this is what we're going to do ``` getFrameImage(fid): returns profilePicture of that fid starrtFrame(image): returns a frame with that custom image // handle the request sent to your Frame Server to get the initial frame get handler (): fid = request.url_params['fid'] image = getFrameImage(fid) return startFrame(image) ``` ### Typescript example code of a custom frame image Now that you know how to achieve our custom frame image on the server side let's build a real world sample First we will build the image generation function based on the FID, we'll use dummy images here ```typescript function getFrameImage(fid: number): string { return `https://picsum.photos/id/${fid}/200/300` } ``` now we can call this and get our custom image link back. If you want to get actual farcaster profile pictures, you can look up the user data on [Farcaster Hubs](/farcaster/hubs/howdofarcasterhubswork/) or use another API. Now that we know how to get the image link we need to build our Frame Metadata. We'll return pure HTML here, but you are free to use any [Frames Framework](/farcaster/frames/whichframesframework/). ```typescript // returns the custom html for our Frame. If you use a framework, this works similarly in your Framework. We use html to be framework agnonstic here function startFrame(image: string) { return ` // Our Frame Button follows here // End of our Frame Button ` } ``` As you can see the code uses the image string to create our custom Frame. **Notice** what changed from our Share Button frame we built [earlier](#how-to-build-the-share-frame-button-with-custom-frame-input-to-get-a-per-user-customized-frame-image-shared) is the Button is now of type post so it sends a Frame Message to the server which allows us to return the new Frame Image and share button! In case you want to learn how to build frames, please follow the [quickstart frames development guide here](/farcaster/frames/quickstart-farcasterframes/). Additionally the image is also different! It is now customized based on our Frame image! If you combine this within your Frame and follow the Pseudocode you have now learned how to build viral frame loops with sharing leading to more sharing based on custom frame images per user! To handle the request we will now put it all together and provide a sample implemented in NextJS ### Custom Image on the shared Frame Code Sample in NextJS The sample - handles the initial GET request potentially with custom frame image based on the user sharing (FID url parameter) - uses a function to get the custom image - when pressing the button on the frame shows the user his image and then lets them share it ![Sample Viral Share Frame](../../../../assets/images/docs/farcaster/frames/customizedStarterFrame.gif) to run the sample change the url to your url and just run it inside any nextjs app or create a new one. ```typescript const url = 'http://localhost:3001/api/test'; interface FrameRequest { untrustedData: { fid: number; url: string; messageHash: string; timestamp: number; network: number; buttonIndex: number; inputText?: string; castId: { fid: number; hash: string; }; }; trustedData: { messageBytes: string; }; } function getFrameImage(fid: number): string { return `https://picsum.photos/id/${fid}/200/300`; } function startFrame(image: string) { return ` ` } // function that builds the frame the user sees when clicking to recieve their custom one function shareFrame(image: string, fid: number): string { return ` `; } export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === 'GET') { /** ** this is the start frame **/ try { // Get the FID from query parameters const { fid } = req.query; let numericFid = parseInt(fid as string); if (isNaN(numericFid)) { numericFid = 0; } // Generate custom image based on FID const customImage = getFrameImage(numericFid); // Generate and return the custom Frame const frameHtml = startFrame(customImage); res.setHeader('Content-Type', 'text/html'); res.status(200).send(frameHtml); } catch (error) { console.error('Error processing frame:', error); res.status(400).json({ error: 'Bad Request' }); } } else if (req.method === 'POST') { /** ** this is the share frame **/ try { // depending on your security assumptions you may wnat to know it's really the user interacting with the frame // in this case you should validate the frameMessage and use data only after verification const { untrustedData } = req.body as FrameRequest; const fid = untrustedData.fid; const customImage = getFrameImage(fid); const frameHtml = shareFrame(customImage, fid); res.setHeader('Content-Type', 'text/html'); res.status(200).send(frameHtml); } catch (error) { console.error('Error processing POST frame:', error); res.status(400).json({ error: 'Bad Request' }); } } else { res.setHeader('Allow', ['GET', 'POST']); res.status(405).end(`Method ${req.method} Not Allowed`); } } ``` --- --- Title: Frames V1: How to turn a Cast Action into a Frame --- # Frames V1: How to turn a Cast Action into a Frame >This guide assumes you know what [Farcaster Frames](/farcaster/frames/) are and that you have a [Cast Action](https://frog.fm/concepts/cast-actions) ready. # How to turn a Cast Action into a Frame? If you already have a cast action you can add an image and button to call the same backend function (logic) that you already have for the cast action. The way this Frame will work is similar to the Cast Action, though the user does now press a button in the Frame and not a button on the context menu of a post (cast). ![Cast Action User and Server Flow visualized](../../../../assets/images/docs/farcaster/frames/castActionFlow.png) ## How to easily turn a preexisting cast action into a frame The code of your cast action will recieve a Frame Message, do something and return a message (or a Frame already). Let's turn that into a "pure" Frame. The technical difference being that you need to serve a starting screen. The cast action does not need a starting screen as it gets invoked by POST request only. The starting screen in a Frame the Frame Metadata you respond to a GET request with. It holds your starting Frame (initial Frame) definition. Your code will look similar to this for the route that handles the cast action. ``` | |__ GET request handler -> returns frame Metadata | |__ POST request handler -> returns message, error or Frame ``` What we need is for another GET endpoint that will serve as Frame entrypoint. Let's say the action will be served for both GET and POST request on the `/action` route and we are now adding a `/frame` route to handle the entry point. We will tell the Frame to just pass control to the action! Though for our action there are two cases 1) THe Cast Action already returns a Frame 2) The Cast Action does not yet return a Frame We will first handle the case that it already returns a Frame and then handle the case where it does not. You will see they are similar. ### How to turn a Cast Action that returns a Frame into a pure Frame Let's add our entry point Frame. As your code will be different here's Pseudo Code of what you want to achieve with the Frame parts being spelled out explicitly. Though you can choose Framework, or handle these yourself. The logic doesn't change, just your implementation may. #### GET handler entrypoint for the Frame Do what ever you want in the function to handle the frame request .. validate the data, or something else. Then we return a frame that sends to the `/action` endpoint as we know that already returns a Frame to the user. We just pass control and have built an entrypoint Frame. The image to show the user we assume is at `https://dtech.vision/frame.png` ```typescript function GET (request) { // do what ever you want // return the Frame Metadata with a button to invoke what was the cast action. // you could use a framework or whatever. The gist is you have a handler and return a frame sending to the /action as that already returns a frame return ( // This is Button 1 which will redirect to the frame of the cast action // set post target to the /action endpoint! ) } ``` ### How to turn a Cast Action that returns a message into a standalone Frame Just as in the case where we already return a Frame, we will add a GET handler that will serve as our entrypoint. As your code will be different here's Pseudo Code of what you want to achieve with the Frame parts being spelled out explicitly. Though you can choose Framework, or handle these yourself. The logic doesn't change, just your implementation may. Though in addition the a GET Handler entrypoint since our action does not return a Frame already, we will reuse the action code and build a `/actionframe` route that responds to POST messages just like our `/action` endpoint but returns a Frame instead of a message / error. The GET Handler is the same as [seen here previously](#get-handler-entrypoint-for-the-frame), just change the target route to `/actionframe` instead of `/action` as we will return the frame from `/actionframe` What is missing is the POST handler #### Turn the Cast Action Message into a Frame Let's take the function that handles our Cast Action for us. The POST handler. Here we will use the exact same logic we already have! we just return a Frame! Note that **the message of the cast action** can be either displayed by the image, or in the button text (we'll put `` as place holder in the sample). It all depends on what you want the Frame to show or display. How? Let's check in the pseudocode below. We assume that `https://dtech.vision/frame.png` is the image the frame should show and that the user should be redirected to `https://dtech.vision` on return of the cast action. Change this to what you see fit for your program. ```typescript function POST (request) { // the exact same logic from your cast action. you may even call it :D // the return is different. We don't return a message here. // We return a frame. You may do this within your framework or use the HTML directly return ( // This is Button 1 which will redirect to https://dtech.vision set as "link" is used as action ) } ``` ### How to turn the Cast Action into a Frame that returns a message and not another Frame Now there is one bonus case that we will include here. The Frame itself could also return a message. It does not need to return another Frame. How? Well the Frame can error. So we can use the error channel to send messages to the user. If we use the same logic and thinking as [when turning the cast action with message return into a frame](#how-to-turn-a-cast-action-that-returns-a-message-into-a-standalone-frame) and just let it return a message the code will look similar to the following: ```typescript function POST (request) { // the exact same logic from your cast action. you may even call it :D // the return is different. We return a 400 error and put the message there return ( status: 400 header: content-type: application/json { message: "Invalid email" } ) } ``` ## How to add a Frame as entry point to multiple cast actions? Making the Frame the initial step The flow to build a Frame from a Cast Action is the same as [before](#how-to-easily-turn-a-preexisting-cast-action-into-a-frame). Therefore we only need to add a new initial step Frame that allows the user to choose what they want to do. Say that we have "hire dTech" on `/hire` and "quickstart frames" on `/quickstart-farcasterframes` on our Frame Server as routes. (btw these do exist [hire us for Farcaster work](https://dtech.vision) and our [Quickstart Frames Tutorial](/farcaster/frames/quickstart-farcasterframes/)). Now our Frame needs the Metadata to do that. I will assume that our Image is present at `dtech.vision/initialFrameImage.png` which may be different in your setup. Now creating two buttons with these routes and adding the image will give us a simple HTML code with Frame Metatags (why Metatags? [learn how Frames work](/farcaster/frames/howdofarcasterframeswork)). The following HTML deployed will give you the initial step Frame you are looking for that then routes to your other Frames. You can also remove buttons or add buttons. Though you can't add more then 4 Buttons in one Frame. Notice how **post** is used as **action** this tells the Farcaster client to send a POST request to that frame endpoint, which effectively calls the frame for us. Passing control from the initial step frame to that Frame. ```html ``` If you don't want to write this HTML each time, you can [pick a Frames Framework or build your own abstraction](/farcaster/frames/whichframesframework). --- --- Title: Start Here Farcaster Frames --- # Start Here Farcaster Frames > How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. > We link out to detailed project documentation where used. ## What are Frames? Farcaster Frames brought custom interactive experiences to Web3 social where the full power of wallets, transactions and public information laying on blockchains can be used to the full imagination of dreamers, developers, artists and the anyone with internet access. With Frames being as simple as one to four buttons, an optional input field and an image with each client rendering the layout as they see fit you can focus on the functionality to bring all you want to do in a simple format that can link out to webpages, do any kind of API call based on the logic of a button press and optional text input and sends back an new user interface to continue. The layout with just buttons, an input field and showing a static image or GIF that is returrned based on the current context without the ability for video and sound means we are looking at a muted Gameboy or Nintendo DS style interface embedded into the social feeds of all of Farcaster. Think everyone sharing the Gameboy game they play with all of their followers and online community every time they play and giving them the opportunity to also join in and play right away! Now imagine what you can do with it or how you can enable people to use your awesome tooling to enable even cooler experiences! ## Where to Frames fit inside the Farcaster Architecture? [How to Frames work? learn more by clicking here](/farcaster/frames/howdofarcasterframeswork). ![farcaster architecture overview](../../../../assets/images/docs/farcaster/farcasterArchitecture.png) For more on the Farcaster Architecture check [the Architecture docs](/farcaster/architecture/) ## How to build Farcaster Frames? [Quickstart building Frames](/farcaster/frames/quickstart-farcasterframes/) is the best zero to hero guide on Farcaster Frames. It takes you from no idea about Frames to having your first frame in a mere couple of minutes. After your initial frame you are building a calculator getting familar with showing one frame after the other and connecting them with custom logic ran in the backend. --- --- Title: Frames V1: Quickstart Farcaster Frames 101 --- # Frames V1: Quickstart Farcaster Frames 101 # Ship Frames as fast as possible! >For simple taste of Samuel we will use Coinbase Onchainkit through out this sample. Though [many other Frame Development Kits exist and can be found here](/farcaster/awesomeopensource#frames) ![full calculator Farcaster Frame showcase](../../../../assets/images/docs/farcaster/frames/quickstartFullCalculator.png) ## Quickstart your Setup Getting setup is as simple as running the command below. Simply copy & paste into your terminal. ```bash git clone https://github.com/Zizzamia/a-frame-in-100-lines.git shippingFrame && cd shippingFrame && npm install ``` We are cloning the starter/example for onchainkit and getting the dependencies installed so we're good to go. ## Look at your first frame The example already ships a frame! You made it! Thats your first frame. by running ```npm run dev``` in your terminal you now serve that frame, though we don't have a way to look at it. Yes you could host this anywhere as it's NextJS but we want to see it locally to debug right? ![npm run dev sample](../../../../assets/images/docs/farcaster/frames/quickstartInitialNpmRunDev.png) ## Setup your debugger For debuggers once you have deployed to production or an online dev instance there is the [Warpcast inspector](https://farcaster.xyz/~/developers/frames) which you should look at if your Frame works since its the largest Farcaster client so most of your users will use that! Locally [frog.fm](https://frog.fm) has a nice debugger and [frames.js](https://framesjs.org) also has one. We'll use Frames.js though either one works because we simply paste our frame Url in there and it simply uses that. Therefore we could also build Frames with other tools and aren't limited to NextJS like [Expo React Native API Routes](https://github.com/dtechvision/expocaster/) or others find an overview of [Frame Samples + Dev Kits here](/farcaster/awesomeopensource#frames) if interested. To spinup your debugger with frames.js run the following command in the terminal like we did the [Quickstart Setup](#quickstart-your-setup) ```bash npx frames ``` You are now running the FramesJS debugger. In case that doesn't work for you check the [framesjs debugging guide](https://framesjs.org/guides/debugger). There in the browser we'll be able to see what our frame looks like so again make sure in the frames folder we setup in [Quickstart Setup](#quickstart-your-setup) run `npm run dev` or make sure it is still running. Now use that url (likely `http://localhost:3000 if still running`) and paste it into the debugger. Meaning in your browser go you `http://localhost:3010/` and enter the URL of the Quickstart Frame we put up. You should now see your Frame. What we will be building today will be a calculator! Have a look below! ![full frames debuger showcase](../../../../assets/images/docs/farcaster/frames/quickstartFullDebuggerSample.gif) ## Shipping custom Frames Now that we have [our setup ready](#quickstart-your-setup) and a working [debugger](#setup-your-debugger) we can ship a custom frame. In your frames working directory (`shippingFrame` if you didn't change it) open `app/page.tsx`. You will be greeted by something similar to the below app/page.tsx ```typescript const frameMetadata = getFrameMetadata({ ... }); ``` Where the getFrameMetadata is simply a helper function that returns the html metadata tags needed for Farcaster clients to render your Frame. Have a look at [the architecture of Frames if you want to learn the specifics.](/farcaster/frames/) Though all you need to know is that here is where we define our initial Frame. The first Frame the user will see when we send him our URL! Remove all buttons and for now just make it one button that links to our webpage https://dtech.vision so we strip everything in the getFrameMetadata and just leave the image in there and one button ```typescript const frameMetadata = getFrameMetadata({ buttons: [ { label: 'Story time!', }, ], image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, }); ``` Now when we check our Frame in the debugger it will do nothing. So we need to add a `link` action that tells the Client that our button should redirect! Let's do it: ```typescript buttons: [ { label: 'Story time!', action: `link`, }, ], ``` though that doesn't work since link requires us to give it a target >hint: mint action is the same, the others default to the postUrl if no target is specified, but we'll get to that let's add a target so the clients know which link they should have the user open when pressing our button ```typescript const frameMetadata = getFrameMetadata({ buttons: [ { label: 'Story time!', action: `link`, target: 'https://dtech.vision', }, ], image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, }); ``` Now when we test it in the debugger we will see our link and it will open our webpage! ![link out to external link in Farcaster Frame showcase](../../../../assets/images/docs/farcaster/frames/quickstartLinkDemo.png) We could deploy this now and just ship a beautiful link preview as farcaster frame. If thats all you want to do check the [launch your frame](#deploy-the-frame-to-dev-or-production) section now. ## Lets actually work with buttons and user input The fun really begins when we start working with the buttons! When a user clicks a frame that has a regular button a postRequest is sent to the Server. Which for us simply means we get data from the client we can handle and work with! This is also how we return a new updated Frame aka have our Frame Application have multiple steps / multiple Screens! Now you can start to understand how you can build complex applications and user interfaces all in frames! If four buttons is all you need you're golden! if you need more you can use 3 and have the fourth one be a menu button ;) to cycle buttons Though let's get started building a simple calculator! We will get user input and have the user tell us if it should be added up or substracted. To get there we will adjust the getFrameMetadata again! You'll see this becomes a pattern. This is also why I personally like the onchainkit way of building frames as I can just use the getFrameMetadata or getFrameHTMLResponse everywhere! It's a Typescript function so as long as I have that it works (ps: also works in [Expo if you're building native apps](https://github.com/dtechvision/expocaster/)) Okay so what exactly do we want now? - input - add button - subtract button so lets do the thing we already know and add to buttons! Since we want to send the data to our server again (recieve the Post Request) we can leave the button's empty besides their label! ```typescript const frameMetadata = getFrameMetadata({ buttons: [ { label: '+', }, { label: '-', }, ], image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, }); ``` Nice so we can now press + or - in our little calculator. But we can't input what to add or substract so let's add that. We just adjust the Frame with input by adding an input: ```typescript const frameMetadata = getFrameMetadata({ buttons: [ { label: '+', }, { label: '-', }, ], input: { text: 'Input your number', }, image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, }); ``` ![full calculator Farcaster Frame showcase](../../../../assets/images/docs/farcaster/frames/quickstartFullCalculator.png) Now we can get user input! Test it in the debugger, you can see that you can press two buttons and enter text, though we can't see our result after... Nothing happens. This is because we don't tell the Frame where to send our data and we don't handle it. To do so we open the `app/api/frame/route.ts` file that exposes the following post endpoint for us: `/api/frame/` in there we will now build out the response frame! This will control the flow of our application! We will build it out in a second. Though first we tell the initial frame to send it's data there! To do so we add the post_url field that we deleted, when we emptied the sample from Coinbase. Now our initial Frame looks like: ```typescript const frameMetadata = getFrameMetadata({ buttons: [ { label: '+', }, { label: '-', }, ], input: { text: 'Input your number', }, image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, postUrl: `${NEXT_PUBLIC_URL}/api/frame`, }); ``` as you can see there the NEXT_PUBLIC_URL is used to define if we have localhost or yourdomain.com This can be a config file like it is in the sample or also easily be an environment variable! Let's adjust the `app/config.ts` to be `http://localhost:3000` or whatever port your frame is running on. > You can also adapt config.ts to ```export const NEXT_PUBLIC_URL = process.env.NODE_ENV == "development" ? 'http://localhost:3000': 'https://yourprod.com'``` >This way locally you have localhost and may only need to change the port, while prod will have your proper domain always. ... or use an env variable. >IMPORTANT: make sure to always have http:// for localhost and https:// for regular domains as otherwise it won't work! Now when you test in the debugger you'll see some fun stuff or not so fun... let's fix and get our calculator working! ## Handle post requests aka build logic into our Frame To get our calculator fully working we will need to - know which button was pressed by the user (1: addition, 2: substraction) - make sure the input is actually a number - return a new frame within 5 seconds otherwise Farcaster clients will not render the frame and think of it as timed out To do so we open the `app/api/frame/route.ts` file that exposes the following post endpoint for us: `/api/frame/` There is already some logic in the sample so what does it do? can we use it? Yes we can actually it already handles user input for us! and it shows how we learn what button was pressed, but starting at the beginning we have the following code that checks if the data we got sent is actually valid. ```typescript if (isValid) { accountAddress = message.interactor.verified_accounts[0]; } ``` you need to know that the `message.interactor...` is part of the API response from Neynar. What's Neynar? neynar.com provides Farcaster APIs so we can easily develop with it. We can also use Farcaster Hubs directly, but we'll stick with the sample and use Neynar! you'll see in lines above that you need to input a NEYNAR_API_KEY i'd recommend not doing it here but instead using .env variables! to get a API Key use neynar.com and create an account. The Read only (cheaper) option is enough for this demo! Now with that we can verify against a Farcaster Hub (a node in the decentralized Network powering the Social Network) that the client actually sent us something nice! Now on to get the input from the message! We provide the user the ability to input a number so here is the logic to get the input the client sent. ```typescript if (message?.input) { text = message.input; } ``` Though we have an issue with that. Can you spot it? We need to make sure we get a number and we need to return an error if we don't get input as we are a calculator that gets input. We'll handle errors at the end, so let's start with making sure it's a number. To do that we try the conversion and catch potential errors though we'll properly handle them later. now our message logic looks like: ```typescript let numberIn: number = 0; if (message?.input) { text = message.input; try{ numberIn = parseInt(text); } catch (e) { console.error(e); } } else { console.error('No input'); } ``` moving on we will want to know if we should add the number or substract. Soooo we'll check the buttons and there is some logic that already does ```typescript if (message?.button === 3) { return NextResponse.redirect( 'https://www.google.com/search?q=cute+dog+pictures&tbm=isch&source=lnms', { status: 302 }, ); } ``` the above logic actually sends a 302 redirect which is how you properly handle a post_redirect action in a Frame! Nice to know, but not needed for us here. So we'll just extend the logic to handle our two button cases and move on! We have a small thing to take care of first though as we don't actually store our current result anywhere. For simplicity we will store one global result so everyone will be using the same calculator :O one calculator for all of farcaster! One ring to rule... okay I'll stop If you want to have it be per user you can use a Key Value store, database, excel or whatever that stores a mapping from FID to result. Then just fetch it in that post logic so before the buttons you would do ```typescript const fid = message?.interactor.fid; // use neynar return to get the FID let result = getResult(fid); // look up the user's result ``` We will keep it simple and use global state. so our result is simply ```let result = 0;``` which we put at the beginning of our route.ts file directly after the imports. Now to handle both buttons of our calculator correctly we get: ```typescript if (message?.button === 1) { result += numberIn; } else if (message?.button === 2) { result -= numberIn; } else { console.error('none of our buttons pressed?!') } ``` and yes it is correct that Frame buttons are indexed with 1 and not by 0 like arrays are for example. Now that we have done our addition we can just return a new calculator Frame that the user can interact with to do more calculations. Luckily we will save our Kindergarden cousin from homework now ;) And we already have built such a Frame it's the first one, remember that? So we can just copy that over in the Frame that is in the sample. though now we use `getFrameHTMLResponse()` instead of the `getFrameMetadata()` function. we then have ```typescript return new NextResponse( getFrameHtmlResponse({ buttons: [ { label: '+', }, { label: '-', }, ], input: { text: 'Input your number', }, image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, postUrl: `${NEXT_PUBLIC_URL}/api/frame`, }) ); ``` ![full calculator Farcaster Frame showcase](../../../../assets/images/docs/farcaster/frames/quickstartFullCalculator.png) though a nice refactoring we can do is put that frame in it's own file so if we create frameCalculator.ts in our app/ directory we can import from this file and just need to adjust the frame once there! so create `frameCalculator.ts` then input: ```typescript const frameCalculator: FrameMetadataType = { buttons: [ { label: '+', }, { label: '-', }, ], input: { text: 'Input your number', }, image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, postUrl: `${NEXT_PUBLIC_URL}/api/frame`, }; export default frameCalculator; ``` and in `app/page.tsx` we now use that Frame ```typescript //.. const frameMetadata = getFrameMetadata(frameCalculator); //... ``` as well as in our `app/api/frame/route.ts` ```typescript //... return new NextResponse( getFrameHtmlResponse(frameCalculator) ); //... ``` that refactoring doesn't make our Frame any different but I find it cleaner to work with. And we'll use the same logic for our Error Frame (Error Screen) as we need that at different points and don't want to replicate code there. ## Handle Errors in Frames. The Error Frame Now that we have a nice Frame, we want to handle errors properly instead of just returning the old frame potentially. So we'll do that. First we'll create a new file in the `app/` directory called frameError.ts and build a simple one Button Frame saying "Error" sending the user back to the calculatorFrame. app/frameError.ts ```typescript const frameError: FrameMetadataType = { buttons: [ { label: 'Error!', }, ], image: { src: `${NEXT_PUBLIC_URL}/park-1.png`, aspectRatio: '1:1', }, postUrl: `${NEXT_PUBLIC_URL}/api/error`, }; export default frameError; ``` now that we have our Error Frame and told it to use the `/api/error/` endpoint let's create that real quick so we'll create the error folder and route.ts file so `app/api/error/route.ts` and create a quick post request handler that serves our original frame. app/api/error/route.ts ```typescript export async function POST(req: NextRequest): Promise { return new NextResponse( getFrameHtmlResponse(frameCalculator) ); } export const dynamic = 'force-dynamic'; ``` as you can see here we now have a super simple file that returns our original calculatorFrame because we have refactored it beautifully to it's extra file and don't need to rewrite the same code :) now we want to use the error Frame in our main frames as well. sooo where we logged error's before we can simply use the NextResponse to the Error Frame ```typescript return new NextResponse( getFrameHtmlResponse(frameError) ); ``` so our `app/api/frame/route.ts` which serves our calculator now becomes ```typescript //... let numberIn: number = 0; if (message?.input) { text = message.input; try { numberIn = parseInt(text); } catch (e) { console.error(e); return new NextResponse( getFrameHtmlResponse(frameError) ); } } else { console.error('No input'); } if (message?.button === 1) { result += numberIn; } else if (message?.button === 2) { result -= numberIn; } else { console.error('none of our buttons pressed?!') return new NextResponse( getFrameHtmlResponse(frameError) ); } //... ``` if you want to make sure you catch all errors you can also wrap the whole getResponse function into a `try {...} catch (e) {...}` that way you could serve an error frame if something happens which then on button press lets the user restart. ## Dynamically generate Images on the fly to show a calculator display Okay this will be fancy and really cool! We will generate images on the fly that show the current result of our calculation and return that image! Whoooot! yes! Lets get to it. // TODO: use satori to generate an image on the fly that has the current result ## Deploy the frame to dev or production No matter where your Frame is developed all it does is respond to some network requests with some HTML Metatags which means technically your frame could just be some static HTML that has the metatags set and a server that serves it. Though in our quickstart we have a nice calculator in NextJS so we can use anything that has a NodeJS Server. with NextJS being developed by Vercel of course you can simply run ```npx vercel``` and have it deployed there. >**IMPORTANT**: You will have your public URL / domain in there as postUrl or somewhere else in the Frame. Make sure it is actually the one where >your environment sits! If it's still localhost or hardcoded your Frame may be broken because of that! > You can also adapt config.ts to ```export const NEXT_PUBLIC_URL = process.env.NODE_ENV == "development" ? 'http://localhost:3000': 'https://yourprod.com'``` >This way locally you have localhost and may only need to change the port, while prod will have your proper domain always. ... or use an env variable. >For localhost you always use **http://** and for your public domain you use **https://** To deploy directly to prod using vercel run ```npx vercel --prod``` and annoy the senior devs if something breaks ;) Make sure to either use environment variables for the NEXT_PUBLIC_URL, set them in vercel and replace the config.ts from our sample or change the URL before deploying. >When entering the url on localhost for testing you need to use ```http://localhost...``` and else you need to use ```https://yourdomain.com``` make sure >this is the case otherwise it will not work! For our sample calculator that is in the file `app/config.ts`. any other solution like netlify, or a simple NodeJS Docker container also works. Or you have your own server and clone your repo/push your code there. Then you can run ```npm run start```. You then likely need to expose the port 3000 or whatever NodeJS uses for you under your domain. --- --- Title: Frames V1: Which Frames Framework to choose? --- # Frames V1: Which Frames Framework to choose? >This guide assumes you know what [Farcaster Frames](/farcaster/frames/) are. There are three big TypeScript Farcaster Frame development frameworks that always come up in conversation. ## Choose your Frames Development Framework One can clearly pick a fitting Framework according to their requirements. Mainly one needs to decide if integration into an existing code base, rendering as well as building frames or getting started quickly with very opinionated abstractions is the way to go. - **Coinbase Onchainkit**: types and helper functions as direct mapping of the Frames Specification, bring your own application - **Frames.js**: somewhere in the middle between abstractions and specification with a Frames Renderer for your Frontend built in - **Frog.fm**: heavily opinionated abstractions to remove boilerplate code aimed at web developers, renaming Frames Specification to their lingue ## Coinbase Onchainkit Now in case one prefers the official specification as their reference document the choice is Coinbase Onchainkit. No other Framework implements it directly while giving helper functions. The next closest being you writing the html meta tags. Onchainkit removes that hastle and simply produces them. With the simple returns of responses or pure metadata that one can return or include in frontends, Onchainkit relies on the developer to do the application and backend building. Coinbase Onchainkit has React components available, though personally I prefer to only use the core typescript functions and integrate into projects that way. ## Frames.js Frames.js started out by being the JSX of Frames. One defined their Frame in JSX syntax and that got included or returned as html metadata tags. Since v0.13.0 that changed to being an abstraction over the specification that aids in removing some of the code one needs to write every time like verifying the package sent by the Farcaster client. The developer adds their own routes and then leverages the Frames.js abstractions to serve frames there. Routing between Frames is done by sending the user to a different route (routes handled by your Framework e.g. NextJS) or by sending to the same route as before and using State to derive what to return. For example we want to send a user from /start to /next then we simply set the postUrl to /next and handle the next Frame there. This API works in every TypeScript Framework while additionally having a Frame renderer component where one can build Frames into their own web applications. With a Context object being available on a one time setup through so called Middlewares a developer can get enriched information within each route handler to work with. Say you add a middleware to add infromation about the user interacting with your Frame you could get their full profiles without needing to manually write the profile lookup. Frames.js also brings a local debugger that can be started using ```npx frames``` ![Frames.js Frames Debugger](../../../../assets/images/docs/farcaster/frames/framesJSDebugger.png) ## Frog.fm Frog is the most done for you Frames Development Framework out there. It registers routes, provides middleware adding context to your request data. While it is quick to write Frames in Frog one will find themselves struggling to find information in the documentation. The documentation feels more like a reference where one opens each detailed reference document and sees if they can find what they know should be there as per Frames Specification. Though you may not even find it by search 1:1 as Frog focuses on existing web developer naming conventions instead of adopting the naming conventions used in the Farcaster Specification and ecosystem. One such example being the link action type where you want to show a button with a predefined link (e.g. dtech.vision). The Farcaster Frames specification calls this a link action with target dtech.vision while Frog makes you use the Link Subclass of Button with attribute href similar to html's ``````. Many more such examples exist which make it hard to find features coming from the official specification or other frameworks. The Frog documentation has no information on the renamings done and as mentioned may not enable you to find it. If one is used to Frog though, the development speed is really useful for fast iterations and Proof of Concepts. The heavy use of abstractions helps, unless you end up doing complex frames and start fighting them. Frog.fm also brings a local debugger that can be started using ```npx frog``` ![Frog.fm Frames Debugger](../../../../assets/images/docs/farcaster/frames/frogFMDebugger.png) ## Comparing code between Frameworks While with Frames.js and Frog.fm you may end up fighting the abstractions in complex projects you are at times writing repetitive boiler plate in Coinbase Onchainkit. Depending on the project choose your respective trade offs. A simple ping then click a button and get pong frame in all three frameworks can be implemented in the following manner. The navigation from one Frame "Ping" to the next "Pong" is a good showcase of the basic layout and differences between the Frameworks. ### State Management & Initial Setup To start in Frames.js and Frog.fm we need to initially setup the abstractions. We do not need to do so in Coinbase Onchainkit. In case we were keeping track of state we would need to initialize that as well. For Onchainkit we can use the state in a Frame directly to pass serialized JSON objects. It may just as well be helpful to define the State type with your types for use in Coinbase Onchainkit as well. Though you do not need any further initialization or the type. ```typescript // frames.js // file: src/app/frames/frames.ts export type State = { //JSON serializable state (no undefined :/) // example: favoriteColor: string; age: number; } export const frames = createFrames({ // ... // define initial state if you want initialState: { favoriteColor: "green", age: 10, } // ... }) ``` ```typescript // frog.fm // file: app/api/[[...routes]]/route.tsx type State = { //JSON serializable state (no undefined :/) // example: favoriteColor: string; age: number; } const app = new Frog<{State: State}>({ // ... // define initial state if you want initialState: { favoriteColor: "green", age: 10, } // ... }) ``` >Note that Frog as of writing uses URL parameters while Onchainkit leaves it open how to pass state with the Frames Specifications State being the logical place. Frames.js comes with abstractions for both URL parameters and the State property. In addition Frames.js adds the possibility to define a signer key to achieve temper proof state due to it being signed by the backend. ### Ping Pong Frame Now we are ready for the "Ping"-Frame. Let's do nothing else besides returning "Ping" and logging the user who pinged. To make sure the user is actually the user they say they are, we require a signature (which is provided by default in signedMessageBytes sent to our Frame Server). For verification we need to use hubs. For Frog and Frames.js this means either doing the call manually or using the middlewares to have it available in context. In Coinbase Onchainkit we will use a provided helper function. ```typescript // coinbase onchainkit // file: app/api/ping/route.ts // ... async function getResponse(req: NextRequest): Promise { const body: FrameRequest = await req.json(); const { isValid, message } = await getFrameMessage(body, { neynarApiKey: process.env.NEYNAR_API_KEY!! }); if(!isValid) {return new NextResponse('FrameMessage not valid!', { status: 500 }) } console.log('Ping by', message.interactor.fid); // you could do whatever for a new state and access current state with message.state console.log('current State:', message.state); const state: State = { favoriteColor: "red", age: 11, } return new NextResponse( getFrameHTMLResponse({ state: state, image: { aspectRatio: '1:1', src: 'https://dtech.vision/frame.png', }, buttons: [ { label: "Ping", action: 'post', // post is the default action and can be omitted, explicitly written for the comparison // NOTICE: this URL is the NEXT Frame or rather what we want to interact with. We want to send the Ping to let the user Pong too :D target: `${NEXT_PUBLIC_URL}/api/pong`, // NEXT_PUBLIC_URL from app/config.ts (see Coinbase Onchainkit Frame Example) } ] }) ); } export async function POST(req: NextRequest): Promise { return getResponse(req); // you could also do your logic directly here instead of calling getResponse, though that pattern allows for easy reuse of frames across the app // by calling getResponse from another route (and you may even rename it to state which Frame it returns/handles) } ``` ```typescript // frames.js // file: app/frames/ping/route.tsx // ... export const POST = frames( async (ctx) => { if(!ctx.message) { throw new Error('no message'); } console.log('PING: current State:', ctx.state); let state: State = { favoriteColor: "red", age: 11, } return { state: state, image: 'https://dtech.vision/frame.png', imageOptions: { aspectRatio: '1:1', }, buttons: [ ], } }) ``` ```typescript // frog.fm // file: app/api/[[...routes]]/route.tsx // ... app.frame('/ping', async (c) => { const { deriveState } = c; console.log('Ping by', c.frameData.fid); if(c.frameData.fid == 3) { // return an Error if fid 3 (Dan Romero) presses Ping return c.error( { message: 'Hello Dan. Gjusta is closed today.' }); } // you could do whatever for a new state and access current state with message.state console.log('PING: current State:', c.previousState); const state: State = deriveState( previousState => { previousState.favoriteColor = "red"; previousState.age = 11; } ); return c.res({ image: 'https://dtech.vision/frame.png', imageAspectRatio: '1:1', intents: [ , ], }); }) ``` As we can see the Ping Frames all point to pong if the user presses the Ping button. We need to also be able to handle the Pong route to return a frame there. As a fun little gadget if Dan Romero (fid: 3) presses the Ping button we will return an error message. This error message will be shown as onscreen notification by the Farcaster clients. Dan sadly won't be able to see our Pong frame, but gets a nice message. Of course these messages can be used for more, but it is a fun showcase. Once someone presses pong the state will also change to be different which you'll see by looking at your console output on the Frames Server. ```typescript // coinbase onchainkit // file: app/api/pong/route.ts // ... async function getResponse(req: NextRequest): Promise { const body: FrameRequest = await req.json(); const { isValid, message } = await getFrameMessage(body, { neynarApiKey: process.env.NEYNAR_API_KEY!! }); if(!isValid) {return new NextResponse('FrameMessage not valid!', { status: 500 }) } console.log('Pong by', message.interactor.fid); if(message.interactor.fid == 3) { // return an Error if fid 3 (Dan Romero) presses Ping return new NextResponse('Hello Dan. Gjusta is closed today.', { status: 500 }); } // you could do whatever for a new state and access current state with message.state console.log('PONG: current State:', message.state); const state: State = { favoriteColor: "blue", age: 69, } return new NextResponse( getFrameHTMLResponse({ state: state, image: { aspectRatio: '1:1', src: 'https://dtech.vision/frame.png', }, buttons: [ { label: "Pong", action: 'post', // post is the default action and can be omitted, explicitly written for the comparison // NOTICE: this URL is the NEXT Frame or rather what we want to interact with. We want to get back to ping here target: `${NEXT_PUBLIC_URL}/api/ping`, // NEXT_PUBLIC_URL from app/config.ts (see Coinbase Onchainkit Frame Example) } ] }) ); } export async function POST(req: NextRequest): Promise { return getResponse(req); // you could also do your logic directly here instead of calling getResponse, though that pattern allows for easy reuse of frames across the app // by calling getResponse from another route (and you may even rename it to state which Frame it returns/handles) } ``` ```typescript // frames.js // file: app/frames/pong/route.tsx // ... export const POST = frames( async (ctx) => { if(!ctx.message) { throw new Error('no message'); } if(ctx.message.requesterFid == 3) { // return an Error if fid 3 (Dan Romero) presses Ping throw new Error('Hello Dan. Gjusta is closed today.'); } console.log('PING: current State:', ctx.state); let state: State = { favoriteColor: "blue", age: 69, } return { state: state, image: 'https://dtech.vision/frame.png', imageOptions: { aspectRatio: '1:1', }, buttons: [ ], } }) ``` ```typescript // frog.fm // file: app/api/[[...routes]]/route.tsx // ... app.frame('/pong', async (c) => { const { deriveState } = c; console.log('Pong by', c.frameData.fid); if(c.frameData.fid == 3) { // return an Error if fid 3 (Dan Romero) presses Ping return c.error( { message: 'Hello Dan. Gjusta is closed today.' }); } // you could do whatever for a new state and access current state with message.state console.log('PONG: current State:', c.previousState); const state: State = deriveState( previousState => { previousState.favoriteColor = "blue"; previousState.age = 69; } ); return c.res({ image: 'https://dtech.vision/frame.png', imageAspectRatio: '1:1', intents: [ , ], }); }) // ... ``` We have now built a sample Ping Pong Frame in all three frameworks. By looking at the code you can tell right away which suits your needs more. An [mental model](#choose-your-frames-development-framework) to know which to choose is available [here](#choose-your-frames-development-framework). To look at a more complex transaction frame with multiple steps, blockchain contract interaction, ccustom API calls and backend verifier logic to further check how each Framework behaves in complex settings refer to the following video.
## Final remarks Always remember Frames are HTML Metadata tags returned in the `````` component of HTML responses. In case it gets to weird in a Farmework or setting one up seems to much, just use the HTML tags directly. One can use HTML tags to point to the next Frames in a Framework. For example use HTML tags in Frontend to not add dependencies that point to a backend server running one of the Frames frameworks to handle further interactions. In case you now want to develop a Frame head to the [Quickstart Frames 101](/farcaster/frames/quickstart-farcasterframes/). --- --- Title: How does Farcaster Storage work? --- # How does Farcaster Storage work? > This article assumes you know what [Farcaster Hubs](/farcaster/hubs/) are. ## Storage on Farcaster Hubs What is Storage on Farcaster? Storage is a means of keeping track of usage on the Farcaster network. Storage is accounted for in bytes to be used for posts, reactions, [links](/farcaster/hubs/farcasterlinks), profile data and verifications. One storage unit is one message of the according type. A payment is required yearly to keep all messages of the fid (user) stored on the network. The accounting for the storage is done on a blockchain via the [Storage Registry](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#13-storage-registry). Blockchains provide the financial infrastructure required to facilitate payments for storage as a credibly neutral accounting layer that is not easily tampered with providing immutable history. For a new message (e.g. post, like, follow and so on) to make it to the Farcaster network the fid must have enough storage allocated in the Storage registry. ![Farcaster Storage Architecture Visualized](../../../../assets/images/docs/farcaster/hubs/storage/farcasterStorageVisualized.png) ### Why do we need to pay for Storage on Farcaster? Why? Because the Farcaster network is a decentralized network, and as such, it's important to keep the network running smoothly. By limiting the amount of data one can maximally store and placing a cost one can't infinitely spam it. One payment for a storage unit lasts one year. So units are freed if not used. The payment can be done for any fid at the Storage Registry so apps could fund it for their users or you may pay for your friends. ### Why does there need to be a Storage limit? Without a limit on storage, the network could be infinitely spammed. Additionally it could grow so quick that it would bring down the computers making up the network. ### When are messages pruned (deleted) If an account exceeds its limit for a message type, the oldest message is pruned to make space for the new one. If the whole storage unit expires there is a grace period of 30 days before the messages are pruned. The user can keep using the network without paying for more storage and Hubs can keep the storage load under control. An account can always purchase more storage to increase its limits. Additionally an account can delete messages at any time by publishing a corresponding delete message. The delete message will remove the contents of the original message, leaving a tombstone in its place. A deleted message will still count towards the account's storage limit until it expires by being pushed out by a newer message. Though remember that the original message was publicly sent so there is no guarantee that no one has kept track of it even though the network itself deleted it. ### How do Hubs know my Storage limits? Hubs interact with the Storage Registry smart contract on the Optimism blockchain through RPC calls. The Storage Registry maintains a record of how much storage each FID (Farcaster ID) has purchased and when it expires. When a Hub starts up, it establishes a connection to Optimism through an RPC endpoint and begins monitoring the Storage Registry contract for events. This allows the Hub to track storage allocations and expirations in real-time. This storage information is cached locally by the Hub and updated whenever new Storage Registry events are detected on-chain. The Hub uses this data to enforce storage limits when processing new messages from users. ### Onchain Events related to Storage The onchain events are also synced across the network as onchain event messages. ## Onchain (Storage Registry Smart Contract) ### What is the maximum amount of storage? That can be read from the Storage Registry smart contract using the maxUnits() function. As of writing (2024-12-26) the maximum amount of storage is 1,000,000 units. ### How to read Storage values? To see how much storage a user has, you have to query Hubs. The Hubble API exposes a `GetCurrentStorageLimitsByFid` function. Onchain you would aggregate over all rent events. ### How to pay for Storage? The Storage Registry smart contract exposes methods to rent storage (pay) `batchRent` and `rent`. You can interact with these methods to rent storage units for a given FID. ### How can I extend my Storage? To extend your personal storage either you use tools like your Farcaster client's built in Storage feature (for Warpcast that's in Settings > Storage which allows you to see limits and extend storage). Though there are other tools like Cast Storage and of course the Smart Contract directly. ## Ressources You can read more about Storage - [in the docs](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#storage) - [in the hubble codebase](https://github.com/farcasterxyz/hub-monorepo/) - [in the smart contract code](https://github.com/farcasterxyz/contracts/) --- --- Title: How do Farcaster Hubs work? Hubble Architecture --- # How do Farcaster Hubs work? Hubble Architecture >This article assumes you know what a [Farcaster Hub](/farcaster/hubs/) is and have some general intuition about software programming. This is a practical overview, consult the [official documentation](https://docs.farcaster.xyz/learn/architecture/hubs#design) and [offical reference](https://docs.farcaster.xyz/reference/) as well as [source code](https://github.com/farcasterxyz/hub-monorepo) if you want to dig deeper. ## Starting Hubble, how do Hubs synchronize with the Farcaster network? Up on starting Hubble synchronizing your Hub to hold the messages sent on Farcaster works the following: 1) Hubble recieves gossip messages sent from other Hubs on the Farcaster network and adds these to the local set of messages. ![Gossip Sync between Hubs visualized](../../../../assets/images/docs/farcaster/hubs/hubGossipSync.png) 1) Hubble will load a snapshot and use it as starting point for Messages sent over the Farcaster protocol. (configurable on/off) - Warpcast Snapshot is daily 09:00 PT ![Snapshot Sync between Hubs visualized](../../../../assets/images/docs/farcaster/hubs/hubSnapshotSync.png) 1) Hubble asks a peer (another Hub) for messages it missed between Gossip messages recieved and the Snapshot. ![Peer Sync between Hubs visualized](../../../../assets/images/docs/farcaster/hubs/HubPeerSync.png) >What are [Messages](https://docs.farcaster.xyz/reference/hubble/datatypes/messages)? A Message is the core data format sent across the peer to peer network Farcaster. It contains actions like adding a post (cast), following someone (add link), reactions (like a post) and more. Though Hubs have onchain Events as well which include Storage, Signer and FID Registry. [Full Reference of the contracts here](https://docs.farcaster.xyz/reference/contracts/) ## How do you know that your Hub is synchronized? Your hub will have a peer and they both check the number of messages each has if you're within 99% of the other hub you can consider yourself synced or 99.5% ... whatever metric you want to use. The Snapshot sync also gives you progress, though for a running hub number of messages compared to peers is the only way to check. If you're running multiple hubs you can reference between them or use Hubs from API providers and other public Hubs to check outside of your Hubs peer too. Firstly know that your Hub is ready right away, to verify messages for Mini Apps or so for example, and as soon is it's live it can also post to the network. It's just reading all messages (past) that takes time to synchronize. Why is this the case? The Hub network guarantees eventual consistency, that means eventually the network will have the same state (same set of messages), though people constantly post new ones, so it takes time to synchronize. Therefore different hubs have different number of messages and exchange missing ones. This is by design. ## Running Hubble when does Data get deleted? Data deletion from Farcaster hubs is often referred to as "protocol pruning" or "pruning hub data" which in the end just means that not all the data on hubs will be on hubs forever. Okay so when and what will be deleted? You need to know that there are [Storage Limits](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#13-storage-registry). These specify how many of your data points are kept on Hubs, these limits exist for - Casts (Posts, Replies) - Reactions (e.g. Likes) - Links (Follows) You can check how much storage to have left in your client or on [caststorage.com](https://caststorage.com). If your storage runs out or you go over the limit there is a grace period, but after that old [Messages](https://docs.farcaster.xyz/reference/hubble/datatypes/messages) (think Data: casts, likes, ...) are pruned (deleted) until you are within your limit again. Why? Because this prevents spamming Hubs and just overloading the network, this way every message you post you pay for storage or have it pruned eventually. This also means that your first ever cast may not be available in clients anymore though. To buy storage one interacts with Smart Contracts on Optimism via a transaction that is nothing other than you saying "this is my Farcaster ID (FID) and I want to buy storage". Since Hubs synchronize the onchain state to their state, they will know if you have enough storage or not. --- --- Title: How do Farcaster Signers work? --- # How do Farcaster Signers work? >You do not need to know how Hubs work or what Farcaster is to understand how Signers work. Start reading and in case you have advanced knowledge, jump directly to topics of interest. ## What is a Signer? A signer is like a special pen, that when you sign marks the document as signed by the one that owns the pen. Since you never give posession of the pen to anyone, we know it was you. No one is able to copy or change the signature of your special pen, without us being able to notice that it doesn't come from your pen anymore. So Signers are special pens, that we can use to attest that it is really us. In Farcaster land we sign messages, like in the middle ages they signed letters and sent them off in envelopes. Only if the letter was signed by the king, people needed to respect it. Because of the special pen, we will know if it's really signed by the king or not. The only risk left is that the kings pen was stolen. In our digital age, we use cryptographic keys as the special pens in cryptographic signatures. We call a signer the key that is used for these signatures. In our example the special pen is the signer. ## Why do we need Signers? In a decentralized network we do not have a central instance that says "yes this is Samuel", we need a way of proving it to each and everyone. Just imagine if someone posted in your name and it would show up, that shouldn't happen right? but how do we know the post is really yours? If we require you to sign each post with your special pen, the signer, we know. Only the one with the signer is able to post, because we will verify the signature before accepting your post (this happens on [Hubs](/farcaster/hubs/)). Think back of the king, that needs to sign all letters because of his authority. You are the king (or queen)! You own your profile, your account (your FID). That is why we need you to prove everytime the authority to act as that profile, account (FID). And this is why we need signers, they enable us to look up if any given special pen (signer) is associated with your account (FID) and if it is we know it is really you. In the end we always know that everything happening comes authentically from each user (programmtic users like bots are users in this case too) or was authorized by them. ## How are Signers added? Initially when you create your account the client you use (if you don't interact with the onchain programs directly) creates the key pair (your special pen) and uses that to register the account. This key pair (special pen) is now called your custody address and added as such. It is how we recognize you king or queen acting on Farcaster. If it's not signed by that, we think it isn't you. Unless after registration at any point you add a signer. (Technically these are delegated signers, because you delegate some of your power, but people shorten it to just "signers") Here's the flow of sign up and using custody address to add signers visualized. Be aware that **you** can add a signer **for yourself** too! That may be the case if you are working on a bot and want to use the signer yourself, as a developer it may not always be another app, but your own app as well ;) ![](../../../../assets/images/docs/farcaster/hubs/signers/farcasterAddSigners.png) >Only the custody address can be used to add signers! ## What permissions do Signers have? Since a signer identifies that it is really you, it can mostly do everything you should be able to do right? Though not every signer can do everything. What? yes there is one pen, we expect to be only owned by you and not given out to other applications that you may want to use to post or similar. This is your "custody address". It is what is registered to be your master pen, the master key. In case you signed in with Warpcast, then it is managed for you by the client. In most cases this is true, the client you sign up with holds your custody address and registered your Farcaster account for you. Why is this important? ### Signers vs the Custody Address The root of trust in Farcaster is your custody address. Another signer can't [add a signer](#how-are-signers-added) to your account. To add a signer we need to use the custody address. To remove a signer it can be revoked by itself, an ancestor in its chain of trust, or by the custody address. It is therefore really important to keep your custody address safe and not share it around. By default the custody address is also used [to recover your account](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#recoverfor) in case you somehow lose control. The custody address can do anything a signer can and more. More being adding/revoking signers and setting the recovery address for your whole account. But what can any signer - be it a custody address or not - do? ### Farcaster Signer Permissions >General rule is a signer (that isn't the custody address) can do anything you can do purely on [hubs](/farcaster/hubs). That means they can't delete your account and can't add new signers. ![Farcaster signer permisions visualized](../../../../assets/images/docs/farcaster/hubs/signers/farcasterSignerPermissions.png) ## Where are Signers stored? Signers are stored on the Optimism Blockchain in the [Key Registry Smart Contracts](https://docs.farcaster.xyz/reference/contracts/reference/key-registry). Remember Smart Contract means program that lives on the blockchain, nothing more. Farcaster Hubs only synchronize that data from the blockchain and check that any message has a valid signature from one of the signers registered onchain. The management of signers happens onchain. Meaning you can not interact with a Hub to change signer data, you need to do onchain transactions. These then synchronize to Hubs. ## How are Signers managed? A signer can be anything that creates EdDSA signatures on Curve25519. Usually this is an Ed255191 key pair. These private keys (think special pen of our initial example) are then managed by who ever operates the signer. That may be your client you use to post and consume Farcaster content (e.g. Warpcast) or an API provider, yourself if you program or use local first software or something else. In the end it's important to make sure these private keys used as signers are secure and not exposed publicly. The registry - what key pair is valid to act as signer for your account (FID) - lives on the blockchain. It is called the [Key Gateway](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway) and [Key Registry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry). Any valid signer (custody address or delegated signer) needs to be registered here, since all signer management is stored onchain. Remember [how signers are stored](#where-are-signers-stored) ## What happens when I want to remove a signer/permissions? If you want to remove a signer, you use a signature from your custody address and tell the Key Registry to remove the signer (via onchain transaction). You prove that it is really you authorizing the removal by the custody address signature. Now what happens? We revoked a signer. There are multiple things going on now: - the signer can't be used to act in your account (FID)'s name anymore - all signatures from the signer are deleted from hubs - that specific signer can't be added to your FID via Key Registry anymore The cascade affect of that signer key being revoked is that in case the messages sent using it are not signed by another signer (rarely the case), these messages get deleted from hubs. Say you're using this new fancy Farcaster client and post from it, if you revoke that signer without having another signer sign the messages of the posts, they will be deleted. Example: Signer A and Signer B valid for FID 1 - Signer A signs "Post "hello world" from FID 1" - Signer A gets revoked - cState What happens at cState? the post "hello world" from FID 1 will be deleted from Hubs and not visible anymore. Now take Signer B, we do the same from the new fancy client we gave Signer A to, but now we use Signer B to resign that post, but not the welcome post from the client. - Signer A signs "Post "hello world" from FID 1" - Signer A signs "Post "dtech.vision is the best, welcome to this fancy client" from FID 1 - Signer B also signs the message from Signer A saying "Post "hello world" from FID 1" - Signer A gets revoked - cState What happens at cState now? The post "hello world" from FID 1 will stay since it still has a valid signer, namely signer B. Though the second post from Signer A will be deleted, since no valid signer signed it. No valid signer since Signer A is now no longer valid. This is not only for posting to the network ("casts") but also for likes, follows and anything else that signer does no Hubs. --- --- Title: Farcaster Hubs --- # Farcaster Hubs >How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. >We link out to detailed project documentation where used. ## What is a Farcaster Hub? Farcaster works by some servers storing history of the social network and one being able to add new history (e.g. posts, likes, follow, ...) to them. These servers are called hubs. If you are familiar with blockchains, you may think of them as nodes. Hubs verify that the rules of the social protocol are followed. How the social protocol differs from the social network you ask? The social network is the content and relationships between accounts. Say I post something, you follow me or maybe someone likes a post that is part of the social network. But to verify that only you as the owner of the account or applications that you authorize may post is defined in the social protocol. To verify these rules and run the underlying social protocol that the social network is built on top of we utilize Hubs. A hub is software running on a server to verify the rules of the social protocol while also storing the data of the social network. Many people and companies run hubs, which creates a distributed network of different parties verifying the social protocol rules are adhered to. Anyone can run a hub and be part of verifying rules as well as storing or adding data. ## Where do Hubs fit within the greater Farcaster architecture Hubs are the base layer, the ground truth. Besides identity (e.g. your account - FID) which is stored and verified on the blockchain (Optimism Mainnet), Hubs store and verify everything going on in the Farcaster protocol. Everything we build on top of Farcaster relies on people running hubs and hubs being available. You build APIs on top of data from Hubs being replicated and enriched into databases, that then serve the API queries. You use Hubs to verify signatures and make sure that a message you see is valid. For a message to be accepted, the fid must be registered in the Id registry, and signed with a valid signer present the Key registry, and the fid must have enough storage allocated in the Storage registry. As stated they are the source of truth. ![farcaster architecture overview](../../../../assets/images/docs/farcaster/farcasterArchitecture.png) ## The Details about Farcaster Hubs One thing people do not realise is that not all data is on hubs, but they also miss that all data on Hubs is public. This is because the data on hubs is the social network data, and the social network is public. Though some client or app data is only available in that client/app. An example of that is views. If you view a post there is no message sent on Hubs that you viewed it, but the client knows. That is why some clients will show you views, which is an underestimate as people are seeing your posts across all clients. The data that is on hubs is described in the [Farcaster protocol specification](https://github.com/farcasterxyz/protocol). The following is an overview of all details regarding Farcaster hubs, answering the most frequently asked questions with links to implementation and references where viable. ### Where does a Farcaster Hub run? Since a Farcaster Hub is just software it can run anywhere you want to run your software. This can be your phone, your home computer, laptop or a server. Hub operators may also decide to run Hubs on AWS, or another cloud service like Google Cloud or Azure. Many developers run their hubs on a VPS from Hetzner or others. In the end there are many ways of running a Hub in Kubernetes, Docker or straight from source. The Software can be run anywhere. ### Who is running Farcaster Hubs? While anyone who wants to run the Hub software can run Hubs many Hub operators are enthusiasts, who want to own their data or explore the data through hobby projects. Commercial operators are companies, who built their product offerings on top of Farcaster data and therefor want the control of their data and lowest latency. ### How does running a Hub support the network? Running a Hub supports the network by verifying the rules of the social protocol and storing the data of the social network. This is important as it is the base layer of the Farcaster protocol. Anyone replicating data makes it more fault tolerant. Anyone verifying the rules of the network are upheld, makes it more resistant and fault tolerant. Additionally running your own hub for your app means you have the lowest latency and can serve users better, which in turn provides Farcaster users are better experience improving the overall network user experience. ### What are the rewards for running a Farcaser Hub? While there are no direct benefits or rewards for running a Farcaster Hub you gain a copy of all data and a short path to access Farcaster. The low latency can mean that as a developer your software runs way faster leading to a better user experience. For example a Farcaster bot needs to respond as fast as possible. It helps if that bot runs of a hub directly since that likely offers the lowest latency. ## What does a Farcaster Hub do? From the official docs "Hubble creates a private instance of Farcaster on your machine. It peers with other instances and downloads a copy of the entire network. Messages uploaded to your Hubble instance will be broadcast to the network." Which translates to it talks to other Hubs in the network to make sure your local data copy is up to date and you have the ability to add your own data (e.g. comments or posts, changing profile picture and more). Data meaning Messages, which are the base layer of the Farcaster protocol. [Learn more about Messages](/farcaster/hubs/howdofarcasterhubswork) ### How do Farcaster Hubs send Messages? Messages are exchanged between Hubs using libp2p under the hood. This leverages a wide spread opensource library for peer to peer networks. Meaning that each Hub that is used to write to the Network (recieve messages and push these new messages to the network) tells a set of other hubs (it's peers) about this. Then these talk to their peers and that way the messages propagate until everyone is eventually in sync. That peer set can be seen as your friend group and how many peers (friends) you have is called peer count. Say you want to tell each friend the name of the cool pokemon you discovered. You have 4 friends that you have direct contact with (peer count 4, these 4 friends make up your peer set). When you tell these friends they tell their peers (their close friends) and after all do it, everyone in your larger friend group will know. That's how this sync process works. To learn more about libp2p check their [webpage](https://libp2p.io/) and to learn how it is used in hubs the [Hubble codebase](https://github.com/farcasterxyz/hub-monorepo/tree/main/apps/hubble) contains the Farcaster implementation. ### How do Farcaster Hubs recieve Messages? Recieving messages is done through a number of ways, while the Hub is running it's done by checking in with peers (other Hubs) to make sure you have all messages and if not, recieving missing messages from them. If you're just starting your Hub though, then you can opt into snapshot synchronization where you choose a peer that offers snapshots and then start with that already. This means that you are getting all the messages this hub has at the time of the snapshot. With a Snapshot you do not need to go back in history as much as without it until you are fully synchronized. The Warpcast team (Merkle Manufactury) publishes a Snapshot every day 9:00AM PT, which is used as default Snapshot for sync. New Messages are propagated through peer to peer networking as [when sending messages](#how-do-farcaster-hubs-send-messages) ### How do Farcaster Hubs store Messages? All messages when using Hubble are stored using RocksDB. [RocksDB](https://rocksdb.org/) is a key-value store that is optimized for fast storage and retrieval of data. Your hub [recieves messages](#how-do-farcaster-hubs-recieve-messages) and adds them to RocksDB if they are new and that way builds up a store of all messages. Additionally many folks choose to write these messages to their own database, but as far as hubs themselves are concerned the Hubble implementation uses RocksDB for storage. #### Where can I find these stored Farcaster Messages on Hubs? The RocksDB storage for messages on Hubs can be found in the `.rocks` directory which when using the Docker Image is present at `/home/node/app/apps/hubble/.rocks`. ### How do Farcaster Hubs synchronize? Farcaster Hubs synchronize by recieving messages from other Hubs (their peers) and by sending their own new messages to other hubs (their peers) and as everyone sends to their peers eventually the whole network will have recieved the messages and be synchronized. An overview with practical example is [available here](#how-do-farcaster-hubs-recieve-messages). The technical details are covered in the [how do Farcaster hubs works](/farcaster/hubs/howdofarcasterhubswork/) section. ## What part of Farcaster Hubs is onchain? Farcaster is not completely onchain, though Farcaster identities are. The usernames are onchain and the amount of messages the users are allowed to consume in terms of storage on Hubs is. Meaning user identity is managed onchain, while data storage is offchain on Hubs. The Messages that Hubs exchange are stored on Hubs as explained [here](#how-do-farcaster-hubs-store-messages). ### Which Chains is Farcaster on? There are multiple onchain parts of farcaster. The Signer (Key) Registration, the storage registration and identity as well as the username (fname) registry. All of these are on Optimism Mainnet except for the fname registry, which is on Ethereum Mainnet. The reason for Ethereum Mainnet on the fname registry is, that it is ENS based. The Key ([Signer](/farcaster/hubs/howdosignerswork/)) Registration happens on Optimism Mainnet via [the Key Registry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry). The Storage Registration happens on Optimism Mainnet via the [Storage Registry](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry). To see a full live overview of the contract addresses and where each part is deployed refer to the [contracts repo](https://github.com/farcasterxyz/contracts). To learn more about the full architecture, you can read and watch the full [Farcaster architecture breakdown](/farcaster/architecture/). ### How to read Farcaster Onchain Data? The easiest way to read Farcaster's onchain data is to use Hubs. The Hub API stores onchain events as messages, just like other Farcaster events and you can query them. Though one can also look at the blockchain directly and watch the Smart Contracts. Popular tools to do so are viem and ethersjs. ### How to use Farcaster Smart Contracts? If you want to write to the Smart Contracts you can read the Code on [Github](https://github.com/farcasterxyz/contracts) or the Reference in the Farcaster docs. What you can do with the Smart Contracts is register a new FID (user account) and extend or add storage so you can post, react or follow more. Additionally since [Signers](/farcaster/hubs/howdosignerswork) are managed via Smart Contracts, you can also let users register public keys that can be used to act on their behalf. This is useful for writing in the name of a user if you're a content scheduling platform for example. ### What is Farcaster Storage? Farcaster Storage refers the amount of messages (likes, follows, casts, ...) that one account is able to have stored on Hubs. In the event of exceeding one's storage limit the oldest messages will be deleted from Hubs and therefore not be available anymore. Yes this means your old posts may no longer be available or your likes that you gave could be gone. ### How does Farcaster Storage work? Every Hub checks the blockchain's smart contracts to verify the storage limit of every account. If an acount goes over that limit, the Hub will not retain old messages. Think of it this way: The Hub is an apple farmer and looking at how many apples he can store. If new Apples come in, which are above his available storage capacity, then he wants to store only the newest apples. To make room for the new apples, he removes the oldest apple each time. Though Hubs do not delete the messages instantly, there is a grace period in which one can buy/register new storage capacity with the smart contracts. In case this happens, the updated storage limits will be applied so if extended, the messages will not be deleted. Not every message is treated equal as the storage limit is split into different limits. Posts (casts, replies, quote casts) are tracked individually as well as reactions (likes) and links (follows). This means that if the storage limit for reactions is exceeded there will NOT be a deletion of your old posts, but of old reactions. In the apple farmers example this means he has three storage facilities for different kinds of apples. He does not throw away and replace apples that do not belong into the facility that is over capacity. Why? Because these can be stored still in accordance to the limits. To summarize from a user perspective if one goes beyond their storage limits (which are stored on the blockchain), one can extend storage by paying for more at the smart contracts. In case this happens within the grace period, the updated limits will be applied. If not for any new message (like, reaction, etc) that is of the same type that is beyond the limit, the oldest messages will be deleted (pruned). If you want to check your storage caapcity, you can do so from within your Farcaster client (like Warpcast) or use Fraems or tools like caststorage.com ## How do Farcaster clients use Hubs? ### What do clients do with Hubs? Farcaster clients use Hubs as their source of truth. The Hubs store the networks content. As clients want to display that state and change it, they eventually read and write from Hubs. That is why for a Farcaster client Hubs are needed. Though as not everyone wants to run Hubs themselves, API Providers exist to run Hubs for you. In the case of clients not running their own hubs, the API Provider will either just run a Hub for them and the client then uses the managed hubs or the provider will expose a full blown API based on Hub data. That API can be optimized for the use cases of the API providers customers. These optimizations could be aggregating data, so that there is less computation on the API consumer or enriching the data, combining data and more. ### Why do Farcaster clients run Hubs? When operating a Hub the client developers get direct access to the underlying Farcaster social graph (data). By running a hub themselves, the latency for writing to Hubs is significantly reduced and there is no dependency on third party Farcaster APIs. It also allows direct event streaming when reading data, so that actions can be taken as fast as possible based on events within the Farcaster network. ### Does a Farcaster client need a Hub? Technically every client needs a hub, the question being who is running it. Farcaster data is stored on hubs, so any write or read eventually reaches Hubs, though if the client developers do not operate their own hub directly, then a 3rd party will. In summary this means, yes every client needs a hub, but the client team may not operate the hub themselves. ### Why do Farcaster clients need a database? Hubble (the Hub implementation) stores data by holding a record of all messages sent. This requires everyone who wants to show the number of likes (reactions with type like) for a post to perform a lookup on all messages counting those where this post has been liked and then substracting those where the like reaction has been removed. Since these queries are not efficient and storing the aggregate in connection to the post may be preferred in addition to the full message history or even a list of everyone who liked the post, one needs a custom data storage solution. This is where storing the data in one's own database comes to the rescue. You may also already have data that you need to keep like payment status in case your users have to pay for service and other data, which means one likely has an application database anyways. ### How to post to the Farcaster Database? If you need to ingest Farcaster data into your database, you can subscribe the a Hubs gRPC Event Stream and add everything you observe to the database. Doing so provides you with your own history of all messages currently new on the Farcaster network. To get historic data you can backfill from the data stored on a hub. Developing a solution to write from Event Stream to the database is rather straight forward since the data format is well defined. Additionally the Merkle Manufactory team provides a ready to use indexing solution [in the hub-monorepo on Github](https://github.com/farcasterxyz/hub-monorepo/). When writing to the database one could also add custom logic to perform aggregates compution, trigger webhooks or anything else. Recall that your database is not a Farcaster hub! If you want to write to the database you'll have to do so by writing your state changes to Farcaster hubs. [Here's how you can write to hubs](#how-to-write-to-hubs). Working with your database, will be specific to the database solution you chose. ### How to read Farcaster Data from your Database? If you have data in your database, then quering it is the same as for any other call to this database. If one is using PostgreSQL then, you'll be using SQL queries. If one is using another database, then this will have it's way of reading (querying) data. ### How to query Hubs? Hubble (the main hub implementation) provides to APIs to query data: a GRPC and a HTTP API. Depending on your needs you may combine them or use one of them. The Info endpoint of the HTTP API is useful to check readyness and see when a hub is up and can verify messages. The hub being online/ready may not mean that it has fully synchronised. One needs to compare to the network to see the sync percentage of a hub. Querying Hubs for data then is calling these APIs and doing the computations over the messages they return. Besides getting data one can use them to verify messages as well (e.g. Frame Message Validation in [Frames](/farcaster/frames/howdofarcasterframeswork/)). ### How to write to Hubs? Hubble (the main hub implementation) provides to APIs to write data: a GRPC and HTTP API. To call them and write to hubs you need to have a private key matching the public key stored for the FID you want to write as. These key pairs are called [Signers](/farcaster/hubs/howdosignerswork/). Hubs will verify that a message is signed by a valid signer and if not reject the message. Assuming you have a signer, writing becomes constructing the message and then calling the respective API endpoints on the Hub. ### The Farcaster Hub API To learn more about the API you can read the reference for the [HTTP API](https://docs.farcaster.xyz/reference/hubble/httpapi/info) and [GRPC API](https://docs.farcaster.xyz/reference/hubble/grpcapi/grpcapi). --- --- Title: How does one use Farcaster Protocol data from Hubs? --- # How does one use Farcaster Protocol data from Hubs? [Farcaster Hubs](/farcaster/hubs/) are the source of truth of Farcaster. It's the data that is valid. >The following assumes you are familiar with [what a Hub is](/farcaster/hubs). You do not need to know [how they work](/farcaster/hubs/howdofarcasterhubswork). ## First What Data is on Hubs? Hubs contain everything that is sent over the Farcaster protocol (so called Events which include Messages). ![Data on Farcaster Hubs](../../../../assets/images/docs/farcaster/hubs/dataOnHubsVisualized.png) >What are [Messages](https://docs.farcaster.xyz/reference/hubble/datatypes/messages)? A Message is the core data format sent across the peer to peer network Farcaster. It contains actions like adding a post (cast), following someone (add link), reactions (like a post) and more. Though Hubs have onchain Events as well which include Storage, Signer and FID Registry. [Full Reference of the contracts here](https://docs.farcaster.xyz/reference/contracts/) For more [learn how Hubs work](/farcaster/hubs/howdofarcasterhubswork). Now how do we get that data and work with it? ## The Event Stream ![hub eventstream](../../../../assets/images/docs/farcaster/hubs/hubEventStream.png) With the Event Stream we use gRPC to subscribe to it and recieve each new Event arriving at this specific Hub. Farcaster Indexers use this stream to write each new event into a datastore like a database. What we effectivly get here is a firehose. This is the lowest latency way to process events since we get them as soon as they go through the protocol. Reading the Messages from the Event we can check if @samuellhuber is mentioned in a cast or the text contains "dTech" and reply from our bot that says "hey cool that you are talking about us". Or of course you can do other things like send push notifications or trade a ticker if there is specific people posting about it. Like "if xyz mentions $dTech then buy 100 $dTech" in an automated fashion because you get it directly as soon as it is published. But that is all, you use the datatype of the message and process it. ## The API A Hub also stores messages in a local database. We can use a HTTP and gRPC API to leverage that storage to query this data. On top of just querying that on Hub storage we can also let our Hub do verification of Messages to check if it is valid. Leading to three functions of the Hub API 1) Check status and get diagnostics of our Hub 2) Query data on our Hub (messages) 3) Use the Hub for verification ![hub api visualized](../../../../assets/images/docs/farcaster/hubs/hubAPIvisualized.png) A full API reference is available on the Farcaster documentation: - [Hub HTTP API Reference](https://docs.farcaster.xyz/reference/hubble/httpapi/httpapi) - [Hub gRPC API Reference](https://docs.farcaster.xyz/reference/hubble/grpcapi/grpcapi) ## The Database To store data locally each Hub has it's own local database (implemented with RocksDB [here](https://github.com/farcasterxyz/hub-monorepo/tree/main/apps/hubble/src/storage/db)), but while this should not be relied up on as a permanent highly available datasource we can work directly against it. It's just a database, so use SQL and write queries to get data directly if you do not want to depend on the [APIs](#the-api) --- --- Title: Mini Apps vs Farcaster Frames, what's the difference? --- # Mini Apps vs Farcaster Frames, what's the difference? # What is the difference between Mini Apps and Farcaster Frames? First we from a user experience side mini apps are way more capable then frames, but that also means we need to build them way better! If we don't, then users will be confused and not use your app. With Frames the user experience is clear, there is an image (or animation) shown, up to four buttons to press and optionally an input field. As long as the developer makes sure the Frame returns fast, then the Frame experience is clear to the user and fast! In case you're Frame isn't fast enough, [let us help you!](https://dtech.vision/) Though what do these differences mean for the experiences we can build? ## What can we build with Farcaster Frames? We think of Farcaster Frames as a Gameboy without Audio support. If you want to learn frames, check our [Frames Documentation](/farcaster/frames/). Just imagine what you can do with a Gameboy. Pokemon and other incredible Nintendo games only required a few buttons and a clunky image in the middle to be fun. Frames are similar in that you need to get creative to really make a consumer experience fun, though you may not need to make it fun. You may just need to make it useful. How can Frames be useful? They are right there with the user! The user doesn't need to visit your webpage, they don't want to. You can remove friction by letting them complete actions right from another app. This removed friction will increase conversion rates for you, leading to more business and happier users. ## What can we build with Mini Apps? Mini Apps are apps within an app. We can build everything we could have a webpage do. There may be some restrictions depending on where the mini app is present, but that's generally how to think about it! This means we can do everything! yep everything. We could have our whole ecommerce page present within the app and the user can just access it right there without having to leave their favorite platform. We could also build full fleged 3D games and anything in between. Within Farcaster the great part about mini apps is, they allow you to prompt the user to post! That provides the chance for the best ad you can get, a user referral! ## How does the user experience differ between Mini Apps and Farcaster Frames? Mini Apps open pandoras box, while Frames set a clear guideline where the user knows what to expect. With Frames directly in the Feed of users, they know how to interact with it and it looks just like the app they already have open. On the other hand mini apps open a seperate webpage that you have built, which means you have to ensure the user feels welcome and has a pleasant experience. The main challenge in Frames is that everytime the user does something you have to respond from the server side very fast! if you don't design around this the experience will be clunky and slow. With Mini Apps you are providing a full blown web application, where you may not need any server interaction besides initially serving the page, which could be way faster and provides more surface for interaction. As a user in the end my experience is dictated by how easy it is to achieve my intended goal. That goal usually is entertainment or getting something done. As developer we can provide a great user experience in both form factors, we just need to make sure we know how to do it. ## Are Farcaster Frames and Mini Apps the same technically? No they are not. Frames call your backend server everytime the user does anything, while mini apps serve a web application where you are in control of how many times your backend is called or not. Let's look at the differences ### The technical difference between Farcaster Frames and Mini Apps The technology used to build Frames is any server that returns HTML Meta tags as covered in [How Farcaster Frames work](/farcaster/frames/howdofarcasterframeswork). While Mini Apps are regular web requests! Yes the Farcaster client will ask your webpage to be rendered and you return the webpage. If your webpage then calls some API or something else, then this is on you. While with frames the Farcaster client calls your backend each time! This also means that with Mini Apps you can generate a session token at the beginning for the user, which serves as authentication but then everything is client side. With Frames since the interface is so restricted and each action is sent to the server firstly you have server side analytics and no blocker so you can track all actions , but secondly you have authentification through the user's farcaster account and signer each time! If you want to authenticate the user in a mini app the first request to ask for which webpage to be served is the Farcaster client sending your server a message and asking it. Here it sends a Frame message that you can authenticate as shown [in this tutorial](/farcaster/frames/authentication-from-frames-with-nextauth/). From there on all actions are client side and could be manipulated unless you send API requests somewhere. That could make a difference for games, as the user may spoof the game score on the client side and cheat, unless you synchronize the game state with your server to ensure no cheats can be done. But as you already noticed how to build mini apps is the same as building a responsive web application. The only difference is you need to respond to a GET request about metadata, and a POST request to then link to the mini app webpage. How to handle these two requests is [explained here](/farcaster/miniapps/quickstart-farcaster-composer-actions/) ### Why is there more latency in Farcaster Frames? With Farcaster Frames the client sends each user action to the server and waits for the response. This roundtrip between server and client takes time, which is latency. There also maybe some complex action going on the server before it returns. As a developer your goal is to minimize this time delay as much as possible! Here is a visualization of how many roundtrips (server to client communications) are down in Frames vs Mini Apps. It's pretty clear how big the difference is. ![Server Roundtrips in Frames vs Mini Apps visualized](../../../../assets/images/docs/farcaster/miniapps/frameVSminiapp.png) --- --- Title: How to develop onchain Farcaster Mini Apps with wallet connect and transactions --- # How to develop onchain Farcaster Mini Apps with wallet connect and transactions > We assume you already know what Mini Apps are, [else learn what Mini Apps are here](/farcaster/miniapps/). > Additionally it's assumed you know how to build Web3 Applications. If not [speed run Ethereum](https://speedrunethereum.com). You've built a website that accepts transactions, sends transaction or interacts with users wallets somehow. If not you're building one. Now to access the large onchain userbase on Farcaster you intend to turn it into a Mini App. Anyone will then be able to open your webapp right from their favorite social media app. Yes that means you're technically in the AppStore as some Farcaster clients are! How cool is that!? ## How to build an onchain Mini App ### 1: make it a Mini App via metadata First we need to set the Metadata. It is required for the Farcaster clients like Warpcast and Coinbase Wallet to recognize our webpage as mini app and render it. Full tutorial on setting the metadata Next we need to add the WalletConnect connector to our website. This will allow users to connect their wallets to our mini app. Farcaster clients inject a provider into your webpage, similar to browser extension wallets, that allows you to interact with the blockchain. ### 2: add the ability to connect wallets Full tutorial on adding WalletConnect Putting both together your onchain app works in Farcaster clients and you can interact with the users wallets. For a sample of such an app, check the [Morpho Deposit application we've built](https://dtech.vision/blog/casestudy-farcaster-defi-frame-morpho). 20 seconds to deposit into a lending protocol from your social media feed. --- --- Title: Wallet Connection in Mini Apps (wagmi) Description: Learn how to let users connect wallets instantly in Mini Apps --- # Wallet Connection in Mini Apps (wagmi) > We assume you are familiar with what a [Farcaster Mini App](/farcaster/miniapps/) is. Additionally this guide assumes you are familiar with wagmi.sh. > In case you are not [learn how to build onchain Mini Apps and do transactions](/farcaster/miniapps/howtobuildonchainfarcasterminiapps/). ## Enable Wallet Connections to your web application from Farcaster Mini Apps --- ### 1. **Install the `@farcaster/miniapp-wagmi-connector` package.** ### 2. **Add the connector to your website** If you already have an app built with wagmi, then you can add the connector directly: ```typescript // other imports export const wagmiConfig = createConfig({ // your wagmi config connectors: [ farcasterFrame(), // your other connectors... ], // rest of your wagmi config }); ``` If not you will want to walk through the [wagmi setup guide](https://wagmi.sh/) and then add the farcasterMiniApp Wagmi connector for your Mini App to have connected wallets. ### 3. **Use your wagmi config** With the Farcaster Mini App connector available you can now use the wagmi config in your Mini APp. This is no different then any other wagmi application. For references to how one can build such Mini Apps or wagmi apps please check the wagmi docs and more Farcaster Mini App docs available [here](/farcaster/miniapps). To learn about testing your Farcaster Mini App with connected wallet refer to the [Farcaster Mini App Testing Guide](/farcaster/miniapps/theultimatefarcasterminiappdebuggingguide). More detailed steps can be found [in the Mini App + Webapp Wallet connect tutorial](/farcaster/miniapps/howtosetupwalletconnectinminiappsandonthewebsite). ## Bonus: Make sure you are on the right chain in your Mini App transactions You may face issues where the chain you expect it to be is not the one the users wallet is. Using the chainId we can detect the current chain and switch to the one we need. Wagmi with the `useChainId` and `useSwitchChain` hooks provides a way to switch chains and monitor everything we need. In a react based project the following effect does the trick: ```typescript // your function / component export default function YourComponent() { const chainId = useChainId(); const { switchChainAsync } = useSwitchChain(); useEffect(() => { const switchToBase = async () => { if (chainId !== base.id) { try { await switchChainAsync({ chainId: base.id }); } catch (error) { console.error("Failed to switch to Base:", error); } } }; switchToBase(); }, [chainId, switchChainAsync]); return ( // some JSX ) } ``` --- --- Title: How to deep link directly into a Mini App Description: Learn how to create links that open your mini app directly in Farcaster. --- # How to deep link directly into a Mini App >This guide assumes you know what [Farcaster Mini Apps](/farcaster/miniapps/) are and have already built one. If you haven't built a Mini App yet, check out our [Quickstart Guide](/farcaster/miniapps/howtobuildonchainfarcasterminiapps/). Or you can use an existing Mini App url. ## Deep linking to Farcaster Mini Apps You can create links that open your Mini App directly in Farcaster clients like Warpcast. This is useful for: - Sharing your Mini App via regular links - Creating marketing campaigns that link directly to your Mini App - Building viral loops where users can easily share your Mini App ## How to create a Mini App deep link The deep link URL structure follows this format: ``` https://farcaster.xyz/~/mini-apps/launch?domain=dtech.vision ``` Where `dtech.vision` is the domain hosting your Mini App. >**IMPORTANT**: Do not include `https://` in the domain parameter. For example, `https://dtech.vision` should be entered as `dtech.vision` The Mini APp will be launched using the information in your Mini App manifest, specifically using the `homeUrl` defined in your `.well-known/farcaster.json` file. If you don't know what that is read all about [Farcaster Mini App Manifests here](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/). Deep links provide a way to directly launch your Mini App experience from anywhere - whether that's a website, social media post, or another Mini App. Just remember to: 1. Use the correct URL structure 2. Omit `https://` from the domain parameter 3. Ensure your Mini App manifest is properly configured at `.well-known/farcaster.json` --- --- Title: Turn your Website/app into a Mini App on Farcaster! Description: Learn how your website becomes a Farcaster Mini App quickly --- # Turn your Website/app into a Mini App on Farcaster! import { Aside, LinkButton, Steps, Tabs, TabItem, } from "@astrojs/starlight/components"; > We assume you are familiar with what a [Farcaster Mini App](/farcaster/miniapps/) is. Additionally nothing more then minimal frontend development experience is assumed, we'll go through each individual step. ## Make the website render large with custom preview and action buttons We can have our website render as a Farcaster Mini App, so it shows up with our own big preview image and call to action button. The button will open a webpage in app, so we can use this either to show the URL shared or use it as call to action for marketing or lead generation. A pure HTML simplest Mini App Github Repo to see it all tied together is available [here](https://github.com/dtechvision/simplest-farcaster-frame). To achieve our website being embedded into Farcaster client's like Warpcast nicely follow these steps 1. **Provide the metadata about the app/website in the HTML metadata:** The Mini App metadata is typed in JSON which we will fill in with our information. Please ensure the following properties about your metadata - imageUrl image is 3:2 format less than 10mb in size - the title as well as name are less than 32 characters - the splashImageUrl must be 200x200px and less than 1MB The full type specification can be found in the [Mini App specification](https://github.com/farcasterxyz/protocol/discussions/205) The metadata JSON looks as follows: ```json { "version": "next", "imageUrl": "https://dtech.vision/frame-thumbnail.png", "button": { "title": "Learn Farcaster", "action": { "type": "launch_frame", "name": "dTech - Farcaster Boutique", "url": "https://dtech.vision/farcaster/", "splashImageUrl": "https://dtech.vision/icon.png", "splashBackgroundColor": "#f7f7f7" } } } ``` The valid JSON is added to the HTML head section ( in Javascript/Typescript you may define it as a variable and then use JSON.stringify ). ```html ``` 2. **Add a .well-known/farcaster.json file to your website** To generate the accountAssociation we will use the **Warpcast mobile app** (though if you have a valid signer you could also do this on your own) > NOTE: Make sure you're in the mobile app! In Warpcast navigate to `Settings > Developer > Domains` where you can type in your domain e.g. _dtech.vision_ to generate the domain manifest. If you don't see the Developer menu, you may need to scroll down or go to `Settings > Advanced > scroll down and enable Developer mode`. Clicking generate domain manifest will give use the accountAssociation section and potentially a sample mini app section we can paste and adjust to our needs. One such sample is as follows: ```json { "accountAssociation": { "header": "", "payload": "", "signature": "" }, "frame": { "name": "dTech - Farcaster docs", "version": "0.0.1", "iconUrl": "https://dtech.vision/icon.png", "homeUrl": "https://dtech.vision/farcaster", "splashImageUrl": "https://dtech.vision/icon.png", "splashBackgroundColor": "#f7f7f7", "webhookUrl": "https://dtech.vision/webhookurl" } } ``` 3. **Call the mini app SDK's ready() function to hide the loading screen** The mini app SDK will show a loading screen until the mini app is ready to be displayed. To hide this loading screen, call the `ready()` function. To get there we need to use the Farcaster Mini App SDK. If we have a Typescript (or JS) project we can install and import the [SDK](https://www.npmjs.com/package/@farcaster/miniapp-sdk). In case we don't, we can **embed the script directly**: ```html ``` now that ready is called, the loading screen will be hidden and your webpage accessible via Farcaster Mini App - your done! For **users of Typescript** (or JS) we install the mini app sdk now on our page we call the ready function. The following is a simplified visualisation of a Hello World page, adapt this to your application. ```typescript export function Home() { sdk.actions.ready(); return
Hello from dTech!
; } ``` Try loading your website as Mini App in the [developer playground](https://farcaster.xyz/~/developers/frames) after deploying it. A more robust useEffect based setup that most React developers will be familiar with is as follows for you Index page: ```typescript export default function Index() { const [isSDKLoaded, setIsSDKLoaded] = useState(false); const [context, setContext] = useState(); // Farcaster Mini App Integration (see https://dtech.vision/farcaster/) useEffect(() => { const load = async () => { setContext(await sdk.context); sdk.actions.ready(); }; if (sdk && !isSDKLoaded) { setIsSDKLoaded(true); load(); } }, [isSDKLoaded]); // end Farcaster Mini App Integration return
Hello from dTech!
; } ``` In the above sample we're using the Mini App context and setting it too! It's powerful data tracking where the Mini App was opened from and which user opened it, what post they saw or if they clicked on a notification and more.
Did you know you can also take that HTML and throw it into a code widget? you maybe able to turn your web2 stack into a mini app with it! A pure HTML simplest Mini App Github Repo --- --- Title: Make Wallet connect outside the Farcaster Mini App (wagmi) Description: Learn how to make your Farcaster Mini App wallet connect work on the website too. --- # Make Wallet connect outside the Farcaster Mini App (wagmi) > We assume you are familiar with what a [Farcaster Mini App](/farcaster/miniapps/) is. Additionally this guide assumes you are familiar with wagmi.sh. > In case you are not [learn how to build onchain Mini Apps and do transactions](/farcaster/miniapps/howtobuildonchainfarcasterminiapps/). We know Mini Apps are web applications that run in Farcaster clients like Warpcast. But what if we want to use the same wallet connection logic on our website? Many mini apps are just built for Farcaster clients like Warpcast, but could be lovely mobile first experiences. We can enable that by allowing users to bring their wallet from outside Farcaster on direct page visits too.
prompting the user to connect a wallet outside the Mini App Access wallets directly in Farcaster Mini Apps
## Prerequisites You have built a [Farcaster Mini App](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/). If your mini app does not have wallet connection functionality right now, skip over the existing mini app migration code in this guide and go straight to the migrated RainbowKit and Farcaster mini app setup. ## Enable Wallet Connections beyond the Farcaster Mini App In the Farcaster Mini App we use the `@farcaster/miniapp-wagmi-connector` to connect wallets. That isn't available when accessed outside of Farcaster Mini Apps. The connector is only use by the Farcaster clients to plug the users wallet right in for us to use. On our webpage, we add it ourselves. To add the wallet ourselves there are many options of connectors we can use. Wagmi, the underlying library we use, has a lot of options documented [here](https://wagmi.sh/react/api/connectors/). Since that does not give us a user interface right away, we'll use a ready made UI for this guide. ### 1) Install RainbowKit ### 2) Add the RainbowKit Wallet Connector to your website Now that we have RainbowKit installed, we need to integrate it with your webpage. This may be a bit different depending on your setup, but here is a general guide. To learn more refer to the [RainbowKit Docs](https://www.rainbowkit.com/) as well. Our mini app will have a `WagmiProvider.txx` or similar file setup that is adding the miniappConnector. We will adapt this to use Rainbowkit. Enabling us to connect all kinds of wallets outside of Farcaster mini apps too. Turning our mini app into a full onchain app. ```typescript // WagmiProvider.tsx export const config = createConfig({ chains: [base], transports: { [base.id]: http(process.env.BASE_RPC_URL), }, connectors: [farcasterFrame()], }); const queryClient = new QueryClient(); export default function Provider({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` In case you don't have one just create the file. We'll update the `WagmiProvider.tsx` to use RainbowKit instead of the Farcaster Mini App connector. We are adding the Mini App Connector a bit later. We are using the default WagmiConfig provided by the RainbowKit Documentation. ```typescript import "@rainbow-me/rainbowkit/styles.css"; export const config = getDefaultConfig({ appName: "Morpho MiniApp App", projectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || "", chains: [base], transports: { [base.id]: http(process.env.BASE_RPC_URL), }, ssr: true, }); const queryClient = new QueryClient(); export default function Provider({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Make sure that your default app/page (could be layout.tsx or index.tsx or app.tsx depending on your Framework) is wrapped in the Provider! For a remix RootLayout this could look like the following layout.tsx ```typescript //... metadata and others export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {children} ); } ``` Now to add connection functionality to our app in the page/app we use the ConnectButton provided from RainbowKit. The ConnectButton handles the Wallet connection for us outside of the Farcaster Mini App. We'll later see how we can automatically ensure the FrameConnector is used when we are inside a mini app using React (which you could adopt to any other Framework). ```typescript // your imports //your logic export function Home() { return (
// your user interface // more userinterface
); } ``` In a live App this may now look something like our nice localized german wallet connect button example below: WalletConnect Button outside the Mini App Now the last step is to ensure that in mini apps we always connect. And when connected we don't show the wallet connect button again. We only need it when not connected right? ### 3) Ensure the Mini App connector is used when available & Web Wallet Connect Button hidden To make sure that when we are in the mini app we use the mini app connector and when we are on the website we use the RainbowKit connector we add some logic. Let's use Wagmi to tell us if there already is a user connected or not. The `useAccount()` hook does that for us. If there is no account we show the ConnectButton. The variable `isConnected` is a boolean that tells us if the user is connected or not. If we conditionally render the ConnectButton based on it, we know it only shows if there is no wallet connected. ```typescript // imports // your logic const { address, isConnected } = useAccount(); // your user interface { !isConnected && ; } // your user interface ``` Now we have a webpage that lets people use what also is intended to be inside a Farcaster mini app. What is missing is the mini app wallet connection itself. Let's add back the Farcaster Wallet connector. We can do this by manually using the `frameConnector` in our page. Likely your existing logic in react looks like the following to call the Farcaster Mini App SDK ready function. ```typescript useEffect(() => { const load = async () => { setContext(await sdk.context); sdk.actions.ready(); }; if (sdk && !isSDKLoaded) { setIsSDKLoaded(true); load(); } }, [isSDKLoaded]); ``` By adding the following effect to the code we ensure that when we are in a Mini App we connect the Farcaster Mini App connector wallet. It also handles calling ready on the sdk. If we don't call ready, the mini app will be stuck on the loading screen. To get the context, which has information about where the mini app was accessed from, we use the sdk and store it in state. ```typescript // imports // your code const [isSDKLoaded, setIsSDKLoaded] = useState(false); const [context, setContext] = useState(); // an effect to ensure if we are in mini app context we connect the FarcasterFrame connector wallet useEffect(() => { const load = async () => { sdk.context .then((context) => { console.log({ context }); if (context) { setContext(context); connect({ connector: farcasterFrame(), chainId: base.id }); sdk.actions.ready(); } }) .catch((err) => { console.error(err); }); }; if (sdk && !isSDKLoaded) { setIsSDKLoaded(true); load(); } }, [isSDKLoaded]); // rest of your code ``` ### Bonus: Advanced Implementation of Mini App Wallet connect/disconnect and webpage Wallet Connect >NOTICE: Since the mini app host (Farcaster client) announces the injected provider, we likely are fine without adding back the connector. >RainbowKit should work out of the box without additional config as long as you include the injected provider. You may have noticed that we have no logic to prompt the user for connecting the wallet, well we don't because we automatically connect in Mini App or show the wallet connect button right? but what if we want to have the user take action and delay the need to prompt for the friction of connecting a wallet? Then we want to know if we are connected at a later stage. Such a component could look like the following code, where we check if we are connected, if we are outside a mini app and tell the user to use the ConnectButton. If we are in a mini app, we use the Farcaster Mini App native connect/disconnect functionality. This is enabled by Wagmi. The state and connect flows are explained previously, so we only show the relevant component here. ```typescript // rest of your imports const { disconnect } = useDisconnect(); const { connect } = useConnect(); // rest of your code { !isConnected ? ( !context?.user ? (
To transact connect a wallet by clicking below
) : (

Connect Wallet

) ) : (
Your Code/Component for when wallet is connected aka your onchain flow
); } // rest of your code ``` --- --- Title: Start Here Farcaster Mini Apps --- # Start Here Farcaster Mini Apps > How do you develop on top of the Farcaster protocol? Dive in. Focus is on giving you just as much documentation as needed as well as conceptual overviews and examples. > We link out to detailed project documentation where used. ## What are Farcaster Mini Apps? Mini Apps enable developers to distribute native-like applications directly to Farcaster users. They are the easiest way to deliver engaging, high-retention, and easy to monetize applications. How do they work? Mini Apps integrate seamlessly with the Farcaster clients - the apps everyone uses to post on Farcaster and consume content. While traditional social media limits you to basic media uploads and posting, Mini Apps unlock endless possibilities. Notice the three colorful items at the bottom here? These are buttons to open Mini Apps right from creating a post in Farcaster clients! ![Warpcast Cast Composer Screenshot](../../../../assets/images/docs/farcaster/miniapps/warpcastComposer.png) ### Mini Apps are accessible everywhere! Mini Apps are not limited to when creating new posts, though that helps to go viral and is a cool feature. Mini Apps are available everywhere in the Farcaster clients and can be opened from the feed, direct messages, all kinds of posts anywhere and even from these context menus like the cast comopser we just looked at! ### Key benefits of Mini Apps include: - **Ship Fast**: Go from idea to users in hours with no app store reviews needed. Build with standard web technologies and use the Mini App SDK. - **Easy Discovery**: Social feed integration puts your app just one click away from users, with viral growth built in. - **User Retention**: Mobile notifications re-engage users, and favorites make Mini Apps always accessible. - **Seamless Transactions**: Built-in Ethereum wallet support enables one-click payments and transactions. - **Social by Default**: Users are automatically signed in, giving you access to rich social data. ## What can we do with it? Mini Apps enable far more than just social media post enhancements. You can build fully-featured applications that: - Create interactive games that share high scores - Enable commerce with social sharing incentives - Access user feeds and social data - Provide utilities like spell checking and media search - Generate AI-assisted content - And much more The only requirement is that your app works well on mobile screens - otherwise, the possibilities are endless. Mini Apps represent a new paradigm for social app development. ## How to build Mini Apps? - [Quickly convert your webpage into a Mini App](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/) - [Develop onchain mini apps with built in wallet support](/farcaster/miniapps/howtobuildonchainfarcasterminiapps/) --- --- Title: Quickstart Composer Actions 101 --- # Quickstart Composer Actions 101 ![Working Ice Cream Picker Cast Action Video](../../../../assets/images/docs/farcaster/miniapps/quickstart-form-warpcast-working.gif) ## Quickstart your Setup Getting setup is as simple as running the command below. Simply copy & paste into your terminal. ```bash git clone https://github.com/dtechvision/fc-composer-actions/ composer-action && cd composer-action && npm install ``` We are cloning the dTech starter/example for Composer Actions which uses NextJS and getting the dependencies installed so we're good to go. You could deploy this publicly or test locally already. On deployment and testing in Warpcast you would see a Composer Action where on clicking the button you are sharing a predefined post. ![A sample Farcaster Composer Action](../../../../assets/images/docs/farcaster/miniapps/quickstart-debugger-preview.png)
## Shipping custom Composer Actions Let's get Snake in there. It's a fun game and I would love to play it from my social media feed and brag about no one beating my score! Wait we can do that? Yes. Composer Actions aren't limited to Forms where you input data and they then generate a new post for you, but that we can do as well. So let's start simple and move to Snake. ### A simple post template form as Composer Action A simple post template takes in some user choices and fills the post. What we will do is ask for you three favorite ice cream flavors and publishing an engaging post showcasing that. Of course you can build Issue templates for bug reports, customer feedback, input into your application, game interactions or much much more. To start understand where to start look at the layout of the template ``` | |__ /src/pages/api/compose.ts | |__ /src/pages/index.tsx ``` The API Route in `compose.ts` defines what the Farcaster client (e.g. Warpcast) will show as Composer Action. Looking at the code we see a type, title, url being returned. Which the client will use and show that URL to the user. Additionally if there's a GET request sent then we return the metadata about our Composer Action. This information is shown to users seeing our Action when browsing all registered actions or seeing previews. Make sure the image is square, no more than 100x100px. ```typescript if (req.method === "POST") { const data = req.body; console.log(data); res.status(200).json({ type: "form", title: "dTech.vision", url: "http://localhost:3000", // make sure this is your public URL e.g. http://localhost:3000 for local testing }); } else if (req.method === "GET") { res.status(200).json({ type: "composer", name: "Create Poll", icon: "check", // supported list: https://docs.farcaster.xyz/reference/actions/spec#valid-icons description: "Create a poll frame", aboutUrl: "https://your-app-server.example.com/about", imageUrl: "https://your-app-server.example.com/static/logo.png", action: { type: "post", }, }); } ``` the other file `src/pages/index.tsx` has our user interface. This is what the user sees. The Farcaster client knows what to show by our API Route and the user flow. ![Farcaster Composer Action Flow visualized](../../../../assets/images/docs/farcaster/miniapps/composerActionSimplified.png) Now remember this could be an external page as well. Then it's just shown and if it doesn't return a new cast well then you just have a mini app in the Farcaster client. But we're building an ice cream flavor picker to share as cast. What we need to let the Farcaster client know that the user is done in our app and what the new cast is push the new cast. This is done by the following code snippet in the example `src/pages/index.tsx`. The snippet pushes to the Farcaster client the JSON data and information that this "createCast" is our new post. ```typescript window.parent.postMessage( { type: "createCast", data: { cast: { text: "dTech is the best!", embeds: ["https://dtech.vision"], }, }, }, "*" ); ``` Now as seen in the sample this is part of a button so the user triggers it, though anyway we invoke this we prefill the cast and we can have embeds like images, videos and other media in there too! To build our Form we now need to build a Form UI and prefill the cast text with the variables selected in our Form. We will change the `

Hello main test?

` part of the sample and exchange it with our form. ```typescript // src/pages/index.tsx const inter = Inter({ subsets: ["latin"] }); export default function Home() { const [selectedFlavors, setSelectedFlavors] = useState([]); const handleFlavorChange = (flavor: string) => { if (selectedFlavors.includes(flavor)) { setSelectedFlavors(selectedFlavors.filter(f => f !== flavor)); } else if (selectedFlavors.length < 3) { setSelectedFlavors([...selectedFlavors, flavor]); } }; const flavors = [ "Chocolate", "Vanilla", "Strawberry", "Mint Chocolate Chip", "Cookies and Cream", "Rocky Road", "Butter Pecan", "Coffee", "Pistachio", "Salted Caramel" ]; return (
{/* ... (previous code remains unchanged) */}

Select your three favorite ice cream flavors:

{flavors.map((flavor, index) => (
handleFlavorChange(flavor)} className="mr-2" />
))}

Selected: {selectedFlavors.length}/3

// ... ) ``` If you wish to test your user interface remember it is a full blown webpage. `npm run dev` and navigate to your browser. ![showcase of simple form](../../../../assets/images/docs/farcaster/miniapps/quickstart-form-showcase.png) What is missing? We have not adjusted our Submit Button to make sure that the ice cream flavors are reflected. Right now we still have the predefined message. Let's change that by first remembering what we picked, and then making sure that it is used in the template. The following code is an excerpt, merge it with the above adjustment and you're good. First adjust the button to use a handleShare button and only work if the user selected all three of his favorite flavors. ```typescript ``` Now we add the handleShare function above our return statement and below the flavors ```typescript const handleShare = () => { const [first, second, third] = selectedFlavors; const castText = `My favorite ice cream flavors🍦 are: ${first}, ${second}, ${third} and I love being able to use Cast Composer Actions built by dTech for my business!`; window.parent.postMessage( { type: "createCast", data: { cast: { text: castText, embeds: ["https://dtech.vision"], }, }, }, "*" ); }; ``` Remember it's just a webpage, so if we log the castText variable using `console.log(castText);` before the window.parent... we can see in our browser at localhost that it works. ![Working Form cast composer action](../../../../assets/images/docs/farcaster/miniapps/quickstart-form-localhost-working.png) What is left? We need to deploy it #### Deploying our cast action Once you have it up (works anywhere you can deploy NodeJS), copy the link of your api endpoint (/api/compose) and paste that into the [Warpcast Debugger](https://farcaster.xyz/~/developers/composer-actions?name=dTech) Type something in the cast composer and click Test Action. You'll see you working cast action producing the Ice Cream Flavors ![Working Ice Cream Picker Cast Action Video](../../../../assets/images/docs/farcaster/miniapps/quickstart-form-warpcast-working.gif) ### Snake as Composer Action sharing Scores Coming Soon! --- --- Title: How to test & debug Farcaster Mini Apps --- # How to test & debug Farcaster Mini Apps > We assume you already know what Mini Apps are, [else learn what Mini Apps are here](/farcaster/miniapps/). ## How to locally test and debug Farcaster Mini Apps Install your local debugger: ``` npm install -g @frames.js/debugger@latest ``` then run it ``` frames ``` Now in your browser you should see the debugger at `http://localhost:3010` ![local framesjs debugger for Farcaster Mini Apps](../../../../assets/images/docs/farcaster/frames/framesv2localDebugger.png) In case you have errors and bugs in your Farcaster Mini Apps you can use the local debugger to see what's going on. The following image shows the local debugger in action with the right side showing an error in our fc:meta metadata: ![local framesjs debugger for Farcaster Mini Apps](../../../../assets/images/docs/farcaster/frames/framesv2localDebuggerError.png) ## Using Testnets For use with Testnets make sure to fund your wallet with testnet tokens. To get the address you can open the wallet, press recieve and see the list of addresses for EVM as well as Solana and all other supported networks. Click copy and use a faucet or wallet with testnet funds to ensure your wallet is funded for doing transactions during testing.
## Warpcast Debugger https://farcaster.xyz/~/developers/ Use cloudflare tunnels to get your localhost to the internet allowing you to test in Warpcast. > **NOTE**: Cloudflare Tunnels can be spun up without having an account! To use cloudflare tunnesl as they have a generous free tier you can follow [this docs page](https://developers.cloudflare.com/pages/how-to/preview-with-cloudflare-tunnel/). Cloudflare earns a lot of money on being a huge backbone of the internet, that's why they can offer such extensive free tiers. On Mac to install cloudflared and expose `localhost:3000`: 1. `brew install cloudflared` 2. `cloudflared tunnel --url http://localhost:3000` When looking at the Warpcast debugger below you see that there's a Preview of your Mini App where you can see how it will look when the link is shared. Then you can click the button and see the Mini App in action. There are also **Demos** which you can check out to see how Mini Apps can be used. At the bottom the "Launch Mini App" section you can use to see different Splash Screens by changing the splash image url and splash background colors. Be aware that not setting these two means your Mini App won't show them when it loads, so test these with the preview -> clicking the button. On mobile this order may be flipped! You want to use the section where you can change splash screen too, else it likely uses your production domain unless you changed the [manifest](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/) too! ![Warpcast debugger for Farcaster Mini Apps](../../../../assets/images/docs/farcaster/frames/framesv2WarpcastDebugger.png) TODO: Mobile debugging kompreni https://www.snippets.so/snip/lukClH6Eri https://farcaster.xyz/deodad/0x9bd2796c --- --- Title: Build on Farcaster: The Ultimate 101 Resources To Learn Farcaster! Description: This guide will show you how to go viral on Farcaster, what tools exist and how to combine all of them for agents, mini apps and general successful apps! --- # Build on Farcaster: The Ultimate 101 Resources To Learn Farcaster! > Note: You do not need to know anything about Farcaster to understand this article. You'll be taken through the basics and learn how to get started with Farcaster. What even is Farcaster? It's a programmable paradise of a social network. It truely is a social network in that conversations and connections are deeply encouraged and part of the culture. Therefore users love their conversations and chatting with each other. Farcaster users are very active with their crypto wallets and do lots of blockchain transactions, making them an ideal first user group as even outside of blockchain technology they are first adopters and provide a lot of feedback on how to improve the product. Builders waste weeks wiring brittle APIs before a single user sees value. Farcaster now collapses identity, data, and distribution into one programmable network so you ship in days, not months. > “Farcaster let us put a wallet-to-feed loop in production over one weekend.” — Mini App builder ### Product snapshot, what Farcaster offers - Identity rails: Sign In with Farcaster, Quick Auth, and App Keys give you verified users without seperate OAuth screens or leaving the flow. - Data plane: Farcaster protocol's Snapchain replicates every cast, reaction, follow graph, so you can query or post without rate limits. - Distribution switchboard: Mini apps, share extensions, bots & agents, intents, and direct casts give you three surfaces—feed, inbox, notifications—out of the box. - Integrations: allow you to build your own tooling and standalone apps outside of the Farcaster clients, while you can also build your own client integrating mini apps and more. so who is this guide for? ## FAQ — Working backwards from the builder ### 1. Who is the builder we serve first? We prioritize teams shipping interactive social, commerce, or agent experiences that need repeatable loops, not one-off campaigns. If you already have a web stack, you bring it. Farcaster provides identity, data, and delivery. Mini Apps in the end are simply web apps. ### 2. Why do mini apps deliver distribution instantly? Mini apps are HTML/JS apps rendered inside Farcaster clients. They inherit the host’s identity context, so a tap immediately reveals `fid`, username, wallet, and notification tokens. No install wall. No SDK fork. They can be launched from feeds, direct casts, share sheets, or universal links. All tied together by the Farcaster mini app sdk with great samples available on dTech's Github Account. ### 3. How do we post to Farcaster and Farcaster protocol Snapchain without OAuth 2.0? Farcaster uses signers. You can use one [Signer](/farcaster/hubs/howdosignerswork) that you registered yourself or request an App Key from the user, store it, then send signed writes onbehalf of the user. Quick Auth gives you a JWT that maps to the user’s FID. Every cast settles on Snapchain and is available to any client within seconds. The permissions are Signer managed and registered on the blockchain. No heavy OAuth setup flows needed, that need to be synced across the ecosystem. ### 4. What loops should marketing teams run on Farcaster? We run three loops: acquisition (feed + share extension), activation (mini app session + direct cast or notification), and retention (automated agents that respond with fresh embeds). Each loop shares the same or similar content primitives so we can reuse assets like graphics and general text outlines. ### 5. Which metrics tell us it is working? Track leading indicators. Mini app launches, add-to-collection events, App Key grants if you ask for your own signers to post on behalf of users, DM reply rate, and Snapchain write success. With notifications you can also track the openrate of notifications down to the individual notification. We have built a tool at dTech our customers use to see all these metrics on one dashboard. Reach out if that's interesting to you too. ### 6. What are the failure modes and how do we prevent them in development? Failure modes: unsigned manifests, missing `sdk.actions.ready()` calls in mini apps, tunnel domains used when prod should be used and vice versa, stale embed images, and unverified notification tokens. We prevent them with automated linting for manifests, `sdk.actions.ready()` gating, production-only flows, dynamic images with `max-age` headers set, and webhook verification via `@farcaster/miniapp-node` and onchain signer checkups so we minimize external dependencies. ## What can you do on Farcaster that you can't do elsewhere? Farcaster is fully programmable! You don't get that elsewhere! Especially not the combination of social x wallet aka social data and financial data. Interactions between user's social media account and their wallets directly in your app! But what does programmable mean? It means that you can programm most if not all parts of it! You can create your own chatbots, automate your customer support or outreach, you can scan and analyze all content on the platform and you can even create your own social network on top of Farcaster. Yes since all follow relationships are also public, you can read them and use them in your app. Need to reach a user? send them a direct message automatically. It's all programmable whether you want to write or read! ### Build fully integrated tools that you would otherwise have to ship in a separate app If you use mini apps, you already have a mobile app in App Stores via existing Farcaster clients showing your webapp on mobile natively! Meaning your app gets App Store distribution for free! Add that these clients show Farcaster posts and have direct messaging as well as in feed interactive apps. This way you can build a fully integrated experience that you would otherwise have to ship in a seperate app as Twitter etc, don't allow it. One such sample is paybot. It's an app to pay people. Typically you go to the groupchat, see messages that one should be paid and ask for a payment method or they send a link somewhere. With Paybot this isn't needed anymore. You are on a Farcaster client, open the mini app, create payment links and use that to pay with a transaction from your wallet. Easy as that. Oh and if you want to request funds, same flow just hit request. It's all within the Farcaster clients and integrated well. That's not happening anywhere else! Here is a sample video showing how you can go from the app to open paybot and get a mini app to pay someone. It is all within the app and you don't have to leave it. The above sample of doing it in replies also works as it opens the app via composer action, that we will see further along this article. ### Using the open data and programmability to bootstrap your network based app Say you want to build a new short form video app. Then what you have to do is get new users in and post content. If you don't have enough views the creators, won't care and if you don't have enough creators the viewers won't care. It's a chicken and egg problem. But on Farcaster you can solve this by creating a mini app that allows users to post and use your app to do it (one example is Face filters for videos to be posted). This way you can bootstrap your app with content and users from Farcaster. This is only possible on Farcaster and not on Twitter or any other web2 platform, since they don't have mini apps. Oh and they don't allow you to read all content so you can not just build your own social media app off of their content. Another way you can achieve it is by using the open social data. You can index all videos people already post to Farcaster clients and that way bootstrap your app's content. Addiitonally if any of these users with a Farcaster account sign up, use their follows and already existing relationships to surface interesting content to them. This has been the formula of short form video app Drakula by Founder Alex Masmej. ![Drakula using Farcaster's Social Graph](../../../../assets/images/docs/farcaster/start/drakulaStory.png) ### Making crypto transactions easier and more social When using the connected wallets you can now show usernames and profile pictures. That's what apps like Interface do to make wallet addresses that usually are `0x002312...` human. Because you have access to all the social data, where users can verify wallets they own, you can show beautiful social graphics like this one instead of boring wallet addresses. ![Interface using Farcaster Data](../../../../assets/images/docs/farcaster/start/interfaceStory.png) ## The Farcaster Developer Legos (Tools) What tools are available to leverage Farcaster? What tools can Farcaster developers build with? While there are plenty and they are mostly compatible, we'll focus on their virality and use in building great experience that matter to you and your intended audience. If you want to learn how to develop with them and program on Farcaster, please refer to the [Farcaster Developer Guides](https://dtech.vision/farcaster/). The individual docs will also be linked throughout this article. Which tools exist and what are they? - **Feed of publicly available content** + profiles through the hubs (usually shown in Farcaster clients, but you can read it from your infrastructure too!) - **Connected Wallets (Verifications)** - most users connect their crypto wallets so you know their addresses! - **Mini Apps - miniapps are web apps within the clients that do everything for you on Farcaster and integrate with the feed, dm's and sharing easily. They are your Farcaster native user interface. - **Share Extensions** (invoke a command you can define to what it does on any post within Farcaster) - **Direct Messages** (send a direct message to a user) - **Farcaster Bots** (AI agents, automated replies, mentions, reactions, posts, ...) - **Farcaster Intents** (your way to direct users to specific content or to instantly take action like posting or sending a direct message) So with clients (the apps showing you content - think traditional social media apps) and you being able to pull all these details like profiles and post feeds from the network, we now have a full list of tools to build with. Many viral Farcaster growth loops can be composed of some or all the developer legos. Mostly built with tools, data and integrations that one can leverage to build incredibly integrated outstanding user experiences. > Please open your mind to programmbility, do not think about something being impossible or something not working with each other. This is Farcaster and not a walled garden. ### Feed (public content, user profiles) The feed is where dicovery happens. Users spend their time here (~90% according to founder Dan Romero). In their feed they see posts, mini apps, mentions, posts from bots and all kinds of account. A user can also take action by clicking a context menu and invoking a cast action or reply to a cast. When replying they can leverage composer actions to help create a new reply. Your backend has full access to all user data (think profile names, location set in profile, profile picture, description, connected wallets) and their content. This means no external API is required to index all that data into your data store and process it. Users also provide consent by using the platform, though you may want to check specifics with your lawyers. ### Connected Wallets Most users have wallet addresses connected to their usernames (FID) that allow identifiaction of onchain actors, assets and more. It enables targeting users specifically using the onchain datasets and to build crypto integrations on top of them. You can easily identify all Farcaster users that hold VeeFriend NFTs, as most will have wallets connected and you can resolve NFT holders to wallets to Farcaster users. ### Mini Apps Mini Apps are displayed in feed or direct messages when sharing/posting a weblink. They enable an authenticated, interactive experience without leaving the app, just in the link preview. How is a click authenticated? As Rafi puts it: `Mini apps have an access to verified identify of the caller`, this happens via signIn or quickauth (more in our [developer docs](/farcaster/miniapps/). These kinds of cryptographically signed messages are common across Farcaster and let us come up with very fun flywheel experiences. A mini app is your webpage (web app) loaded full screen in the Farcaster client like Farcaster app with you being able to authenticate the user before they even visit the page [tutorial](/farcaster/miniapps). That way you can personalize and tailor the experience to the actual user visiting the page. Imagine you had a users full social context AND their financial context right when they visit your landing page the first time. How conversion optimized could you make it? As a user how lovely is it to see a page with helpful and good suggestions for you? The tools being combined are the Hubs (Social data) from Farcaster and the connected wallets that allow you to pull in the blockchain data (e.g. financial) as well. Typically the way users find mini apps is through seeing a mini app shared that then has an button which when the button is pressed opens the mini app. Remember Mini Apps can be sent via Direct Messages, Posts/Replies and Intent links (include the mini app link in the message or embed). Meaning it's easy to reach the users and build viral loops if your mini app is good enough so the user wants to share it. Most importantly it's a great showcase of how all the above legos/tools combine together. Remember that a share extension can invoke a mini app that can be used to direct to your mini app. Each of these button presses can be customized so you have decision trees at each step so the user gets the perfect experience to just do what they intent to do or be maximally entertained. > **NOTE**: Technically Mini Apps run on the Mini App developers/companies server. Which is a benefit to you as you can do anything there, it also means they may not be fully decentalized. ### Share Extensions Share Extensions are used from a context menu on the cast (post). When pressing the button to "invoke the action" you will see a small text popup with information you wanted, an eror or even a mini app popup adding context to your mini app so you can develop custom user journeys. This can be used to build experiences based on the post itself using the used data, post content or more. One such example is a custom bookmark tool, where it sends to an integration that will then add into your bookmarking service. It can also serve flows like look up the user's wallet, or show me their friends and bring up interactive experiences. Using to display a short message like "cast added to bookmarks" is one fun integration, but imagination should not stop there. ### Direct Messages (Direct Casts) :::caution While the vast majority of users are on Farcaster this is not a generic Farcaster standard. It is a Farcaster specific feature. This is mostly fine, but good to know. XMTP is what some other clients use. ::: You can send a targeted / automatic message to a user. For example your server/backend from a mini app button/share extension can trigger a direct message. One may also use direct casts (direct messages), to have a Farcaster native notification system of reminders or happenings in your application. Being able to get into the message inbox of a user is powerful! ### Farcaster Bots & Farcaster Agents Automatic replies on posts based on patterns like @mentions or custom business logic are common across all kinds of social media platforms, Farcaster being no different. Though as with Farcaster we do not need to rely on external APIs we control the full experience and have way more data context available. Bots allow you to get into users feeds or notifications (likes, replies, quotes, mentions, ...). As with any cast Bots can also include Mini Apps, Images, and all kinds of embeds like links. Meaning it's a powerful tool to remove friction and have users take action right from their feed! Bots play very well across the Farcaster developer legos. A Farcaster Bot is a Farcaster account, so it can be created by you or even your own account. It can then be taken back as your own personal account with all forms in between also possible. From the outside the Bot does not look different and is not different to any other account, because it is a regular Farcaster Account (FID). ### Farcaster Intents :::caution While the vast majority of users are on Farcaster this is not a generic Farcaster standard. It is a Farcaster specific feature. This is mostly fine, but good to know. ::: Intents are linkst that when opened by a user prompt them to take an action. They are the intent to do something within Farcaster, that's where the name Farcaster Intent comes from. You can prompt the user via Farcaster Intent to - send someone a predefined message (user opens link and see's the direct message chat with a prefilled message in Farcaster) - post a predefined cast (user opens the link and see's the post composer with a prefilled message in Farcaster) - deeplink into opening the mini app (user opens the link and see's the mini app open in Farcaster) They are very powerful when combined with the personalized text from business logic or their farcaster social data. Intents are best used to take the friction out of having to come up with what text to use, such they can be used to encourage outreach and virality. Most users will share your marketing copy if you're building something like a viral mini app that has a button to share a cast intent (intent link to have the user post a cast). They can also be used to reach out to users directly. Say you are working on a marketplace and don't want to build a new messaging system that the user won't see, because it's one more tool they will not keep track of. Now you can use Farcaster Intents to have your user reach out to the other Farcaster user via direct casts (direct messages) and now they are chatting in an app they already both use. ### Combining Farcaster Legos to user experiences You can send a direct message or mention a user in replies/posts by which ever logic you want. You could leverage the social data, blockchain (financial) data based on the connected wallets or something entirely different from your business logic. The social data isa users activity that really shows what they like, recast, interact with in general so it's proactive data and up to data sentiment. Sending these posts/direct messages you can include mini apps, that allow the user to take action immediatly. The text you use to get the user to take action can be personalized as shown previously. Since all of this data is available you can use Artificial Intelligence (AI) to do the personalisation and analysis for you. Assuming you are able to reach the user with a quality text they want to engage with, then one viral loop example is building a mini app that lets the user open a mini app, which then you use to let the user cast (post) with a prefilled message that contains the fame. The mini app and posts in feed serving as entry and exit close the loop and spread the word. How do these Legos compare? ![Farcaster Lego comparison](../../../../assets/images/docs/farcaster/start/farcasterLegoOverview.png) :::tip Want to talk about how to leverage Farcaster viral loops for your business? Schedule a [call](https://cal.com/team/dtech/dtech-requirements). ::: ## Discovery - How do users find your app? > **NOTE**: The discovery overview is focused on Farcaster's own as a lot of users are on their first party client and most other apps try to be discovered there. Knowing what the [Farcaster "Legos"](#the-farcaster-developer-legos-tools) are and how we can combine them to construct user experiences, we also need to make sure the user knows about our experience. How can we take each of the legos and make sure the user finds our app? How are they discoverable? ### Mini Apps Discovery of Mini Apps is widely done through the feed, though they can be shared via DMs. Say I send you `dtech.vision` then the mini app will render and you can press the buttons.
Share Extension pick action modal Share Extension pick action modal
If you share casts or replies the same applies. One viral loop for mini apps often done is a share button that then attaches the Mini App and has it in the new cast / reply. This way the Mini App gets shared. As Mini Apps are rendered in casts and direct messages one can use bots to spread them. Share Extensions can also be used to bring up Mini Apps [as documented here](/farcaster/frames/howtoturnacastactionintoaframe). Share Extension explore page Mini Apps are also features in the Farcaster Explore Page. ### Share Extensions Share extensions once installed can be invoked on EVERY CAST! Though to discover them usually mini apps are leveraged that have an install button (using a Farcaster Intent linking to install the share extension). Installing meaning the user adds them to their list that gets shown if they press the context menu on a cast. share extension usage visualized Here's how using a share extension looks like using Farcaster app as the example Farcaster client and the second screen also showcases a link to the discovery/explore page. share extension pick action modal Another way they can discover is via the share extension page. There is a page listing share extensions if a user goes to the list of installed ones and looks to add a new one in Settings. share extension explore page ### Bots Bots are discovered through the Feed. Yes one can invoke share extensions, mini apps and and then these in the background can trigger your bot, but the discovery itself is the Feed. Here's me using askgina to get a summary of an article. Now anyone will see my reply and then see the bot reply if they see the cast and potentially the comments. That way Gina gets discovered. Farcaster agent discovery to go viral ### Mini Apps Mini apps can be discovered through webpages that have a deeplink or any other link that people click to open them, but most are discovered through the feed by seeing someone post them. By now then can also send push notifications and be stored to a mini app screen, where users can come back to their favorite apps. That is great for retention!
open the mini app via cast composer pick which mini app / composer action to use mini app usage visualized
Most of the discovery need is getting your users to use it once so it's in their recents and getting users back in. Mini Apps help. Viral Mini App loop with notifications for retention are your friend here. ### Webpages/Products/Other Any other product can be shared via Mini App which could be already used as setter for the app or to [authenticate users to be logged in on arrival at the webpage](/farcaster/frames/authentication-from-frames-with-nextauth). What makes Farcaster great for product promotion is you can use Bots, Mini Apps, Share Extensions and Direct Messages to pull together a full army of touch points with each user. And you can target users based on all activity (open data) visible on the Farcaster protocol through Hubs. In Farcaster and other clients links are also not deranked by the algorithm as otherwise they would derank Mini Apps as well, so you don't have to fear sharing external links! ### Farcaster Discovery Summary Putting all the discoverability methods together and comparing each Farcaster building block with each other, here's a matrix to provide an overview. ![Farcaster App Discovery Overview](../../../../assets/images/docs/farcaster/start/farcasterDiscoveryOverview.png) :::tip[Want to learn more?] You want to make sure you really understand how to leverage Farcaster viral loops ? \ Schedule a [call](https://cal.com/team/dtech/dtech-requirements). ::: --- --- Title: IPFS - decentralized storage Description: Looking for decentralized storage? Learn what IPFS is and how to use IPFS! --- # IPFS - decentralized storage ## Why use IPFS? IPFS allows use to store files in a decentralized manner leveraging a peer to peer system. This is seen as a benefit in the NFT space as we can't change the artwork or remove it after the fact. Even if we don't pay for hosting or the project is "dead" the artwork will be available, because it is decentralized, can't be changed and is stored hopefully permanently. There is no guarantee of permanence though for a fully permanent solution please see [Arweave](/storage/arweave/). Why is IPFS not permanent? Because no one is checking, whether or not your file is still stored by someone on IPFS. Arweave solves that by leveraging distributed ledger (DLT/Blockchain) technology. For more check the [ipfs website](https://ipfs.io/). ## What is IPFS? A peer-to-peer hypermedia protocol designed to preserve and grow humanity's knowledge by making the web upgradeable, resilient, and more open. When you add a file to IPFS, your file is split into smaller chunks, cryptographically hashed, and given a unique fingerprint called a content identifier (CID). This CID acts as an permanent record of your file as it exists at that point in time. For more check the [ipfs website](https://ipfs.io/). --- --- Title: Vue.JS & Nuxt Description: Learn how to build Vue.JS & Nuxt Farcaster Mini Apps including Wallet Connection --- # Vue.JS & Nuxt import { Tabs, TabItem, } from "@astrojs/starlight/components"; > We assume you already know what Mini Apps are, [else learn what Mini Apps are here](/farcaster/miniapps/). > Additionally it's assumed you know your way around VueJS & Nuxt.
## Step 0: Add the Metatags To add the metatags literally follow the guide in [the Webpage to Mini App starter](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/) where the metadata can be configued in your `nuxt.config.ts` file. Here we will go and add the meta tag so that the Farcaster client's know this webpage should render as mini app. ```typescript // define your mini app embed, you could also set this per page to have different embeds or adjust the image const miniappEmbed = { version: "next", imageUrl: "https://pepetrump.io/images/pepe-banner.png", button: { title: "Play Games $KEK", action: { type: "launch_frame", name: "KEKVERSE Defi Platform", url: "https://pepetrump.io/", // url: "http://localhost:3000", // uncomment this to debug locally splashImageUrl: "https://pepetrump.io/icon.png", splashBackgroundColor: "#f7f7f7", }, }, }; export default defineNuxtConfig({ // your config here app: { // your config here head: { meta: [ // your meta tags here { name: "fc:frame", content: JSON.stringify(miniappEmbed), }, // rest of your nuxt config here ``` ## Step 1: Add a Farcaster Mini App plugin to your Vue.JS project Now that it renders as mini app and points to our website, we need to make sure that it also opens. Install the dependency: To make it open we need to call the ready function of the Farcaster Mini App SDK. To do that on all page loads we'll use a plugin. ```typescript // plugins/farcaster.client.ts export default defineNuxtPlugin(async (nuxtApp) => { console.log("Farcaster plugin initializing..."); // you may want to track context here to get user information and analytics // to do that please reference the documentation // https://dtech.vision/farcaster/miniapps // e.g. https://dtech.vision/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/ if (process.client) { try { console.log("About to call sdk.actions.ready()"); await sdk.actions.ready(); console.log("Farcaster frame ready"); // You can also verify the SDK is working by checking the context const ctx = await sdk.context; console.log("Farcaster context:", ctx); // Add a mounted hook to verify it persists nuxtApp.hook("app:mounted", () => { console.log("App mounted, Farcaster SDK still available"); }); } catch (error) { console.error("Failed to initialize Farcaster frame:", error); } } }); ``` ### Step 2: Add a Farcater Mini App Manifest Make sure to follow the [the Webpage to Mini App starter](/farcaster/miniapps/howtoturnyourwebpageintoaminiapp/) to add a manifest (farcaster.json) in case you want to send push notifications to users and have them save the app in their Farcaster clients. This fill you can either do via a route or add to the `public/` folder. ### Step 3: Add Wallet Connection If you have an injected provider most likely your wallet connection will work out of the box, you can test that. Though the recommended path is to add the Farcaster Connector to your app which is provided as a wagmi connector. You can do that as [documented in the wagmi Mini App Guide here](/farcaster/miniapps/howtodowagmiwalletconnectinfarcasterminiapps/). --- --- Title: Use Viem with Degen Layer 3 Chain Description: How to connect viem.sh to Degen L3 blockchain for Smart Contract interaction and development based on the degen.tips project. --- # Use Viem with Degen Layer 3 Chain Viem supports the degen chain so if you already have viem installed update it, else install it using ```npm install viem``` and then create a PublicClient. ```typescript export const degenClient = createPublicClient({ chain: degen, transport: http(process.env.DEGEN_RPC_URL || "https://rpc.degen.tips") }) ``` Now you can use degenClient like any other viem client to read from, write to and watch SmartContracts. --- --- Title: Farcaster Links (Follows, and more) --- # Farcaster Links (Follows, and more) ## What are Links? From the [specification](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#27-links) `A Link is a relationship between two users`, which means if you follow somebody that's a relation of type link. For now there is one link type implemented. Follows. In Social Media apps following somebody is used to express that you intend to see their posts in the future. Many apps then also allow you to message each other, show notifications of your interactions (e.g. likes, comments, etc.) and generally think you are friends. In Farcaster it can be more generalized to more then just follows. Links are a definition of any relation between users. For now it's used to express follows, though can be extended to express more relations. How? Links are defined by type. A link type is a string. So any identifier could be used as long as it's smaller then 8 bytes (8 characters). ## What do Links mean on Farcaster? (type "follow") Farcaster clients (what users use to see posts e.g. Warpcast) interpret links with type follow aka "follows" as a relation between to users to show posts of the user you follow. Some clients have reverse chronological following feeds, where they show only posts from users you follow. Some clients have a mix of following and other posts, some clients have a feed of all posts and you can filter by following (like explorers). ## How to add a new link type? One can go to github and open a pull request with their change in the hub repository (Hub = Farcaster Nodes, the code running the network). If the change is approved it goes into the protocol. Then the new link type is live. A link could look like: ``` message LinkBody { string type = follow; optional uint32 displayTimestamp = 2; // If set, clients should use this as the following create time oneof target { uint64 fid = 3; } } ``` where type "follow" means follow for example. If you now change the type to 2 and hubs are updated by your code changes to support this new type (not reject messages with type 2), then your new link type is live. The interpretation of what it is happens at the client level, [as explained here](#what-do-links-mean-on-farcaster-type-follow). --- --- Title: How do Cast Embeds (links, videos, images, ...) work? --- # How do Cast Embeds (links, videos, images, ...) work? >You do not need to know how Hubs work or the Farcaster protocol work to understand how Embeds work. Start reading and in case you have advanced knowledge, jump directly to topics of interest. Though generally it is useful to know what [Farcaster Messages](/farcaster/hubs/howdofarcasterhubswork) are. ## What is an Embed? When talking about Embeds one refers to anything that is embedded with a post. Usually via some kind of link (url). When you paste dtech.vision into a text message, you expect a preview of that url (dtech.vision) to show up in the text editor. That preview is what we refer to as embed from a user standpoint, though from a developer standpoint the embed is the url (dtech.vision in our case) that is pasted and should be embedded with the message. ![Farcaster Emebed rendered](../../../../assets/images/docs/farcaster/hubs/hubMessageWithEmbedRendered.jpeg) ## How do Embeds work on Farcaster? On Farcaster Embeds are part of the [Farcaster Messages](/farcaster/hubs/howdofarcasterhubswork). To add an image, video, mini app or whatever you want to have as embed on your post (cast) one adds an url to it. Yes this literally means to cast an image we as developers take the image from the user, and turn it into a link. Then we take this link and add it to their past as embed. The user things they have uploaded and posted to Farcaster, but in reality they have uploaded to wherever the developer chose and attached that url to their post. The same goes for videos, mini apps and all kinds of urls like articles. Think of embeds more like pointers to media then true uploads to the protocol. Since the Farcaster protocol doesn't store your image (or video) you need another solution for that. As developer that means you are tasked with providing that infrastructure and making sure that when someone wants to view the post and the url resolves and is available. Yes if anyone - no matter from your app or another client - wants to watch a video that means your video infrastructure (our your provider) gets traffic, which you most likely pay for. ### A look at Farcaster protocol data - the message Here is some representation of raw strings of what a message looks like that includes an embed so one gets an understanding of the underlying data type. ![Farcaster Embed Message Example](../../../../assets/images/docs/farcaster/hubs/hubMessageWithEmbeds.jpg) ## How do Farcaster clients render embeds? When rendering casts a Farcaster client sees the embed in the message or API response they watch. Most Farcaster clients use some form of processed data instead of raw protocol messages. The data format they see likely is something akin to the following: ``` { text: "This is a sample illustration of a cast. look at the great video and mini app I am posting here" embeds: ["https://url-to.video/1", "https://miniapp.dtech.vision"] author: { // information about the author } signer: {} // more details } ``` From this representation of a cast it's very clear that you'd access the url's in the embed array. But how do you know what kind they are? Either your API provider has done that work for you already and hydrates with that information. They'd give you the MIME type e.g. image/jpg. Or you'll have to run a GET request against the URL, look at the header and multiplex over the returned content MIME-type to select the proper render in your user interface (e.g. your image or video preview tool or your mini app preview renderer). If your API provider gives you the content type already, then you make this part of your cast rendering logic and directly pick the right visual representation. Though be aware, that in case there are indexing delays on your provider you may have to fallback for undefined content types and add the logic yourself. ## Tipps and Tricks to upload videos to Farcaster (handling video embeds) ### Be careful with total cost of ownership when supporting video upload and how to save money! For efficient usage of videos the Base App and Farcaster App expect your video to be a stream and not a raw .mp4 file or similar! This is important since your users will highly likely not upload a stream, but an mp4 or mov... You will handle the transcoding from their filetype to .mp3u8 streams. That requires paying a provider like Cloudflare Stream to do the transcoding, video serving and storage for you (they have a nice upload file, get stream link back API) or handling it yourself. When doing it yourself or when saving money on CDN providers you'd host a VPS for little money with high CPU capacity to run handbrake/ffmpeg. These are powerful video transcoding programs that are super optimized and will do the conversion to a streaming format like mp3u8 for you. Your server will be the endpoint the video is uploaded to, converted and then posted to the CDN/streaming provider unless you also host these yourself. Here you could then host the stream on S3 or anywhere. What you are saving is a lot of money that is spent on transcoding (usually paid by the minute). The total cost of ownership for video content is often cited as reason for why smaller Farcaster clients won't provide video uploading support. The developers would have to suffer the cost of every single view the video gets. Their financial incentive is to never have a video shown. Though yours is to get as many views as possible. So there would need to be a way to pay for the cost of the client. When a client has enough revenue to offset the video cost, they usually do. For eaxmple the Farcaster app allows you to upload videos and social media companies like Facebook do so to as they make money with Ads and use that to pay for video hosting, transcoding etc. ### Legalities of hosting videos When a user uploads a video to your website there maybe a risk that you become liable for their upload. Of course every jurisdiction is differnt and we are no lawyers, though you'd want to research that. Most likely you need to filter videos when uploaded and screen them for illegal content. That's a service that large providers likely provide to you (at a cost of course). LLMs becoming cheap may help you here. Where you'd develop a pipeline of getting the upload, LLM scanning it with a cheap model that supports video files to have the contents checked against your rules and laws, then if fine transcoding to a stream and pushing that stream to your hosting solution. The final stream url is what is then used in the Message that composes the cast. So your user would wait for that process to finish and see an indicator of the video uploading & processing. You may do that in the background and notifiy the user in case of failure and on success so they can use the app without being blocked from your video processing pipeline. --- --- Title: How to use Arweave for Smart Contracts & NFT Metadata Description: How can we upload full folders to Arweave and leverage permanent storage for Smart Contract Metadata in e.g. NFTs? --- # How to use Arweave for Smart Contracts & NFT Metadata ## What to upload when? Make sure to first upload your Art as you need to reference the art in your metadata JSON files. The Smart Contract itself only needs to point the TokenURI to your JSON which then exposes the properties and Art for Marketplaces like Opensea to display. Upload order therefore is: Kunst -> Metadata -> Smart Contract ## How to correctly upload folders to Arweave Normally we can simply use ArDrive to get an awesome Online Filemanager backed by the Arweave Network! It's awesome, but doesn't give us folder/filename out of the box. If we upload a folder called ourData which has a file named 1.png and 2.png then we want the link ourData/1.png to access that 1.png file. To achieve that we'll use [ARKB](https://github.com/textury/arkb#readme). Now we can upload our folder using arkb ```bash arkb deploy /path/to/folder --wallet /path/to/arweave-key.json ``` arkb is now preparing files for upload, which may take a while. ![Preparing files](/src/assets/images/docs/storage/arweave/arkb-preparing.png) after preparation we need to double check that everything is correct and confirm/deny the upload. ![Doublecheck arkb upload](/src/assets/images/docs/storage/arweave/arkb-confirmUpload.png) We'll confirm your upload by pressing Y and hitting Enter afterwards. arkb will now deploy our files to the Arweave network. Since it's all in one bundle we get the folder/filename structure we want! ![arkb uploading/deploying](/src/assets/images/docs/storage/arweave/arkb-deploying.png) Now we have our NFT metadata uploaded to Arweave! ![arkb deployed folder](/src/assets/images/docs/storage/arweave/arkb-finishedDeployment.png) The link shown below with ```arweave.net/``` is where you're files are located. If you dont want to use arweave.net as gateway and want to use the generic ar:// then replace ```https://arweave.net``` with ```ar://```. Everything after the *.net/* is the TX ID/Hash where you're files are stored. This is permanent and based on your file contents. If you're familiar with NFT metadata this Hash is your baseURI. It will be used to concatenate the TokenURI in the Smart Contract like: ar://$BASEURI/$tokenID.json where $BASEURI and $tokenID are variables representing your Arweave ID and TokenID respectively. The baseURI shown by arkb above is: https://arweave.net/fIlS8rS9ZwkTP_IILzqHCl4bcFGneGtZRy9gK2e75CE/ In our Flux Smackman example shown here there are 1111 coins so the following URLs for each token exist: ``` TokenID: 0 ar://fIlS8rS9ZwkTP_IILzqHCl4bcFGneGtZRy9gK2e75CE/0.json TokenID: 1 ar://fIlS8rS9ZwkTP_IILzqHCl4bcFGneGtZRy9gK2e75CE/1.json ... TokenID: 1110 ar://fIlS8rS9ZwkTP_IILzqHCl4bcFGneGtZRy9gK2e75CE/1110.json ``` --- --- Title: Learn decentralized Storage --- # Learn decentralized Storage >How to store data in decentralized Applications and Smart Contracts. What are the best practices? ## How to think about Storage Storage, the hero of the digital age. It's the backbone that upholds our entire digital existence. Just close your eyes for a moment and imagine a world without storage – a place where all your posts, your treasured family photos, and crucial business data simply vanish into the digital ether. It's a chilling thought, isn't it? As we turn more and more digital storage is the new brain memory. Your brain has memories, digitally we have storage. We want that storage to either be very fast or to last for a long time and not break. Ideally both but we'll focus on the longevity of storage. How long will these memories, that storage last and what is it for? For NFTs and blockchain projects we typically want the data to be there forever or at least decades. How bad would it be if your favorite NFT picture would be gone tomorrow because storage failed? Some projects have their images and metadata modifiable so they can level up your token or track redemptions of some kind. Maybe they also want to have the option to fix errors. If you're okay with that it is fine. Otherwise you want permanent storage. So how do we get there? There are different types of storage trying to achieve our goals. We'll first go through the Onchain storage meaning that there is a blockchain used to actually store our data. Therefore the likelyhood of our storage being dead and gone is the likely hood of the blockchain failing. If all your data is on Ethereum, how likely is it dead? not very likely right? Then due the disadvantages we see we'll learn about the types of Offchain storage used to circumvent them. Finally we'll discuss what to choose and look forward to the future afterwards. ## Types of Onchain Storage ### Fully Onchain We can store all the data on the blockchain itself. This is Onchain storage (on the blockchain) so the guarantee about longevity is the same as for the blockchain itself. This means as long as the blockchain is around you will have the data available. No risk about the data getting lost and the blockchain still being around to have broken links. Additionally depending on the implementation the data can't be changed. You get what you see forever. No risk of your legendary item turning into a normal one because the developers want it themselves. Though you should check the Smart Contract to see if there is actually no way of changing it. It could be the case that storage is onchain but changable. This is actually the norm. Like the owner of an NFT is changable and stored onchain. You will forever know who that NFT on Ethereum belongs to as long as Ethereum is around, but of course it is changable otherwise you couldn't transfer your NFT to your friend or sell it. Technically Onchain is mostly a variable in the smart contract storing the data value as with every other program or database you already know outside of blockchain. ![Fully Onchain Storage visualized](../../../assets/images/docs/storage/index/FullyOnchainStorage.png) If we are talking about NFT Metadata there are two ways of doing fully on chain storage - Fully stored Onchain - Fully generated Onchain #### Fully stored Onchain If we fully store our data onchain then we guarantee that it is available as long as the chain exists. How can we do that? Typically how is to use Smart Contract variables just like you have fields in your traditional database. Store the data values in the variables. Let us look at an example. We want to store the loyalty points of our lemonade stand and we want all of it to be on the blockchain itself with the data being available at once without generation! What we do is we store the amount of points belonging to the corresponding user. ```solidity mapping(address => uint256) pointsOf; ``` Here we assume the user to be identified by a wallet address though if you change it to bytes32 you can easily map any value (string) you want like the Fullname or customerId. ![Onchain Storage access visualized](../../../assets/images/docs/storage/index/onchainVisualized.png) What we get is we can ask pointsOf to return the amount of points our user has. If we need extra information we just add variables and then we can query for all of them. One of the ways to do that would be to build a Struct holding all variables and then one mapping of user identification to that struct. But what if we can infer some data by calculation and don't need to store it? #### Fully generated Onchain Every single piece of storage on chain costs huge amounts of fees. If we can reduce the amount of times we write to storage we end up with huge savings! So how do we do it? >We combine data we already have to derive what we need. Since that data is stored completely on chain we retain the guarantee that data is available as long as the underlying blockchain is. Lets stick with our lemonade stand royalty program example and assume we made the whole thing bigger. We added some alcoholic beverage and will not let childreen redeem or collect points for these. If we have the users birthday stored we can get their age by simple calculation and we can also give them a bonus loyalty point of they come in on their birthday. How can we do this with just one variable stored? Well we have the date. By comparing the date to the current time the difference is their age. By looking at the current time and checking if day and month match the ones in the birthdate we can get if its their birthday or not. This is a simplistic example but it illustrates that you do not need to store every value and should look to infer from the minimal set of variables needed. In the below example we leverage the fact that the blockchain exposes the current date in form of a timestamp (block.timestamp in EVM) and we store the birthdate for each address in our smart contract locally to compute the age in the ageOf(address) function. ![Fully generated Onchain visualized](../../../assets/images/docs/storage/index/fullyOnChainVisualized.png) But what if we have sooooo much data that we can't store it on chain? for simple SVG images you can do generation on chain, but what if we have movies? Let us look at linking to another chain. ### Link to another chain >There are chains optimized for storage. These allow for much lower costs per unit compared to execution optimized blockchains like Ethereum. We will be looking at the biggest storage optimized chains - [Arweave](#arweave) - [IPFS + Filecoin](#ipfs--filecoin) What is done here is our Smart Contract on the blockchain only remembers one variable. Namely where to send us to lookup the data we actually want to get. This is how most NFTs are operating today, they send you somewhere else then the Blockchain the Smart Contract (NFT) is on, though that link may also not go to a storage Blockchain, but we'll cover that when talking about Offchain Storage. So the SmartContract when asked about "Hey give me the data for this identifier" says "sure you can look it up at dtech.vision" where dtech.vision will be a link to the storage location. This way you only pay for one storage slot on the Blockchain like Ethereum which saves costs. Now you are dealing with two additional risks though. Additional Risks when not using Onchain Storage in your Smart Contract: 1) The lifetime of the other storage solution (e.g. Storage Blockchain like Arweave) may be shorter then the one of your Smart Contract -> links will not work, data unaccessible 2) The link you put in the Smart Contract may not be resolved by the user asking about the data -> data may be there, but not accessible -> data unaccessible These additional risks should be considered when deciding on the optimal storage solution and thinking about cost. The cost of breaking things may be higher then the increased ongoing cost of running fully onchain storage when changing the variables. >The below example illustrates needing to look up the URL offchain instead of getting all the data to directly use from the blockchain smartcontract. ![Linking to Offchain storage from Blockchain SmartContract visualized](../../../assets/images/docs/storage/index/offchainlinkingVisualized.png) #### Arweave What would a blockchain system look like with the single purpose of storing data for eternity? Arweave is using a blockweave (thats where the name is from) system each block is linked to both the block that preceded it and a recall block – a block from the earlier history of the blockweave. So each piece of data is in a neighbourhood that checks if it is still there. If you now duplicate it onto multiple cities (nodes) you get redundancy and others checking that it will not disappear. This is basically what Arweave and its consensus algorithm do by asking new block proposers to randomly proof they still have a historic piece of data. Due to randomness the optimal solution to maximize rewards for Arweave storage providers is to store the full network. ![The weave of Arweave](../../../assets/images/docs/storage/index/arWEAVE.png) The Arweave nodes store some part of all the data on the network so that the chance of permanent loss is very unlikely as if one node goes missing others take on that storage and keep it. If there are enough nodes with free storage capacity your data will never be deleted on Arweave. That guarantee about permanence that Arweave attempts to give is also the reason why I include it in the types of onchain storage instead of putting it offchain since it's not on the original blockchain where the Smart Contract is stored, but is as close as one gets. Arweave files will, guaranteed by network design, be available as long as Arweave exists. To link to Arweave we have to either add the protocol, then our identifier (example: ar://) or use https links to a gateway (example: https://arweave.net/). The benefit of direct links is that any user knows how to deal with them. Say we take *arweave.net* as gateway then our link starts with *https://arweave.net* which is like linking to any other webcontent. Though if that Gateway is down, no one can reach our content as they treat it like normal links. For the link to a gateway we have the issue of the gateway disappearing. This will break our link. Also if the DNS name of the gateway has issue we are doomed as we can’t use the link. We could extract the identifier and query our own gateway but then we can just use ar:// links. Though what can go wrong with protocol links? Well the person or service we give the protocol link to may try to use it as web link and it will not work. You need to know that ar:// means Arweave and use the Gateway of your choice to access it. Otherwise it is awesome as we as user or service developer choose the gateway. If one breaks we change our default gateway and the link is still fine. Link will work as long as Arweave is around! That’s why I‘d recommend the following: - When sending to people (e.g. messages, mail, social media posts) make it as easy as possible and send Gateway Links. - For anything else use protocol links! Your API returns Arweave links? If not sending to end consumer use protocol. Your smart contract points to Arweave? You want permanence? Use protocol links. The receiver will implement the gateway logic of their choosing and can use what works best for them! Potentially using their own nodes/gateways! How can we deal with that? Well if we use protocol links *ar://* then we need tool support to resolve to a gateway, but it will never change and users/tools choose their own gateway that is online. This way we negate the issue of dns or gateway failures. So what should we do? If you share it on social media use the link that works for most people and include the gateway. When building permanent infrastructure like Smart Contracts use *ar://* the protocol link so that it is future proof! You can't predict what gateway will work and should let tools and infrastructure resolve that issue. A simple solution would be to strip ar:// and replace it with arweave.net and voila you have your working link created by tooling. This could also be any other gateway. Why do we want different gateways to exist in the first place? Why is it important? With DNS (on the Gateway) we resolve the name to the IP, to where we are actually going. So ```arweave.net``` becomes the server we access and then ask for the content we wish to see. But if that resolution is faulty or manipulated we can get anything back and get no guarantees. If we used ```ar://``` and then decided on our own gateway we could simply switch gateway to a non manipulated one, but with the fixed link we can't unless we implement the same logic that makes ```ar://``` work in the first place. So there is no benefit to the full link with the gateway when you start thinking security and resiliance. Though like stated earlier the full link including the gateway is what's compatible with what we have now in terms of social link sharing and browser support. Just make sure you understand the trade offs outlined here. With Arweave as a unique identifier is applied for each file uploaded, we get a new link for each upload, making permanent names not directly possible. We would then update our pointers in the SmartContract or plattform we use if a new file version should be refered to. Or we leverage the Arweave Name System which is a project aiming to bring humand readable permanent names to content on Arweave. So that you can say "dtechsnewblogpost" always points to the newest blog post even though that post changes with a new upload obviously. To learn how to use Arweave in your application, please refer to our [Arweave documentation](/storage/arweave/usingarweave/) The Arweave ecosystem has expanded quite far beyond simple storing of files to databases and smart contracts running on the permaweb (the permanent version of the web based on permanent storage on Arweave). To learn more please refer [to our Arweave Section in the Documentation](/storage/arweave/). #### IPFS + Filecoin IPFS itself does not guarantee permanence as it relies on nodes to "pin" (keep) your content. If no nodes wants to keep your content then it is gone, which is not the case with Arweave which guarantees it is kept. For Links to IPFS content you can either use protocol links (ipfs://) or Links to your gateway (ifps.io/). For a discussion of when to use which and what their pros and cons are please refer to the [Arweave section where we discussed this](#arweave) But you can back IPFS with Filecoin, which is a project aiming to give the permanance guarantee. Combined you could have IPFS for tooling and accessibility with Filecoin as backing for your data to be stored permanently. Both public documentations of IPFS and Filecoin have guides to get you started if you want to use that. The advantages and disadvantages discussed in the [Arweave](#arweave) section do apply. A total [overview](#why-choose-onchain-or-offchain-storage) including cost is presented at the end of this post. When not using Filecoin for permanance, then IPFS falls under the [semipermanent offchain storage](#link-to-semipermanent-storage) category. #### Remarks >I intentionally put linking to a storage blockchain solution in Onchain Storage, because it is onchain though not on the original chain where the Smart Contract is. As long as you can give guarantees about your links not breaking and the other blockchain being alive for at least as long as it needs to be or even as long as your original blockchain where the Smart Contract is then you are likely to not experience issues and saving costs. You want to check if the Storage Solution fits your requirements though as Arweave for example doesn't let you change data available at some link. If you change data on Arweave you need to change the link. There are solutions to this but they require more effort and therefore introduce additional complexity cost. ## Types of Offchain Storage ### Link to semipermanent storage >When linking to semipermanent storage we get no guarantee about permanance, though I call it semipermanent because it could be permanently stored, but we do not get the guarantee. #### IPFS IPFS is a distributed file storage network, where anyone can join and store data on the network. This allows for anyone to also host the content you host and even keep hosting it after you want it deleted, still making it accessible. Meaning there is no delete on IPFS besides everyone on the network not "pinning" (keeping) it anymore. But that can happen so you also don't get a guarantee that it is never gone (deleted). There are service providers you can pay a subscription fee to store the content while you pay on IPFS or you can run your own node. Other then permanence the decentralized nature of IPFS behaves similar to the discussed [permanent storage solutions linking to another chain](#link-to-another-chain). As we can use ```ipfs://``` as well as direct links including the gateway. The tradeoff being future proofing and potentially corrupt gateways as discussed before. Also if you want to change a file on IPFS you need to reupload and get a new link, which is the same for [Arweave](#arweave) as well. #### IPNS Though what if you don't want a new link? Say you want one link that keeps true even when changing the file on IPFS. Enter IPNS (InterPlanetary Name System) which is a system for creating such mutable pointers to CIDs known as names or IPNS names. IPNS names can be thought of as links that can be updated over time, while retaining the verifiability of content addressing. It gives the same guarantees as [IPFS](#ipfs) or [IPFS with permanence on Filecoin](#ipfs--filecoin) and you can read more about it in the [IPFS Docs about IPNS](https://docs.ipfs.tech/concepts/ipns/). ### Link to Offchain Storage When resiliance through using a decentralized solution and permanance don't matter for your usecase or the cost of running a server is cheaper then the onetime upload cost to Arweave you can always leverage your existing infrastructure. There are two options to consider when going the own offchain infrastructure approach - AWS (or any other Cloud provider) - Own Server When using your own server at least you gain full control and are the king of your data, while using cloud providers can give your better scalability, speed and potentially ease of use as you may already use them and don't need to teach people the upload process to other solutions. Especially for NFT Metadata this is considered highly suboptimal as you offer no permanance guarantee. If you don't pay your monthly bill the data is gone, where as on Arweave or Onchain you pay once and have the data as long as the network lives. And you are at the mercy of the cloud provider or your regulatory overseers to the extend in which you can freely choose what to host and who to serve it to. Imagine a country being banned, then they can't see your data. ## Why choose Onchain or Offchain Storage? You want to check if the Storage Solution fits your requirements though as Arweave for example doesn't let you change data available at some link. If you change data on Arweave you need to change the link. There are solutions to this but they require more effort and therefore introduce additional complexity cost. Also encryption and privacy are things to consider. Putting encrypted data that only the intended reciever can read on a permanent solution like a Blockchain or Arweave may not be a good idea. Say your encryption will be broken in 10 years, then the data will be readable. When the encryption used is strong enough so that the odds of it being decrypted are low in the timeframe you intend then you are fine. This needs to be checked with lawyers and your product team though. And yes you can't change it once its on the permanent storage layer, else it would not be permanent ;) One of the main reasons for using Offchain Storage is exactly this concern as you may consider anything that goes onto a Blockchain or permanent data storage layer (potentially even semi-permanent layer) to be public. With Offchain Storage you have access controls not available on public networks as of today, though Zero Knowledge (ZK) technology and other cryptographic tools being developed may make these available on decentralized networks at some point in the future. **From a cost standpoint:** - Onchain: gas fees network dependand, but very expensive for large amounts of data, very little capacity - Permanent offchain: One time fee for upload (Arweave), works fine with large or small amounts of data - Offchain: Mostly mothly fees for managed services, not only storage but also traffic costs or manage your own hardware ## Looking forward Other honorable mentions to look into and dive deeper: - Arweave naming system (ARNS) - WarpContracts (Smart Contracts on Arweave) - WeaveDB (database on top of Arweave) - KwilDB (database on top of Arweave) - Arweave's GraphQL interface - Sia - Ceramic Network - Storj - ICP (Internet Computer - Dfinity) Thanks to Sam for pointing out the Arweave options on X [here](https://x.com/samecwilliams/status/1724098083997229245?s=20) Link article on Farcaster/Warpcast: https://farcaster.xyz/samuellhuber/0x7e0b3245 Fun Little addon: Here is what a LLM (codellama) has to say ``` >>> would you use arweave, ipfs or aws to store your valuable data? It depends on the specific use case and requirements. However, I can provide some general insights on each option: 1. Arweave: Arweave is a decentralized data storage network that allows users to store data in a highly secure and fault-tolerant manner. It uses a blockchain-based architecture to ensure data integrity and authenticity. Arweave charges a small fee for storing data, but it provides a high level of security and durability. 2. IPFS: InterPlanetary File System (IPFS) is a decentralized storage system that allows users to store and share files in a peer-to-peer manner. It is built on top of the blockchain technology and uses a distributed hash table (DHT) to store and retrieve data. IPFS provides a highly secure and decentralized storage solution, but it may not be as scalable or cost-effective as other options. 3. AWS: Amazon Web Services (AWS) is a cloud computing platform that offers a wide range of services for storing, processing, and analyzing data. It provides a high level of security, scalability, and reliability, but it can be more expensive than other options. ``` --- --- Title: Get a client from an API using effect-ts platform Description: How to use effect/platform to derive a client from an API to use in React and other frontends --- # Get a client from an API using effect-ts platform > The following is a conversation from the effect-ts discord 2024-12-26 I am trying to derive a client from an API I created using effect/platform . I want to be able to use the client in my React frontend. This is the code I have for deriving it: ```typescript export const getApiClient: Effect.Effect = Effect.gen(function* () { const client = yield* HttpApiClient.make(Api, { baseUrl: "/api", }); return client; }); ``` I am trying to create a hook that will get the resulting client but I am getting typescript errors. I had to provide FetchHttpClient.layer to apiClient, like this: ```typescript type Client = Effect.Effect.Success; export function useApiClient() { const [client, setClient] = useState(null); useEffect(() => { const program = Effect.provide(getApiClient, FetchHttpClient.layer); Effect.runPromise(program) .then(setClient) .catch((error: unknown) => { Effect.logError(`API Client Error: ${String(error)}`); }); }, []); return client; } ``` as seen in the [effect discord here](https://discord.com/channels/795981131316985866/1125094089281511474/1318772814370701384) --- --- Title: Arweave - decentralized permanent storage Description: Looking for decentralized and permanent storage? Learn what Arweave is and how to use Arweave! --- # Arweave - decentralized permanent storage ## Why use Arweave? As a collectively owned hard drive that never forgets, Arweave allows us to remember and preserve valuable information, apps, and history indefinitely. By preserving history, it prevents others from rewriting it. The decentralized, permanent, distributed ledger based nature of Arweave makes it superb for permanent storage, because it will always remember all files published on to the network, where as [ipfs](/storage/ipfs) might forget. ## What is Arweave? [Arweave](https://www.arweave.org/) is a new type of storage that backs data with sustainable and perpetual endowments, allowing users and developers to truly store data forever – for the very first time. Basically it's decentralized and actual permanent storage. For more feel free to read the explaination from ArDrive [here](https://ardrive.io/what-is-arweave/) or the Arweave Lightpaper [here](https://www.arweave.org/files/arweave-lightpaper.pdf) ## How to use Arweave? Uploads to Arweave can be done using ArDrive [link](https://ardrive.io/) There is also the **arkb** upload tool from the Arweave team, which is documented [here](/storage/arweave/usingarweave) To retrieve from arweave you can try the following: ``` https://arweave.net/ ``` so for example to get the image of a bored ape NFT: ``` https://arweave.net/ZQY3ditXQtI10IPb-cMYWrBkD-oIGIif_2uHgLS5UYE ``` In Smart Contracts we typically use ```ar://``` instead of specifying the gateway. So that Bored Ape link would be: ``` ar://ZQY3ditXQtI10IPb-cMYWrBkD-oIGIif_2uHgLS5UYE ``` That way the one consuming our link can decide which gateway to use and the link still works even if the arweave.net domain/gateway may become obsolete. --- --- Title: How to clean npm junk from disk (node_modules, nvm, npm) --- # How to clean npm junk from disk (node_modules, nvm, npm) Ported from [this article](https://dev.to/radomirperisic/how-to-clean-your-dev-junk-and-free-space-2a18) >**WARNING: All processes are irreversible!** Delete all unused global node modules and Node.js executables installed with nvm Running nvm ls will show you all versions installed, you can use nvm uninstall v12.2.0 for example to uninstall a specific version, however this doesn't remove global npm packages that you installed for this version. It's better to go to ~/.nvm/versions/node folder and rm -rf versions that you don't need. A useful command to delete all versions that you aren't currently using: ```bash cd ~/.nvm/versions/node; ls -A | grep -v `nvm current` | xargs rm -rf ``` Delete all node_modules folders from your projects ## Check size of node_modules in folder with your projects ``` # Mac / Linux cd documents find . -name "node_modules" -type d -prune | xargs du -chs ``` ``` # Windows cd documents FOR /d /r . %d in (node_modules) DO @IF EXIST "%d" echo %d" ``` ``` # --- Example output --- 255M ./someProject/node_modules 482M ./anotherOne/node_modules 707M total ``` ## Delete all node_modules folders ### Mac / Linux ``` find . -name "node_modules" -type d -prune -exec rm -rf '{}' + ``` ### Windows ``` FOR /d /r . %d in (node_modules) DO @IF EXIST "%d" rm -rf "%d" ``` ### Yarn and npm cache ``` yarn cache clean npm cache clean --force ``` ### Xcode Really nice tool for deleting old simulators, archives and other Xcode junk: https://apps.apple.com/us/app/devcleaner-for-xcode/id1388020431?mt=12 ### Homebrew Homebrew periodacaly performs cleanup but if you need some extra space now, you can run: ``` brew cleanup ``` ### Docker Docker needs to be running for this to work. Be aware that docker removes all images that currently aren't used, so if you want to keep something, start it and it won't be deleted. Check docs for more info. ``` docker system prune -a ``` >add '--volumes' to delete all volumes as well If you have some lang/tool/ide that requires cleanup leave a comment and I will add it.