# Flow Developer Portal - Complete Documentation This file contains all documentation in Markdown format. --- # File: /md/blockchain-development-tutorials.md --- sidebar_position: 1 title: Blockchain Development Tutorials description: Blockchain development tutorials to take advantage of the power of Flow with EVM and Cadence. keywords: - tutorials - guides - learning - flow - cadence - evm - smart contracts - development - blockchain - advanced - gas efficiency - onchain storage - benchmarks - performance - web3 - dapp - onchain app - cross-vm - hybrid apps - COAs - batched transactions - VM bridge - token launch - image gallery - wagmi - rainbowkit - FCL - VRF - randomness - Random Beacon - commit-reveal - AI - ChatGPT - Cursor - AgentKit - Flow documentation - Flow AI assistance --- # Blockchain Development Tutorials Flow Cadence and Flow EVM are two VMs running on the Flow blockchain. A few months after the release of the Crescendo upgrade, we're seeing more apps that aren't Cadence apps -OR- EVM apps, they're both! Cadence unlocks superpowers such as vast computation and storage, native VRF, a much safer and more secure language for handling digital ownership, and more. Flow EVM unlocks the power of the Ethereum ecosystem, allowing you to bring in traditional tools, assets, and liquidity. For this grand future, we'll need a new suite of blockchain development tutorials, guides, and resources to help you build with the best of both worlds. This section is dedicated to those tutorials. ## [Flow 101] - [Flow Blockchain 101] - Learn why Flow blockchain is uniquely designed for consumer-scale decentralized applications with its multi-role architecture, native account abstraction, and EVM equivalence. ## [Forte Network Upgrade] Tutorials covering new features and capabilities introduced in the Forte network upgrade for Flow blockchain, including Flow Actions and Scheduled Transactions. - [Forte Overview] - Introduction to the Forte network upgrade and its new capabilities for building sophisticated decentralized applications. ### Flow Actions Learn how to build composable DeFi applications using the Flow Actions framework with standardized interfaces and reusable components. - [Introduction to Flow Actions] - Learn about Flow Actions, a suite of standardized Cadence interfaces that enable developers to compose complex DeFi workflows using small, reusable components like Sources, Sinks, Swappers, PriceOracles, and Flashers. - [Flow Actions Transactions] - Learn how to create transactions that can chain multiple DeFi operations atomically. - [Connectors] - Build Flow Actions connectors that integrate protocols with Flow Actions primitives, serving as protocol adapters that translate bespoke APIs into standardized interfaces. - [Basic Combinations] - Learn how to combine Flow Actions primitives to create powerful DeFi workflows using atomic composition, weak guarantees, and event traceability across multiple protocols. ### Scheduled Transactions Learn how to implement scheduled transactions for time-based smart contract execution and blockchain automation. - [Scheduled Transactions Introduction] - Learn how to implement scheduled transactions for time-based smart contract execution on Flow, enabling recurring jobs, deferred actions, and autonomous workflows without external transactions. ## [Cadence Development] Cadence tutorials covering Flow's native smart contract language for secure and resource-oriented blockchain development. - [Mobile Development] - Mobile development tutorials for building Flow blockchain applications on iOS, Android, and React Native platforms. - [iOS Quickstart] - Build native iOS applications that interact with Flow blockchain using Swift and Flow SDK for mobile-first blockchain experiences. - [React Native Quickstart] - Get started building mobile applications on Flow using React Native with FCL integration for wallet connections and blockchain interactions. - [Walletless PWA] - Build a Progressive Web App with walletless authentication on Flow, enabling user onboarding without requiring traditional crypto wallets. - [Account Management] - Comprehensive guides for managing Flow accounts, including key management, account linking, and advanced account features. - [Parent Accounts] - Implement parent account functionality on Flow to manage hierarchical account structures and delegate account operations securely. - [Child Accounts] - Create and manage child accounts on Flow for hierarchical account structures and delegated account management with proper access controls. - [Account Linking with Dapper] - Link Flow accounts with Dapper Wallet to enable seamless user experiences and account management across different wallet providers. - [Fork Testing] - Run Cadence tests against a forked mainnet using real contracts and production data without deploying to live networks, enabling safe integration testing. ## [Flow EVM Development] Comprehensive tutorials for building on Flow EVM using Solidity smart contracts and Ethereum-compatible tools and frameworks. - [EVM Setup] - Setup guides for Flow EVM development environment, network configuration, and toolchain preparation. - [EVM Development Tools] - Overview of development tools for building Solidity smart contracts on Flow EVM, including Hardhat, Foundry, and Remix IDE. - [Flow Hardhat Guide] - Using Hardhat to deploy a Solidity contract to Flow EVM with step-by-step configuration, deployment, and interaction examples including contract verification. - [Using Foundry with Flow] - Using Foundry to deploy a Solidity contract to Flow EVM, covering ERC-20 token development, testing, deployment, and state interaction with Foundry tools. - [Flow Remix Guide] - Deploy and interact with Solidity smart contracts on Flow EVM using the Remix IDE with network configuration and contract verification. - [EVM Frameworks] - JavaScript frameworks and libraries for building frontend applications that interact with Flow EVM, including RainbowKit, wagmi, Ethers.js, and Web3.js. - [RainbowKit Integration] - Integrate RainbowKit with Flow EVM to provide wallet connection functionality in React applications with custom wallet support and network configuration. - [Wagmi Integration] - Integrate wagmi React hooks with Flow EVM for type-safe Ethereum interactions, wallet management, and smart contract integration in React applications. - [Ethers.js Integration] - Connect to Flow EVM using Ethers.js library for blockchain interactions, smart contract deployment, and transaction management in JavaScript applications. - [Web3.js Integration] - Use Web3.js library to interact with Flow EVM, covering wallet connections, smart contract interactions, and transaction handling in JavaScript applications. - [Build a Fully-Onchain Image Gallery] - Learn how to store images up to approximately 32kb onchain on Flow EVM using Solidity smart contracts and Next.js frontend. Spend millions of gas for less than a tenth of a cent. ## [Token Development] Tutorials for creating, deploying, and managing fungible tokens and NFTs on Flow using both Cadence and Solidity smart contracts. - [Fungible Token (Cadence)] - Create and deploy fungible tokens using Cadence on Flow with proper standards implementation, minting, and transfer functionality. - [NFT (Cadence)] - Build and deploy Non-Fungible Token contracts using Cadence with MetadataViews implementation for marketplace compatibility and proper resource handling. - [Register Your ERC20 Token] - Register ERC-20 tokens deployed on Flow EVM with the Flow Token Registry for ecosystem integration and cross-VM compatibility. - [Register Your Assets in Cadence] - Register Cadence-based fungible tokens and NFTs with the Flow Token Registry for ecosystem-wide recognition and integration with wallets and applications. ## [Cross-VM Applications] Build applications that span both Flow EVM and Cadence virtual machines, enabling unique cross-VM functionality and asset interoperability. - [Cross-VM Application Introduction] - Introduction to building applications that leverage both Flow EVM and Cadence environments for enhanced functionality and cross-VM asset management. - [Add Flow Cadence to Your wagmi App] - Integrate cross-VM functionality with wagmi React hooks to enable seamless interactions between Flow EVM and Cadence environments in frontend applications. - [Interacting with COAs] - Interact with Cadence-Owned Accounts (COA) to bridge assets and functionality between Cadence and EVM environments on Flow blockchain. - [Batched EVM Transactions] - Execute batched transactions on Flow EVM to improve efficiency and enable atomic multi-operation workflows. - [Direct Calls] - Make direct calls between Cadence and EVM environments on Flow for seamless cross-VM smart contract interactions and data exchange. - [Cross-VM Bridge] - Use the VM Bridge to transfer assets and data between Flow's Cadence and EVM environments for cross-VM application development. ## [Native VRF (Built-in Randomness)] Tutorials for using Flow's native Verifiable Random Function (VRF) to generate cryptographically secure random numbers in smart contracts. - [Commit-Reveal with Cadence] - Implement commit-reveal schemes using Flow's native VRF in Cadence smart contracts for secure random number generation and fair gaming applications. - [VRF in Solidity] - Access Flow's native Verifiable Random Function from Solidity smart contracts deployed on Flow EVM for random number generation. ## [Gasless Transactions] Implement gasless transaction patterns on Flow to improve user experience by removing the need for users to hold native tokens for gas fees. - [Gas-Free EVM Endpoint] - Build a sponsored transaction EVM endpoint to enable sponsored transactions that remove gas fee barriers for users interacting with EVM smart contracts. ## [Use AI to Build on the Flow Blockchain] Comprehensive tutorials for integrating AI tools and services with Flow blockchain development, covering LLMs, AI agents, development assistants, and automated workflows. - [Large Language Models (LLMs)] - Learn how to integrate various AI assistants and large language models with Flow development to enhance productivity, code quality, and development workflows. - [Use Flow Knowledge Base in ChatGPT] - Create a Custom GPT using ChatGPT that references Flow's comprehensive documentation to answer development questions and provide Flow-specific guidance. - [Claude Code for Flow Development] - Learn how to leverage Claude Code for efficient Cadence smart contract development and Flow blockchain application building with AI-powered workflows and systematic deployment strategies. - [Use Flow Knowledge Base in Gemini AI] - Create a Custom GEM using Gemini AI that specializes in Flow blockchain development with access to comprehensive Flow documentation and development guidance. - [Cursor IDE Integration] - Comprehensive guidance for setting up and using Cursor with Flow's documentation ecosystem through data sources, indexing, and Cadence rules for enhanced AI-assisted development. - [Cadence Rules] - Learn how to use Cursor Rules to enhance AI assistance for Cadence and Flow development with persistent context, specialized syntax patterns, and automated workflows. - [Flow Data Sources] - Flow Data Sources is a comprehensive repository that automatically aggregates and formats Flow ecosystem content into Markdown files optimized for AI ingestion and development assistance. - [Indexing Flow Documentation in Cursor] - Step-by-step guide for indexing Flow documentation within Cursor's AI system to create a comprehensive Flow development environment with enhanced AI assistance. - [AI Agents] - Build intelligent AI agents on Flow blockchain using frameworks like Eliza for autonomous blockchain interactions and smart contract automation. - [AgentKit Flow Guide] - Build AI agents on Flow using AgentKit framework for creating intelligent blockchain applications with natural language processing and automated smart contract interactions. - [Eliza on Flow] - Learn how to build AI Agent on Flow with Eliza framework, covering setup, configuration, character creation, and plugin development for intelligent blockchain agents. - [Eliza Plugin Guide] - Learn how to build Eliza plugins for your AI Agent on Flow, covering plugin development workflow, dependency injection, and plugin registry integration. - [Model Context Protocol (MCP)] - Learn about Model Context Protocol (MCP) for Flow blockchain development, enabling standardized AI context sharing and enhanced development tool integration. - [Use MCP in Cursor] - Integrate Model Context Protocol (MCP) with Cursor IDE to enhance AI assistance for Flow blockchain development with standardized context sharing. - [Contribute to MCP] - Contribute to Model Context Protocol (MCP) development for Flow blockchain, enabling better AI integration and context sharing across development tools. ## [Integrations] Integration guides for third-party services and tools that enhance Flow blockchain development, including payment processors, authentication providers, and infrastructure services. - [Crossmint Integration] - Comprehensive integration guides for using Crossmint's Web3 infrastructure on Flow, covering authentication, payment checkout, and minting platform features. - [Authentication Integration Guide] - Set up user authentication for your Flow application using Crossmint's integrated authentication system with email, social logins, and wallet connections for unified identity management. - [Payment Checkout Integration] - Enable fiat and cross-chain payments for Flow assets with credit cards, Apple Pay, Google Pay, and crypto across 40+ chains using hosted, embedded, or headless checkout solutions. - [Minting Platform Integration] - Create and distribute tokens at scale on Flow using Crossmint's no-code and API-based minting platform with smart contract deployment and airdrop capabilities. ### Building in Web3 has never been easier Flow will continue to provide quality walkthroughs and tutorials to provide developers all of the tools needed to build the next generation of web3 apps on a fast blockchain, with built in randomness, gasless transactions, and AI integration. [Flow 101]: ./flow-101.md [Flow Blockchain 101]: ./flow-101.md [Forte Network Upgrade]: ./forte/index.md [Forte Overview]: ./forte/index.md [Flow Actions]: ./forte/flow-actions/index.md [Scheduled Transactions]: ./forte/scheduled-transactions/index.md [Introduction to Flow Actions]: ./forte/flow-actions/intro-to-flow-actions.md [Flow Actions Transactions]: ./forte/flow-actions/flow-actions-transaction.md [Connectors]: ./forte/flow-actions/connectors.md [Basic Combinations]: ./forte/flow-actions/basic-combinations.md [Scheduled Transactions Introduction]: ./forte/scheduled-transactions/scheduled-transactions-introduction.md [Cadence Development]: ./cadence/index.md [Mobile Development]: ./cadence/mobile/index.md [iOS Quickstart]: ./cadence/mobile/ios-quickstart.md [React Native Quickstart]: ./cadence/mobile/react-native-quickstart.md [Walletless PWA]: ./cadence/mobile/walletless-pwa.md [Account Management]: ./cadence/account-management/index.md [Parent Accounts]: ./cadence/account-management/parent-accounts.md [Child Accounts]: ./cadence/account-management/child-accounts.md [Account Linking with Dapper]: ./cadence/account-management/account-linking-with-dapper.md [Fork Testing]: ./cadence/fork-testing/index.md [Flow EVM Development]: ./evm/index.md [EVM Setup]: ./evm/setup/index.md [EVM Development Tools]: ./evm/development-tools/index.md [Flow Hardhat Guide]: ./evm/development-tools/hardhat.md [Using Foundry with Flow]: ./evm/development-tools/foundry.md [Flow Remix Guide]: ./evm/development-tools/remix.md [EVM Frameworks]: ./evm/frameworks/index.md [RainbowKit Integration]: ./evm/frameworks/rainbowkit.md [Wagmi Integration]: ./evm/frameworks/wagmi.md [Ethers.js Integration]: ./evm/frameworks/ethers.md [Web3.js Integration]: ./evm/frameworks/web3-js.md [Build a Fully-Onchain Image Gallery]: ./evm/image-gallery.md [Token Development]: ./tokens/index.md [Fungible Token (Cadence)]: ./tokens/fungible-token-cadence.md [NFT (Cadence)]: ./tokens/nft-cadence.md [Register Your ERC20 Token]: ./tokens/register-erc20-token.md [Register Your Assets in Cadence]: ./tokens/register-cadence-assets.md [Cross-VM Applications]: ./cross-vm-apps/index.md [Cross-VM Application Introduction]: ./cross-vm-apps/introduction.md [Add Flow Cadence to Your wagmi App]: ./cross-vm-apps/add-to-wagmi.md [Interacting with COAs]: ./cross-vm-apps/interacting-with-coa.md [Batched EVM Transactions]: ./cross-vm-apps/batched-evm-transactions.md [Direct Calls]: ./cross-vm-apps/direct-calls.md [Cross-VM Bridge]: ./cross-vm-apps/vm-bridge.md [Native VRF (Built-in Randomness)]: ./native-vrf/index.md [Commit-Reveal with Cadence]: ./native-vrf/commit-reveal-cadence.md [VRF in Solidity]: ./native-vrf/vrf-in-solidity.md [Gasless Transactions]: ./gasless-transactions/index.md [Gas-Free EVM Endpoint]: ./gasless-transactions/sponsored-transactions-evm-endpoint.md [Use AI to Build on the Flow Blockchain]: ./use-AI-to-build-on-flow/index.md [Large Language Models (LLMs)]: ./use-AI-to-build-on-flow/llms/index.md [Use Flow Knowledge Base in ChatGPT]: ./use-AI-to-build-on-flow/llms/chatgpt.md [Claude Code for Flow Development]: ./use-AI-to-build-on-flow/llms/claude-code.md [Use Flow Knowledge Base in Gemini AI]: ./use-AI-to-build-on-flow/llms/gemini.md [Cursor IDE Integration]: ./use-AI-to-build-on-flow/cursor/index.md [Cadence Rules]: ./use-AI-to-build-on-flow/cursor/cadence-rules.md [Flow Data Sources]: ./use-AI-to-build-on-flow/cursor/flow-data-sources.md [Indexing Flow Documentation in Cursor]: ./use-AI-to-build-on-flow/cursor/indexing-docs.md [AI Agents]: ./use-AI-to-build-on-flow/agents/index.md [AgentKit Flow Guide]: ./use-AI-to-build-on-flow/agents/agentkit-flow-guide.md [Eliza on Flow]: ./use-AI-to-build-on-flow/agents/eliza/index.md [Eliza Plugin Guide]: ./use-AI-to-build-on-flow/agents/eliza/build-plugin.md [Model Context Protocol (MCP)]: ./use-AI-to-build-on-flow/mcp/index.md [Use MCP in Cursor]: ./use-AI-to-build-on-flow/mcp/use-mcp-in-cursor.md [Contribute to MCP]: ./use-AI-to-build-on-flow/mcp/contribute-to-mcp.md [Integrations]: ./integrations/index.md [Crossmint Integration]: ./integrations/crossmint/index.md [Authentication Integration Guide]: ./integrations/crossmint/authentication.md [Payment Checkout Integration]: ./integrations/crossmint/payment-checkout.md [Minting Platform Integration]: ./integrations/crossmint/minting-platform.md --- # File: /md/blockchain-development-tutorials/cadence.md --- title: Cadence Tutorials description: Learn Cadence development through practical tutorials covering account management, mobile development, and advanced Flow features. sidebar_position: 5 keywords: - Cadence - Flow development - account management - mobile development - hybrid custody - account linking - blockchain tutorials - smart contracts - Flow tutorials - native applications --- # Cadence tutorials Master Cadence development on Flow through comprehensive tutorials that cover essential concepts and practical implementations. These tutorials guide you through how to build real-world applications with Flow's unique features like account linking, mobile integration, and advanced account management patterns. ## What you'll learn In this tutorial series, you'll discover how to: - Implement Flow's unique account linking and hybrid custody models. - Build native mobile applications with Flow integration. - Create progressive onboarding experiences for mainstream users. - Manage complex account relationships and permissions. - Leverage Flow's native features in mobile environments. - Build secure, user-friendly blockchain applications. # Cadence development tutorials ## Account Management Learn about Flow's revolutionary Account Linking system that allow shared ownership and progressive onboarding experiences. These tutorials cover the hybrid custody model, parent-child account relationships, and how to implement walletless onboarding that seamlessly transitions users to self-custody. - **[Account Linking Overview]** - Understand Flow's unique account linking concepts and hybrid custody model. - **[Building with Child Accounts]** - Create walletless applications with progressive onboarding flows. - **[Parent Account Management]** - Implement parent account functionality and account delegation. - **[Account Linking with Dapper]** - Integrate with Dapper Wallet's account linking system. ## Mobile development Discover how to build native mobile applications that leverage Flow's blockchain capabilities. These tutorials cover mobile SDKs, secure key management, wallet integration, and how to create rich user experiences on iOS and Android platforms. - **[Mobile Development Overview]** - Learn about Flow's mobile development capabilities and ecosystem. - **[iOS Quickstart]** - Build your first iOS app with Flow integration. - **[React Native Integration]** - Create cross-platform mobile apps with React Native and Flow. - **[Walletless Mobile Apps]** - Implement progressive web apps with walletless onboarding. ## Testing Learn how to test Cadence smart contracts with a focus on fork testing, which allows you to run tests against real on-chain state from Flow mainnet or testnet. - **[Fork Testing]** - Run Cadence tests against a forked mainnet with real contracts and production data without deploying to live networks. ## Key features covered ### Account Linking & hybrid custody - **Progressive onboarding**: Start users with custodial accounts, transition to self-custody. - **Shared ownership**: Allow multiple parties to control and access accounts. - **Flexible permissions**: Fine-grained access control through capabilities and entitlements. - **Seamless transitions**: Move from custodial to non-custodial without losing assets. ### Mobile-first development - **Native integration**: Build truly native mobile experiences with blockchain functionality. - **Secure key management**: Leverage device security features for key storage. - **Wallet integration**: Connect with popular Flow wallets and WalletConnect. - **Rich user experiences**: Create smooth, Web2-like experiences in Web3 apps. ### Flow-specific advantages - **Account abstraction**: Built-in multi-signature and sponsored transactions. - **Resource safety**: Cadence's resource-oriented programming prevents common mobile app vulnerabilities. - **Fast finality**: Near-instant transaction confirmation for responsive mobile UIs. - **Low costs**: Turn on micro-transactions and frequent interactions in mobile apps. ## Next steps After you complete these tutorials, you'll be equipped to build sophisticated Cadence applications that leverage Flow's unique capabilities. Consider exploring our other tutorial series: - **[Flow Actions]** - Build composable DeFi workflows with standardized interfaces. - **[Cross-VM Apps]** - Create applications that span both Cadence and Flow EVM. - **[Native VRF]** - Implement verifiable random functions in your applications. - **[Use AI to Build on Flow]** - Enhance development with AI-powered tools. ## Conclusion Flow's Cadence offers unparalleled capabilities for building user-friendly blockchain applications. The combination of account linking, mobile-first development tools, and resource-oriented programming creates opportunities to build mainstream-ready Web3 applications. These tutorials provide the foundation for creating applications that can onboard millions of users while maintaining the security and decentralization principles of blockchain technology. [Account Linking Overview]: ./account-management/index.md [Building with Child Accounts]: ./account-management/child-accounts.md [Parent Account Management]: ./account-management/parent-accounts.md [Account Linking with Dapper]: ./account-management/account-linking-with-dapper.md [Mobile Development Overview]: ./mobile/index.md [iOS Quickstart]: ./mobile/ios-quickstart.md [React Native Integration]: ./mobile/react-native-quickstart.md [Walletless Mobile Apps]: ./mobile/walletless-pwa.md [Fork Testing]: ./fork-testing/index.md [Flow Actions]: ../forte/flow-actions/index.md [Cross-VM Apps]: ../cross-vm-apps/index.md [Native VRF]: ../native-vrf/index.md [Use AI to Build on Flow]: ../use-AI-to-build-on-flow/index.md --- # File: /md/blockchain-development-tutorials/cadence/account-management.md --- title: Account Linking sidebar_position: 3 description: Learn about Flow's unique account linking feature that enables shared ownership of accounts. Understand how accounts can be accessed, delegated, and managed through capabilities and hybrid custody. keywords: - account linking - FLIP 72 - account capabilities - hybrid custody - account access - account delegation - Flow accounts - account ownership - account security - parent accounts - child accounts - account management - Flow protocol - account control - custody model --- # Account Linking Account Linking is a unique Flow concept that allows sharing ownership over [accounts]. To understand how we can achieve that, we must first understand how to access accounts on Flow. You can access accounts on flow in Cadence through two types, `PublicAccount` and `Account`. As the name implies, the `PublicAccount` type gives access to all public account information such as address, balance, storage capacity, etc., but doesn't allow changes to the account. The `Account` type (or more specifically, an [entitled]`&Account`) allows the same access as `PublicAccount` but also allows changes to the account, which includes adding or revoking account keys, managing the deployed contracts, as well as linking and publishing Capabilities. ![Flow account structure](./imgs/account-structure.png) ## Access Account When you access `Account`, you can modify account storage, so it's essential to mandate that the account being accessed signs all transactions, which safeguards this access. [Account entitlements] allow for more granular access control over the specific parts of the account that you can access from within the signed transaction. A transaction can list multiple authorizing account it wants to access as part of the `prepare` section of the transaction. Read more about transaction signing in the [transaction documentation]. Since access to the `Account` object allows state change, the idea of account ownership actually translates to the ability to access the underlying account. Traditionally, you might consider this the same as having key access on an account, but we'll see in just a minute how programmatic, ownership-level access is unlocked with [Cadence Capabilities] ## Account Capabilities Before you continue with this section, you'll need a clear understanding of [Cadence Capabilities]. Advanced features such as Account Capabilities are powerful, but they can put your app or users at risk if used incorrectly. Cadence allows for Capabilities creation to delegate access to account storage, which means any account that obtains a valid Ccapability to another account object in the storage can access it. This is a powerful feature on its own - to access another account programmatically without the need for an active key on the accessible account. You can limit the access to the object when you create a Capability so your users can only access intended functions or fields. Account Linking is made possible by the extension of Capabilities on the `Account` object itself. Similar to how storage capabilities allow access to a value stored in an account's storage, `&Account` Capabilities allow delegated access to the issuing `Account`. These Capabilities allow for access to key assignment, contract deployment, and other privileged actions on the delegating `Account` - which effectively shares account ownership without the need to add or share a key. The delegating account can revoke this Capability at any time. ### Create Account Links When we refer to 'Account Linking,' we mean that the parent account creates an `&Account` Capability and published to another account. The account that owns the `&Account` Capability which was made available to another account is the child account. The account in possession of the Capability given by the child account becomes its parent account. ![Account linking on Flow relational diagram](./imgs/account-linking-relational-diagram.png) You can create a link between two current accounts on Flow in two steps: 1. A child account creates an `&Account` Capability and publishes it to the parent account. 2. The parent account, claims that Capability and can access the child's account through it. ![Account linking steps on Flow](./imgs/account-linking-steps-high-level.png) These two steps are implemented in Cadence as two transactions: **\*\***\*\*\*\***\*\***\*\*\*\***\*\***\*\*\*\***\*\***Create capability**\*\***\*\*\*\***\*\***\*\*\*\***\*\***\*\*\*\***\*\*** The account B creates and publishes the `&Account` Capability to the account A at the address `0x01` ```cadence #allowAccountLinking transaction { prepare(signer: auth(IssueAccountCapabilityController, PublishInboxCapability) &Account) { // Issue a fully-entitled account capability let capability = signer.capabilities .account .issue() // Publish the capability for the specified recipient signer.inbox.publish(capability, name: "accountCapA", recipient: 0x1) } } ``` \***\*\*\*\*\*\*\***\*\*\*\*\***\*\*\*\*\*\*\***Claim capability\***\*\*\*\*\*\*\***\*\*\*\*\***\*\*\*\*\*\*\*** The account A claims the Capability published by account B. ```cadence transaction { prepare(signer: auth(ClaimInboxCapability) &Account) { let capabilityName = "accountCapB" let providerAddress = 0x2 // Claim the capability published by the account 0x2 let capability = signer.inbox .claim( capabilityName, provider: providerAddress ) ?? panic( "Capability with name ".concat(capabilityName) .concat(" from provider ").concat(providerAddress.toString()) .concat(" not found") ) // Simply borrowing an Account reference here for demonstration purposes let accountRef = capability.borrow()! } } ``` ## What is Account Linking most useful for? Account Linking was specifically designed to allow smooth and seamless custodial onboarding of users to your Flow-based application without the requirement of a wallet to do so. This pattern overcomes both the technical hurdle, as well as user's reluctance to install a wallet, which opens access to Flow applications to every user. Users can experience an app without any delay and still offer a path to self-sovreign ownership. Naturally, users may expect to use their account with another application, or otherwise move assets stored in that account elsewhere - at minimum from their wallet. When an app initially leverages Account Linking, the app creates the account instead of the user and stores that user's specific state in the app-created account. At a later point, users can take ownership of the app account if they possess a full [Flow account], which they can get if they install a wallet app. Account Linking allows users to possess multiple linked child accounts from different apps. Access complexities associated with those child accounts are eliminated if you abstract access to them through the user's parent account. :::info Simply put, child accounts are accessed and can be treated as a seamless part of the parent account. ::: All assets in the app account can now jump the walled garden to play in the rest of the Flow ecosystem. The user does not need to rely on the custodial app to execute transactions moving assets from the child account as the parent account already has access to the assets in the child account. ![Multiple parent-child accounts on Flow](./imgs/account-linking-multiple-accounts.png) This shared control over the digital items in the in-app account allows users to establish real ownership of the items beyond the context of the app, where they can use their parent account to view inventory, take the items to other apps in the ecosystem, such as a marketplace or a game. Most importantly, users can do this without the need to transfer the digital items between accounts, which makes it seamless to continue to use the original app and enjoy their assets in other contexts. ## Security considerations Account Linking is a _very_ powerful Cadence feature, and thus you must treat it with care. So far in this document, we've discussed Account Linking between two accounts we own, even if a third-party application manages the child account. But, we can't make the same trust assumptions about custodial accounts in the real world. If we create an `&Account` Capability and publish it to an account we don't own, we give that account full access to our account. This should be seen as an anti-pattern. :::warning If you create an `&Account` Capability and share it with a third-party account, you effectively give that person your account's private keys. ::: Because unfiltered account linking can be dangerous, Flow introduces the `HybridCustody` contract that helps custodial applications regulate access and allows parent accounts to manage their many child accounts and assets within them. Learn more about it in the [Working With Parent Accounts] documentation. ## Hybrid custody and Account Linking Apps need assurances that their own resources are safe from malicious actors, so to permit full access might not be what they want. Hybrid custody contracts will allow the app to maintain control of their managed accounts, but they can: 1. Share capabilities freely, with a few built-in controls over the types of capabilities that can be retrieved by parent accounts via helper contracts (the `CapabilityFactory`, and `CapabilityFilter`). 2. Share additional capabilities (public or private) with a parent account via a `CapabilityDelegator` resource. ### Guides - [Building Walletless Applications Using Child Accounts] covers how apps can leverage Account Linking to create a seamless user experience and allow future self-custody. - [Working With Parent Accounts] covers features activated by the core `HybridCustody` contract to access child account assets from parent accounts. This is useful for apps like marketplaces or wallets that work with accounts that have potential child accounts. ### Resources - [Forum Post] where core concepts were introduced and discussed. - [GitHub repository] where `HybridCustody` core contracts and scripts are maintained. Check out the repository for more advanced script or transaction examples. - [Example] - Account Linking project with [Magic]. - [Starter template] for [Niftory] Account Linking API. [accounts]: ../../../build/cadence/basics/accounts.md [Account entitlements]: https://cadence-lang.org/docs/language/accounts/#performing-write-operations [Building Walletless Applications Using Child Accounts]: ./child-accounts.md [Cadence capabilities]: https://cadence-lang.org/docs/language/capabilities [entitled]: https://cadence-lang.org/docs/language/access-control#entitlements) [Flow account]: ../../../build/cadence/basics/accounts.md [Forum Post]: https://forum.flow.com/t/hybrid-custody/4016 [GitHub repository]: https://github.com/onflow/hybrid-custody [Example]: https://github.com/jribbink/magic-link-hc-sample/ [Magic]: https://magic.link/ [Starter template]: https://github.com/Niftory/niftory-samples/tree/main/walletless-onboarding [Niftory]: https://niftory.com/ [transaction documentation]: ../../../build/cadence/basics/transactions.md [Working With Parent Accounts]: ./parent-accounts.md --- # File: /md/blockchain-development-tutorials/cadence/account-management/account-linking-with-dapper.md --- title: Account Linking With NBA Top Shot description: Use Account Linking between the Dapper Wallet and Flow Wallet to effortlessly use NBA Top Shot Moments in your app. sidebar_position: 3 sidebar_custom_props: icon: ⛓️ keywords: - NBA Top Shot - Dapper Wallet - account linking - Flow wallet - NFT integration - Moments NFTs - wallet connection - Flow dApp - blockchain integration - smart contracts - wallet management - NFT display - Flow development - wallet interoperability - asset management --- # Account Linking with NBA Top Shot [Account Linking] is a powerful Flow feature that allows users to connect their wallets, which allows linked wallets to view and manage assets in one wallet with another. This feature helps reduce or even eliminate the challenges posed by other account abstraction solutions, which often lead to multiple isolated wallets and fragmented assets. ![Top Shot Preview](./imgs/top-shot-preview.png) In this tutorial, you'll build a [simple onchain app] that allows users to sign in to your app with their Flow wallet and view [NBA Top Shot] Moments that reside in their [Dapper Wallet] without the need for those users to sign in with Dapper. ## Objectives After you complete this guide, you'll be able to: - Pull your users' NBA Top Shot Moments into your Flow app without the need to transfer them out of their Dapper wallet. - Retrieve and list all NFT collections in any child wallet linked to a given Flow address. - Write a [Cadence] script to iterate through the storage of a Flow wallet to find NFT collections. - Run Cadence Scripts from the frontend. ## Prerequisites ### Next.js and Modern Frontend Development This tutorial uses [Next.js]. You don't need to be an expert, but it's helpful to be comfortable with development with a current React framework. You'll be on your own to select and use a package manager, manage Node versions, and other frontend environment tasks. If you don't have your own preference, you can just follow along with us and use [Yarn]. ### Flow Wallet You'll need a [Flow Wallet], but you don't need to deposit any funds. ## Moments NFTs You'll need a [Dapper Wallet] that contains some Moments NFTs, such as [NBA Top Shot] Moments. ## Get Started This tutorial will use a [Next.js] project as the foundation of the frontend. Create a new project with: ```zsh npx create-next-app@latest ``` We will use TypeScript and the App Router, in this tutorial. Open your new project in the editor of your choice, install dependencies, and run the project. ```zsh yarn install yarn run dev ``` If everything works properly, you can navigate to `localhost:3000` and see the default [Next.js] page. ## Flow Cadence setup You'll need a few more dependencies to efficiently work with Cadence inside of your app. ### Flow CLI and types The [Flow CLI] contains a number of command-line tools to interact with the Flow ecosystem. If you don't have it installed, you can add it with Brew (or with [other installation methods]): ```zsh brew install flow-cli ``` After it's installed, initialize Flow in your `Next.js` project. From the root, run: ```zsh flow init --config-only ``` The `--config-only` flag [initializes a project] with the just the config file. This allows the Flow CLI to interact with your project without the need to add the other files you want for most projects. Next, you'll need to do a little bit of config work so that your project knows how to read Cadence files. Install the Flow Cadence Plugin: ```zsh yarn add flow-cadence-plugin --dev ``` Finally, open `next.config.ts` and update it to use the plugin with Raw Loader: ```tsx // next.config.ts import type { NextConfig } from 'next'; import FlowCadencePlugin from 'flow-cadence-plugin'; const nextConfig: NextConfig = { webpack: (config) => { config.plugins.push(new FlowCadencePlugin()); return config; }, }; export default nextConfig; ``` ## Frontend setup We'll use the Flow Client Library [FCL] to manage blockchain interaction from the frontend. It's similar to `viem`, `ethers`, or `web3.js`, but works with the Flow blockchain and transactions and scripts written in Cadence. ```zsh yarn add @onflow/fcl ``` Go ahead and install `dotenv` as well: ``` yarn add dotenv ``` ### Provider setup You'll need a fair amount of boilerplate code to set up your provider. We'll provide it, but since it's not the purpose of this tutorial, we'll be brief on explanations. For more details, check out the [App Quickstart Guide]. Add `app/providers/AuthProvider.tsx`: ```tsx 'use client'; /* eslint-disable @typescript-eslint/no-explicit-any */ import { createContext, useContext, ReactNode } from 'react'; import useCurrentUser from '../hooks/use-current-user.hook'; interface State { user: any; loggedIn: any; logIn: any; logOut: any; } const AuthContext = createContext(undefined); interface AuthProviderProps { children: ReactNode; } const AuthProvider: React.FC = ({ children }) => { const [user, loggedIn, logIn, logOut] = useCurrentUser(); return ( {children} ); }; export default AuthProvider; export const useAuth = (): State => { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within a AuthProvider'); } return context; }; ``` Then, add `app/hooks/use-current-user-hook.tsx`: ```tsx import { useEffect, useState } from 'react'; import * as fcl from '@onflow/fcl'; export default function useCurrentUser() { const [user, setUser] = useState({ addr: null }); const logIn = () => { fcl.authenticate(); }; const logOut = () => { fcl.unauthenticate(); }; useEffect(() => { fcl.currentUser().subscribe(setUser); }, []); return { user, loggedIn: user?.addr != null, logIn, logOut }; } ``` ## .env Add a `.env` to the root and fill it with: ```text NEXT_PUBLIC_ACCESS_NODE_API="https://rest-mainnet.onflow.org" NEXT_PUBLIC_FLOW_NETWORK="mainnet" NEXT_PUBLIC_WALLETCONNECT_ID= ``` :::warning Don't forget to replace `` with your own [Wallet Connect] app id! ::: ### Implement the provider and Flow Ccnfig Finally, open `layout.tsx`. TO start, import Flow dependencies and the AuthProvider: ```tsx import flowJSON from '../flow.json'; import * as fcl from '@onflow/fcl'; import AuthProvider from './providers/AuthProvider'; ``` Then add your Flow config: ```tsx fcl .config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/authn', 'accessNode.api': process.env.NEXT_PUBLIC_ACCESS_NODE_API, 'flow.network': process.env.NEXT_PUBLIC_FLOW_NETWORK, 'walletconnect.projectId': process.env.NEXT_PUBLIC_WALLETCONNECT_ID, }) .load({ flowJSON }); ``` :::warning We're going to force some things client side to get this proof-of-concept working quickly. Use `Next.js` best practices for a production app. ::: Add a `'use client';` directive to the top of the file and **delete** the import for Metadata and fonts, as well as the code related to them. Finally, update the `` to remove the font references and suppress hydration warnings: ```tsx ``` Your code should be: ```tsx // layout.tsx 'use client'; import './globals.css'; import flowJSON from '../flow.json'; import * as fcl from '@onflow/fcl'; import AuthProvider from './providers/AuthProvider'; fcl .config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/authn', 'accessNode.api': process.env.NEXT_PUBLIC_ACCESS_NODE_API, 'flow.network': process.env.NEXT_PUBLIC_FLOW_NETWORK, 'walletconnect.projectId': process.env.NEXT_PUBLIC_WALLETCONNECT_ID, }) .load({ flowJSON }); export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` ### Add the connect button Open `page.tsx` and clean up the demo code, leaving only the `
` block: ```tsx import Image from 'next/image'; export default function Home() { return (
TODO
); } ``` Add a `'use client';` directive, import the `useAuth` hook and instantiate it in the `Home` function: ```tsx 'use client'; import { useAuth } from './providers/AuthProvider'; ``` ```tsx const { user, loggedIn, logIn, logOut } = useAuth(); ``` Then add a button in the `
` to handle logging in or out: ```tsx
Welcome
``` ## Test pass Run the app: ```zsh yarn dev ``` ![Welcome](./imgs/welcome.png) Click `Log In` in the middle of the window and log in with your Flow wallet. ![Flow Wallet](./imgs/flow-wallet.png) ## Account Linking Now that your app is set up, you can make use of [Account Linking] to to pull your NFTs from your Dapper Wallet, through your Flow Wallet, and into the app. ### Set up Account Linking If you haven't yet, you'll need to [link your Dapper Wallet] to your Flow Wallet. :::warning The Dapper Wallet requires that you complete KYC before you can use Account Linking. While this may frustrate some members of the community, it makes it much easier for app developers to design onboarding rewards and bonuses that are less farmable. ::: ### Discover the NFTs with a script With your accounts linked, your Flow Wallet now has a set of capabilities related to your Dapper Wallet and it can use those to view and even manipulate those NFTs and assets. Before you can add a script that can handle this, you'll need to import the `HybridCustody` contract with the [Flow Dependency Manager]: ```zsh flow dependencies install mainnet://d8a7e05a7ac670c0.HybridCustody ``` Choose `none` to skip deployment on the `emulator` and skip adding testnet aliases. There's no point, these NFTs are on mainnet! You'll get a complete summary from the Dependency Manager: ```zsh 📝 Dependency Manager Actions Summary 🗃️ File System Actions: ✅️ Contract HybridCustody from d8a7e05a7ac670c0 on mainnet installed ✅️ Contract MetadataViews from 1d7e57aa55817448 on mainnet installed ✅️ Contract FungibleToken from f233dcee88fe0abe on mainnet installed ✅️ Contract ViewResolver from 1d7e57aa55817448 on mainnet installed ✅️ Contract Burner from f233dcee88fe0abe on mainnet installed ✅️ Contract NonFungibleToken from 1d7e57aa55817448 on mainnet installed ✅️ Contract CapabilityFactory from d8a7e05a7ac670c0 on mainnet installed ✅️ Contract CapabilityDelegator from d8a7e05a7ac670c0 on mainnet installed ✅️ Contract CapabilityFilter from d8a7e05a7ac670c0 on mainnet installed 💾 State Updates: ✅ HybridCustody added to emulator deployments ✅ Alias added for HybridCustody on mainnet ✅ HybridCustody added to flow.json ✅ MetadataViews added to flow.json ✅ FungibleToken added to flow.json ✅ ViewResolver added to flow.json ✅ Burner added to flow.json ✅ NonFungibleToken added to flow.json ✅ CapabilityFactory added to emulator deployments ✅ Alias added for CapabilityFactory on mainnet ✅ CapabilityFactory added to flow.json ✅ CapabilityDelegator added to emulator deployments ✅ Alias added for CapabilityDelegator on mainnet ✅ CapabilityDelegator added to flow.json ✅ CapabilityFilter added to emulator deployments ✅ Alias added for CapabilityFilter on mainnet ✅ CapabilityFilter added to flow.json ``` Add `app/cadence/scripts/FetchNFTsFromLinkedAccts.cdc`. In it, add this script. Review the inline comments to see what each step does: ```cadence import "HybridCustody" import "NonFungibleToken" import "MetadataViews" // This script iterates through a parent's child accounts, // identifies private paths with an accessible NonFungibleToken.Provider, and returns the corresponding typeIds access(all) fun main(addr: Address): AnyStruct { let manager = getAuthAccount(addr).storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist") var typeIdsWithProvider: {Address: [String]} = {} var nftViews: {Address: {UInt64: MetadataViews.Display}} = {} let providerType = Type() let collectionType: Type = Type<@{NonFungibleToken.CollectionPublic}>() for address in manager.getChildAddresses() { let acct = getAuthAccount(address) let foundTypes: [String] = [] let views: {UInt64: MetadataViews.Display} = {} let childAcct = manager.borrowAccount(addr: address) ?? panic("child account not found") // Iterate through storage paths to find NFTs that are controlled by the parent account // To just find NFTs, check if thing stored is nft collection and borrow it as NFT collection and get IDs for s in acct.storage.storagePaths { // Iterate through capabilities for c in acct.capabilities.storage.getControllers(forPath: s) { if !c.borrowType.isSubtype(of: providerType){ // If this doen't have providerType, it's not an NFT collection continue } // We're dealing with a Collection but we need to check if accessible from the parent account if let cap: Capability = childAcct.getCapability(controllerID: c.capabilityID, type: providerType) { // Part 1 let providerCap = cap as! Capability<&{NonFungibleToken.Provider}> if !providerCap.check(){ // If I don't have access to control the account, skip it. // Disable this check to do something else. // continue } foundTypes.append(cap.borrow<&AnyResource>()!.getType().identifier) typeIdsWithProvider[address] = foundTypes // Don't need to keep looking at capabilities, we can control NFT from parent account break } } } // Iterate storage, check if typeIdsWithProvider contains the typeId, if so, add to views acct.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { if typeIdsWithProvider[address] == nil { return true } for key in typeIdsWithProvider.keys { for idx, value in typeIdsWithProvider[key]! { let value = typeIdsWithProvider[key]! if value[idx] != type.identifier { continue } else { if type.isInstance(collectionType) { continue } if let collection = acct.storage.borrow<&{NonFungibleToken.CollectionPublic}>(from: path) { // Iterate over IDs & resolve the Display view for id in collection.getIDs() { let nft = collection.borrowNFT(id)! if let display = nft.resolveView(Type())! as? MetadataViews.Display { views.insert(key: id, display) } } } continue } } } return true }) nftViews[address] = views } return nftViews } ``` :::warning The above script is a relatively naive implementation. For production, you'll want to filter for only the collections you care about, and you will eventually need to add handling for very large collections in a wallet. ::: ### Run the script and display the NFTs Add a component in `app/components` called `DisplayLinkedNFTs.cdc`. In it, import dependencies from React and FCL, as well as the script you just added: ```tsx import React, { useState, useEffect } from 'react'; import * as fcl from '@onflow/fcl'; import * as t from '@onflow/types'; import FetchNFTs from '../cadence/scripts/FetchNFTsFromLinkedAccts.cdc'; ``` As we use TypeScript, add some types as well to manage the data from the NFTs nicely. For now, just add them to this file: ```typescript type Thumbnail = { url: string; }; type Moment = { name: string; description: string; thumbnail: Thumbnail; }; type MomentsData = { [momentId: string]: Moment; }; type ApiResponse = { [address: string]: MomentsData; }; interface AddressDisplayProps { address: string; } ``` Then, add the function for the component: ```tsx const DisplayLinkedNFTs: React.FC = ({ address }) => { // TODO... return
Nothing here yet
; }; export default DisplayLinkedNFTs; ``` In the function, add a state variable to store the data that the script retrieves: ```typescript const [responseData, setResponseData] = useState(null); ``` Then, use `useEffect` to fetch the NFTs with the script and `fcl.query`: ```tsx useEffect(() => { const fetchLinkedAddresses = async () => { if (!address) return; try { const cadenceScript = FetchNFTs; // Fetch the linked addresses const response: ApiResponse = await fcl.query({ cadence: cadenceScript, args: () => [fcl.arg(address, t.Address)], }); console.log(JSON.stringify(response, null, 2)); setResponseData(response); } catch (error) { console.error('Error fetching linked addresses:', error); } }; fetchLinkedAddresses(); }, [address]); ``` Return to `page.tsx`, import your new component, and add an instance of `` that passes in the user's address and is only displayed while `loggedIn`. ```tsx { loggedIn && ; } ``` ### Test Run the app again. If you have linked your account and have NFTs in that account, you'll see them in the console! ### Display the moments Now that they're here, all that's left to do is display them nicely! Return to `DisplayLinkedNFTs.tsx`. Add a helper function to confirm each returned NFT matches the Moments format. You can update this to handle other NFTs you'd like to show as well. :::warning Remember, you'll also need to update the script in a production app to filter for only the collections you want, and handle large collections. ::: ```tsx // Type-checking function to validate moment structure // eslint-disable-next-line @typescript-eslint/no-explicit-any const isValidMoment = (moment: any): moment is Moment => { const isValid = typeof moment.name === 'string' && typeof moment.description === 'string' && moment.thumbnail && typeof moment.thumbnail.url === 'string'; if (!isValid) { console.warn('Invalid moment data:', moment); } return isValid; }; ``` Next, add a render function with some basic styling: ```tsx // Function to render moments with validation const renderMoments = (data: ApiResponse) => { return Object.entries(data).map(([addr, moments]) => (

Linked Wallet: {addr}

{Object.entries(moments).map(([momentId, moment]) => isValidMoment(moment) ? (
{moment.name}

{moment.description}

{moment.name}
) : null, )}
)); }; ``` Finally, update the `return` with some more styling and the rendered NFT data: ```tsx return (
{address ? (

Moments Data:

{responseData ? ( renderMoments(responseData) ) : (

No Moments Data Available

)}
) : (
No Address Provided
)}
); ``` ### Further polish Finally, you can polish up your `page.tsx` to look a little nicer, and guide your users to the Account Linking process in the Dapper Wallet: ```tsx 'use client'; import DisplayLinkedNFTs from './components/DisplayLinkedNFTs'; import { useAuth } from './providers/AuthProvider'; export default function Home() { const { user, loggedIn, logIn, logOut } = useAuth(); return (
{/* Message visible for all users */}

Please link your Dapper wallet to view your NFTs. For more information, check the{' '} Account Linking and FAQ .

{/* Display user address or linked NFTs if logged in */} {loggedIn ? (
Address: {user.addr}
) : (
Please log in to view your linked NFTs.
)} {/* Login/Logout Button */}
{/* Display NFTs if logged in */} {loggedIn && }
); } ``` Your app will now look like the [simple onchain app] demo! ## Conclusion In this tutorial, you took your first steps towards building powerful new experiences that meet you customers where they are. They can keep their assets in the wallet associate with one app, but also give your app the ability to use them - seamlessly, safely, and beautifully! ## Reference Solution :::warning You **won't** save time if you skipo the the reference implementation. You'll learn much faster if you do the tutorials as presented! Reference solutions are functional, but may not be optimal. ::: [Reference Solution] [Account Linking]: ./index.md [NBA Top Shot]: https://nbatopshot.com [simple onchain app]: https://nextjs-topshot-account-linking.vercel.app [Dapper Wallet]: https://meetdapper.com [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [Yarn]: https://yarnpkg.com [Flow CLI]: ../../../build/tools/flow-cli/index.md [other installation methods]: ../../../build/tools/flow-cli/install.md [initializes a project]: ../../../build/tools/flow-cli/commands.md#init [Flow Dependency Manager]: ../../../build/tools/flow-cli/dependency-manager.md [FCL]: ../../../build/tools/clients/fcl-js/index.md [App Quickstart Guide]: ../getting-started/index.md [Wallet Connect]: https://cloud.walletconnect.com/sign-in [Flow Wallet]: https://wallet.flow.com [link your Dapper Wallet]: https://support.meetdapper.com/hc/en-us/articles/20744347884819-Account-Linking-and-FAQ [Reference Solution]: https://github.com/briandoyle81/nextjs-topshot-account-linking --- # File: /md/blockchain-development-tutorials/cadence/account-management/child-accounts.md --- title: Building Walletless Applications Using Child Accounts sidebar_position: 1 description: Learn how to implement progressive onboarding flows using Flow's Hybrid Custody model. Create walletless applications, manage child accounts, and enable account linking for seamless user experiences. keywords: - child accounts - hybrid custody - account linking - walletless onboarding - progressive onboarding - Flow accounts - custody model - account delegation - blockchain onboarding - account capabilities - parent accounts - account management - Flow development - account security - user onboarding --- # Building Walletless Applications Using Child Accounts In this tutorial, we'll dive into a progressive onboarding flow, along with the Cadence scripts and transactions that go into its implementation in your app. These components will allow any implementing app to create a custodial account, mediate the user's onchain actions on their behalf, and later delegate access of that app-created account to the user's wallet. We'll refer to this custodial pattern as the Hybrid Custody Model and the process of app account control delegation as Account Linking. ## Objectives - Create a [walletless onboarding] transaction. - Link a current app account as a child to a newly authenticated parent account. - Get your app to recognize "parent" accounts along with any associated "child" accounts. - Put it all together to create a blockchain-native onboarding transaction. - View fungible and non-fungible Token metadata that relates to assets across all of a user's associated accounts - their wallet-mediated "parent" account and any "child" accounts. - Facilitate transactions acting on assets in child accounts. ## Point of clarity Before we dive in, let's make a distinction between "Account Linking" and "linking accounts". ### Account Linking :::info Since Account Linking is a sensitive action, transactions where an account may be linked are designated by a topline pragma `#allowAccountLinking`. This lets wallet providers inform users that their account may be linked in the signed transaction. ::: Very simply, Account Linking is a [feature in Cadence] that lets an [Account] create a [Capability] on itself. Below is an example that demonstrates how to issue an `&Account` Capability from a signing account transaction: ```cadence link_account.cdc #allowAccountLinking transaction(linkPathSuffix: String) { prepare(signer: auth(IssueAccountCapabilityController) &Account) { // Issues a fully-entitled Account Capability let accountCapability = signer.capabilities .account .issue() } } ``` From there, the signing account can retrieve the privately linked `&Account` Capability and delegate it to another account, which revokes the Capability if they wish to revoke delegated access. To link an account, a transaction must state the `#allowAccountLinking` pragma in the top line of the transaction. This is an interim safety measure so that wallet providers can notify users they're about to sign a transaction that may create a Capability on their `Account`. ### Linking accounts Linking accounts leverages this account link, otherwise known as an **`&Account` Capability**, and encapsulates it. The [components and actions] involved in this process - what the Capability is encapsulated in, the collection that holds those encapsulations, and so on is what we'll dive into in this doc. ## Terminology **Parent-Child accounts** - For the moment, we'll call the account that the app creates the "child" account and the account that receives its `&Account` Capability the "parent" account. Current methods of account access and delegation (for example, keys) still imply ownership over the account, but where linked accounts are concerned, the account to which both the user and the app share access via `&Account` Capability are considered the "child" account. **Walletless onboarding** - An onboarding flow whereby an app creates a custodial account for a user and onboards them to the app, which obviates the need for user wallet authentication. **Blockchain-native onboarding** - Similar to the already familiar Web3 onboarding flow where a user authenticates with their existing wallet, an app onboards a user via wallet authentication while it also creates a custodial app account and links it with the authenticated account, which creates a "hybrid custody" model. **Hybrid Custody Model** - A custodial pattern in which an app and a user maintain access to an app-created account and user access to that account is mediated via Account Linking. **Account Linking** - Account Linking in our context means to give some other account an `&Account` Capability from the granting account. This Capability is maintained in standardized resource called a `HybridCustody.Manager`, which provides its owning user access to any and all of their linked accounts. **Progressive Onboarding** - An onboarding flow that walks a user up to self-custodial ownership, which starts with walletless onboarding and later links the app account with the user's authenticated wallet when the user chooses to do so. **Restricted Child Account** - An account delegation where the access on the delegating account is restricted according to rules set by the linking child account. We will expand on the distinctions between this and the subsequent term ("owned" account) later. **Owned Account** - An account delegation where the delegatee has unrestricted access on the delegating child account, which gives the delegatee presiding authority that supersedes any other "restricted" parent accounts. ## Account Linking Linking an account delegates account access via `&Account` Capability. Of course, we want to do this in a way that allows the receiving account to maintain that Capability and allows easy identification of the accounts on either end of the linkage - the user's main "parent" account and the linked "child" account. This is accomplished in the `HybridCustody` contract which we'll continue to use in this guidance. ### Prerequisites Since account delegation is mediated by developer-defined rules, you should make sure to first configure the resources that contain those rules. Contracts that help define and enforce this ruleset are [`CapabilityFilter`] and [`CapabilityFactory`]. The former enumerates those types that are and are not accessible from a child account while the latter allows the access of those allowable Capabilities such that the returned values can be properly typed - for example, to retrieve a Capability that can be cast to `Capability<&NonFungibleToken.Collection>`. Here's how you would configure an `AllowlistFilter` and add allowed types to it: ```cadence setup_allow_all_filter.cdc import "CapabilityFilter" transaction(identifiers: [String]) { prepare(acct: auth(BorrowValue, SaveValue, StorageCapabilities, PublishCapability, UnpublishCapability) &Account) { // Setup the AllowlistFilter if acct.storage.borrow<&AnyResource>(from: CapabilityFilter.StoragePath) == nil { acct.storage.save( <-CapabilityFilter.createFilter(Type<@CapabilityFilter.AllowlistFilter>()), to: CapabilityFilter.StoragePath) } // Ensure the AllowlistFilter is linked to the expected PublicPath acct.capabilities.unpublish(CapabilityFilter.PublicPath) acct.capabilities.publish( acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath), at: CapabilityFilter.PublicPath ) // Get a reference to the filter let filter = acct.storage.borrow( from: CapabilityFilter.StoragePath ) ?? panic("filter does not exist") // Add the given type identifiers to the AllowlistFilter // **Note:** the whole transaction fails if any of the given identifiers are malformed for identifier in identifiers { let c = CompositeType(identifier)! filter.addType(c) } } } ``` And the following transaction configures a `CapabilityFactory.Manager`, adding NFT-related `Factory` objects: :::info The Manager configured here allows retrieval of castable Capabilities. We recommend that you implement Factory resource definitions to support any NFT Collections related with the use of your application so that users can retrieve Typed Capabilities from accounts linked from your app. ::: ```cadence setup_factory.cdc import "NonFungibleToken" import "CapabilityFactory" import "NFTCollectionPublicFactory" import "NFTProviderAndCollectionFactory" import "NFTProviderFactory" import "NFTCollectionFactory" transaction { prepare(acct: auth(BorrowValue, SaveValue, StorageCapabilities, PublishCapability, UnpublishCapability) &Account) { // Check for a stored Manager, saving if not found if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { let f <- CapabilityFactory.createFactoryManager() acct.storage.save(<-f, to: CapabilityFactory.StoragePath) } // Check for Capabilities where expected, linking if not found acct.capabilities.unpublish(CapabilityFactory.PublicPath) acct.capabilities.publish( acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath), at: CapabilityFactory.PublicPath ) assert( acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check(), message: "CapabilityFactory is not setup properly" ) let manager = acct.storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("manager not found") /// Add generic NFT-related Factory implementations to enable castable Capabilities from this Manager manager.updateFactory(Type<&{NonFungibleToken.CollectionPublic}>(), NFTCollectionPublicFactory.Factory()) manager.updateFactory(Type(), NFTProviderAndCollectionFactory.Factory()) manager.updateFactory(Type(), NFTProviderFactory.Factory()) manager.updateFactory(Type(), NFTCollectionFactory.WithdrawFactory()) manager.updateFactory(Type<&{NonFungibleToken.Collection}>(), NFTCollectionFactory.Factory()) } } ``` ![resources/hybrid_custody_high_level](./imgs/hybrid_custody_high_level.png) _In this scenario, a user custodies a key for their main account which maintains access to a wrapped `Account` Capability. This provides the user restricted access on the app account. The app maintains custodial access to the account and regulates the access restrictions to delegatee "parent" accounts._ You can link accounts in one of two ways. Put simply, the child account needs to get the parent an `Account` Capability, and the parent needs to save that Capability so they can retain access. This delegation must occur in a way that represents each side of the link and safeguard the integrity of any access restrictions an application puts in place on delegated access. To achieve issuance from the child account and claim from the parent account pattern, we can either: 1. Leverage [Cadence's `Account.Inbox`] to publish the Capability from the child account and have the parent claim the Capability in a subsequent transaction. 2. Execute a multi-party signed transaction, signed by both the child and parent accounts. Let's take a look at both. :::info You'll want to consider whether you would like the parent account to be configured with some app-specific resources or Capabilities and compose you multisig or claim transactions to include such configurations. For example, if your app deals with specific NFTs, you may want to configure the parent account with Collections for those NFTs so the user can easily transfer them between their linked accounts. ::: ### Publish and claim #### Publish Here, the account delegates access to itself, which links its `&Account` Capability and publishes it to be claimed by the designated parent account. ```cadence publish_to_parent.cdc import "HybridCustody" import "CapabilityFactory" import "CapabilityFilter" import "CapabilityDelegator" transaction(parent: Address, factoryAddress: Address, filterAddress: Address) { prepare(acct: auth(BorrowValue) &Account) { // NOTE: The resources and Capabilities needed for this transaction are assumed to have be pre-configured // Borrow the OwnedAccount resource let owned = acct.storage.borrow( from: HybridCustody.OwnedAccountStoragePath ) ?? panic("owned account not found") // Get a CapabilityFactory.Manager Capability let factory = getAccount(factoryAddress).capabilities .get<&CapabilityFactory.Manager>( CapabilityFactory.PublicPath ) assert(factory.check(), message: "factory address is not configured properly") // Get a CapabilityFilter.Filter Capability let filter = getAccount(filterAddress).capabilities .get<&{CapabilityFilter.Filter}>( CapabilityFilter.PublicPath ) assert(filter.check(), message: "capability filter is not configured properly") // Publish the OwnedAccount to the designated parent account owned.publishToParent(parentAddress: parent, factory: factory, filter: filter) } } ``` #### Claim On the other side, the receiving account claims the published `ChildAccount` Capability, which adds it to the signer's `HybridCustody.Manager.childAccounts` indexed on the child account's Address. ```cadence redeem_account.cdc import "MetadataViews" import "ViewResolver" import "HybridCustody" import "CapabilityFilter" transaction(childAddress: Address, filterAddress: Address?, filterPath: PublicPath?) { prepare(acct: auth(Storage, Capabilities, Inbox) &Account) { // Get a Manager filter if a path is provided var filter: Capability<&{CapabilityFilter.Filter}>? = nil if filterAddress != nil && filterPath != nil { filter = getAccount(filterAddress!).capabilities .get<&{CapabilityFilter.Filter}>( filterPath! ) } // Configure a Manager if not already configured if acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) acct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { c.delete() } acct.capabilities.unpublish(HybridCustody.ManagerPublicPath) acct.capabilities.publish( acct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>( HybridCustody.ManagerStoragePath ), at: HybridCustody.ManagerPublicPath ) acct.capabilities .storage .issue( HybridCustody.ManagerStoragePath ) } // Claim the published ChildAccount Capability let inboxName = HybridCustody.getChildAccountIdentifier(acct.address) let cap = acct.inbox.claim(inboxName, provider: childAddress) ?? panic("child account cap not found") // Get a reference to the Manager and add the account & add the child account let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") manager.addAccount(cap: cap) } } ``` ### Multi-Signed Transaction We can combine the two transactions in [Publish] and [Claim] into a single multi-signed transaction to achieve Hybrid Custody in a single step. :::info While this code links both accounts in a single transaction, in practicality you may find it easier to execute publish and claim transactions separately depending on your custodial infrastructure. ::: ```cadence setup_multi_sig.cdc #allowAccountLinking import "HybridCustody" import "CapabilityFactory" import "CapabilityDelegator" import "CapabilityFilter" import "MetadataViews" import "ViewResolver" transaction(parentFilterAddress: Address?, childAccountFactoryAddress: Address, childAccountFilterAddress: Address) { prepare(childAcct: auth(Storage, Capabilities) &Account, parentAcct: auth(Storage, Capabilities, Inbox) &Account) { // --------------------- Begin setup of child account --------------------- var optCap: Capability? = nil let t = Type() for c in childAcct.capabilities.account.getControllers() { if c.borrowType.isSubtype(of: t) { optCap = c.capability as! Capability break } } if optCap == nil { optCap = childAcct.capabilities.account.issue() } let acctCap = optCap ?? panic("failed to get account capability") if childAcct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) childAcct.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) } for c in childAcct.capabilities.storage.getControllers(forPath: HybridCustody.OwnedAccountStoragePath) { c.delete() } // configure capabilities childAcct.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) childAcct.capabilities.publish( childAcct.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), at: HybridCustody.OwnedAccountPublicPath ) // --------------------- End setup of child account --------------------- // --------------------- Begin setup of parent account --------------------- var filter: Capability<&{CapabilityFilter.Filter}>? = nil if parentFilterAddress != nil { filter = getAccount(parentFilterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) } if parentAcct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) parentAcct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) } for c in parentAcct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { c.delete() } parentAcct.capabilities.publish( parentAcct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), at: HybridCustody.ManagerPublicPath ) parentAcct.capabilities.storage.issue(HybridCustody.ManagerStoragePath) // --------------------- End setup of parent account --------------------- // Publish account to parent let owned = childAcct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") let factory = getAccount(childAccountFactoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") let filterForChild = getAccount(childAccountFilterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filterForChild.check(), message: "capability filter is not configured properly") owned.publishToParent(parentAddress: parentAcct.address, factory: factory, filter: filterForChild) // claim the account on the parent let inboxName = HybridCustody.getChildAccountIdentifier(parentAcct.address) let cap = parentAcct.inbox.claim(inboxName, provider: childAcct.address) ?? panic("child account cap not found") let manager = parentAcct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") manager.addAccount(cap: cap) } } ``` ## Onboarding flows Given the ability to establish an account and later delegate access to a user, apps are freed from the constraints of dichotomous custodial and self-custodial paradigms. A developer can choose to onboard a user via traditional Web2 identity and later delegate access to the user's wallet account. Alternatively, an app can enable wallet authentication at the outset, which creates an app-specific account & link with the user's wallet account. As specified above, these two flows are known as "walletless" and "blockchain-native" onboarding respectively. Developers can choose to implement one for simplicity or both for maximum flexibility. ### Walletless onboarding This transaction creates an account, funding creation via the signer and adding the provided public key. You'll notice this transaction is pretty much your standard account creation. The magic for you will be how you custody the key for this account (locally, KMS, wallet service, and so on) in a manner that allows your app to mediate onchain interactions on behalf of your user. ```cadence walletless_onboarding import "FungibleToken" import "FlowToken" transaction(pubKey: String, initialFundingAmt: UFix64) { prepare(signer: auth(BorrowValue) &Account) { /* --- Account Creation --- */ // **NOTE:** your app may choose to separate creation depending on your custodial model) // // Create the child account, funding via the signer let newAccount = Account(payer: signer) // Create a public key for the new account from string value in the provided arg // **NOTE:** You may want to specify a different signature algo for your use case let key = PublicKey( publicKey: pubKey.decodeHex(), signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 ) // Add the key to the new account // **NOTE:** You may want to specify a different hash algo & weight best for your use case newAccount.keys.add( publicKey: key, hashAlgorithm: HashAlgorithm.SHA3_256, weight: 1000.0 ) /* --- (Optional) Additional Account Funding --- */ // // Fund the new account if specified if initialFundingAmt > 0.0 { // Get a vault to fund the new account let fundingProvider = signer.storage.borrow( from: /storage/flowTokenVault )! // Fund the new account with the initialFundingAmount specified let receiver = newAccount.capabilities.get<&FlowToken.Vault>( /public/flowTokenReceiver ).borrow()! let fundingVault <-fundingProvider.withdraw( amount: initialFundingAmt ) receiver.deposit(from: <-fundingVault) } /* --- Continue with use case specific setup --- */ // // At this point, the newAccount can further be configured as suitable for // use in your app (e.g. Setup a Collection, Mint NFT, Configure Vault, etc.) // ... } } ``` ### Blockchain-native onboarding This onboarding flow is really a single-transaction composition of the steps covered above. This is a testament to the power of the complex transactions you can compose on Flow with Cadence! :::info Recall the [prerequisites] needed to be satisfied before linking an account: 1. CapabilityFilter Filter saved and linked. 2. CapabilityFactory Manager saved and linked as well as Factory implementations supporting the Capability Types you'll want accessible from linked child accounts as Typed Capabilities. ::: #### Account creation & linking Compared to walletless onboarding where a user does not have a Flow account, blockchain-native onboarding assumes a user already has a wallet configured and immediately links it with a newly created app account. This allows the app to sign transactions on the user's behalf via the new child account and immediately delegate control of that account to the onboarding user's main account. After this transaction, both the custodial party (presumably the client/app) and the signing parent account will have access to the newly created account - the custodial party via key access and the parent account via their `HybridCustody.Manager` that maintains the new account's `ChildAccount` Capability. ```cadence blockchain_native_onboarding.cdc #allowAccountLinking import "FungibleToken" import "FlowToken" import "MetadataViews" import "ViewResolver" import "HybridCustody" import "CapabilityFactory" import "CapabilityFilter" import "CapabilityDelegator" transaction( pubKey: String, initialFundingAmt: UFix64, factoryAddress: Address, filterAddress: Address ) { prepare(parent: auth(Storage, Capabilities, Inbox) &Account, app: auth(Storage, Capabilities) &Account) { /* --- Account Creation --- */ // // Create the child account, funding via the signing app account let newAccount = Account(payer: app) // Create a public key for the child account from string value in the provided arg // **NOTE:** You may want to specify a different signature algo for your use case let key = PublicKey( publicKey: pubKey.decodeHex(), signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 ) // Add the key to the new account // **NOTE:** You may want to specify a different hash algo & weight best for your use case newAccount.keys.add( publicKey: key, hashAlgorithm: HashAlgorithm.SHA3_256, weight: 1000.0 ) /* --- (Optional) Additional Account Funding --- */ // // Fund the new account if specified if initialFundingAmt > 0.0 { // Get a vault to fund the new account let fundingProvider = app.storage.borrow(from: /storage/flowTokenVault)! // Fund the new account with the initialFundingAmount specified newAccount.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! .borrow()! .deposit( from: <-fundingProvider.withdraw( amount: initialFundingAmt ) ) } /* Continue with use case specific setup */ // // At this point, the newAccount can further be configured as suitable for // use in your dapp (e.g. Setup a Collection, Mint NFT, Configure Vault, etc.) // ... /* --- Link the AuthAccount Capability --- */ // let acctCap = newAccount.capabilities.account.issue() // Create a OwnedAccount & link Capabilities let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) newAccount.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) newAccount.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) newAccount.capabilities.publish( newAccount.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), at: HybridCustody.OwnedAccountPublicPath ) // Get a reference to the OwnedAccount resource let owned = newAccount.storage.borrow(from: HybridCustody.OwnedAccountStoragePath)! // Get the CapabilityFactory.Manager Capability let factory = getAccount(factoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") // Get the CapabilityFilter.Filter Capability let filter = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filter.check(), message: "capability filter is not configured properly") // Configure access for the delegatee parent account owned.publishToParent(parentAddress: parent.address, factory: factory, filter: filter) /* --- Add delegation to parent account --- */ // // Configure HybridCustody.Manager if needed if parent.storage.borrow<&AnyResource>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) parent.storage.save(<- m, to: HybridCustody.ManagerStoragePath) for c in parent.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { c.delete() } // configure Capabilities parent.capabilities.storage.issue<&{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath) parent.capabilities.publish( parent.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), at: HybridCustody.ManagerPublicPath ) } // Claim the ChildAccount Capability let inboxName = HybridCustody.getChildAccountIdentifier(parent.address) let cap = parent .inbox .claim( inboxName, provider: newAccount.address ) ?? panic("child account cap not found") // Get a reference to the Manager and add the account let managerRef = parent.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") managerRef.addAccount(cap: cap) } } ``` ## Funding & Custody Patterns Aside from the implementation of onboarding flows and Account Linking, you'll want to also consider the account funding & custodial pattern appropriate for the app you want to build. The only pattern compatible with walletless onboarding (and therefore the only one showcased above) is one in which the app custodies the child account's key and funds account creation. In general, the funding pattern for account creation will determine, to some extent, the backend infrastructure needed to support your app and the onboarding flow your app can support. For example, if you want to to create a service-less client (a totally local app without backend infrastructure), you could forego walletless onboarding in favor of a user-funded blockchain-native onboarding to achieve a hybrid custody model. Your app maintains the keys to the app account locally to sign on behalf of the user, and the user funds the creation of the the account, which links to their main account on account creation. This would be a **user-funded, app custodied** pattern. Again, custody may deserve some regulatory insight depending on your jurisdiction. If you build for production, you'll likely want to consider these non-technical implications in your technical decision-making. Such is the nature of building in crypto. Here are the patterns you might consider: ### App-funded, app-custodied If you want to implement walletless onboarding, you can stop here as this is the only compatible pattern. In this scenario, a backend app account funds the creation of a new account and the app custodies the key for said account either on the user's device or some backend KMS. ### App-funded, user-custodied In this case, the backend app account funds account creation, but adds a key to the account which the user custodies. For the app to act on the user's behalf, it has to be delegated access via `&Account` Capability which the backend app account would maintain in a `HybridCustody.Manager`. This means that the new account would have two parent accounts - the user's and the app. While this pattern provides the user maximum ownership and authority over the child account, it may present unique considerations and edge cases for you as a builder depending on your app's access to the child account. Also note that this and the following patterns are incompatible with walletless onboarding in that the user must have a walletvpre-configured before onboarding. ### User-funded, app-custodied As mentioned above, this pattern unlocks totally service-less architectures - just a local client and smart contracts. An authenticated user signs a transaction creating an account, adds the key that the client provides, and links the account as a child account. At the end of the transaction, hybrid custody is achieved and the app can sign with the custodied key on the user's behalf with the newly-created account. ### User-funded, user-custodied While perhaps not useful for most apps, this pattern may be desirable for advanced users who wish to create a shared access account themselves. The user funds account creation, adds keys they custody, and delegates secondary access to some other account. [Account]: https://cadence-lang.org/docs/language/accounts#authaccount [Cadence's `Account.Inbox`]: https://cadence-lang.org/docs/language/accounts#account-inbox [Capability]: https://cadence-lang.org/docs/language/capabilities [`CapabilityFilter`]: https://github.com/onflow/hybrid-custody/blob/main/contracts/CapabilityFilter.cdc [`CapabilityFactory`]: https://github.com/onflow/hybrid-custody/blob/main/contracts/CapabilityFactory.cdc [Claim]: #claim [components and actions]: https://github.com/onflow/flips/pull/72 [feature in Cadence]: https://github.com/onflow/flips/pull/53 [walletless onboarding]: https://flow.com/post/flow-blockchain-mainstream-adoption-easy-onboarding-wallets [prerequisites]: #prerequisites [Publish]: #publish --- # File: /md/blockchain-development-tutorials/cadence/account-management/parent-accounts.md --- title: Working With Parent Accounts sidebar_position: 2 description: Learn how to work with parent accounts in Flow's hybrid custody model. Understand how to manage child accounts, access assets across accounts, and implement unified account experiences in wallets and marketplaces. keywords: - parent accounts - hybrid custody - account management - child accounts - account access - NFT management - token balances - wallet integration - marketplace integration - Flow accounts - account delegation - asset management - account hierarchy - capability management - account security --- # Working With Parent Accounts In this tutorial, we'll continue from the perspective of a wallet or marketplace app who seeks to facilitate a unified account experience, and abstract away the partitioned access between accounts into a single dashboard for user interactions on all their owned assets. ## Objectives - Understand the Hybrid Custody account model. - Differentiate between restricted child accounts and unrestricted owned accounts. - Get your app to recognize "parent" accounts along with any associated "child" accounts. - View Fungible and NonFungible Token metadata relating to assets across all of a user's associated accounts - their wallet-mediated "parent" account and any hybrid custody model "child" accounts. - Facilitate transactions acting on assets in child accounts. ## Design Overview :::info TL;DR: An account's [`HybridCustody.Manager`] is the entry point for all of a user's associated accounts. ::: The basic idea in the Hybrid Custody model is relatively simple. A parent account is one that has received delegated (albeit restricted) access on another account. The account which has delegated authority over itself to the parent account is the child account. In the [Hybrid Custody Model], this child account would have shared access between the app - the entity which created and likely custodies the account - and the linked parent account. How does this delegation occur? Typically when we think of shared account access in crypto, we think keys. However, Cadence allows [accounts to link Capabilities on themselves] and issue those Capabilities to other parties (more on [capability-based access here]). This feature was leveraged in an ecosystem standard so that apps can implement a hybrid custody model whereby the app creates an account it controls, then later delegates access on that account to the user once they've authenticated with their wallet. All related constructs are used together in the [`HybridCustody` contract] to define the standard. Parent accounts own a `Manager` resource which stores Capabilities to `ChildAccount` (restricted access) and `OwnedAccount` (unrestricted access) resources, both of which are stored in any given child account. Therefore, the presence of a `Manager` in an account implies there are potentially associated accounts for which the owning account has delegated access. This resource is intended to be configured with a public Capability that allows you to query an account's child account addresses via `getAccountAddresses()` and `getOwnedAccountAddresses()`. As you can deduce from these two methods, there is a notion of "owned" accounts which we'll expand on later. If a wallet or marketplace wants to discover all of a user's accounts and assets within them, they can first look to the user's `Manager`. ### Identify account hierarchy To clarify, insofar as the standard is concerned, an account is a parent account if it contains a `Manager` resource, and an account is a child account if it contains at minimum an `OwnedAccount` or additionally a `ChildAccount` resource. Within a user's `Manager`, its mapping of `childAccounts` points to the addresses of its child accounts in each key, with corresponding values that give the `Manager` access to those accounts via corresponding `ChildAccount` Capability. ![HybridCustody Conceptual Overview](./imgs/hybrid_custody_conceptual_overview.png) Likewise, the child account's `ChildAccount.parentAddress` (which owns a `Manager`) points to the user's account as its parent address. This makes it easy to both identify whether an account is a parent, child, or both, and its associated parent or child account(s). `OwnedAccount` resources underly all account delegations, so can have multiple parents whereas `ChildAccount`s are 1:1. This provides more granular revocation as each parent account has its own Capability path on which its access relies. #### Restricted vs. Owned Accounts `ChildAccount` Capabilities allow access to the underlying account according to rules configured when the child account delegates access. The `ChildAccount` maintains these rules along with an `OwnedAccount` Capability within which the `&Account` Capability is stored. Anyone with access to the surface level `ChildAccount` can then access the underlying `Account`, but only within the pre-defined rule set. These rules are fundamentally a list of Types that can or can't be retrieved from an account. The app developer can codify these rule sets on allowable Capability types in a [`CapabilityFilter`] along with a [`CapabilityFactory`] defining retrieval patterns for those Capabilities. When delegation occurs, the developer would provide the `CapabilityFilter` and `CapabilityFactory` Capabilities to an `OwnedAccount` resource which stores them in a `ChildAccount` resource. Then, capabilities are created for the `OwnedAccount` and `ChildAccount` resource and are given to the specified parent account. So, if an app developer wants to turn on Hybrid Custody but doesn't want to allow parent accounts to access FungibleToken Vaults, for example, the app developer can codify rule sets enumerating allowable Capability types in a `CapabilityFilter` along with a `CapabilityFactory` defining retrieval patterns for those Capabilities. When delegation occurs, they would provide the `CapabilityFilter` and `CapabilityFactory` Capabilities to an `OwnedAccount`. This `OwnedAccount` then wraps the given filter & factory Capabilities in a `ChildAccount` along with a Capability to itself before it publishes the new `ChildAccount` Capability for the specified parent account to claim. :::info If you enumerate allowable Types in your `CapabilityFilter.Filter` implementation, you by default exclude access to anything other than the Types you declare as allowable. ::: As mentioned earlier, `Manager`s also maintain access to "owned" accounts - accounts which define unrestricted access as they allow direct retrieval of encapsulated `&Account` Capabilities. These owned accounts, found in `Manager.ownedAccounts`, are simply `OwnedAccount` Capabilities instead of `ChildAccount` Capabilities. ![HybridCustody Total Overview](./imgs/hybrid_custody_low_level.png) ### Considerations This construction does not prevent an account from having multiple parent accounts or a child account from being the parent to other accounts. While initial intuition might lead one to believe that account associations are a tree with the user at the root, the graph of associated accounts among child accounts may lead to cycles of association. We believe it's unlikely for a use case to demand a user delegates authority over their main account (in fact we'd discourage such constructions), but it might be useful to delegate access between child accounts. As an example, consider a set of local game clients across mobile and web platforms, each with self-custodied app accounts that have delegated authority to each other while both are child accounts of the user's main account. Ultimately, it's' up to the wallet or marketplace who implements this how far down the graph of account associations they'd want to traverse and display to the user. ## Implementation From the perspective of a wallet or marketplace app, some relevant things to know about the user are: - Does this account have associated linked (child) accounts? - What are those associated linked accounts, if any? - What NFTs are owned by this user across all associated accounts? - What are the balances of all FungibleTokens across all associated accounts? And with respect to actions on the assets of child accounts and management of the child accounts themselves: - Access an NFT from a linked account's Collection - Remove a linked account ## Examples ### Query whether an address has associated accounts This script will return `true` if a `HybridCustody.Manager` is stored and `false` otherwise ```cadence has_child_accounts.cdc import "HybridCustody" access(all) fun main(parent: Address): Bool { let acct = getAuthAccount(parent) if let manager = acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { return manager.getChildAddresses().length > 0 } return false } ``` ### Query all accounts associated with address The following script will return an array of addresses associated with a given account's address, inclusive of the provided address. If a `HybridCustody.Manager` is not found, the script will revert. ```cadence get_child_addresses.cdc import "HybridCustody" access(all) fun main(parent: Address): [Address] { let acct = getAuthAccount(parent) let manager = acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") return manager.getChildAddresses() } ``` ### Query all owned NFT metadata While it is possible to iterate over the storage of all associated accounts in a single script, memory limits prevent this approach from scaling well. Since some accounts hold thousands of NFTs, we recommend that you break up iteration and use several queries to iterate over accounts and the storage of each account. Based on the number of NFTs held, you might be required to batch the queries on individual accounts. 1. Get all associated account addresses (see above). 2. Loop over each associated account address client-side and get each address's owned NFT metadata. For simplicity, we'll show a condensed query that returns NFT display views from all accounts associated with a given address for a specified NFT Collection path. ```cadence get_nft_display_view_from_public.cdc import "NonFungibleToken" import "MetadataViews" import "HybridCustody" /// Returns resolved Display from given address at specified path for each ID or nil if ResolverCollection is not found /// access(all) fun getViews(_ address: Address, _ resolverCollectionPath: PublicPath): {UInt64: MetadataViews.Display} { let account: PublicAccount = getAccount(address) let views: {UInt64: MetadataViews.Display} = {} // Borrow the Collection if let collection = account.capabilities.borrow<&{NonFungibleToken.Collection}>(resolverCollectionPath) { // Iterate over IDs & resolve the view for id in collection.getIDs() { if let nft = collection.borrowNFT(id) { if let display = nft.resolveView(Type()) as? MetadataViews.Display { views.insert(key: id, display) } } } } return views } /// Queries for MetadataViews.Display each NFT across all associated accounts from Collections at the provided /// PublicPath /// access(all) fun main(address: Address, resolverCollectionPath: PublicPath): {Address: {UInt64: MetadataViews.Display}} { let allViews: {Address: {UInt64: MetadataViews.Display}} = { address: getViews(address, resolverCollectionPath) } /* Iterate over any associated accounts */ // let seen: [Address] = [address] if let managerRef = getAuthAccount(address) .storage .borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { allViews.insert(key: childAccount, getViews(address, resolverCollectionPath)) seen.append(childAccount) } for ownedAccount in managerRef.getOwnedAddresses() { if seen.contains(ownedAccount) == false { allViews.insert(key: ownedAccount, getViews(address, resolverCollectionPath)) seen.append(ownedAccount) } } } return allViews } ``` At the end of this query, the caller will have a mapping of `Display` views indexed on the NFT ID and grouped by account Address. This script does not take batching into consideration and assumes that each NFT resolves the `MetadataViews.Display` view type. ### Query all acount FungibleToken balances Similar to the previous example, we recommend that you break up this task due to memory limits. 1. Get all linked account addresses (see above). 2. Loop over each associated account address client-side and get each address's owned FungibleToken Vault metadata. However, we'll condense both of these steps down into one script for simplicity: ```cadence get_all_vault_bal_from_storage.cdc import "FungibleToken" import "MetadataViews" import "HybridCustody" /// Returns a mapping of balances indexed on the Type of resource containing the balance /// access(all) fun getAllBalancesInStorage(_ address: Address): {Type: UFix64} { // Get the account let account = getAuthAccount(address) // Init for return value let balances: {Type: UFix64} = {} // Track seen Types in array let seen: [Type] = [] // Assign the type we'll need let balanceType: Type = Type<@{FungibleToken.Balance}>() // Iterate over all stored items & get the path if the type is what we're looking for account.forEachStored(fun (path: StoragePath, type: Type): Bool { if (type.isInstance(balanceType) || type.isSubtype(of: balanceType)) && !type.isRecovered { // Get a reference to the resource & its balance let vaultRef = account.borrow<&{FungibleToken.Balance}>(from: path)! // Insert a new values if it's the first time we've seen the type if !seen.contains(type) { balances.insert(key: type, vaultRef.balance) } else { // Otherwise just update the balance of the vault (unlikely we'll see the same type twice in // the same account, but we want to cover the case) balances[type] = balances[type]! + vaultRef.balance } } return true }) return balances } /// Queries for FT.Vault balance of all FT.Vaults in the specified account and all of its associated accounts /// access(all) fun main(address: Address): {Address: {Type: UFix64}} { // Get the balance for the given address let balances: {Address: {Type: UFix64}} = { address: getAllBalancesInStorage(address) } // Tracking Addresses we've come across to prevent overwriting balances (more efficient than checking dict entries (?)) let seen: [Address] = [address] /* Iterate over any associated accounts */ // if let managerRef = getAuthAccount(address) .storage .borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { balances.insert(key: childAccount, getAllBalancesInStorage(address)) seen.append(childAccount) } for ownedAccount in managerRef.getOwnedAddresses() { if seen.contains(ownedAccount) == false { balances.insert(key: ownedAccount, getAllBalancesInStorage(address)) seen.append(ownedAccount) } } } return balances } ``` The above script returns a dictionary of balances indexed on the type and further grouped by account Address. The returned data at the end of address iteration should be sufficient to achieve a unified balance of all Vaults of similar types across all of a user's associated account as well as a more granular per account view. You might resolve [`FungibleTokenMetadataViews`] to aggregate more information about the underlying Vaults. ### Access NFT in child account from parent account A user with NFTs in their child accounts will likely want to utilize said NFTs. In this example, the user signs a transaction with their authenticated account that retrieves a reference to a child account's `NonFungibleToken.Provider`, which allows withdrawal from the child account that signs as the parent account. ```cadence withdraw_nft_from_child.cdc import "NonFungibleToken" import "FlowToken" import "HybridCustody" transaction( childAddress: Address, // Address of the child account storagePath: StoragePath, // Path to the Collection in the child account collectionType: Type, // Type of the requested Collection from which to withdraw withdrawID: UInt64 // ID of the NFT to withdraw ) { let providerRef: auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider} prepare(signer: auth(BorrowValue) &Account) { // Get a reference to the signer's HybridCustody.Manager from storage let managerRef = signer.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("Could not borrow reference to HybridCustody.Manager in signer's account at expected path!") // Borrow a reference to the signer's specified child account let account = managerRef .borrowAccount(addr: childAddress) ?? panic("Signer does not have access to specified child account") // Get the Capability Controller ID for the requested collection type let controllerID = account.getControllerIDForType( type: collectionType, forPath: storagePath ) ?? panic("Could not find Capability controller ID for collection type ".concat(collectionType.identifier) .concat(" at path ").concat(storagePath.toString())) // Get a reference to the child NFT Provider and assign to the transaction scope variable let cap = account.getCapability( controllerID: controllerID, type: Type() ) ?? panic("Cannot access NonFungibleToken.Provider from this child account") // We'll need to cast the Capability - this is possible thanks to CapabilityFactory, though we'll rely on the relevant // Factory having been configured for this Type or it won't be castable let providerCap = cap as! Capability self.providerRef = providerCap.borrow() ?? panic("Provider capability is invalid - cannot borrow reference") } execute { // Withdraw the NFT from the Collection let nft <- self.providerRef.withdraw(withdrawID: withdrawID) // Do stuff with the NFT // NOTE: Without storing or burning the NFT before scope closure, this transaction will fail. You'll want to // fill in the rest of the transaction with the necessary logic to handle the NFT // ... } } ``` At the end of this transaction, you withdrew an NFT from the specified account with an NFT `Provider` Capability. A similar approach could get you any allowable Capabilities from a signer's child account. ### Revoke secondary access on a linked account The expected uses of child accounts for progressive onboarding implies that they will be accounts with shared access. A user may decide that they no longer want secondary parties to have access to the child account. There are two ways a party can have delegated access to an account - keys and `&Account` Capability. With `ChildAccount` mediated access, a user wouldn't be able to revoke anyone's access except for their own. With unrestricted access via `OwnedAccount`, one could remove parents (`OwnedAccount.removeParent(parent: Address)`) thereby unlinking relevant Capabilities and further destroying their `ChildAccount` and `CapabilityDelegator` resources. For now, we recommend that if users want to revoke secondary access, they transfer any assets from the relevant child account and remove it from their `Manager` altogether. ### Remove a Child Account As mentioned above, if a user no longer wishes to share access with another party, we recommended that they transfer desired assets from that account to either their main account or other linked accounts and the linked account be removed from their `HybridCustody.Manager`. Let's see how to complete that removal. ```cadence remove_child_account.cdc import "HybridCustody" transaction(child: Address) { prepare (acct: auth(BorrowValue) &Account) { let manager = acct.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("manager not found") manager.removeChild(addr: child) } } ``` After removal, the signer no longer has delegated access to the removed account via their `Manager` and the caller is removed as a parent of the removed child. It's also possible for a child account to remove a parent. This is necessary to give application developers and ultimately the owners of these child accounts the ability to revoke secondary access on owned accounts. [`HybridCustody.Manager`]: https://github.com/onflow/hybrid-custody/blob/main/contracts/HybridCustody.cdc [Hybrid Custody Model]: https://forum.flow.com/t/hybrid-custody/4016 [accounts to link Capabilities on themselves]: https://cadence-lang.org/docs/language/accounts/capabilities#accountcapabilities [capability-based access here]: https://cadence-lang.org/docs/language/capabilities. [`HybridCustody` contract]: https://github.com/onflow/hybrid-custody/tree/main [`CapabilityFilter`]: https://github.com/onflow/hybrid-custody/blob/main/contracts/CapabilityFilter.cdc [`CapabilityFactory`]: https://github.com/onflow/hybrid-custody/blob/main/contracts/CapabilityFactory.cdc [`FungibleTokenMetadataViews`]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleTokenMetadataViews.cdc --- # File: /md/blockchain-development-tutorials/cadence/cadence-advantages.md --- title: Cadence Advantages description: A series of tutorials showcasing the unique advantages and capabilities of Cadence smart contracts on Flow. sidebar_position: 2 keywords: - Cadence advantages - smart contracts - Flow blockchain - tutorials - composition - native data availability - resource-oriented programming - transaction composition - script queries - contract upgrades - contract updatability - incremental upgrades --- # Cadence Advantages This series explores the unique advantages and capabilities of Cadence smart contracts on Flow, and demonstrates how Cadence's innovative features allow powerful development patterns that aren't possible on other blockchain platforms. From native data availability to seamless transaction composition, these tutorials showcase why Cadence represents the future of smart contract development. ## Tutorials ### [Compose with Cadence Transactions] Learn how to compose with someone else's on-chain contracts by writing a Cadence transaction that conditionally calls a public contract on testnet, then extend it to mint NFTs when conditions are met, with no redeploy required. This tutorial demonstrates Cadence's powerful composition model, which lets you build complex application logic that interacts with multiple contracts in a single atomic transaction. You'll work with the Flow command line interface (CLI) dependency manager, learn to set up NFT collections, and view results using Flowscan. ### [Native Data Availability with Cadence Scripts] Discover how Cadence scripts provide native data availability, which allows you to query any on-chain data directly from Flow's state without the need to rely on external indexers or APIs. This comprehensive tutorial shows you how to build scripts that can discover and query NFT collections across multiple child accounts with Hybrid Custody, then extend it to include both NBA Top Shot and NFL All Day NFTs. You'll learn to filter and process NFT collections, extract specific metadata, and compare Cadence's native data availability with Solidity's limitations. ### [Upgrading Cadence Contracts] Learn how to upgrade deployed Cadence contracts through multiple incremental upgrades, preserve the current state, and maintain the same contract address. This tutorial demonstrates Cadence's sophisticated contract upgrade system through two realistic scenarios: - Add an event to notify users when the counter reaches an even number. - Extend the contract with additional functionality like increment by two and check if numbers are even. You'll understand what you can and can't change when you upgrade, perform multiple contract updates with Flow CLI, and test upgraded functionality with comprehensive transactions and scripts. ## Conclusion Cadence's unique features, such as resource-oriented programming to native data availability, seamless transaction composition, and sophisticated contract upgrade capabilities, represent a fundamental advancement in smart contract development. These tutorials demonstrate how Cadence allows developers to build sophisticated applications with capabilities that simply aren't possible on other blockchain platforms and maintain security and developer experience as core principles. [Compose with Cadence Transactions]: ./compose-with-cadence-transactions.md [Native Data Availability with Cadence Scripts]: ./native-data-availibility-with-cadence-scripts.md [Upgrading Cadence Contracts]: ./upgrading-cadence-contracts.md --- # File: /md/blockchain-development-tutorials/cadence/cadence-advantages/compose-with-cadence-transactions.md --- title: Compose with Cadence Transactions description: Learn how to compose with someone else's on-chain contracts by writing a Cadence transaction that conditionally calls a public contract on testnet, then extend it to mint NFTs when conditions are met—no redeploy required. sidebar_position: 1 keywords: - Cadence transactions - composition - public contracts - Flow testnet - Flow CLI - commands - dependency manager - Counter - ExampleNFT - NonFungibleToken - NFT minting - onchain reads - onchain writes - Flowscan --- # Compose wth Cadence Transactions In this tutorial, you'll **compose with someone else's contracts** on Flow testnet. You'll write a Cadence transaction that reads public state from a contract named `Counter` and only increments the counter when it is odd. Then you'll extend the transaction to mint NFTs when the counter is odd, demonstrating how to compose multiple contracts in a single transaction. Everything runs against testnet using the Flow CLI and the dependency manager. You can use transactions developed and tested this way from the frontend of your app. ## Objectives After you complete this guide, you will be able to: - Configure the Flow CLI _dependency manager_ to import named contracts from **testnet**. - Write a Cadence **transaction** that reads and writes to a public contract you didn't deploy. - Run the transaction on **testnet** with a funded account using the Flow command line interface (CLI). - Extend the transaction to compose multiple public contracts (`Counter` + `ExampleNFT` + `NonFungibleToken`) without redeploying anything. - Set up NFT collections and mint NFTs conditionally based on on-chain state. - View transaction results and NFT transfers using Flowscan. ## Prerequisites - [Flow CLI installed] - A **funded testnet account** to sign transactions See **Create accounts** and **Fund accounts** in the Flow CLI commands: - Create: https://developers.flow.com/build/tools/flow-cli/commands#create-accounts - Fund: https://developers.flow.com/build/tools/flow-cli/commands#fund-accounts ## Get started Create a [new project] with the [Flow CLI]: ```bash flow init ``` Follow the prompts and create a `Basic Cadence project (no dependencies)`. ### Install dependencies We will resolve imports **using string format** (`import "Counter"`) with the [dependency manager]. We recommend that you work this way with imports of already-deployed contracts. You should also use the CLI to create new files and add existing ones to `flow.json`. :::warning For this exercise, **delete** the existing contract entry for `Counter` from your `flow.json`. You could also use an alias here, but this is simpler since you won't deploy the `Counter` contract. ::: You can install dependencies for already deployed contracts, whether yours or those that others deployed: ```bash # Add a deployed instance of the Counter contract flow dependencies install testnet://0x8a4dce54554b225d.Counter ``` Pick `none` for the deployment account as you won't need to redeploy these contracts. After they're installed with the dependency manager, Cadence imports like `import "Counter"` will resolve to the testnet address when they send transactions on testnet. :::info In Cadence, contracts deploy to the account storage of the deploying address. Due to security reasons, the same private key produces different address on Cadence testnet and mainnet. One of the features of the dependency manager is to automatically select the right address for imports based on the network you're working on. ::: --- ## Compose with the public `Counter` contract Review the `Counter` simple contract that's created as an example by `flow init`: ```cadence access(all) contract Counter { access(all) var count: Int // Event to be emitted when the counter is incremented access(all) event CounterIncremented(newCount: Int) // Event to be emitted when the counter is decremented access(all) event CounterDecremented(newCount: Int) init() { self.count = 0 } // Public function to increment the counter access(all) fun increment() { self.count = self.count + 1 emit CounterIncremented(newCount: self.count) } // Public function to decrement the counter access(all) fun decrement() { self.count = self.count - 1 emit CounterDecremented(newCount: self.count) } // Public function to get the current count view access(all) fun getCount(): Int { return self.count } } ``` Unlike in Solidity, apps aren't limited to the functionality deployed in a smart contract. One of the ways you can expand your app is to write new transactions that call multiple functions in multiple contracts, with branching based on conditions and state, with a single call and a single signature. You don't need to deploy a new contract, use a proxy, or switch to V2. In this simple example, imagine that you've already deployed a product that has thousands of users and is dependent on the `Counter` smart contract. After a time, you realize that a significant portion of your users only wish to use the `increment` feature if the current `count` is odd, to try and make the number be even. In Cadence, this sort of upgrade is easy, even if you didn't anticipate the need at contract deployment. All you need to do is to write a new [transaction] that **reads** the current count from `Counter` and **only increments** it if the value is odd. Create a new [transaction] called `IncrementIfOdd` using the Flow CLI: ```bash flow generate transaction IncrementIfOdd ``` Start by adding the code from the existing `IncrementCounter` [transaction]: ```cadence import "Counter" transaction { prepare(acct: &Account) { // Authorizes the transaction } execute { // Increment the counter Counter.increment() // Retrieve the new count and log it let newCount = Counter.getCount() log("New count after incrementing: ".concat(newCount.toString())) } } ``` Then, modify it to handle the new feature: ```cadence import "Counter" transaction() { prepare(account: &Account) {} execute { // Get the current count from the Counter contract (public read) let currentCount = Counter.getCount() // Print the current count log("Current count: ".concat(currentCount.toString())) // If odd (remainder when divided by 2 is not 0), increment if currentCount % 2 != 0 { Counter.increment() log("Counter was odd, incremented to: ".concat(Counter.getCount().toString())) } else { log("Counter was even, no increment performed") } } } ``` :::info As with most blockchains, `logs` are not exposed or returned when transactions are run on testnet or mainnet, but they are visible in the console when you use the [emulator]. ::: ### Run on testnet You need a **funded** testnet account to sign the transaction. For development tasks, the CLI has [account commands] that you can use to create and manage your accounts. Create and fund an account called `testnet-account`: ```bash # If needed, create a testnet account (one-time) flow accounts create --network testnet # If needed, fund it (one-time) flow accounts fund testnet-account ``` :::danger As with other blockchain accounts, after an account's private key is compromised, anyone with that key completely controls an account and it's assets. **Never** put private keys directly in `flow.json`. ::: When you create an account with the CLI, it automatically puts the private key in a `.pkey` file, which is already in `.gitignore`. [Send the transaction] to testnet, signed with `testnet-account`: ```bash flow transactions send cadence/transactions/IncrementIfOdd.cdc --signer testnet-account --network testnet ``` You will see logs that show the prior value and whether the increment occurred. :::tip You could trigger this same transaction **from an app** and **signed by a wallet** with a single user click. Your dApp would assemble and submit this exact Cadence transaction with your preferred client library, and the user's wallet would authorize it. ::: --- ## Extend with NFT minting Now lets take our composition to the next level and add NFT minting functionality when the counter is odd. We'll use an example NFT contract that's already deployed on testnet. This is a silly use case, but it demonstrates the complex use cases you can add to your apps, after contract deployment, and even if you aren't the author of any of the contracts! ### Install the NFT contract First, let's install the ExampleNFT contract dependency: ```bash flow dependencies install testnet://012e4d204a60ac6f.ExampleNFT ``` :::warning This repository uses different deployments for core contracts than those that the Flow CLI installs. If you previously installed core contract dependencies (like `NonFungibleToken`, `MetadataViews`, etc.) with the CLI, manually delete all `dependencies` except `Counter` from your `flow.json` file to avoid conflicts. ::: ### Understand NFT minting Let's look at how NFT minting works with this contract. The [MintExampleNFT transaction] shows the pattern: ```cadence import "ExampleNFT" import "NonFungibleToken" transaction( recipient: Address, name: String, description: String, thumbnail: String, creator: String, rarity: String ) { let recipientCollectionRef: &{NonFungibleToken.Receiver} prepare(signer: &Account) { self.recipientCollectionRef = getAccount(recipient) .capabilities.get<&{NonFungibleToken.Receiver}>(ExampleNFT.CollectionPublicPath) .borrow() ?? panic("Could not get receiver reference to the NFT Collection") } execute { ExampleNFT.mintNFT( recipient: self.recipientCollectionRef, name: name, description: description, thumbnail: thumbnail, creator: creator, rarity: rarity ) } } ``` You can copy this functionality and adapt it for our use case. ### Update the IncrementIfOdd transaction Now let's update our `IncrementIfOdd` transaction to mint an NFT when the counter is odd. You can either modify the current transaction or create a new one: ```cadence import "Counter" import "ExampleNFT" import "NonFungibleToken" transaction() { let recipientCollectionRef: &{NonFungibleToken.Receiver} prepare(acct: &Account) { // Get the recipient's NFT collection reference self.recipientCollectionRef = getAccount(acct.address) .capabilities.get<&{NonFungibleToken.Receiver}>(ExampleNFT.CollectionPublicPath) .borrow() ?? panic("Could not get receiver reference to the NFT Collection") } execute { // Get the current count from the Counter contract (public read) let currentCount = Counter.getCount() // Print the current count log("Current count: ".concat(currentCount.toString())) // If odd (remainder when divided by 2 is not 0), increment and mint NFT if currentCount % 2 != 0 { Counter.increment() let newCount = Counter.getCount() log("Counter was odd, incremented to: ".concat(newCount.toString())) // Mint an NFT to celebrate the odd number ExampleNFT.mintNFT( recipient: self.recipientCollectionRef, name: "Odd Counter NFT #".concat(newCount.toString()), description: "This NFT was minted when the counter was odd!", thumbnail: "https://example.com/odd-counter.png", creator: "Counter Composer", rarity: "Rare" ) log("Minted NFT for odd counter!") } else { log("Counter was even, no increment performed") } } } ``` ### Setup NFT collection Before you can mint NFTs, set up an NFT collection in your account. Create a transaction to do this: ```bash flow generate transaction SetupCollection ``` Add this content to the new transaction: ```cadence import "ExampleNFT" import "NonFungibleToken" import "MetadataViews" transaction { prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { if signer.storage.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) != nil { return } let collection <- ExampleNFT.createEmptyCollection(nftType: Type<@ExampleNFT.NFT>()) signer.storage.save(<-collection, to: ExampleNFT.CollectionStoragePath) let cap = signer.capabilities.storage.issue<&ExampleNFT.Collection>(ExampleNFT.CollectionStoragePath) signer.capabilities.publish(cap, at: ExampleNFT.CollectionPublicPath) } } ``` Run the setup transaction: ```bash flow transactions send cadence/transactions/SetupCollection.cdc --signer testnet-account --network testnet ``` ### Test the enhanced transaction Now run the enhanced transaction: ```bash flow transactions send cadence/transactions/IncrementIfOdd.cdc --signer testnet-account --network testnet ``` You may need to run the regular `IncrementCounter` transaction first to get an odd number: ```bash flow transactions send cadence/transactions/IncrementCounter.cdc --signer testnet-account --network testnet ``` ### View your NFT Click the transaction link in the console to view the transaction in [testnet Flowscan]. After you run the transaction **while the counter is odd**, you'll see an NFT in the `Asset Transfers` tab. :::info The broken image is expected. We didn't use a real URL in the example nft metadata. ::: ![NFT](nft.png) --- ## Why this matters - **No redeploys, no forks:** You composed your app logic with on-chain public contracts you do not control. - **Cadence-first composition:** Transactions can include _arbitrary logic_ that calls into multiple contracts in one atomic operation with a single signature. - **Production-ready path:** The same code path works from a CLI or a dApp frontend, authorized by a wallet. ## Conclusion In this tutorial, you learned how to compose with multiple on-chain contracts using Cadence transactions. You built a transaction that conditionally interacts with a Counter contract based on its current state, and then extended it to mint NFTs when the counter is odd, which demonstrates the power and flexibility of Cadence's composition model. Now that you have completed the tutorial, you should be able to: - Configure the Flow CLI _dependency manager_ to import named contracts from **testnet**. - Write a Cadence **transaction** that reads and writes to a public contract you did not deploy. - Run the transaction on **testnet** with a funded account with the Flow CLI. - Extend the transaction to compose multiple public contracts (`Counter` + `ExampleNFT` + `NonFungibleToken`) without the need to redeploy anything. - Set up NFT collections and mint NFTs conditionally based on on-chain state. - View transaction results and NFT transfers with Flowscan. This approach gives you the freedom to build complex application logic that composes with any public contracts on Flow, which makes Cadence's composition model a powerful tool for developers building on Flow. [Flow CLI installed]: ../../../build/tools/flow-cli/install.md [dependency manager]: ../../../build/tools/flow-cli/dependency-manager.md [new project]: ../../../build/tools/flow-cli/flow.json/initialize-configuration [Flow CLI]: ../../../build/tools/flow-cli/index.md [transaction]: https://cadence-lang.org/docs/language/transactions [account commands]: ../../../build/tools/flow-cli/commands#account-management [Send the transaction]: ../../../build/tools/flow-cli/commands#send-transaction [emulator]: ../../../build/tools/emulator/index.md [MintExampleNFT transaction]: https://github.com/mfbz/flow-nft-tester/blob/main/cadence/transactions/MintExampleNFT.cdc [testnet Flowscan]: https://testnet.flowscan.io/ --- # File: /md/blockchain-development-tutorials/cadence/cadence-advantages/native-data-availibility-with-cadence-scripts.md --- title: Native Data Availability With Cadence Scripts sidebar: Data Availability description: Learn why Cadence scripts are more powerful than Solidity views by incrementally building a post-1.0 Cadence script that lists a parent account's child accounts and returns their NBA Top Shot NFTs using the Flow CLI commands. sidebar_position: 1 keywords: - Cadence scripts - Solidity views - data availability - blockchain indexers - Hybrid Custody - capabilities - entitlements - Flow CLI - commands - NonFungibleToken - MetadataViews - NBA Top Shot - Flow NFTs - account storage - read operations --- # Native Data Availability With Cadence Scripts In Solidity, you can only retrieve data from **view** functions that the contract author anticipated and included in the original contract. If the exact query you want isn't exposed, teams typically rely on a _data availability service_ such as The Graph, Covalent, Alchemy Enhanced APIs, Reservoir, or NFTScan to compute and serve that view. In Cadence, **scripts** are general-purpose read programs. They can traverse public account storage, read public capabilities, and compose types from multiple contracts to answer new questions without the need to modify those contracts. You are not limited to the pre-written surface area of a single contract's views. :::info In Cadence, a _script_ is a read-only program that can access public data across accounts and contracts in a strongly typed way. It does not require a user signatures not does it incur any fees. ::: ## Objectives After you complete this guide, you will be able to: - Explain why Cadence **scripts** are more powerful than Solidity **view** functions. - Use the [Flow CLI Commands] to execute a Cadence script against mainnet. - Analyze an account for [NBA Top Shot] NFTs held by the account or its child accounts. - Build the script incrementally to: - Query a parent account for child accounts via [_Hybrid Custody_]. - Inspect each child account's storage paths. - Detect NFT collections the parent can control. - List only NBA Top Shot NFTs with display metadata. - Update the script to also list NFL All Day NFT metadata. ## Prerequisites - Basic familiarity with [Cadence] and [Flow accounts]. - Flow command line interface (CLI) installed and authenticated for mainnet (see [Flow CLI Commands]). - The target parent account uses _Hybrid Custody_ and controls at least one child account that holds NBA Top Shot NFTs. - If you don't have an account that owns NBA Top Shots, you can use `0xfeb88a0fcc175a3d` for this tutorial. :::tip If you are new to [_Hybrid Custody_], the high-level idea is that in Cadence, a parent account can manage one or more child accounts through managed capabilities. This guide uses those capabilities to enumerate NFT collections the parent can control. ::: ## Get started Create a new Flow project and generate a script file: ```bash # Create a new Flow project flow init cadence-scripts-tutorial # Navigate to the project directory cd cadence-scripts-tutorial # Generate a new script file flow generate script TopShotQuery ``` This creates a proper Flow project structure with `flow.json` configuration and generates a script template at `cadence/scripts/TopShotQuery.cdc`. We will **revise one script file** in four passes, and run it after each step. This mirrors how you would build and verify a script from scratch. --- ## Query the account to find child accounts To start, write a script that borrows the parent's _Hybrid Custody_ manager and returns the child addresses it controls. This verifies that imports resolve and that the parent account is configured as expected. First, you'll need to install the `HybridCustody` contract from mainnet. :::info In Cadence, you can import a file from a path as you'd expect. You can also **import an already deployed contract** into your project. ::: Use the [dependency manager] to install the contract with: ```bash flow dependencies install mainnet://0xd8a7e05a7ac670c0.HybridCustody ``` This will install the contract and its own dependencies. You don't need to deploy these contracts again, so pick `none` for the account to deploy. You also don't need an alias. :::warning The language server treats dependency installations in this way similar to package installations in other platforms. You'll need to close and reopen the file or type something to trigger a refresh. ::: Open `scripts/TopShotQuery.cdc` Replace the file contents with: ```cadence import "HybridCustody" // Return the child account addresses managed by the given parent. access(all) fun main(addr: Address): [Address] { let parent = getAuthAccount(addr) let manager = parent.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("manager does not exist") return manager.getChildAddresses() } ``` Run it: ```bash flow scripts execute cadence/scripts/TopShotQuery.cdc --network mainnet 0xfeb88a0fcc175a3d ``` You will see a list of child addresses. If you do not, confirm the parent actually stores a manager at `HybridCustody.ManagerStoragePath`. ```bash Result: [0xa16b948ba2c9a858] ``` --- ## Listing the storage paths found in each child account Next, for each child, enumerate storage paths. This helps us understand what each account stores before we try to detect NFTs. :::info In Cadence, data is stored in a users account in [storage paths]. ::: Update the query to iterate through the child addresses in the `manager` and collect their storage paths and return those paths: ```cadence import "HybridCustody" // Map child address -> array of storage path strings (e.g. "storage/SomePath"). access(all) fun main(addr: Address): {Address: [String]} { let parent = getAuthAccount(addr) let manager = parent.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("manager does not exist") var pathsByChild: {Address: [String]} = {} for child in manager.getChildAddresses() { let acct = getAuthAccount(child) var paths: [String] = [] for sp in acct.storage.storagePaths { paths.append(sp.toString()) } pathsByChild[child] = paths } return pathsByChild } ``` Run it again: ```bash flow scripts execute cadence/scripts/TopShotQuery.cdc --network mainnet 0xfeb88a0fcc175a3d ``` You'll see a map from each child address to its storage paths. This tells us where to look for potential collections. ```bash Result: {0xa16b948ba2c9a858: ["/storage/flowTokenVault", "/storage/PinnacleNFTCollectionProviderForNFTStorefront", "/storage/BackpackCollection", "/storage/PackNFTCollection", "/storage/HybridCustodyChild_0xd8a7e05a7ac670c0", "/storage/ChildAccount_0xfeb88a0fcc175a3d", "/storage/privateForwardingStorage", "/storage/PinnacleCollection", "/storage/AllDayNFTCollection", "/storage/NFTStorefrontV2", "/storage/PinnaclePackNFTCollection", "/storage/ChildAccount_0x0f566b3217c33c4a", "/storage/dapperUtilityCoinReceiver", "/storage/CapFilterParent0xfeb88a0fcc175a3d", "/storage/ChildCapabilityDelegator_0x0f566b3217c33c4a", "/storage/CapFilterParent0x0f566b3217c33c4a", "/storage/flowUtilityTokenReceiver", "/storage/MomentCollection", "/storage/ChildCapabilityDelegator_0xfeb88a0fcc175a3d", "/storage/NFTStorefrontV20x3cdbb3d569211ff3"]} ``` --- ## Detecting NFT collections the parent can control Now you can identify which stored items are NFT collections that the **parent** can act on. In Cadence, a [_capability_] exposes specific interfaces on a stored value. We look for a capability whose type includes [`{NonFungibleToken.Provider}`] and confirm the parent has access via _Hybrid Custody_. Update the script to iterate through storage paths found in child accounts to search for providers of the `NonFungibleToken.Provider` type: ```cadence import "HybridCustody" import "NonFungibleToken" // Map child address -> array of type identifiers for NFT collections // that the parent can control (i.e. has a Provider capability for). access(all) fun main(addr: Address): {Address: [String]} { let parent = getAuthAccount(addr) let manager = parent.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("manager does not exist") let providerType = Type() var controllableTypes: {Address: [String]} = {} for child in manager.getChildAddresses() { let acct = getAuthAccount(child) let childAcct = manager.borrowAccount(addr: child) ?? panic("child account not found") var found: [String] = [] // For each storage path, inspect its controllers (capability controllers). for sp in acct.storage.storagePaths { for ctrl in acct.capabilities.storage.getControllers(forPath: sp) { // If the controller's borrow type does not include Provider, skip. if !ctrl.borrowType.isSubtype(of: providerType) { continue } // Verify the parent has an accessible capability through Hybrid Custody. if let cap: Capability = childAcct.getCapability( controllerID: ctrl.capabilityID, type: providerType ) { let providerCap = cap as! Capability<&{NonFungibleToken.Provider}> if providerCap.check() { // Record the concrete type identifier behind this capability. let typeId = cap.borrow<&AnyResource>()!.getType().identifier found.append(typeId) // One confirmation per path is sufficient. break } } } } controllableTypes[child] = found } return controllableTypes } ``` Run it: ```bash flow scripts execute cadence/scripts/TopShotQuery.cdc --network mainnet 0xfeb88a0fcc175a3d ``` You will now see type identifiers such as `A.
..` for collections the parent can control. We will use these identifiers to filter for Top Shot. ```bash Result: {0xa16b948ba2c9a858: ["A.807c3d470888cc48.Backpack.Collection", "A.e4cf4bdc1751c65d.AllDay.Collection", "A.0b2a3299cc857e29.TopShot.Collection"]} ``` --- ## Filter NFT collection to find and return Top Shots Finally, for each detected collection, [borrow] the collection `{NonFungibleToken.CollectionPublic}`, iterate IDs, resolve `MetadataViews.Display`, and return only Top Shot items. We add a small `isTopShot` predicate that you can customize to your deployment. :::info The [borrow] function is how you use a published [_capability_] in your code. In this case, you borrow the **public** functionality of Cadence NFTs, which includes [`MetadataViews`] that return a view of the **fully-onchain metadata** for the NFT. ::: Update the query to borrow a reference to each public collection and return metadata for those that are NBA Top Shots: ```cadence import "HybridCustody" import "NonFungibleToken" import "MetadataViews" // Map child address -> { tokenId : MetadataViews.Display } for Top Shot NFTs only. access(all) fun main(addr: Address): {Address: {UInt64: MetadataViews.Display}} { let parent = getAuthAccount(addr) let manager = parent.storage.borrow( from: HybridCustody.ManagerStoragePath ) ?? panic("manager does not exist") let providerType = Type() let collectionIface: Type = Type<@{NonFungibleToken.CollectionPublic}>() // Customize this to match other collections found in the previous step! fun isTopShot(_ typeId: String): Bool { // Common pattern: typeId.contains("TopShot") return typeId.contains("TopShot") } var result: {Address: {UInt64: MetadataViews.Display}} = {} for child in manager.getChildAddresses() { let acct = getAuthAccount(child) let childAcct = manager.borrowAccount(addr: child) ?? panic("child account not found") // First, collect controllable type identifiers for this child. var typesWithProvider: [String] = [] for sp in acct.storage.storagePaths { for ctrl in acct.capabilities.storage.getControllers(forPath: sp) { if !ctrl.borrowType.isSubtype(of: providerType) { continue } if let cap: Capability = childAcct.getCapability( controllerID: ctrl.capabilityID, type: providerType ) { let providerCap = cap as! Capability<&{NonFungibleToken.Provider}> if providerCap.check() { let typeId = cap.borrow<&AnyResource>()!.getType().identifier typesWithProvider.append(typeId) break } } } } var displays: {UInt64: MetadataViews.Display} = {} // Walk storage again to borrow the matching collections and read their items. acct.storage.forEachStored(fun (path: StoragePath, t: Type): Bool { // Only consider types we know are controllable and that match Top Shot. var match = false for tid in typesWithProvider { if tid == t.identifier && isTopShot(tid) { match = true break } } if !match { return true } // Skip the concrete resource type token; we want the collection interface. if t.isInstance(collectionIface) { return true } if let col = acct.storage.borrow<&{NonFungibleToken.CollectionPublic}>(from: path) { for id in col.getIDs() { let nft = col.borrowNFT(id)! if let display = nft.resolveView(Type())! as? MetadataViews.Display { displays[id] = display } } } return true }) result[child] = displays } return result } ``` Run it: ```bash flow scripts execute cadence/scripts/TopShotQuery.cdc --network mainnet 0xfeb88a0fcc175a3d ``` The output is a Cadence representation of: ``` { Address: { UInt64: MetadataViews.Display } } ``` which maps each child account address to a map of NFT IDs to their display metadata (name, description, thumbnail). ```bash Result: {0xa16b948ba2c9a858: {44311697: A.1d7e57aa55817448.MetadataViews.Display(name: "Immanuel Quickley 3 Pointer", description: "", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/44311697?width=256")), 44274843: A.1d7e57aa55817448.MetadataViews.Display(name: "Rudy Gobert Rim", description: "", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/44274843?width=256")), 44219960: A.1d7e57aa55817448.MetadataViews.Display(name: "Sasha Vezenkov 3 Pointer", description: "", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/44219960?width=256")), 44300175: A.1d7e57aa55817448.MetadataViews.Display(name: "Malik Monk Assist", description: "", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/44300175?width=256")), 43995280: A.1d7e57aa55817448.MetadataViews.Display(name: "Kelly Olynyk 3 Pointer", description: "Regardless of the stakes, regardless of the stage, Kelly Olynyk is calm and collected beyond the arc. Trailing in the fourth quarter of a tight contest, the Utah Jazz big gets to his spot in the corner and buries a triple to claw within one. After a defensive stop on the next possession Olynyk doubles down on the momentum and drains a transition three to give his team the lead. Olynyk finished with 15 points on 5 of 6 shooting in the October 27, 2023 matchup and the Jazz held on for the W at the hands of the LA Clippers.", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/43995280?width=256"))}} ``` --- ## Extend the script to include AllDay NFTs Now that you have a working script for Top Shot NFTs, let's extend it to also return NFL All Day NFTs. This demonstrates the flexibility of Cadence scripts - you can easily modify them to answer new questions without the need to change any contracts. Update the `isTopShot` function to also include AllDay NFTs: ```cadence // Customize this to match other collections found in the previous step! fun isTopShot(_ typeId: String): Bool { // Include both TopShot and AllDay NFTs return typeId.contains("TopShot") || typeId.contains("AllDay") } ``` Run the updated script: ```bash flow scripts execute cadence/scripts/TopShotQuery.cdc --network mainnet 0xfeb88a0fcc175a3d ``` You will see both Top Shot and AllDay NFTs in the results (truncated for space): ```bash Result: {0xa16b948ba2c9a858: {44311697: A.1d7e57aa55817448.MetadataViews.Display(name: "Immanuel Quickley 3 Pointer", description: "", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://assets.nbatopshot.com/media/44311697?width=256")), 8220605: A.1d7e57aa55817448.MetadataViews.Display(name: "Zach Ertz Reception", description: "Normally used to overwhelming his NFC East foes in a different, midnight-green attire, Zach Ertz, in his most productive yardage-based game since 2022, showed in Week 2 that productivity remains well within reach. Challenged to a \u{201c}who wants it more\u{201d}-type battle during a corner route, Ertz adjusted to a floated ball, using both a 6-foot-5 frame and pure strength to rip away a potential interception, turning it into a 21-yard catch for himself. The 12-year veteran helped the Washington Commanders \u{2014} whose seven field goals offset the New York Giants\u{2019} three touchdowns \u{2014} survive for a unique 21-18 win, with Ertz providing four catches (on four targets) and 62 yards on Sept. 15, 2024.", thumbnail: A.1d7e57aa55817448.MetadataViews.HTTPFile(url: "https://media.nflallday.com/editions/3304/media/image?format=jpeg&width=256"))}} ``` This demonstrates how you can easily modify Cadence scripts to answer different questions about the same data, unlike Solidity, where you'd need to deploy new contracts or rely on external indexers. --- ## Troubleshoot - If you see `manager does not exist`, confirm the parent address actually stores a `HybridCustody.Manager` at `HybridCustody.ManagerStoragePath`. - If you see empty arrays in Step 3, the parent may not have _provider_ access to any collections in those child accounts. - If you see empty results in Step 4, confirm `isTopShot` matches the identifiers you observed in Step 3. - If you are not using _Hybrid Custody_, you can adapt Steps 2-4 to use `getAccount(child)` and scan **publicly exposed** `{NonFungibleToken.CollectionPublic}` capabilities, but you will not be able to assert provider access. ## How this compares to Solidity - **Solidity views are fixed**: You can only retrieve what the contract author exposed via `view` or `pure` functions. If you need a different aggregation or cross-contract traversal, you typically rely on a _data availability service_ or write a new contract to expose that view. - **Cadence scripts are flexible**: You compose types across modules, traverse account storage, and read public capabilities at query time. You do not need to redeploy contracts to answer new questions. Common _data availability service_ examples used in EVM ecosystems: - The Graph (subgraphs) - Covalent (unified API) - Alchemy Enhanced APIs - Reservoir (NFT market APIs) - NFTScan (NFT inventory APIs) ## Conclusion In this tutorial, you learned how to use Cadence scripts to query onchain data directly from Flow's state, without the need to rely on external indexers or APIs. You built a script that can discover and query NFT collections across multiple child accounts with Hybrid Custody, and then extended it to include both NBA Top Shot and NFL All Day NFTs, which demonstrates the power and flexibility of Cadence's native data availability. Now that you have completed the tutorial, you should be able to: - Query onchain data directly with Cadence scripts without external dependencies - Use Hybrid Custody to access child account data from parent accounts - Filter and process NFT collections to extract specific metadata - Modify scripts to answer different questions about the same onchain data - Compare Cadence's native data availability with Solidity's limitations - Build applications that can access any onchain data in real-time This approach gives you the freedom to build applications that can access any onchain data in real-time, which makes Flow's native data availability a powerful tool for developers who build on Flow. [NBA Top Shot]: https://nbatopshot.com/ [account linking tutorial]: ../account-management/account-linking-with-dapper.md [_Hybrid Custody_]: ../account-management/index.md [dependency manager]: ../../../build/tools/flow-cli/dependency-manager.md [Hybrid Custody]: ../account-management/index.md [Flow accounts]: ../../../build/cadence/basics/accounts.md [Cadence]: https://cadence-lang.org/docs/tutorial/first-steps [Flow CLI Commands]: ../../../build/tools/flow-cli/commands.md [storage paths]: https://cadence-lang.org/docs/tutorial/resources#storage-paths [_capability_]: https://cadence-lang.org/docs/language/capabilities [`{NonFungibleToken.Provider}`]: https://github.com/onflow/flow-nft/blob/21c5e18a0985528b53931dc14c55332b3b47939e/contracts/NonFungibleToken.cdc#L154 [borrow]: https://cadence-lang.org/docs/language/capabilities#borrowing-public-capabilities-with-borrow [NFL All Day]: https://nflallday.com/ --- # File: /md/blockchain-development-tutorials/cadence/cadence-advantages/upgrading-cadence-contracts.md --- title: Upgrading Cadence Contracts description: Learn how to upgrade already deployed Cadence contracts by adding new functionality while preserving existing state. Deploy a Counter contract, modify it to add even/odd counting functionality, and update it on testnet using Flow CLI. sidebar_position: 2 keywords: - Cadence contract upgrades - contract updates - Flow CLI - contract deployment - testnet deployment - contract modification - state preservation - contract versioning - Flow CLI commands - account update contract - contract evolution --- # Upgrading Cadence Contracts In Cadence, to upgrade deployed contracts, you can add new functionality while preserving the current state and maintain the same contract address. Unlike other blockchain platforms that require complex proxy patterns or complete redeployment, Cadence allows you to seamlessly extend your contracts with new functions and events through multiple incremental upgrades. This tutorial demonstrates how to upgrade a deployed contract through two scenarios: - Add an event to notify users when the counter reaches an even number. - Extend the contract with additional functionality, like incrementing by two and checking if numbers are even. ## Objectives After you complete this guide, you will be able to: - **Deploy a contract** to Flow testnet using Flow command line interface (CLI). - **Perform incremental contract upgrades** by adding new events and functions. - **Update deployed contracts multiple times** using the `flow accounts update-contract` command - **Test upgraded functionality** with Cadence transactions and scripts. - **Understand what can and cannot be changed** during contract upgrades. - **Apply realistic upgrade scenarios** based on user feedback and requirements. ## Prerequisites - [Flow CLI installed] and configured. - Basic familiarity with [Cadence] and [Flow accounts]. - A **funded testnet account** to deploy and update contracts. - See [Create accounts] and [Fund accounts] in the Flow CLI commands. ## Contract upgrade overview Cadence provides a sophisticated contract upgrade system that allows you to modify deployed contracts while ensuring data consistency and preventing runtime crashes. It's crucial for successful upgrades that you understand what you can and can't change. ### What you CAN upgrade - **Add new functions** - Extend contract functionality with new methods. - **Add new events** - Emit additional events for monitoring and indexing. - **Modify function implementations** - Change how existing functions work. - **Change function signatures** - Update parameters and return types. - **Remove functions** - Delete functions that are no longer needed. - **Change access modifiers** - Update visibility of functions and fields. - **Reorder existing fields** - Field order doesn't affect storage. ### What you CANNOT upgrade - **Add new fields** - Would cause runtime crashes when loading existing data. - **Change field types** - Would cause deserialization errors. - **Remove existing fields** - Fields become inaccessible, but data remains. - **Change enum structures** - Raw values must remain consistent. - **Change contract name** - Contract address must remain the same. ### Why these restrictions exist The [Cadence Contract Updatability documentation](https://cadence-lang.org/docs/language/contract-updatability) explains that these restrictions prevent: - **Runtime crashes** from missing or garbage field values. - **Data corruption** from type mismatches. - **Storage inconsistencies** from structural changes. - **Type confusion** from enum value changes. The validation system ensures that current stored data remains valid and accessible after upgrades. ## Get started Create a new Flow project for this tutorial: ```bash # Create a new Flow project flow init upgrading-contracts-tutorial ``` Follow the prompts and create a `Basic Cadence project (no dependencies)` then open the new project in your editor. ### Create and fund testnet account You'll need a funded testnet account to deploy and update contracts. In a terminal in the root of your project folder: ```bash # Create a testnet account flow accounts create --network testnet ``` When prompted: 1. **Account name**: Enter `testnet-account` 2. Select `testnet` as the network when prompted Fund your account with testnet FLOW tokens: ```bash # Fund the account flow accounts fund testnet-account ``` This will open the faucet in your browser where you can request 100,000 testnet FLOW tokens. :::info The faucet provides free testnet tokens for development and testing purposes. These tokens have no real value and are only used on the testnet network. ::: --- ## Deploy the initial counter contract To start, let's deploy a simple Counter contract to testnet. Open and review `cadence/contracts/Counter.cdc`. This is a simple contract created with all projects: ```cadence access(all) contract Counter { access(all) var count: Int // Event to be emitted when the counter is incremented access(all) event CounterIncremented(newCount: Int) // Event to be emitted when the counter is decremented access(all) event CounterDecremented(newCount: Int) init() { self.count = 0 } // Public function to increment the counter access(all) fun increment() { self.count = self.count + 1 emit CounterIncremented(newCount: self.count) // NEW: Also emit event if the result is even if self.count % 2 == 0 { emit CounterIncrementedToEven(newCount: self.count) } } // Public function to decrement the counter access(all) fun decrement() { self.count = self.count - 1 emit CounterDecremented(newCount: self.count) } // Public function to get the current count view access(all) fun getCount(): Int { return self.count } } ``` ### Configure deployment Add testnet deployment configuration to your `flow.json`: ```bash flow config add deployment ``` Follow the prompts: 1. **Network**: `testnet` 2. **Account**: `testnet-account` 3. **Contract**: `Counter` 4. **Deploy more contracts**: `no` Your `flow.json` will now include a testnet deployment section: ```json { "deployments": { "testnet": { "testnet-account": ["Counter"] } } } ``` ### Deploy to Testnet Deploy your Counter contract to testnet: ```bash flow project deploy --network testnet ``` You will see output similar to: ```bash Deploying 1 contracts for accounts: testnet-account Counter -> 0x9942a81bc6c3c5b7 (contract deployed successfully) 🎉 All contracts deployed successfully ``` ### Test the initial contract Use the provided transaction to test initial functionality: Review `cadence/transactions/TestCounter.cdc`. This transaction simply increments the counter: ```cadence import "Counter" transaction { prepare(acct: &Account) { // Authorizes the transaction } execute { // Increment the counter Counter.increment() // Retrieve the new count and log it let newCount = Counter.getCount() log("New count after incrementing: ".concat(newCount.toString())) } } ``` :::info Cadence transactions are written in Cadence and can call one or more functions on one or more contracts, all with a single user signature. Check out our tutorial to learn how to [Compose with Cadence Transactions] to learn more! ::: Run the test transaction: ```bash flow transactions send cadence/transactions/IncrementCounter.cdc --signer testnet-account --network testnet ``` You will see logs that show the counter incrementing and decrementing as expected. ```bash Transaction ID: 251ee40a050b8c7298d33f1b73ed94996a9d99deae8559526d9dddae182f7752 Block ID 25cdb14fcbaf47b3fb13e6ec43bdef0ede85a6a580caea758220c53d48493e17 Block Height 284173579 Status ✅ SEALED ID 251ee40a050b8c7298d33f1b73ed94996a9d99deae8559526d9dddae182f7752 Payer adb1efc5826d3768 Authorizers [adb1efc5826d3768] Proposal Key: Address adb1efc5826d3768 Index 0 Sequence 1 No Payload Signatures Envelope Signature 0: adb1efc5826d3768 Signatures (minimized, use --include signatures) Events: Index 0 Type A.adb1efc5826d3768.Counter.CounterIncremented Tx ID 251ee40a050b8c7298d33f1b73ed94996a9d99deae8559526d9dddae182f7752 Values - newCount (Int): 1 ``` --- ## Upgrade the contract - Part 1: add event for even numbers Let's start with a realistic scenario: What if we've realized it's very important to our users that they know when the counter reaches an even number, but we forgot to add an event for that case? Let's add that functionality first. ### Modify the Counter contract - first upgrade Update `cadence/contracts/Counter.cdc` to add the new event and enhance the current `increment()` function: ```cadence access(all) contract Counter { access(all) var count: Int // Event to be emitted when the counter is incremented access(all) event CounterIncremented(newCount: Int) // Event to be emitted when the counter is decremented access(all) event CounterDecremented(newCount: Int) // NEW: Event to be emitted when the counter is incremented and the result is even access(all) event CounterIncrementedToEven(newCount: Int) init() { self.count = 0 } // Public function to increment the counter access(all) fun increment() { self.count = self.count + 1 emit CounterIncremented(newCount: self.count) // NEW: Also emit event if the result is even if self.count % 2 == 0 { emit CounterIncrementedToEven(newCount: self.count) } } // Public function to decrement the counter access(all) fun decrement() { self.count = self.count - 1 emit CounterDecremented(newCount: self.count) } // Public function to get the current count view access(all) fun getCount(): Int { return self.count } } ``` ### Key changes made - part 1 This first upgrade adds: 1. **New event**: `CounterIncrementedToEven` to notify when incrementing results in an even number. 2. **Enhanced existing function**: The `increment()` function now also emits the new event when appropriate. 3. **No new fields**: We only use the current `count` field to avoid validation errors. :::info This demonstrates how you can add new behavior and modify current function behavior, which enhances current functionality. The original `CounterIncremented` event still works as before, which ensures backward compatibility. ::: --- ## Update the deployed contract - part 1 Now let's update the deployed contract on testnet with the Flow CLI update command with our first upgrade. ### Update the contract Use the [Flow CLI update contract command] to upgrade your deployed contract: ```bash flow accounts update-contract ./cadence/contracts/Counter.cdc --signer testnet-account --network testnet ``` You will see output similar to: ```bash Contract 'Counter' updated on account '0x9942a81bc6c3c5b7' Address 0x9942a81bc6c3c5b7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key [your public key] Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 2 Index 0 Contracts Deployed: 1 Contract: 'Counter' ``` :::success The contract successfully updated! Notice that: - The contract address remains the same (`0x9942a81bc6c3c5b7`). - The current state (`count`) is preserved. - New functionality is available. ::: ### Test the first upgrade Let's test the new event functionality. Create a simple transaction to test the enhanced `increment()` function: ```bash flow generate transaction TestEvenEvent ``` Replace the contents of `cadence/scripts/CheckCounter.cdc` with: ```cadence import "Counter" access(all) fun main(): {String: AnyStruct} { return { "count": Counter.getCount(), "isEven": Counter.isEven() } } ``` Run the script to check the current state: ```bash flow scripts execute cadence/scripts/CheckCounter.cdc --network testnet ``` You will see output that shows the counter state: ```bash Result: {"count": 1, "isEven": false} ``` Notice that: - The original `count` value is preserved (showing the increment from our earlier test). - The new `isEven()` function works correctly (1 is odd, so it returns false). --- ## Upgrade the contract - part 2: add more functionality Now that we've successfully added the even number event, let's add more functionality to our contract. This demonstrates how you can make multiple incremental upgrades to extend your contract's capabilities. ### Modify the Counter contract - second upgrade Update `cadence/contracts/Counter.cdc` to add the additional functionality: ```cadence access(all) contract Counter { access(all) var count: Int // Event to be emitted when the counter is incremented access(all) event CounterIncremented(newCount: Int) // Event to be emitted when the counter is decremented access(all) event CounterDecremented(newCount: Int) // Event to be emitted when the counter is incremented and the result is even access(all) event CounterIncrementedToEven(newCount: Int) // NEW: Event to be emitted when the counter is incremented by 2 access(all) event CounterIncrementedByTwo(newCount: Int) // NEW: Event to be emitted when the counter is decremented by 2 access(all) event CounterDecrementedByTwo(newCount: Int) init() { self.count = 0 } // Public function to increment the counter access(all) fun increment() { self.count = self.count + 1 emit CounterIncremented(newCount: self.count) // Also emit event if the result is even if self.count % 2 == 0 { emit CounterIncrementedToEven(newCount: self.count) } } // Public function to decrement the counter access(all) fun decrement() { self.count = self.count - 1 emit CounterDecremented(newCount: self.count) } // Public function to get the current count view access(all) fun getCount(): Int { return self.count } // NEW: Public function to increment the counter by 2 access(all) fun incrementByTwo() { self.count = self.count + 2 emit CounterIncrementedByTwo(newCount: self.count) } // NEW: Public function to decrement the counter by 2 access(all) fun decrementByTwo() { self.count = self.count - 2 emit CounterDecrementedByTwo(newCount: self.count) } // NEW: Public function to check if the current count is even view access(all) fun isEven(): Bool { return self.count % 2 == 0 } } ``` ### Key changes made - part 2 This second upgrade adds: 1. **New functions**: `incrementByTwo()` and `decrementByTwo()` that modify the current counter by two. 2. **New events**: `CounterIncrementedByTwo` and `CounterDecrementedByTwo` for the new functionality. 3. **New view function**: `isEven()` to check if the current count is even. 4. **Preserved existing functionality**: All previous functionality remains intact. --- ## Update the deployed contract - part 2 Now let's update the deployed contract with our second upgrade. ### Update the contract again Use the [Flow CLI update contract command] to upgrade your deployed contract with the additional functionality: ```bash flow accounts update-contract ./cadence/contracts/Counter.cdc --signer testnet-account --network testnet ``` You will see output similar to: ```bash Contract 'Counter' updated on account '0x9942a81bc6c3c5b7' Address 0x9942a81bc6c3c5b7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key [your public key] Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 3 Index 0 Contracts Deployed: 1 Contract: 'Counter' ``` :::success The contract successfully updated again! Notice that: - The contract address remains the same (`0x9942a81bc6c3c5b7`). - The current state (`count`) is preserved. - All previous functionality is still available. - New functionality is now available. ::: ### Verify the update Let's verify that the existing functionality still works and the new functionality is available. Create a script to check the current state: ```bash flow generate script CheckCounter ``` Replace the contents of `cadence/scripts/CheckCounter.cdc` with: ```cadence import "Counter" access(all) fun main(): {String: AnyStruct} { return { "count": Counter.getCount(), "isEven": Counter.isEven() } } ``` Run the script to check the current state: ```bash flow scripts execute cadence/scripts/CheckCounter.cdc --network testnet ``` You will see output showing the counter state: ```bash Result: {"count": 2, "isEven": true} ``` Notice that: - The original `count` value is preserved (showing the increments from our earlier tests). - The new `isEven()` function works correctly (two is even, so it returns true). --- ## Test the new functionality Now let's create a transaction to test the new even counter functionality. ### Create test transaction Create a new transaction to test the upgraded functionality: ```bash flow generate transaction TestNewCounter ``` Replace the contents of `cadence/transactions/TestNewCounter.cdc` with: ```cadence import "Counter" transaction { prepare(acct: &Account) { // Authorizes the transaction } execute { // Test the new functionality log("Current count: ".concat(Counter.getCount().toString())) log("Is even: ".concat(Counter.isEven().toString())) // Test the new incrementByTwo function Counter.incrementByTwo() log("After incrementByTwo: ".concat(Counter.getCount().toString())) log("Is even now: ".concat(Counter.isEven().toString())) Counter.incrementByTwo() log("After second incrementByTwo: ".concat(Counter.getCount().toString())) // Test the new decrementByTwo function Counter.decrementByTwo() log("After decrementByTwo: ".concat(Counter.getCount().toString())) // Verify original functionality still works and test the new event Counter.increment() log("After regular increment: ".concat(Counter.getCount().toString())) log("Is even now: ".concat(Counter.isEven().toString())) // Increment again to trigger the CounterIncrementedToEven event Counter.increment() log("After second increment: ".concat(Counter.getCount().toString())) log("Is even now: ".concat(Counter.isEven().toString())) } } ``` ### Run the test transaction Execute the transaction to test the new functionality: ```bash flow transactions send cadence/transactions/TestNewCounter.cdc --signer testnet-account --network testnet ``` You will see logs that show: - The counter incrementing by two each time with `incrementByTwo()` - The counter decrementing by two with `decrementByTwo()` - The `isEven()` function working correctly - The original `increment()` function still working normally - The new `CounterIncrementedToEven` event being emitted when incrementing results in an even number ### Verify final state Run the check script again to see the final state: ```bash flow scripts execute cadence/scripts/CheckCounter.cdc --network testnet ``` You will see output similar to: ```bash Result: {"count": 6, "isEven": true} ``` This confirms that: - The new functions work correctly with the existing counter. - The original state was preserved during the upgrade. - The new functionality is fully operational. --- ## Understand contract upgrades in Cadence Cadence provides a sophisticated contract upgrade system that ensures data consistency and allows controlled modifications. The [Cadence Contract Updatability documentation] provides comprehensive details about the validation rules and restrictions. ### What you can upgrade When you upgrade Cadence contracts, you can: - **Add new state variables** (like `countEven`) - **Add new functions** (like `incrementEven()` and `decrementEven()`) - **Add new events** (like `EvenCounterIncremented`) - **Add new interfaces** and resource types - **Modify function implementations** (with careful consideration) - **Remove existing functions** (they are not stored as data) - **Change function signatures** (parameters, return types) - **Change access modifiers** of fields and functions - **Reorder existing fields** (order doesn't affect storage) ### What You cannot change There are important limitations to contract upgrades: - **Cannot add new fields** to current structs, resources, or contracts. - This would cause runtime crashes when you load current data. - The initializer only runs once during deployment, not on updates. - **Cannot change the type** of current state variables. - Would cause deserialization errors with stored data. - **Cannot remove existing state variables** (though they become inaccessible). - **Cannot change enum structures** (raw values must remain consistent). - **Cannot change the contract name** or address. ### Validation goals The contract update validation ensures that: - **Stored data doesn't change its meaning** when a contract updates. - **Decoding and using stored data** does not lead to runtime crashes. - **Type safety is maintained** across all stored values. :::warning The validation system focuses on how to prevent runtime inconsistencies with stored data. It does not ensure that programs which import the updated contract remain valid - you may need to update dependent code if you change function signatures or remove functions. ::: ### Advanced upgrade patterns #### The `#removedType` pragma For cases where you need to remove a type declaration (which is normally invalid), Cadence provides the `#removedType` pragma. This allows you to "tombstone" a type, which prevents it from being re-added with the same name: ```cadence access(all) contract Foo { // Remove the resource R permanently #removedType(R) // Other contract code... } ``` This pragma: - **Prevents security issues** from type confusion. - **Cannot be removed** after you add it (prevents circumventing restrictions). - **Only works with composite types**, not interfaces. #### Enum upgrade restrictions Enums have special restrictions due to their raw value representation: - **Can only add enum cases at the end** of current cases. - **Cannot reorder, rename, or remove** current enum cases. - **Cannot change the raw type** of an enum. - **Cannot change enum case names** (would change stored values' meaning). ### Best practices When you upgrade contracts: 1. **Plan upgrades carefully** - Consider future extensibility and avoid breaking changes. 2. **Test thoroughly** - Verify both old and new functionality work correctly. 3. **Use events** - Emit events for new functionality to allow monitoring and indexing. 4. **Document changes** - Keep track of what was added, removed, or modified in each upgrade. 5. **Consider dependent code** - Update any programs that import your contract if you change function signatures. 6. **Use the `#removedType` pragma** - When you need to permanently remove types. 7. **Validate enum changes** - Ensure enum modifications follow the strict rules. 8. **Test with existing data** - Verify upgrades work with real stored state, not just empty contracts. --- ## Why this matters Cadence's contract upgrade model provides several advantages: - **No proxy patterns needed** - Unlike Ethereum, you don't need complex proxy contracts. - **State preservation** - Current data and functionality remain intact. - **Address stability** - Contract addresses don't change during upgrades. - **Gas efficiency** - Upgrades are more efficient than redeployment. - **User experience** - Applications continue working without interruption. This approach allows you to evolve your contracts over time, You can add new features and capabilities and maintain backward compatibility and preserve user data. ## Conclusion In this tutorial, you learned how to upgrade deployed Cadence contracts through multiple incremental upgrades by: - **Deploying an initial contract** to Flow testnet. - **Performing a first upgrade** to add an event for even numbers based on user feedback. - **Testing the first upgrade** to verify the new event functionality works correctly. - **Performing a second upgrade** to add additional functions and events. - **Testing the complete upgraded functionality** with comprehensive transactions. - **Verifying state preservation** and backward compatibility across multiple upgrades. Now that you have completed the tutorial, you should be able to: - Deploy contracts to Flow testnet with Flow CLI. - Perform incremental contract upgrades by adding new functions and events. - Update deployed contracts multiple times and preserve the current state. - Test upgraded functionality with Cadence transactions and scripts. - Understand what can and cannot be changed during contract upgrades. - Apply realistic upgrade scenarios based on user feedback and requirements. - Plan and execute multiple contract upgrades over time. This incremental upgrade model makes Cadence contracts more flexible and maintainable than traditional smart contract platforms, which allows you to evolve your applications over time based on real user needs without complex migration patterns or breaking changes. The ability to make multiple upgrades while you maintain state and the same contract address provides a powerful foundation for long-term application development. [Flow CLI installed]: ../../../build/tools/flow-cli/install.md [Cadence]: https://cadence-lang.org/docs/tutorial/first-steps [Flow accounts]: ../../../build/cadence/basics/accounts.md [Create accounts]: ../../../build/tools/flow-cli/commands.md#create-accounts [Fund accounts]: ../../../build/tools/flow-cli/commands.md#fund-accounts [Flow CLI update contract command]: ../../../build/tools/flow-cli/accounts/account-update-contract.md [Cadence Contract Updatability documentation]: https://cadence-lang.org/docs/language/contract-updatability [Compose with Cadence Transactions]: ./compose-with-cadence-transactions.md --- # File: /md/blockchain-development-tutorials/cadence/emulator-fork-testing.md --- sidebar_position: 21 sidebar_label: Emulator Fork Testing title: Interactive Testing with Forked Emulator description: Run your app, E2E tests, and manual explorations against a forked mainnet or testnet using the Flow Emulator. Test with production state and real contracts without deploying to live networks. keywords: - flow emulator --fork - emulator fork mode - E2E testing - app testing - frontend testing - Cypress testing - Playwright testing - FCL configuration - mainnet fork - testnet fork - account impersonation - interactive testing - production state - local development - forked emulator - React testing - wallet testing - migration testing - exploratory testing - manual testing - fork-height - pinned fork --- # Interactive Testing with Forked Emulator Fork testing gives you a local copy of mainnet state that you can freely modify and reset instantly. Test your DeFi app against real DEX liquidity pools and lending protocols without risking funds, verify integrations with existing mainnet contracts before deploying, and debug production issues at specific block heights with exact mainnet state. This tutorial teaches you how to run your app and E2E tests against Flow mainnet using `flow emulator --fork`. You'll connect your frontend to production-like state, impersonate any mainnet account, and test with real balances and assets—all running locally. ## What You'll Learn After you complete this tutorial, you'll be able to: - **Start the emulator in fork mode** with `flow emulator --fork`. - **Connect your app frontend** to the forked emulator. - **Test DeFi integrations** against real liquidity pools, DEXs, and protocols. - **Test against real mainnet contracts** and production data interactively. - **Run E2E tests** (Cypress, Playwright) against forked state. - **Use account impersonation** to test as any mainnet account with real balances and assets. - **Pin to specific block heights** for reproducible testing. - **Debug and explore** contract interactions manually. ## What You'll Build You'll create a complete forked emulator setup that demonstrates: - Starting the emulator with forked mainnet state. - A React app connected to the forked emulator reading real FlowToken data. - Manual testing flows using account impersonation. - Automating tests with E2E frameworks against forked state. - A reusable pattern for interactive testing and debugging. ## Prerequisites ### Flow CLI This tutorial requires [Flow CLI] v1.8.0 or later installed. If you haven't installed it yet and have [homebrew] installed, run: ```bash brew install flow-cli ``` For other operating systems, refer to the [installation guide]. ### Node.js and npm You'll need Node.js (v16+) and npm to run the React frontend examples. Check your installation: ```bash node --version npm --version ``` ### Frontend development knowledge Basic familiarity with React and JavaScript is helpful but not required. The examples use the [Flow React SDK] for Flow blockchain integration. :::tip This tutorial uses `@onflow/react-sdk` for all React examples. The React SDK provides hooks and components that make Flow development feel native to React. For non-React applications, you can use `@onflow/fcl` directly. ::: ### Network access You'll need network access to Flow's public access nodes: - Mainnet: `access.mainnet.nodes.onflow.org:9000` - Testnet: `access.devnet.nodes.onflow.org:9000` :::info This tutorial covers `flow emulator --fork` (interactive testing with a forked emulator), which is different from `flow test --fork` (running Cadence test files against forked state). For an overview of both modes, see [Fork Testing](../../../build/tools/flow-cli/fork-testing.md). For testing Cadence contracts with test files, see [Fork Testing with Cadence]. ::: ## Understanding Emulator Fork Mode ### What is `flow emulator --fork`? The emulator's fork mode starts a local Flow blockchain that connects to a real network (mainnet or testnet) and fetches state on-demand. Your app, scripts, and transactions run locally but can read from and interact with real network data. **Key capabilities:** - Full gRPC and REST API servers running locally - On-demand fetching of accounts, contracts, and state from the live network - Disabled signature validation. You can impersonate any mainnet account to execute transactions - All mutations stay local—never affect the real network - Perfect for E2E tests, manual exploration, and debugging ### When to Use This Use `flow emulator --fork` for: - **DeFi application testing**: Test against real liquidity pools, DEXs, and lending protocols with production state - **E2E and frontend testing**: Run Cypress/Playwright tests against production-like state - **Manual exploration**: Interact with your app connected to forked mainnet - **Debugging user issues**: Reproduce bugs at specific block heights - **Migration testing**: Test contract upgrades with real account state - **Wallet integration**: Test wallet connect flows and transactions - **Bot and indexer testing**: Run automated tools against forked data **Don't use this for:** - Cadence unit/integration tests (use `flow test --fork` instead—see [Fork Testing with Cadence]) ### Emulator Fork vs Test Framework Fork | Feature | `flow emulator --fork` | `flow test --fork` | | --------------- | --------------------------------------- | ------------------------------ | | **Use for** | App E2E, manual testing, debugging | Cadence unit/integration tests | | **Connects to** | Frontend, wallets, bots, E2E tools | Cadence Testing Framework | | **Run with** | FCL, Cypress, Playwright, manual clicks | `flow test` command | | **Best for** | User flows, UI testing, exploration | Contract logic validation | | **Examples** | React app, wallet flows, E2E suites | `*_test.cdc` files | Both modes are valuable—use the right tool for the job. ## Quick Start: Run in 60 Seconds Want to see it work immediately? Here's the fastest path: ```bash # 1. Initialize a Flow project flow init # 2. Install FlowToken dependency flow dependencies install FlowToken FungibleToken # 3. Start forked emulator (in a separate terminal) flow emulator --fork mainnet # 4. Create a script to check the forked state flow generate script getFlowSupply ``` Add the following to `cadence/scripts/getFlowSupply.cdc`: ```cadence import "FlowToken" access(all) fun main(): UFix64 { return FlowToken.totalSupply } ``` First, verify the script works against real mainnet: ```bash flow scripts execute cadence/scripts/getFlowSupply.cdc --network mainnet ``` Then, in another terminal, run the script against the fork: ```bash flow scripts execute cadence/scripts/getFlowSupply.cdc --network mainnet-fork ``` You'll see the real mainnet FlowToken supply! Now let's build a complete example with a frontend. ## Create Your Project Navigate to your development directory and create a new Flow project: ```bash mkdir emulator-fork-demo cd emulator-fork-demo flow init --yes ``` This creates an empty Flow project with default configuration. ## Start the Forked Emulator Start the emulator in fork mode, connected to mainnet: ```bash flow emulator --fork mainnet ``` You'll see output like: ``` INFO[0000] ⚙️ Using service account 0xf8d6e0586b0a20c7 INFO[0000] 🌱 Starting Flow Emulator in fork mode (mainnet) INFO[0000] 🛠 GRPC server started on 127.0.0.1:3569 INFO[0000] 📡 REST server started on 127.0.0.1:8888 INFO[0000] 🌐 Forking from access.mainnet.nodes.onflow.org:9000 ``` **Leave this terminal running.** The emulator is now serving: - **REST API**: `http://localhost:8888` (for FCL/frontend) - **gRPC API**: `localhost:3569` (for Flow CLI) :::info Fork Network Configuration When you run `flow init`, the CLI automatically configures a `mainnet-fork` network in your `flow.json` that inherits all contract aliases from mainnet. This means you don't need to manually configure fork networks—it just works! For details on fork network configuration, see the [Fork Testing Overview](../../../build/tools/flow-cli/fork-testing.md) and [flow.json Configuration Reference](../../../build/tools/flow-cli/flow.json/configuration.md#networks). ::: :::tip Pin to a specific block height for reproducibility: ```bash flow emulator --fork mainnet --fork-height ``` This ensures the forked state is consistent across runs—essential for E2E tests in CI. ::: ## Deploy Your Contracts Against Mainnet State The most common use case: deploy your NEW contracts to the forked emulator so they can interact with real mainnet contracts and data. This lets you test your DeFi protocol against live DEXs, lending protocols, liquidity pools, and other production DeFi infrastructure. ### Example: Deploy and Test Your Contract **1. Create your contract:** ```bash flow generate contract MyDeFiProtocol ``` Edit `cadence/contracts/MyDeFiProtocol.cdc`: ```cadence import "FlowToken" access(all) contract MyDeFiProtocol { // Your DeFi logic that reads real mainnet FlowToken data access(all) fun getTotalSupply(): UFix64 { return FlowToken.totalSupply } } ``` **2. Start the forked emulator:** ```bash flow emulator --fork mainnet ``` When the emulator starts, note the service account address in the logs: ``` ⚙️ Using service account 0xe467b9dd11fa00df ``` **3. Configure the service account:** Add the forked emulator's service account (use the address from the startup logs and a dummy key). First, create a dummy key file. Use a simple P-256 placeholder so tooling that validates curve membership continues working: ```bash echo "0x0000000000000000000000000000000000000000000000000000000000000001" > blank-key.pkey ``` Then manually add to your `flow.json`: ```json { "accounts": { "mainnet-fork-service": { "address": "0xe467b9dd11fa00df", "key": { "type": "file", "location": "blank-key.pkey" } } } } ``` Since signature validation is disabled in fork mode, the key value doesn't matter. **4. Configure deployment:** ```bash flow config add deployment \ --network mainnet-fork \ --account mainnet-fork-service \ --contract MyDeFiProtocol ``` **5. Deploy your contract:** ```bash flow project deploy --network mainnet-fork --update ``` :::tip Use `--update` if you're working on an existing project that's already deployed to mainnet. The forked emulator mirrors mainnet state, so if your contract already exists at that address on mainnet, it will exist in the fork too. The `--update` flag replaces the mainnet version with your local changes. ::: **6. Test your contract:** Your contract can now interact with real mainnet contracts! Create a script to test it: ```bash flow generate script getTotalSupply ``` Add the following to `cadence/scripts/getTotalSupply.cdc`: ```cadence import "MyDeFiProtocol" access(all) fun main(): UFix64 { return MyDeFiProtocol.getTotalSupply() } ``` Run the script: ```bash flow scripts execute cadence/scripts/getTotalSupply.cdc --network mainnet-fork ``` You'll see something like `Result: 1628083999.54686045` - the real mainnet FlowToken supply! Your contract runs locally but reads production data. Perfect for testing integrations before mainnet deployment. ## Mock Existing Mainnet Contracts You can override existing mainnet contracts with your own versions for testing. This is useful for testing contract upgrades, fixing bugs, or adding test functionality to mainnet contracts. ### Example: Mock a Mainnet Contract Let's say you want to test how your DeFi protocol behaves with a modified version of an existing mainnet contract. **1. Create your mock oracle contract:** ```bash flow generate contract PriceOracle ``` Edit `cadence/contracts/PriceOracle.cdc` to match the interface of the mainnet oracle you want to mock: ```cadence // Mock implementation of mainnet PriceOracle with fixed test prices access(all) contract PriceOracle { access(all) fun getPrice(): UFix64 { return 123.45 // Fixed test price for predictable testing } } ``` **2. Deploy to the SAME address as the mainnet oracle:** In your `flow.json`, configure deployment to use the mainnet oracle's address: ```json { "contracts": { "PriceOracle": "cadence/contracts/PriceOracle.cdc" }, "deployments": { "mainnet-fork": { "mainnet-oracle-account": ["PriceOracle"] } }, "accounts": { "mainnet-oracle-account": { "address": "0x1654653399040a61", "key": { "type": "file", "location": "blank-key.pkey" } } } } ``` **3. Deploy with `--update` flag:** ```bash flow project deploy --network mainnet-fork --update ``` Now your mock oracle replaces the mainnet oracle at that address. All imports and references to the original oracle will use your mocked version with fixed test prices instead! :::tip This is how you test contract upgrades or modifications against real mainnet state without affecting the live network. ::: ## Install Dependencies Use the [Dependency Manager] to install common Flow contracts. This adds them to your `flow.json` with mainnet aliases that will automatically work on the fork: ```bash flow dependencies install FlowToken FungibleToken ``` Your `flow.json` now includes: ```json { "dependencies": { "FlowToken": { "source": "mainnet://1654653399040a61.FlowToken", "aliases": { "emulator": "0x0ae53cb6e3f42a79", "mainnet": "0x1654653399040a61", "testnet": "0x7e60df042a9c0868" } }, "FungibleToken": { "source": "mainnet://f233dcee88fe0abe.FungibleToken", "aliases": { "emulator": "0xee82856bf20e2aa6", "mainnet": "0xf233dcee88fe0abe", "testnet": "0x9a0766d93b6608b7" } } } } ``` **Key insight:** Notice there's no `mainnet-fork` alias. That's the beauty—`mainnet-fork` automatically inherits the `mainnet` aliases thanks to the fork configuration! ## Test with Flow CLI Scripts Before connecting a frontend, verify the fork works with a simple script. Generate a script file using the Flow CLI: ```bash flow generate script getFlowSupply ``` Add the following to `cadence/scripts/getFlowSupply.cdc`: ```cadence import "FlowToken" access(all) fun main(): UFix64 { return FlowToken.totalSupply } ``` Notice we're using the import shorthand `import "FlowToken"` instead of an address. The CLI will automatically resolve this to the mainnet address on the fork. First, verify the script works against real mainnet: ```bash flow scripts execute cadence/scripts/getFlowSupply.cdc --network mainnet ``` Then, in a **new terminal** (keep the emulator running), execute the script against the fork: ```bash flow scripts execute cadence/scripts/getFlowSupply.cdc --network mainnet-fork ``` You should see the real mainnet FlowToken supply (e.g., `Result: 1523456789.00000000`). **What happened:** 1. Your script ran on the local emulator 2. The CLI resolved `"FlowToken"` to the mainnet address (`0x1654653399040a61`) 3. The emulator fetched FlowToken contract state from mainnet on-demand 4. The script returned real production data Now let's connect a frontend. ## Create a React App Create a Next.js app with Flow integration: ```bash npx create-next-app@latest flow-fork-app ``` During setup, choose: - **Use TypeScript**: Yes - **Use src directory**: Yes - **Use App Router**: Yes Then install the Flow React SDK: ```bash cd flow-fork-app npm install @onflow/react-sdk ``` Copy your project's `flow.json` into the app's `src` directory: ```bash # From your flow-fork-app directory cp ../flow.json src/ ``` This allows the `FlowProvider` to resolve contract imports. ### Configure for Fork Testing Since Next.js uses the App Router with server components, create a client component wrapper. First, create the components directory: ```bash mkdir -p src/components ``` Then create `src/components/FlowProviderWrapper.tsx`: ```typescript 'use client'; import { FlowProvider } from '@onflow/react-sdk'; import flowJSON from '../flow.json'; export default function FlowProviderWrapper({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` Then update `src/app/layout.tsx` to use the wrapper: ```typescript import FlowProviderWrapper from '@/components/FlowProviderWrapper'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` ### Create a Demo Component Create a simple demo that queries FlowToken supply from the forked mainnet. Update `src/app/page.tsx`: ```typescript 'use client'; import { useState } from 'react'; import { useFlowCurrentUser, useFlowQuery, Connect } from '@onflow/react-sdk'; export default function Home() { const { user } = useFlowCurrentUser(); const [shouldFetch, setShouldFetch] = useState(false); // Query FlowToken supply from forked mainnet const { data: flowSupply, isLoading, error, } = useFlowQuery({ cadence: ` import "FlowToken" access(all) fun main(): UFix64 { return FlowToken.totalSupply } `, args: (arg, t) => [], query: { enabled: shouldFetch, // Only run when button is clicked }, }); return (

🌊 Flow Emulator Fork Demo

Connected to: Forked Mainnet (localhost:8888)

FlowToken Supply (Real Mainnet Data)

{error &&

Error: {(error as Error).message}

} {flowSupply && (

Total Supply: {Number(flowSupply).toLocaleString()} FLOW

)}

Wallet Connection

{user?.loggedIn && (

Connected: {user.addr}

)}
); } ``` ### Start the dev wallet (optional) For wallet authentication flows, start the FCL dev wallet in another terminal: ```bash flow dev-wallet ``` This starts the dev wallet at `http://localhost:8701`. ### Run your app Start the Next.js dev server: ```bash npm run dev ``` Navigate to `http://localhost:3000`. Click "Get FlowToken Supply" to see real mainnet data! **What's happening:** 1. `FlowProvider` receives `flow.json` and configures import resolution 2. The string import `import "FlowToken"` resolves to the mainnet address automatically 3. `useFlowQuery` executes the Cadence script via the local emulator 4. The emulator fetches FlowToken state from mainnet on-demand 5. Your app displays real production data—all running locally! **Key React SDK features used:** - `FlowProvider` – Wraps your app, configures the Flow connection, and resolves contract imports from `flow.json` - `useFlowCurrentUser` – Provides wallet authentication state - `useFlowQuery` – Executes Cadence scripts with automatic caching and loading states - `Connect` – Pre-built wallet connection UI component :::tip Contract Import Resolution By passing `flowJson` to the `FlowProvider`, string imports like `import "FlowToken"` automatically resolve to the correct network addresses. **How it works:** 1. SDK looks up contract aliases for the specified `flowNetwork` 2. For fork networks, it checks if the network has a `fork` property and inherits aliases from the parent network 3. Contract imports in your Cadence code are replaced with the resolved addresses **Example:** With `flowNetwork: 'mainnet-fork'` (which has `fork: 'mainnet'`), `import "FlowToken"` resolves to `0x1654653399040a61` (the mainnet FlowToken address). ::: ## Account Impersonation The forked emulator's superpower: you can execute transactions as **any mainnet account** because signature validation is disabled. ### Read Account Balance Generate a script to read account balances: ```bash flow generate script getBalance ``` Add the following to `cadence/scripts/getBalance.cdc`: ```cadence import "FlowToken" import "FungibleToken" access(all) fun main(address: Address): UFix64 { let account = getAccount(address) let vaultRef = account.capabilities .borrow<&{FungibleToken.Balance}>(/public/flowTokenBalance) ?? panic("Could not borrow FlowToken Balance reference") return vaultRef.balance } ``` Check the Flow service account balance (a real mainnet account): ```bash flow scripts execute cadence/scripts/getBalance.cdc 0x1654653399040a61 --network mainnet-fork ``` You'll see the service account's actual mainnet balance! The imports automatically resolved to mainnet addresses because you're using the `mainnet-fork` network. ### Execute Transaction as Any Account Generate a transaction to transfer tokens: ```bash flow generate transaction transferTokens ``` Add the following to `cadence/transactions/transferTokens.cdc`: ```cadence import "FungibleToken" import "FlowToken" transaction(amount: UFix64, to: Address) { let sentVault: @{FungibleToken.Vault} prepare(signer: auth(Storage) &Account) { let vaultRef = signer.storage.borrow( from: /storage/flowTokenVault ) ?? panic("Could not borrow reference to the owner's Vault") self.sentVault <- vaultRef.withdraw(amount: amount) } execute { let recipient = getAccount(to) let receiverRef = recipient.capabilities .borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Could not borrow receiver reference") receiverRef.deposit(from: <-self.sentVault) } } ``` The forked emulator disables transaction signature validation, allowing you to send transactions as any address without valid signatures. Now let's test transferring tokens from a mainnet account using impersonation. ### CLI-Based Impersonation To use impersonation with the CLI, you need to add the mainnet account to your `flow.json` (signature validation is disabled, so the key value doesn't matter). Manually add to your `flow.json` (using the same `blank-key.pkey` file): ```json { "accounts": { "mainnet-service": { "address": "0x1654653399040a61", "key": { "type": "file", "location": "blank-key.pkey" } } } } ``` Transfer tokens from the mainnet service account to another mainnet account: ```bash # Transfer from mainnet service account to any mainnet address (impersonation!) flow transactions send cadence/transactions/transferTokens.cdc 100.0 0xRECIPIENT_ADDRESS \ --signer mainnet-service \ --network mainnet-fork # Verify the transfer flow scripts execute cadence/scripts/getBalance.cdc 0xRECIPIENT_ADDRESS \ --network mainnet-fork ``` ### Dev Wallet Authentication with Impersonation The most powerful feature: when connecting your app to the forked emulator with the dev wallet, **you can authenticate as ANY mainnet account** directly in the UI. Start the dev wallet: ```bash flow dev-wallet ``` In your app (running against the forked emulator), click the wallet connect button. In the dev wallet UI: 1. **Enter any mainnet address** in the address field (e.g., a whale wallet, liquidity provider, or DeFi protocol account) 2. Click "Authenticate" 3. Your app is now authenticated as that mainnet account with all its real balances, liquidity positions, and storage! **Additional dev wallet features in fork mode:** - **Fund accounts**: The dev wallet can add FLOW tokens to any account, even real mainnet accounts - **No configuration needed**: The dev wallet handles impersonation automatically when connected to a forked emulator - **Full account state**: Access all assets, storage, and capabilities from the real mainnet account This lets you: - Test your app as a user with specific assets or permissions - Debug issues reported by specific mainnet accounts - Verify flows work for accounts with large balances or complex liquidity positions - Test edge cases with real account states - Add test funds to accounts that need more FLOW for testing :::tip How "Impersonation" Works The forked emulator simply skips signature verification. You can specify any mainnet address as the signer, and the emulator will execute the transaction as that account. Empty or invalid signatures are accepted. This lets you test with real account balances, storage, and capabilities without needing private keys. For frontend flows with the dev wallet, it works the same way—the wallet can "sign" as any address because the emulator doesn't validate signatures. ::: ## Automating with E2E Testing The forked emulator works with any E2E testing framework (Cypress, Playwright, Puppeteer, etc.). This lets you automate your app tests against production-like state. ### Quick Example with Cypress ```bash npm install --save-dev cypress ``` Create `cypress/e2e/flowFork.cy.js`: ```javascript describe('Flow Fork Test', () => { it('reads real mainnet data', () => { cy.visit('http://localhost:3000'); cy.contains('Get FlowToken Supply').click(); cy.contains('Total Supply:', { timeout: 10000 }).should('be.visible'); }); }); ``` ### Running E2E Tests Run three terminals: 1. **Terminal 1**: `flow emulator --fork mainnet --fork-height ` 2. **Terminal 2**: `npm start` (your React app) 3. **Terminal 3**: `npx cypress run` Your tests now run against forked mainnet—**perfect for CI/CD pipelines** with pinned block heights ensuring deterministic results. :::tip Use the same approach with Playwright, Puppeteer, or any browser automation tool. The key is having your app connect to the forked emulator (`http://localhost:8888`) while your E2E framework tests the UI. ::: ## Common Use Cases ### Testing DeFi Applications Test your DeFi application against real mainnet liquidity and protocols: 1. Fork mainnet at a specific block height 2. Impersonate accounts with large token balances or LP positions 3. Test your swap, lending, or yield farming logic against real DEX state 4. Verify slippage calculations with actual liquidity pool reserves 5. Test edge cases like low liquidity scenarios using real market conditions **Example: Testing a swap integration** ```bash # Fork at a known block with specific liquidity conditions flow emulator --fork mainnet --fork-height # In your test, impersonate a whale account # Execute swaps against real DEX contracts (IncrementFi, etc.) # Verify your price calculations match actual execution ``` This lets you test against production liquidity without spending real tokens or affecting live markets. ### Testing Contract Upgrades Test a contract upgrade against real mainnet state by mocking the contract with your upgraded version: 1. Configure the mock in `flow.json` (see [Mocking Mainnet Contracts](#mocking-mainnet-contracts)) 2. Start the forked emulator 3. Deploy your upgraded contract: `flow project deploy --network mainnet-fork --update` 4. Test your app against the upgraded contract with all real mainnet state intact 5. Verify existing integrations and users aren't broken by the upgrade ### Debugging User-Reported Issues Reproduce a bug at the exact block height it occurred: ```bash flow emulator --fork mainnet --fork-height ``` Then manually interact with your app or run specific transactions to reproduce the issue. ### Testing Wallet Integrations Test wallet connect flows, transaction signing, and account creation against production-like state: 1. Start forked emulator and dev wallet 2. Use your app to authenticate 3. Sign transactions as real mainnet accounts (via impersonation) 4. Verify balance updates, event emissions, etc. ### Running Bots and Indexers Test automated tools against forked data by pointing your SDK to the local emulator: **Any Flow SDK works:** - **JavaScript/TypeScript**: `@onflow/fcl` - **Go**: `flow-go-sdk` - **Python**: `flow-py-sdk` - **Other languages**: Configure to connect to `http://localhost:8888` **Example with JavaScript:** ```javascript // Node.js bot that monitors FlowToken transfers const fcl = require('@onflow/fcl'); fcl.config({ 'accessNode.api': 'http://localhost:8888', // Point to forked emulator }); async function monitorTransfers() { // Subscribe to blocks and process FlowToken events // Bot reads real mainnet data but runs locally } ``` **Example with Go:** ```go import "github.com/onflow/flow-go-sdk/client" // Connect to forked emulator flowClient, err := client.New("localhost:3569", grpc.WithInsecure()) // Your bot/indexer logic reads from forked mainnet state ``` ## Best Practices ### 1. Pin Block Heights for Reproducibility Always pin heights in E2E tests and CI: ```bash flow emulator --fork mainnet --fork-height 85432100 ``` **Why:** Ensures tests run against identical state every time. ### 2. Keep Emulator Running During Development Start the forked emulator once and leave it running. Restart only when you need to change the fork height or network. ### 3. Use Testnet Before Mainnet Test against testnet first to avoid mainnet access node rate limits: ```bash flow emulator --fork testnet --fork-height ``` ### 4. Mock External Dependencies The forked emulator only mirrors Flow blockchain state. External APIs, oracles, and cross-chain data won't work. Mock them in your E2E tests: ```javascript // In Cypress: Mock external oracle response cy.intercept('GET', 'https://api.example.com/price', { statusCode: 200, body: { price: 123.45 }, }); ``` In your React app, you can mock API calls during testing while keeping real implementations for production. ### 5. Test Against Real User Accounts The forked emulator disables signature validation, so you can transact as any mainnet account. Just reference the address—empty or invalid signatures are accepted: ```bash # Execute a transaction as any mainnet account flow transactions send my_transaction.cdc \ --signer 0x1234567890abcdef \ --network mainnet-fork ``` This lets you test with real whale wallets, liquidity provider accounts, or any address that has interesting DeFi state on mainnet. ### 6. Document Your Fork Heights Keep a log of which block heights you use for testing and why: ```bash # .env.test FORK_HEIGHT_STABLE= # Known stable state FORK_HEIGHT_LATEST= # Latest tested state ``` ## Limitations and Considerations ### Network State Fetching Fork mode fetches state from the access node on-demand. The first access to an account or contract fetches data over the network; subsequent accesses benefit from caching. With pinned block heights, caching is very effective. ### Spork Boundaries Historical data is only available within the current spork. You cannot fork to block heights from previous sporks via public access nodes. See: [Network Upgrade (Spork) Process]. ### Off-Chain Services The fork only includes Flow blockchain state. External services don't work: - **Oracles**: Mock responses - **IPFS/Arweave**: Mock or run local nodes - **Cross-chain bridges**: Mock or test separately ## Troubleshooting ### Emulator Won't Start **Error:** `network "mainnet" not found in flow.json` **Solution:** Make sure your `flow.json` includes the mainnet network: ```json { "networks": { "mainnet": "access.mainnet.nodes.onflow.org:9000" } } ``` Or use `--fork-host` directly: ```bash flow emulator --fork-host access.mainnet.nodes.onflow.org:9000 ``` ### Contract Import Fails **Error:** `import "FlowToken" could not be resolved` **Solution:** Make sure you've installed dependencies with the mainnet alias: ```bash flow dependencies install FlowToken FungibleToken ``` Verify the contract has a mainnet alias that the fork can inherit. ### App Can't Connect **Error:** Frontend can't reach the emulator **Solution:** Verify FlowProvider is configured correctly: ```javascript ``` Check the emulator is running and serving on port 8888. **Common mistakes:** 1. **Wrong network:** Using `flowNetwork: 'emulator'` when forking mainnet will use emulator contract addresses (`0x0ae53cb6...`) instead of mainnet addresses. Use your fork network name (`'mainnet-fork'`). 2. **Missing flowJson prop:** The `flowJson` prop is required for contract import resolution. Make sure you're importing and passing your `flow.json` file. ### Script Returns Stale Data **Issue:** Script returns unexpected/old values **Solution:** The fork fetches state at the pinned height or latest. Verify: ```bash # Check which block the emulator is at flow blocks get latest --network emulator ``` If you need fresher data, restart without `--fork-height`. ### E2E Tests Flaky **Issue:** Tests pass sometimes but fail randomly **Solution:** 1. Pin block height for consistency 2. Add longer timeouts for network calls 3. Check for race conditions in async code ## When to Use Emulator Fork vs Test Framework Fork Choose the right tool: | Use Case | Tool | | --------------------------------------------- | ---------------------- | | Cadence unit tests | `flow test` (no fork) | | Cadence integration tests with real contracts | `flow test --fork` | | Manual testing with app | `flow emulator --fork` | | E2E testing (Cypress/Playwright) | `flow emulator --fork` | | Debugging frontend issues | `flow emulator --fork` | | Testing wallets/bots/indexers | `flow emulator --fork` | Both modes complement each other. See [Testing Strategy] for the full picture. ## Conclusion In this tutorial, you learned how to use the forked emulator for interactive testing, E2E test automation, and manual exploration. You created a React app using the Flow React SDK connected to forked mainnet, used account impersonation to test with real account states, and saw how to automate tests with E2E frameworks—all without deploying to a live network. Now that you have completed this tutorial, you can: - **Start the emulator in fork mode** with `flow emulator --fork`. - **Connect your app frontend** to the forked emulator. - **Test against real mainnet contracts** and production data interactively. - **Run E2E tests** (Cypress, Playwright) against forked state. - **Use account impersonation** to test as any mainnet account. - **Pin to specific block heights** for reproducible testing. - **Debug and explore** contract interactions manually. The forked emulator bridges the gap between local development and testnet/mainnet deployments. Use it to catch integration issues early, test against real-world conditions, and validate your app before going live. ### Next Steps - Add E2E tests to your CI/CD pipeline using pinned fork heights - Test your app's upgrade flows against forked mainnet - Review the [Fork Testing Overview] for both emulator and test framework fork modes - For Cadence contract testing, see [Fork Testing with Cadence] - Explore [Flow React SDK] hooks and components (events, mutations, Cross-VM features) - Review the [Testing Strategy] for the full testing approach - Check [Flow Emulator] docs for advanced emulator flags [Flow CLI]: ../../../build/tools/flow-cli/index.md [homebrew]: https://brew.sh [installation guide]: ../../../build/tools/flow-cli/install.md [Fork Testing Overview]: ../../../build/tools/flow-cli/fork-testing.md [Fork Testing with Cadence]: ../fork-testing/index.md [Testing Strategy]: ../../../build/cadence/smart-contracts/testing-strategy.md [Network Upgrade (Spork) Process]: ../../../protocol/node-ops/node-operation/network-upgrade.md [Flow Emulator]: ../../../build/tools/emulator/index.md [Dependency Manager]: ../../../build/tools/flow-cli/dependency-manager.md [Flow React SDK]: ../../../build/tools/react-sdk/index.mdx --- # File: /md/blockchain-development-tutorials/cadence/fork-testing.md --- sidebar_position: 20 sidebar_label: Fork Testing title: Fork Testing with Cadence description: Run your Cadence test suite against a forked mainnet or testnet using fork testing. Test against real contracts and production data without deploying to live networks. keywords: - fork testing - test_fork pragma - flow test - cadence tests - mainnet fork - testnet fork - integration testing - Flow CLI - account impersonation - production testing - real contracts - on-chain state - block height - historical debugging - reproducible tests - test deployment - FlowToken - FungibleToken - test scripts - test transactions - local testing - safe testing - forked runtime --- # Fork Testing with Cadence This tutorial teaches you how to run your Cadence tests against a snapshot of Flow mainnet using `flow test` with the `#test_fork` pragma. You'll learn how to test your contracts against real deployed contracts and production data without needing to deploy anything to a live network or bootstrap test accounts. Fork testing bridges the gap between isolated local unit tests and testnet deployments. It allows you to validate your contracts work correctly with real on-chain state, test integrations with deployed contracts, and debug issues with historical blockchain data—all in a safe, local environment. ## What you'll learn After you complete this tutorial, you'll be able to: - **Run Cadence tests against forked networks** with `#test_fork`. - **Test contracts that depend on real mainnet contracts** without manual setup. - **Use account impersonation** to execute transactions as any mainnet account. - **Read from production blockchain state** in your test suite. - **Pin tests to specific block heights** for historical debugging. - **Integrate fork testing** into your development workflow. ## What you'll build You'll create a complete fork testing setup that demonstrates: - How to read from the live FlowToken contract on mainnet. - How to deploy your own contract that interacts with mainnet contracts. - How to test custom logic against real account balances and state. - How to execute transactions with impersonated mainnet accounts. - A reusable pattern for integration tests your Flow applications. ## Prerequisites ### Flow CLI This tutorial requires [Flow CLI] v1.8.0 or later installed. If you haven't installed it yet and have [homebrew] installed, run: ```bash brew install flow-cli ``` For other operating systems, refer to the [installation guide]. ### Basic Cadence testing knowledge You should be familiar with how to write basic Cadence tests. If you're new to Cadence testing, start with [Testing Smart Contracts] first. ### Network access You'll need network access to Flow's public access nodes. The tutorial uses these endpoints, which are freely available: - Mainnet: `access.mainnet.nodes.onflow.org:9000` - Testnet: `access.devnet.nodes.onflow.org:9000` :::info This tutorial covers fork testing with `flow test` (running tests against forked network state), which is different from `flow emulator --fork` (starting the emulator in fork mode for manual interaction). ::: ## Create your project Navigate to your development directory and create a new Flow project: ```zsh mkdir fork-testing-demo cd fork-testing-demo flow init --yes ``` The `--yes` flag accepts defaults non-interactively. `flow init` is interactive by default and can scaffold various templates. Alternatively, create the directory and initialize in one command: ```zsh flow init fork-testing-demo --yes cd fork-testing-demo ``` ## Install dependencies Use the [Dependency Manager] to install the `FlowToken` and `FungibleToken` contracts: ```zsh flow dependencies install FlowToken FungibleToken ``` This downloads the contracts into the `imports/` folder and configures aliases for different networks. ## Understanding `mainnet-fork` When you run `flow init`, the CLI automatically creates a `mainnet-fork` network in your `flow.json`. This network inherits all contract addresses from mainnet, so imports like `"FlowToken"` automatically resolve to their mainnet addresses (`0x1654653399040a61`). Using `#test_fork(network: "mainnet-fork")` in your test files runs your tests against a local snapshot of mainnet state. You can deploy contracts, impersonate accounts, and modify state locally without affecting the real network. ## Test reading live state Generate a script to read `FlowToken` supply: ```zsh flow generate script GetFlowTokenSupply ``` Open `cadence/scripts/GetFlowTokenSupply.cdc` and replace its contents with: ```cadence cadence/scripts/GetFlowTokenSupply.cdc import "FlowToken" access(all) fun main(): UFix64 { return FlowToken.totalSupply } ``` Generate the test file: ```zsh flow generate test FlowToken ``` Open `cadence/tests/FlowToken_test.cdc` and replace its contents with: ```cadence cadence/tests/FlowToken_test.cdc #test_fork(network: "mainnet-fork", height: nil) import Test access(all) fun testFlowTokenSupplyIsPositive() { let scriptResult = Test.executeScript( Test.readFile("../scripts/GetFlowTokenSupply.cdc"), [] ) Test.expect(scriptResult, Test.beSucceeded()) let supply = scriptResult.returnValue! as! UFix64 Test.assert(supply > 0.0, message: "FlowToken supply should be positive") } ``` :::info - **The `#test_fork` pragma** configures this test to run against the `mainnet-fork` network - Use `Test.executeScript()` to read contract state - The script imports `FlowToken` by name - the dependency manager handles address resolution - Because we're using `mainnet-fork`, this automatically uses the mainnet FlowToken contract - Extract the return value with proper type casting and assert on it - File paths in `Test.readFile()` are relative to the test file location (use `../scripts/` from `cadence/tests/`) ::: #### Quick verify Run just this test file to confirm your setup works: ```zsh flow test cadence/tests/FlowToken_test.cdc ``` The pragma handles the fork configuration automatically! You will see the test PASS. If not, verify your network host in `flow.json` and that dependencies are installed. ## Deploy and test Your contract Now you'll create a contract that depends on FlowToken and test it against the forked mainnet state. There's no need to bootstrap tokens or set up test accounts. ### Create a test account Create a new account to deploy your contract: ```zsh flow accounts create ``` Follow the prompts: - Select "mainnet" for the network. - Name your account as desired. This will output the new account address. Use this address as the mainnet alias for your contract in flow.json. :::note This creates a local account with a mainnet-format address for fork testing. When you're ready to deploy to actual mainnet, you'll use this same account—see the [Deploying Contracts guide](pathname:///build/cadence/smart-contracts/deploying) for details. ::: ### Create a contract that uses `FlowToken` Generate a new contract: ```zsh flow generate contract TokenChecker ``` This creates `cadence/contracts/TokenChecker.cdc` and adds it to `flow.json`. Now update the contract with your logic: ```cadence cadence/contracts/TokenChecker.cdc import "FlowToken" access(all) contract TokenChecker { access(all) fun checkBalance(address: Address): UFix64 { let account = getAccount(address) let vaultRef = account.capabilities .borrow<&FlowToken.Vault>(/public/flowTokenBalance) ?? panic("Could not borrow FlowToken Vault reference") return vaultRef.balance } access(all) fun hasMinimumBalance(address: Address, minimum: UFix64): Bool { return self.checkBalance(address: address) >= minimum } } ``` ### Configure contract in flow.json Add the `TokenChecker` contract configuration to `flow.json`. The contract needs a **mainnet alias** so that imports can resolve properly during fork testing. Update your `flow.json` to include the contract with aliases, and use the address you generated in the previous step: ```json { "contracts": { "TokenChecker": { "source": "cadence/contracts/TokenChecker.cdc", "aliases": { "testing": "0000000000000008", "mainnet": "" } } }, "accounts": { "mainnet-test": { "address": "" } } } ``` :::info No local private key is required for forked tests. The accounts entry above is included so you can copy and reference the address in your config. You can also omit keys for fork tests. Contracts deploy to the testing environment at `testing` alias, and transactions that interact with forked state can use impersonation. The `Test.deployContract` function will automatically deploy your contract to the testing environment during test execution. ::: ### Create scripts for testing Generate the scripts: ```zsh flow generate script CheckBalance flow generate script HasMinimumBalance ``` Open `cadence/scripts/CheckBalance.cdc` and replace its contents with: ```cadence cadence/scripts/CheckBalance.cdc import "TokenChecker" access(all) fun main(addr: Address): UFix64 { return TokenChecker.checkBalance(address: addr) } ``` Open `cadence/scripts/HasMinimumBalance.cdc` and replace its contents with: ```cadence cadence/scripts/HasMinimumBalance.cdc import "TokenChecker" access(all) fun main(addr: Address, min: UFix64): Bool { return TokenChecker.hasMinimumBalance(address: addr, minimum: min) } ``` ### Test Your contract with forked state Generate the test file: ```zsh flow generate test TokenChecker ``` Open `cadence/tests/TokenChecker_test.cdc` and replace its contents with: ```cadence cadence/tests/TokenChecker_test.cdc #test_fork(network: "mainnet-fork", height: nil) import Test access(all) fun setup() { // Deploy TokenChecker to the test account let err = Test.deployContract( name: "TokenChecker", path: "../contracts/TokenChecker.cdc", arguments: [] ) Test.expect(err, Test.beNil()) } access(all) fun testCheckBalanceOnRealAccount() { // Test against a real mainnet account (Flow service account) let scriptResult = Test.executeScript( Test.readFile("../scripts/CheckBalance.cdc"), [Address(0x1654653399040a61)] // Flow service account on mainnet ) Test.expect(scriptResult, Test.beSucceeded()) let balance = scriptResult.returnValue! as! UFix64 // The Flow service account should have a balance Test.assert(balance > 0.0, message: "Service account should have FLOW tokens") } access(all) fun testHasMinimumBalance() { let scriptResult = Test.executeScript( Test.readFile("../scripts/HasMinimumBalance.cdc"), [Address(0x1654653399040a61), 1.0] ) Test.expect(scriptResult, Test.beSucceeded()) let hasMinimum = scriptResult.returnValue! as! Bool Test.assert(hasMinimum == true, message: "Service account should have at least 1 FLOW") } ``` ### What's happening here 1. **The `#test_fork` pragma configures the test**: At the top of the file, the pragma tells the test framework to run against mainnet. 2. **Your contract uses FlowToken**: `TokenChecker` imports and interacts with the real FlowToken contract. 3. **No bootstrapping needed**: With the fork pragma, real mainnet accounts (like `0x1654653399040a61`, the Flow service account) already have balances. 4. **Test against real state**: You can query actual accounts and verify your contract logic works with production data. 5. **Local deployment**: Your `TokenChecker` contract is deployed locally to the test environment, but it reads from forked mainnet state. ## Execute transactions with account impersonation Fork testing includes built-in account impersonation—you can execute transactions as **any mainnet account** without the need for private keys. This lets you test interactions with real accounts and their current state. ### Create transactions Generate the transactions: ```zsh flow generate transaction SetupFlowTokenVault flow generate transaction TransferTokens ``` Open `cadence/transactions/SetupFlowTokenVault.cdc` and replace its contents with: ```cadence cadence/transactions/SetupFlowTokenVault.cdc import "FungibleToken" import "FlowToken" transaction { prepare(signer: auth(Storage, Capabilities) &Account) { if signer.storage.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) == nil { signer.storage.save(<-FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()), to: /storage/flowTokenVault) let cap = signer.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault) signer.capabilities.publish(cap, at: /public/flowTokenReceiver) signer.capabilities.publish(cap, at: /public/flowTokenBalance) } } } ``` Open `cadence/transactions/TransferTokens.cdc` and replace its contents with: ```cadence cadence/transactions/TransferTokens.cdc import "FungibleToken" import "FlowToken" transaction(amount: UFix64, to: Address) { let sentVault: @{FungibleToken.Vault} prepare(signer: auth(Storage) &Account) { let vaultRef = signer.storage.borrow( from: /storage/flowTokenVault ) ?? panic("Could not borrow reference to the owner's Vault") self.sentVault <- vaultRef.withdraw(amount: amount) } execute { let recipient = getAccount(to) let receiverRef = recipient.capabilities .borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Could not borrow receiver reference") receiverRef.deposit(from: <-self.sentVault) } } ``` ### Test transaction execution with impersonation Add this test function to the current `cadence/tests/TokenChecker_test.cdc` file (the pragma is already at the top of the file): ```cadence access(all) fun testTransactionAsMainnetAccount() { // Impersonate the Flow service account (or any mainnet account) // No private keys needed - fork testing has built-in impersonation let serviceAccount = Test.getAccount(0x1654653399040a61) // Check initial balance let initialBalanceScript = Test.executeScript( Test.readFile("../scripts/CheckBalance.cdc"), [serviceAccount.address] ) Test.expect(initialBalanceScript, Test.beSucceeded()) let initialBalance = initialBalanceScript.returnValue! as! UFix64 // Create a test recipient account and set up FlowToken vault let recipient = Test.createAccount() // Set up the recipient's FlowToken vault let setupResult = Test.executeTransaction( Test.Transaction( code: Test.readFile("../transactions/SetupFlowTokenVault.cdc"), authorizers: [recipient.address], signers: [recipient], arguments: [] ) ) Test.expect(setupResult, Test.beSucceeded()) // Execute transaction AS the mainnet service account // This works because fork testing allows impersonating any account let txResult = Test.executeTransaction( Test.Transaction( code: Test.readFile("../transactions/TransferTokens.cdc"), authorizers: [serviceAccount.address], signers: [serviceAccount], arguments: [10.0, recipient.address] ) ) Test.expect(txResult, Test.beSucceeded()) // Verify the sender's balance decreased let newBalanceScript = Test.executeScript( Test.readFile("../scripts/CheckBalance.cdc"), [serviceAccount.address] ) Test.expect(newBalanceScript, Test.beSucceeded()) let newBalance = newBalanceScript.returnValue! as! UFix64 // Balance should have decreased by exactly the transfer amount Test.assertEqual(initialBalance - 10.0, newBalance) // Verify the recipient received the tokens let recipientBalanceScript = Test.executeScript( Test.readFile("../scripts/CheckBalance.cdc"), [recipient.address] ) Test.expect(recipientBalanceScript, Test.beSucceeded()) let recipientBalance = recipientBalanceScript.returnValue! as! UFix64 // Recipient should have at least 10.0 (may be slightly more due to storage refunds) Test.assert(recipientBalance >= 10.0, message: "Recipient should have at least 10 FLOW") } ``` ### Key points about account impersonation 1. **Any account can be used**: Call `Test.getAccount(address)` with any mainnet address. 2. **No private keys needed**: Fork testing has built-in impersonation—you can sign transactions as any account. 3. **Real account state**: The account has its actual mainnet balance, storage, and capabilities. 4. **Mutations are local**: Changes only affect your test environment, not the real network. 5. **Test complex scenarios**: Impersonate whale accounts, protocol accounts, or any user to test edge cases. ## Run all tests together Now that you have multiple test files with the `#test_fork` pragma, simply run: ```zsh flow test ``` That's it! The pragma handles all the fork configuration. This runs all `*_test.cdc` files in your project—both local tests and fork tests together. You will see: ``` Test results: "cadence/tests/FlowToken_test.cdc" - PASS: testFlowTokenSupplyIsPositive Test results: "cadence/tests/TokenChecker_test.cdc" - PASS: testCheckBalanceOnRealAccount - PASS: testHasMinimumBalance - PASS: testTransactionAsMainnetAccount ``` ### Best Practices: In-File Configuration vs CLI Flags **Recommended:** Configure fork tests in your test file with `#test_fork` ```cadence #test_fork(network: "mainnet-fork", height: nil) import Test // Your tests... ``` Then run with: ```bash flow test ``` **Not recommended:** CLI flags (legacy approach) ```bash flow test --fork mainnet # Requires typing flags every time ``` Configuring fork tests in the file keeps the configuration with your test code, making tests self-documenting and easier to maintain. You can also run specific test files or change the network/block height in the pragma as needed. See the [Fork Testing Flags] reference for more options. ## Mocking Mainnet Contracts in Tests Just like mocking dependencies in unit tests, you can **mock real mainnet contracts** by deploying modified versions—perfect for testing upgrades, bug fixes, or alternative implementations against real production state. Use `Test.deployContract()` to deploy your mock to any mainnet account address. Your mock takes precedence while other contracts continue using real mainnet versions. ### Example ```cadence #test_fork(network: "mainnet-fork", height: nil) import Test access(all) fun setup() { // Deploy mock FlowToken to the real mainnet address let err = Test.deployContract( name: "FlowToken", path: "../contracts/FlowTokenModified.cdc", arguments: [] ) Test.expect(err, Test.beNil()) } access(all) fun testMockedFlowToken() { // Test now uses mocked FlowToken // All other contracts (FungibleToken, USDC, etc.) use real mainnet versions let scriptResult = Test.executeScript( Test.readFile("../scripts/CheckBalance.cdc"), [Address(0x1654653399040a61)] ) Test.expect(scriptResult, Test.beSucceeded()) } ``` This validates your contract changes against real production state and integrations. ## Pinning block heights for reproducibility For reproducible test results, pin your tests to a specific block height: ```cadence #test_fork(network: "mainnet-fork", height: 85229104) ``` This ensures your tests run against the same blockchain state every time, useful for: - Deterministic test results in CI/CD - Reproducing historical bugs at a specific point in time - Testing against known network state To use the latest state instead, use `height: nil`: ```cadence #test_fork(network: "mainnet-fork", height: nil) ``` Note that block heights are only available within the current spork (network upgrade period). See [Testing Smart Contracts] for more on managing pinned heights over time. ## When to use fork testing Fork testing is most valuable for: - Integration testing with real onchain contracts and data. - Pre-deployment validation before mainnet releases. - Upgrade testing against production state. - Reproducing issues at a specific block height. - Testing interactions with high-value or protocol accounts. - Validating contract behavior with real-world data patterns. For strategy, limitations, and best practices, see the guide: [Testing Smart Contracts]. ## Conclusion In this tutorial, you learned how to use fork testing to validate your Cadence contracts against live Flow network state. You created tests that read from real mainnet contracts, deployed custom contracts that interact with production data, and executed transactions using account impersonation—all without the need deploy to a live network or bootstrap test accounts. Now that you have completed this tutorial, you can: - **Run Cadence tests against forked networks** with `#test_fork`. - **Test contracts that depend on real mainnet contracts** without manual setup. - **Use account impersonation** to execute transactions as any mainnet account. - **Read from production blockchain state** in your test suite. - **Pin tests to specific block heights** for historical debugging. - **Integrate fork testing** into your development workflow. Fork testing bridges the gap between local unit tests and testnet deployments, which allows you to catch integration issues early and test against real-world conditions. Use it as part of your pre-deployment validation process, alongside emulator unit tests for determinism and isolation, and testnet deployments for final verification. ### Next Steps - Explore additional assertions and helpers in the [Cadence Testing Framework]. - Add more real-world tests that read from standard contracts like Flow NFT. - Keep unit tests on the emulator for determinism and isolation; run forked integration tests selectively in CI. - Review the [Fork Testing Flags] reference for advanced options. - Learn about [Flow Networks] and public access nodes. [Flow CLI]: ../../../build/tools/flow-cli/index.md [homebrew]: https://brew.sh [installation guide]: ../../../build/tools/flow-cli/install.md [Testing Smart Contracts]: ../../../build/cadence/smart-contracts/testing-strategy.md [Dependency Manager]: ../../../build/tools/flow-cli/dependency-manager.md [Fork Testing Flags]: ../../../build/tools/flow-cli/tests.md#fork-testing-flags [Cadence Testing Framework]: https://cadence-lang.org/docs/testing-framework [Flow Networks]: ../../../protocol/flow-networks/index.md [Testing Strategy on Flow]: ../../../build/cadence/smart-contracts/testing-strategy.md [Flow Emulator]: ../../../build/tools/emulator/index.md --- # File: /md/blockchain-development-tutorials/cadence/getting-started.md --- title: Getting Started with Cadence description: Learn the fundamentals of Flow blockchain development with Cadence sidebar_position: 1 keywords: - Flow development - Cadence programming - Smart contracts - Flow CLI - Flow emulator - Blockchain development - Counter contract - Testnet deployment - Mainnet deployment - Frontend development - Flow SDK - Production deployment --- # Getting Started With Cadence The Cadence is designed for the next generation of apps, games, and digital assets. This comprehensive tutorial series will guide you from development environment setup to production-ready application deployment on Flow's mainnet as a complete Counter application that demonstrates all essential Flow development patterns.
## What you'll learn In this tutorial series, you'll discover how to: - Set up a complete Flow development environment with CLI tools and local emulator. - Build and deploy smart contracts with Cadence. - Integrate external dependencies and work with Flow's composable ecosystem. - Create transactions and implement comprehensive testing strategies. - Build interactive frontend applications with @onflow/react-sdk. - Deploy applications to testnet and mainnet with production best practices. - Implement monitoring, security, and maintenance for live blockchain applications. ## What you'll build Throughout these tutorials, you'll build a complete **Counter Application** that demonstrates the core aspects of Flow development: - **Smart Contracts**: counter contract with increment/decrement functionality. - **External Dependencies**: integration with NumberFormatter for enhanced display. - **Frontend Interface**: react-based web application with wallet authentication. - **Production Deployment**: live application accessible on Flow's public networks. By the end, you'll have a fully functional blockchain application and the skills to build your own Flow projects. ## Environment setup Learn how to set up your Flow development environment and deploy your first smart contract. This foundational tutorial covers CLI installation, project creation, contract deployment, and basic blockchain interaction patterns with the local Flow emulator. Tutorial: [Cadence Environment Setup] ## Smart contract interaction Gain advanced Flow development skills including dependency management, sophisticated transaction patterns, and comprehensive testing strategies. Learn to integrate external contracts, handle complex state changes, and implement test-driven development workflows. Tutorial: [Smart Contract Interaction] ## Build a frontend app Create a `Next.js` frontend application that interacts with your Flow smart contracts via `@onflow/react-sdk`. Implement wallet authentication, real-time data queries, transaction submission, and status monitoring for a complete user experience. Tutorial: [Building a Frontend App] ## Production deployment To take your application live, deploy to Flow's testnet and mainnet networks. Learn security best practices, production configuration, monitoring strategies, and maintenance practices you can use to manage live blockchain applications. Tutorial: [Production Deployment] ## Next steps After you complete these tutorials, you'll have the fundamental skills needed for Flow development. You can explore our other tutorial series to expand your blockchain development expertise: - [Cross-VM Apps] - Build applications that integrate Flow EVM and Cadence - [Native VRF] - Implement verifiable random functions in your applications - [Token Launch] - Create and launch tokens on Flow [Cadence Environment Setup]: ./cadence-environment-setup.md [Smart Contract Interaction]: ./smart-contract-interaction.md [Building a Frontend App]: ./building-a-frontend-app.md [Production Deployment]: ./production-deployment.md [Cross-VM Apps]: ../../cross-vm-apps/introduction.md [Native VRF]: ../../native-vrf/index.md [Token Launch]: ../../tokens/index.md --- # File: /md/blockchain-development-tutorials/cadence/getting-started/building-a-frontend-app.md --- sidebar_position: 3 sidebar_label: Building a Frontend App title: Building a Frontend App description: Learn how to build a Next.js frontend application using @onflow/react-sdk to interact with Flow smart contracts. Set up wallet authentication, read contract data, send transactions with kit's React hooks, and display transaction status updates. keywords: - '@onflow/react-sdk' - Next.js - frontend development - wallet integration - smart contract interaction - Flow emulator - Dev Wallet - authentication - transactions - blockchain queries - React development - dApp development - Flow development - web3 frontend --- # Building a Frontend App This tutorial builds on the `Counter` contract you deployed in [Cadence Environment Setup] and [Smart Contract Interaction]. It shows you how to create a simple `Next.js` frontend that interacts with the `Counter` smart contract deployed on your local Flow emulator. Rather than use FCL directly, you'll leverage [**@onflow/react-sdk**] to simplify authentication, querys, transactions, and to display real-time transaction status updates with convenient React hooks. ## Objectives After you complete this tutorial, you will be able to: - Wrap your `Next.js` app with a Flow provider using [**@onflow/react-sdk**]. - Read data from a Cadence smart contract (`Counter`) with kit's query hook. - Send a transaction to update the smart contract's state with kit's mutation hook. - Monitor a transaction's status in real time with kit's transaction hook. - Authenticate with the Flow blockchain with kit's built-in hooks and the local [Dev Wallet]. ## Prerequisites - Completion of [Cadence Environment Setup] and [Smart Contract Interaction]. - [Flow CLI] installed. - Node.js and npm installed. ## Set Up the Next.js app Follow these steps to set up your Next.js project and integrate [**@onflow/react-sdk**]. :::tip You can visit this [React-sdk Demo] to see how the hooks and components are used. ::: ### Step 1: Create a new Next.js app Run the following command in your project directory: ```bash npx create-next-app@latest kit-app-quickstart ``` During setup, choose the following options: - **Use TypeScript**: **Yes** - **Use src directory**: **Yes** - **Use App Router**: **Yes** This command creates a new Next.js project named `kit-app-quickstart` inside your current directory. We will generate the frontend in a subdirectory so we can next move it into our current project structure from the previous steps (you can't create an app in a non-empty directory). ### Step 2: Move the Next.js app up a directory Move the contents of the `kit-app-quickstart` directory into your project root. You can use the gui in your editor, or the console. :::warning You'll want to consolidate both `.gitignore` files, which keeps the contents of both in the file that ends up in the root. ::: On macOS/Linux: ```bash mv kit-app-quickstart/* . mv kit-app-quickstart/.* . # To move hidden files (e.g. .env.local) rm -r kit-app-quickstart ``` On Windows (PowerShell): ```powershell Move-Item -Path .\kit-app-quickstart\* -Destination . -Force Move-Item -Path .\kit-app-quickstart\.* -Destination . -Force Remove-Item -Recurse -Force .\kit-app-quickstart ``` :::tip When you move hidden files (those that start with a dot) like `.gitignore`, be cautious not to overwrite any important files. ::: ### Step 3: Install @onflow/react-sdk Install the kit library in your project: ```bash npm install @onflow/react-sdk ``` This library wraps FCL internally and exposes a set of hooks for authentication, querys, to send transactions, and track transaction status. ## Configure the local Flow emulator and Dev Wallet :::warning You should already have the Flow emulator running from the local development step. If it's not running, you can start it again — but when you restart the emulator, it will clear all blockchain state, which includes any contracts deployed in [Step 2: Local Development]. ::: ### Start the Flow emulator (if not already running) Open a new terminal window in your project directory and run: ```bash flow emulator start ``` This will start the Flow emulator on `http://localhost:8888`. Make sure to keep it running in a separate terminal. ### Start the Dev Wallet In another terminal window, run: ```bash flow dev-wallet ``` This will start the [Dev Wallet] on `http://localhost:8701`, which you'll use for authentication during development. ## Wrap Your app with FlowProvider [**@onflow/react-sdk**] provides a `FlowProvider` component that sets up the Flow Client Library configuration. In `Next.js`, use the App Router to add or update your `src/app/layout.tsx` as follows: ```tsx 'use client'; import { FlowProvider } from '@onflow/react-sdk'; import flowJson from '../flow.json'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` This configuration initializes the kit with your local emulator settings and maps contract addresses based on your `flow.json` file. For more information on Discovery configurations, refer to the [Wallet Discovery Guide]. ## Interact With the chain Now that we've set our provider, lets start to interact with the chain. ### Query the chain First, use the kit's [`useFlowQuery`] hook to read the current counter value from the blockchain. ```tsx import { useFlowQuery } from '@onflow/react-sdk'; const { data, isLoading, error, refetch } = useFlowQuery({ cadence: ` import "Counter" import "NumberFormatter" access(all) fun main(): String { let count: Int = Counter.getCount() let formattedCount = NumberFormatter.formatWithCommas(number: count) return formattedCount } `, query: { enabled: true }, }); // Use the count data in your component as needed. ``` This script fetches the counter value, formats it via the `NumberFormatter`, and returns the formatted string. :::info - **Import Syntax:** The imports (`import "Counter"` and `import "NumberFormatter"`) don't include addresses because those are automatically resolved with the `flow.json` file configured in your `FlowProvider`. This keeps your Cadence scripts portable and environment-independent. - **`enabled` Flag:** This controls whether the query should run automatically. Set it to `true` to run on mount, or pass a condition (e.g. `!!user?.addr`) to delay execution until the user is available. This is useful for queries that depend on authentication or other asynchronous data. ::: ### Send a transaction Next, use the kit's [`useFlowMutate`] hook to send a transaction that increments the counter. ```tsx import { useFlowMutate } from '@onflow/react-sdk'; const { mutate: increment, isPending: txPending, data: txId, error: txError, } = useFlowMutate(); const handleIncrement = () => { increment({ cadence: ` import "Counter" transaction { prepare(acct: &Account) { // Authorization handled via wallet } execute { Counter.increment() let newCount = Counter.getCount() log("New count after incrementing: ".concat(newCount.toString())) } } `, }); }; ``` #### Explanation This sends a Cadence transaction to the blockchain with the `mutate` function. The transaction imports the `Counter` contract and calls its `increment` function. The connected wallet handles authorization automatically during the `prepare` phase. After it's submitted, you cna use the returned `txId` to track the transaction's status in real time. ### Subscribe to transaction status Use the kit's [`useFlowTransactionStatus`] hook to monitor and display the transaction status in real time. ```tsx import { useFlowTransactionStatus } from '@onflow/react-sdk'; const { transactionStatus, error: txStatusError } = useFlowTransactionStatus({ id: txId || '', }); useEffect(() => { if (txId && transactionStatus?.status === 3) { refetch(); } }, [transactionStatus?.status, txId, refetch]); // You can then use transactionStatus (for example, its statusString) to show updates. ``` #### Explanation: - `useFlowTransactionStatus(txId)` subscribes to real-time updates about a transaction's lifecycle with the transaction ID. - `transactionStatus.status` is a numeric code representing the state of the transaction: - `0`: **Unknown** – The transaction status is not yet known. - `1`: **Pending** – The transaction has been submitted and is waiting to be included in a block. - `2`: **Finalized** – The transaction has been included in a block, but not yet executed. - `3`: **Executed** – The transaction code has run successfully, but the result has not yet been sealed. - `4`: **Sealed** – The transaction is fully complete, included in a block, and now immutable onchain. - We recommend that you call `refetch()` when the status reaches **3 (Executed)** to update your UI more quickly after the transaction runs, rather than waiting for sealing. - The `statusString` property gives a human-readable version of the current status you can display in the UI. #### Why we recommend `Executed` for UI Updates: Waiting for `Sealed` provides full onchain confirmation but can introduce a delay — especially in local or test environments. Since most transactions (like incrementing a counter) don't require strong finality guarantees, you can typically refetch data after the transaction reaches `Executed` for a faster, more responsive user experience. However: - If you experience critical state changes (for example, token transfers or contract deployments), wait for `Sealed`. - For non-critical UI updates, `Executed` is usually safe and significantly improves perceived performance. ### Integrate authentication and build the complete UI Finally, integrate the query, mutation, and transaction status hooks with authentication via `useFlowCurrentUser`. Combine all parts to build the complete page. ```tsx 'use client'; import { useState, useEffect } from 'react'; import { useFlowQuery, useFlowMutate, useFlowTransactionStatus, useFlowCurrentUser, } from '@onflow/react-sdk'; export default function Home() { const { user, authenticate, unauthenticate } = useFlowCurrentUser(); const { data, isLoading, error, refetch } = useFlowQuery({ cadence: ` import "Counter" import "NumberFormatter" access(all) fun main(): String { let count: Int = Counter.getCount() let formattedCount = NumberFormatter.formatWithCommas(number: count) return formattedCount } `, query: { enabled: true }, }); const { mutate: increment, isPending: txPending, data: txId, error: txError, } = useFlowMutate(); const { transactionStatus, error: txStatusError } = useFlowTransactionStatus({ id: txId || '', }); useEffect(() => { if (txId && transactionStatus?.status === 3) { // Transaction is executed refetch(); // Refresh the counter } }, [transactionStatus?.status, txId, refetch]); const handleIncrement = () => { increment({ cadence: ` import "Counter" transaction { prepare(acct: &Account) { // Authorization handled via wallet } execute { Counter.increment() let newCount = Counter.getCount() log("New count after incrementing: ".concat(newCount.toString())) } } `, }); }; return (

Flow Counter dApp

{isLoading ? (

Loading count...

) : error ? (

Error: {error.message}

) : (

{(data as string) || '0'}

Current Count

)} {user?.loggedIn ? (

Connected: {user.addr}

{transactionStatus?.statusString && transactionStatus?.status && (

Status: {transactionStatus.status >= 3 ? 'Successful' : 'Pending'}

)} {txError &&

Error: {txError.message}

} {txStatusError &&

Status Error: {txStatusError.message}

}
) : ( )}
); } ``` In this complete page: - **Step 1** queries the counter value. - **Step 2** sends a transaction to increment the counter and stores the transaction ID. - **Step 3** subscribes to transaction status updates with the stored transaction ID and uses a `useEffect` hook to automatically refetch the updated count when the transaction is sealed (status code 4). - **Step 4** integrates authentication via `useFlowCurrentUser` and combines all the pieces into a single user interface. :::tip In this tutorial, we inlined Cadence code for simplicity. For real projects, we recommend that you store Cadence in separate `.cdc` files, with the [Cadence VSCode extension], and import them with the [`flow-cadence-plugin`](https://github.com/chasefleming/flow-cadence-plugin) for Next.js or Webpack projects. ::: ## Run the app Start your development server: ```bash npm run dev ``` :::warning If you have the Flow wallet browser extension installed, you might automatically log into the app. Normally this is desirable for your users, but you don't want to use it here. Log out, and log back in. Select the Dev Wallet instead of the Flow Wallet. ::: :::warning For your app to connect with contracts deployed on the emulator, you need to have completed [Step 1: Contract Interaction] and [Step 2: Local Development]. ::: Then visit [http://localhost:3000](http://localhost:3000) in your browser. You should see: - The current counter value displayed (formatted with commas using `NumberFormatter`). - A **Log In** button that launches the kit Discovery UI with your local [Dev Wallet]. - Once logged in, your account address appears with options to **Log Out** and **Increment Count**. - When you click **Increment Count**, the transaction is sent; its status updates are displayed in real time below the action buttons, and after the transaction is sealed, the updated count is automatically fetched. ## Conclusion When you follow these steps, you've built a simple `Next.js` dApp that interacts with a Flow smart contract with [**@onflow/react-sdk**]. In this guide you learned how to: - Wrap your application in a `FlowProvider` to configure blockchain connectivity. - Use kit hooks such as `useFlowQuery`, `useFlowMutate`, `useFlowTransactionStatus`, and `useFlowCurrentUser` to manage authentication, query onchain data, submit transactions, and monitor their status. - Integrate with the local Flow emulator and Dev Wallet for a fully functional development setup. For additional details and advanced usage, refer to the [@onflow/react-sdk documentation] and other Flow developer resources. [React-sdk Demo]: https://react-sdk-demo-git-master-onflow.vercel.app/ [Cadence Environment Setup]: ./cadence-environment-setup.md [Smart Contract Interaction]: ./smart-contract-interaction.md [Wallet Discovery Guide]: ../../../build/tools/clients/fcl-js/discovery.md [`useFlowQuery`]: ../../../build/tools/react-sdk#useflowquery [`useFlowMutate`]: ../../../build/tools/react-sdk#useflowmutate [Dev Wallet]: ../../../build/tools/flow-dev-wallet [@onflow/react-sdk documentation]: ../../../build/tools/react-sdk [**@onflow/react-sdk**]: ../../../build/tools/react-sdk [Flow CLI]: ../../../build/tools/flow-cli/install.md [Cadence VSCode extension]: ../../../build/tools/vscode-extension --- # File: /md/blockchain-development-tutorials/cadence/getting-started/cadence-environment-setup.md --- sidebar_position: 1 sidebar_label: Cadence Environment Setup title: Cadence Environment Setup description: Learn how to set up your complete Flow development environment, deploy your first smart contract, and master the fundamentals of blockchain development with Cadence on the Flow emulator. keywords: - Flow development - Flow CLI - smart contracts - local development - Flow emulator - contract deployment - Cadence programming - blockchain development - Counter contract - testing - VSCode extension --- # Cadence Environment Setup This comprehensive tutorial will guide you through how to set up your complete development environment, deploy your first smart contract, and learn the fundamentals of Flow development. You'll work hands-on with the Flow CLI, local emulator, and a real smart contract to build practical skills from day one. Flow is a blockchain built for the next generation of apps, games, and digital assets. With its unique multi-role architecture and resource-oriented programming language Cadence, Flow allows developers to create secure, composable, and scalable applications. This tutorial focuses on getting you productive with Flow's developer tools as quickly as possible. ## What you'll learn After you complete this tutorial, you'll be able to: - **Set up a complete Flow development environment** with CLI tools and VSCode integration. - **Create and manage Flow projects** with the Flow CLI and understand project structure. - **Deploy and interact with smart contracts** on the local Flow emulator. - **Execute scripts and transactions** to read from and modify blockchain state. - **Understand Flow's account model** and how contracts are deployed to account storage. - **Navigate the Flow ecosystem** and know where to find help and resources. ## What you'll build You'll work with a `Counter` contract, a simple but comprehensive example that demonstrates core Flow development patterns. This contract maintains a count value and provides functions to increment, decrement, and read the current count. By the end of this tutorial, you'll have: - A fully functional local Flow development environment. - A deployed Counter contract that runs on your local emulator. - Scripts to query the contract's state. - Transactions to modify the contract's state. - Knowledge of how to extend this foundation for more complex applications. **Time Commitment:** Approximately 30-45 minutes **Prerequisites:** - Basic command line familiarity - Code editor (VSCode recommended) - `Node.js` installed (for future frontend development) --- ### Install Flow CLI The [Flow Command Line Interface] (CLI) is a set of tools that developers can use to interact with the Flow blockchain. Developers can manage accounts, send transactions, deploy smart contracts, run the emulator, and more. This quickstart will get you familiar with its main concepts and functionality. The first thing you'll need to do is install the Flow CLI. If you have [homebrew] installed, run: ```zsh brew install flow-cli ``` **For other operating systems,** refer to the [installation guide] for detailed instructions. **Verify Installation:** ```zsh flow version ``` You will see output showing your Flow CLI version. ### Install VSCode extension Install the [Flow Cadence VSCode Extension] from the marketplace. This extension provides: - Syntax highlighting for Cadence. - Code completion and IntelliSense. - Error checking and diagnostics. - Integrated development tools. ## Create your first project Navigate to your desired development directory and create a new Flow project: ```zsh flow init ``` When prompted: 1. **Project name:** Enter your preferred project name. 2. Select `Basic Cadence project (no dependencies)`. The `flow init` command creates: - **`flow.json`**: Central configuration file that contains accounts, contracts, deployments, and network settings. - **`emulator-account.pkey`**: Private key for the default emulator account. - **`cadence/`**: Directory structure for your Cadence code: - `contracts/`: Smart contract files - `scripts/`: Read-only blockchain queries - `transactions/`: State-changing operations - `tests/`: Contract test files Navigate into your project directory: ```zsh cd your-flow-project-name ``` :::info For additional details on how `flow.json` is configured, review the [configuration docs]. ::: ### Start the Flow emulator The emulator is a local version of the Flow blockchain that you can use to test your contracts and scripts. It's a great way to develop and test your contracts locally - before you try them on the `testnet` or `mainnet`. Before we deploy, let's open a new terminal window and run the emulator. From the root of your project directory, where your `emulator-account.pkey` and `flow.json` files are located, run: ```zsh flow emulator start ``` Keep this terminal running. The emulator provides: - Local blockchain environment. - Fast transaction processing. - No real-world costs. - Complete Flow feature set. ## Your first contract Now let's examine, deploy, and interact with the Counter contract that was created in your project. ### Examine the Counter contract Open `cadence/contracts/Counter.cdc` in your editor. Let's break down this contract: ```cadence access(all) contract Counter { access(all) var count: Int // Event to be emitted when the counter is incremented access(all) event CounterIncremented(newCount: Int) // Event to be emitted when the counter is decremented access(all) event CounterDecremented(newCount: Int) init() { self.count = 0 } // Public function to increment the counter access(all) fun increment() { self.count = self.count + 1 emit CounterIncremented(newCount: self.count) } // Public function to decrement the counter access(all) fun decrement() { self.count = self.count - 1 emit CounterDecremented(newCount: self.count) } // Public function to get the current count view access(all) fun getCount(): Int { return self.count } } ``` **Key components:** - **Contract Declaration**: `access(all) contract Counter` creates a public contract named Counter. - **State Variable**: `access(all) var count: Int` stores the counter value, accessible to everyone. - **Events**: `CounterIncremented` and `CounterDecremented` notify listeners when changes occur. - **Initializer**: `init()` sets the initial count to 0 when the contract is deployed. - **Public Functions**: - `increment()`: Increases count by 1 and emits an event - `decrement()`: Decreases count by 1 and emits an event - `getCount()`: Returns the current count (read-only, marked with `view`) ### Create and configure deployment account When you create a project, you'll see that a `Counter` contract was added to your [`flow.json`] configuration file, but it's not set up for deployment yet. We could deploy it to the automatically created `emulator-account`, but for this example, lets also create a new account on the emulator to deploy it to. :::info **Reminder**: On Flow Cadence, contracts are deployed to the storage of the account that deploys them. ::: Leave your emulator running, and open a second terminal. Run the following command: ```zsh flow accounts create ``` When prompted: 1. **Account name:** Enter `test-account` 2. **Network:** Select `Emulator` This adds the new account to your `flow.json` configuration file. After you've created you accounts, then you can view all your accounts on the with the Flow CLI with: ```zsh 📋 Account Status Across Networks This shows which networks your configured accounts are accessible on: 🌐 Network 🟢 Local (running) 🔴 Local (stopped) ✓ Found ✗ Error ───────────────────────────────────────────────────── 🟢 emulator ✓ default (f3fcd2c1a78f5eee): 0.00100000 FLOW ✓ emulator-account (f8d6e0586b0a20c7): 999999999.99300000 FLOW ✓ test-account (e03daebed8ca0615): 0.00100000 FLOW 🌐 mainnet No accounts found 🌐 testnet No accounts found 🟢 testing ✓ default (f3fcd2c1a78f5eee): 0.00100000 FLOW ✓ emulator-account (f8d6e0586b0a20c7): 999999999.99300000 FLOW ✓ test-account (e03daebed8ca0615): 0.00100000 FLOW 💡 Tip: To fund testnet accounts, run: flow accounts fund ``` This is a great tool to visualize your different accounts and balances while you develop.. ### Configure contract deployment To deploy the `Counter` contract to the emulator, you'll need to add it to your project configuration. To do this, run: ```zsh flow config add deployment ``` Follow the prompts: 1. **Network:** Select `emulator` 2. **Account:** Select `test-account` 3. **Contract:** Select `Counter` 4. **Deploy more contracts:** Select `no` This configures your `flow.json` to deploy the Counter contract to your test account on the emulator. ### Deploy the contract To deploy the `Counter` contract to the emulator, run: ```zsh flow project deploy ``` You'll see output similar to: ```zsh Deploying 1 contracts for accounts: test-account Counter -> 0x179b6b1cb6755e31 (a98c155fe7afc8eb2af5551748759b08a80a0ae85d1b09f92f1afc293c61ca98) 🎉 All contracts deployed successfully ``` That's it! You've just deployed your first contract to the Flow Emulator. ### Verify deployment with a script Scripts are used to read data from the Flow blockchain. There is no state modification. Let's verify the deployment by reading the counter value. Run the included script: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc ``` You should see: ```zsh Result: 0 ``` This confirms your contract is deployed and functional. The counter starts at zero (0), as defined in the contract's `init()` function. If we wanted to generate a new script, we could run: ```zsh flow generate script ScriptName ``` :::info For more information about generating Cadence files, see the [Generating Cadence Boilerplate] documentation. **You'll usually want to use these commands instead of adding files manually!** ::: :::tip To learn more about writing scripts, check out the docs for [basic scripts]. ::: ### Execute transactions Now let's increment the counter with a transaction: ```zsh flow transactions send cadence/transactions/IncrementCounter.cdc ``` By default, this uses the `emulator-account` to sign the transaction and the emulator network. If you want to use your `test-account` account, you can specify the `--signer` flag with the account name. The command would look like this: ```zsh flow transactions send cadence/transactions/IncrementCounter.cdc --signer test-account ``` The transaction output shows detailed information including: - Transaction ID and block information. - Status confirmation (`✅ SEALED`). - Events emitted (including `CounterIncremented`). ```zsh Transaction ID: 9cc7ac4d3d5239016965aba89b9692d3401a48a090d1ad1a8d9ef9cfca685e6e Block ID b8537860b0fc9ca8b3195b121e762502f9a220874b605d6a810998e8b62321a3 Block Height 3 Status ✅ SEALED ID 9cc7ac4d3d5239016965aba89b9692d3401a48a090d1ad1a8d9ef9cfca685e6e Payer f8d6e0586b0a20c7 Authorizers [f8d6e0586b0a20c7] Proposal Key: Address f8d6e0586b0a20c7 Index 0 Sequence 1 No Payload Signatures Envelope Signature 0: f8d6e0586b0a20c7 Signatures (minimized, use --include signatures) Events: Index 0 Type A.179b6b1cb6755e31.Counter.CounterIncremented Tx ID 9cc7ac4d3d5239016965aba89b9692d3401a48a090d1ad1a8d9ef9cfca685e6e Values - newCount (Int): 1 Code (hidden, use --include code) Payload (hidden, use --include payload) Fee Events (hidden, use --include fee-events) ``` Run the script to check the counter again. You'll see that it has incremented: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc ``` ```zsh Result: 1 ``` :::tip To learn more about writing transactions, read the docs for [basic transactions]. ::: ## Conclusion You've successfully established a solid foundation for building on Flow. Let's recap what you've accomplished and learned. Through this hands-on tutorial, you've successfully built a complete Flow development foundation: ✅ **Complete Flow development environment** - Flow CLI installed and configured for project management. - Local Flow emulator running and ready for development. - Project creation and management workflow with `flow init`. ✅ **Smart contract deployment skills** - Counter contract successfully deployed to your local emulator. - Account creation and contract deployment configuration mastered. ✅ **Blockchain interactions** - Scripts to query contract state (read blockchain data). - Transactions to modify contract state (write to blockchain). - Real-time interaction with blockchain data through CLI commands. ### Resources for continued learning As you continue your Flow development journey: - **[Flow Discord Community]**: Connect with other developers, get help, and share your projects. - **[Cadence Language Reference]**: Deep dive into Flow's programming language features and best practices. - **[Flow GitHub]**: Explore open source tools, examples, and contribute to the ecosystem. The foundation you've built today will serve you well as you explore Flow's capabilities and build applications that take advantage of blockchain's unique properties: permanence, transparency, and decentralization. Welcome to the Flow developer community—you're ready to build the future of digital experiences! [Flow Command Line Interface]: ../../../build/tools/flow-cli/index.md [installation guide]: ../../../build/tools/flow-cli/install [Flow Cadence VSCode Extension]: https://marketplace.visualstudio.com/items?itemName=onflow.cadence [`flow.json`]: ../../../build/tools/flow-cli/flow.json/configuration.md [Generating Cadence Boilerplate]: https://developers.flow.com/build/tools/flow-cli/generate [basic scripts]: https://developers.flow.com/build/cadence/basics/scripts [basic transactions]: https://developers.flow.com/build/cadence/basics/transactions [tests documentation]: https://developers.flow.com/build/tools/flow-cli/tests [homebrew]: https://brew.sh/ [configuration docs]: ../../../build/tools/flow-cli/flow.json/configuration.md [Flow Discord Community]: https://discord.com/invite/flow [Cadence Language Reference]: https://cadence-lang.org [Flow GitHub]: https://github.com/onflow --- # File: /md/blockchain-development-tutorials/cadence/getting-started/production-deployment.md --- sidebar_position: 4 sidebar_label: Production Deployment title: Production Deployment description: Learn how to deploy your Flow application to production networks. Deploy contracts to testnet and mainnet, configure your frontend for live networks, and implement production best practices for monitoring and maintenance. keywords: - production deployment - Flow testnet - Flow mainnet - contract deployment - testnet faucet - production security - network configuration - mainnet deployment - Flow CLI - production monitoring - live deployment - blockchain production --- # Production Deployment You've developed locally with the emulator, integrated external dependencies, built sophisticated transactions, implemented comprehensive testing, and created a frontend interface. Now it's time to take your application live and deploy it to Flow's public networks. This tutorial will guide you through deployment of your Counter application to both testnet and mainnet, which ensures that your contracts and frontend work seamlessly in production environments. You'll learn the essential practices for how to manage live blockchain applications, from security considerations to monitoring and maintenance. ## What you'll learn After you complete this tutorial, you'll be able to: - **Deploy contracts to Flow testnet** with proper account setup and funding. - **Configure your application** for different network environments (emulator, testnet, mainnet). - **Deploy to mainnet** with security best practices and production considerations. - **Update frontend configuration** to work with live networks. - **Implement monitoring and maintenance** practices for production applications. - **Understand the deployment pipeline** from development to production. **Prerequisites:** - Completed all previous tutorials ([Environment Setup], [Smart Contract Interaction], [Building a Frontend App]). - Counter contract and frontend app working locally. - Flow CLI installed and configured. ## Deploy to testnet Testnet is Flow's public test network that mirrors mainnet functionality without using real FLOW tokens. It's the perfect environment to test your application in a live blockchain environment before you commit to mainnet deployment. ### Understanding Flow networks Flow has several networks for different purposes: - **Emulator**: Local development environment (what you currently use). - **Testnet**: Public test network with free test tokens. - **Mainnet**: Production network with real Flow tokens. Each network has its own: - Access nodes and APIs. - Account addresses and contract deployments. - Token economics (free on testnet, real value on mainnet). ### Create a testnet account First, you'll need a testnet account to deploy your contracts. You can create one with the Flow CLI: ```zsh flow accounts create --network testnet ``` When prompted: 1. **Account name**: Enter `testnet-account` 2. **Select "Testnet" Network** This creates a new account on testnet and adds it to your `flow.json` configuration. The CLI will show you the account address and save the private key locally. ### Fund your testnet account To deploy contracts and send transactions on testnet, you need Flow tokens. Flow provides a faucet service to get free testnet tokens. 1. Visit the [Flow Testnet Faucet]. 2. Enter your testnet account address. 3. Complete any required verification (captcha, and so on). 4. Request tokens (you'll receive 1000 FLOW tokens). This command automatically requests tokens from the testnet faucet for your account. ```zsh flow accounts fund --network testnet testnet-account ``` **Verify funding:** Check your account balance: ```zsh flow accounts list ``` You will see your account details with a balance of Flow tokens. ### Configure testnet deployment Update your `flow.json` to include testnet deployment configuration. The `NumberFormatter` contract already exists on testnet, so you only need to deploy your Counter contract. ```zsh flow config add deployment ``` Follow the prompts: 1. **Network**: `testnet` 2. **Account**: `testnet-account` 3. **Contract**: `Counter` 4. **Deploy more contracts**: `yes` 5. **Contract**: `NumberFormatter` Your `flow.json` now includes a testnet deployment section: ```json { "deployments": { "emulator": { "default": ["Counter"], "emulator-account": ["NumberFormatter"] }, "testnet": { "testnet-account": ["Counter", "NumberFormatter"] } } } ``` ### Deploy Counter contract to testnet Deploy your Counter contract to the public testnet: ```zsh flow project deploy --network testnet ``` You will see output similar to: ```zsh Deploying 2 contracts for accounts: testnet-account Counter -> 0x9942a81bc6c3c5b7 (d8fe130e5b2212a5c7b3c34fe6e74ede80c750bc4c57e57788e81b247dcd7fe0) NumberFormatter -> 0x9942a81bc6c3c5b7 (9a550aeefa5ede62cb95f0549084b2ab7abf3a493cf853d50c1c377a7be733b2) 🎉 All contracts deployed successfully ``` ### Test your testnet deployment Verify your contract works on testnet with this script: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc --network testnet ``` You should see: ```zsh Result: "0" ``` Test a transaction to increment the counter: ```zsh flow transactions send cadence/transactions/IncrementCounter.cdc --network testnet --signer testnet-account ``` Run the script again to verify the increment worked: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc --network testnet ``` ```zsh Result: "1" ``` Perfect! Your Counter contract is now live on testnet and works correctly. ### Update frontend for testnet Now update your `Next.js` application to connect to testnet instead of the emulator. **Update `src/app/layout.tsx`:** ```tsx 'use client'; import { FlowProvider } from '@onflow/react-sdk'; import flowJson from '../flow.json'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` **Key changes:** - `accessNodeUrl`: changed from localhost to Flow's testnet REST API. - `flowNetwork`: changed from 'emulator' to 'testnet'. - `discoveryWallet`: updated to use testnet wallet discovery. ### Test your testnet frontend Start your frontend application: ```bash npm run dev ``` Visit `http://localhost:3000` and you will see: 1. **Counter value**: displays the current count from your testnet contract. 2. **Connect Wallet**: connect with various Flow wallets (not just Dev Wallet). 3. **Increment functionality**: transactions are sent to the live testnet. 4. **Real transaction costs**: small amounts of testnet Flow are used for compute units, the Flow Cadence equivalence of gas. **Important**: When you connect your wallet, make sure to: - Switch your wallet to Testnet network. - Use an account that has testnet Flow tokens. - Confirm you're interacting with the correct contract address. ## Deploy to mainnet Mainnet deployment is the final step in your application's journey. Unlike testnet, mainnet uses real Flow tokens and serves real users, so additional security considerations and best practices are essential. ### Create a mainnet account For mainnet, you'll need to acquire Flow tokens through exchanges or other means, as there's no faucet. **Option 1: Use Flow Wallet** 1. Download and install [Flow Wallet]. 2. Create a new wallet and securely store your recovery phrase. 3. Purchase Flow tokens from a supported exchange. 4. Transfer tokens to your Flow Wallet. **Option 2: Use Flow CLI** ```zsh flow accounts create --network mainnet ``` When prompted: 1. **Account name**: Enter `mainnet-account` 2. **Select "Mainnet" Network** ### Acquire FLOW tokens You can purchase Flow tokens from major exchanges like [Coinbase], [Moonpay], and [Binance]. To obtain Flow directly from the Flow Wallet, click "Buy" in your account. ![flow-wallet-icons](./imgs/flow-wallet-icons.png) Then, click on a provider to purchase FLOW. ![provider](./imgs/provider.png) ### Configure mainnet deployment Add mainnet deployment configuration to your `flow.json`: ```zsh flow config add deployment --network mainnet ``` Follow the prompts: 1. **Network**: `mainnet` 2. **Account**: `mainnet-account` 3. **Contract**: `Counter` 4. **Deploy more contracts**: `yes` 5. **Contract**: `NumberFormatter` Your `flow.json` will now include mainnet configuration: ```json { "dependencies": { "NumberFormatter": { "source": "testnet://8a4dce54554b225d.NumberFormatter", "aliases": { "mainnet": "1654653399040a61", "testnet": "8a4dce54554b225d" } } }, "deployments": { "emulator": { "default": ["Counter"], "emulator-account": ["NumberFormatter"] }, "testnet": { "testnet-account": ["Counter", "NumberFormatter"] }, "mainnet": { "mainnet-account": ["Counter", "NumberFormatter"] } } } ``` ### Deploy to mainnet Deploy your Counter contract to mainnet: ```zsh flow project deploy --network mainnet ``` **⚠️ Important**: This deployment costs real FLOW tokens and you can't undo it. You will see output similar to: ```zsh Deploying 2 contracts for accounts: mainnet-account Counter -> 0xABC123DEF456789 (contract deployed successfully) NumberFormatter -> 0x123456789ABC (contract deployed successfully) 🎉 All contracts deployed successfully ``` ### Production frontend configuration Create a production build of your frontend configured for mainnet: **Update `src/app/layout.tsx` for production:** ```tsx 'use client'; import { FlowProvider } from '@onflow/react-sdk'; import flowJson from '../flow.json'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` Build your production frontend: ```bash npm run build ``` [Flow Wallet]: https://wallet.flow.com/ [Coinbase]: https://www.coinbase.com/en-in/how-to-buy/flow [Moonpay]: https://www.moonpay.com/buy/flow [Binance]: https://www.binance.com/en-IN/how-to-buy/flow [Flow Testnet Faucet]: https://faucet.flow.com/ --- # File: /md/blockchain-development-tutorials/cadence/getting-started/smart-contract-interaction.md --- sidebar_position: 2 sidebar_label: Smart Contract Interaction title: Smart Contract Interaction description: Master advanced Flow development skills including external dependencies, sophisticated transactions, and comprehensive testing strategies. Learn to integrate contracts and implement test-driven development workflows. keywords: - Flow dependencies - contract interaction - Cadence transactions - NumberFormatter - dependency manager - transaction anatomy - smart contract testing - test-driven development - Flow CLI - external contracts - blockchain transactions - Flow development --- # Smart Contract Interaction Building on your local development setup from the previous tutorial, you'll now master advanced Flow development skills that every professional developer needs. This tutorial focuses on how to work with external dependencies, build sophisticated transactions, and establish robust testing practices. Flow's composability is one of its greatest strengths, becuase contracts can easily import and use functionality from other contracts. You'll learn to leverage this power while you build reliable, well-tested applications that interact seamlessly with the broader Flow ecosystem. ## What you'll learn After you complete this tutorial, you'll be able to: - **Manage external dependencies** with Flow's dependency manager and integrate third-party contracts. - **Build sophisticated transactions** that interact with multiple contracts and handle complex state changes. - **Master transaction anatomy** and understand how Cadence transactions work under the hood. - **Implement comprehensive testing** strategies including edge cases and error conditions. - **Apply test-driven development** workflows to ensure code quality and reliability. - **Handle transaction monitoring** and error management in production scenarios. ## What you'll build Building on your Counter contract, you'll enhance it with external dependencies and create a comprehensive testing suite. By the end of this tutorial, you'll have: - **Enhanced Counter app** that uses the NumberFormatter contract for better display. - **Complex transactions** that demonstrate advanced interaction patterns. - **Comprehensive test suite** that covers normal operations, edge cases, and error conditions. - **Professional workflow** for you to develop, test, and deploy contract interactions. **Prerequisites:** - Completed Environment Setup tutorial. - Flow CLI, emulator running, and Counter contract deployed. - Basic understanding of Cadence syntax. ## Manage dependencies In addition to creating your own contracts, you can also install contracts that you previously deployed to the network with the [Dependency Manager]. This is useful for interacting with contracts that are part of the Flow ecosystem or that other developers deployed. Flow's dependency manager allows you to: - Install contracts deployed on any Flow network (mainnet, testnet, emulator). - Automatically manage contract addresses across different environments. - Keep your code portable and environment-independent. For example, let's say we want to format the result of our `GetCounter` script so that we display the number with commas if it's greater than 999. To do that we can install a contract called [`NumberFormatter`] from `testnet` that has a function to format numbers. ### Install NumberFormatter contract The [`NumberFormatter`] contract provides utilities for formatting numbers with commas, which makes large numbers more readable. Let's install it from testnet: ```zsh flow dependencies install testnet://8a4dce54554b225d.NumberFormatter ``` When prompted: 1. **Account to deploy to:** Select `emulator-account` (this will deploy it locally for development). 2. **Alias for mainnet:** To skip this, press Enter. This command: - Downloads the NumberFormatter contract from testnet and any of its dependencies. - Adds it to your `imports/` directory. - Configures deployment settings in [`flow.json`]. - Sets up automatic address resolution. ### Configure dependencies in flow.json Open your `flow.json` file and view the new sections: ```json { . . . "dependencies": { "NumberFormatter": { "source": "testnet://8a4dce54554b225d.NumberFormatter", "hash": "dc7043832da46dbcc8242a53fa95b37f020bc374df42586a62703b2651979fb9", "aliases": { "testnet": "8a4dce54554b225d" } } }, . . . "deployments": { "emulator": { "emulator-account": [ "NumberFormatter" ] } } } ``` This configuration: - Maps the `NumberFormatter` dependency to its testnet source. - Sets up deployment to your emulator account. - Allows automatic address resolution in your code. ### Deploy external dependencies Now we can deploy the `NumberFormatter` contract to the emulator: ```zsh flow project deploy ``` You will see output like: ```zsh Deploying 1 contracts for accounts: emulator-account NumberFormatter -> 0xf8d6e0586b0a20c7 (66e6c4210ae8263370fc3661f148f750175bb4cf2e80637fb42eafe2d6c5b385) 🎉 All contracts deployed successfully ``` ### Integrate external contract Now let's update your `GetCounter.cdc` script to use the NumberFormatter. Open `cadence/scripts/GetCounter.cdc` and update it: ```cadence import "Counter" import "NumberFormatter" access(all) fun main(): String { // Retrieve the count from the Counter contract let count: Int = Counter.getCount() // Format the count using NumberFormatter let formattedCount = NumberFormatter.formatWithCommas(number: count) // Return the formatted count return formattedCount } ``` **Key points:** - **Import syntax**: `import "Counter"` and `import "NumberFormatter"` don't require addresses. - **Contract interaction**: We call `NumberFormatter.formatWithCommas()` just like any other function. - **Return type change**: The script now returns a `String` instead of an `Int`. ### Test the integration Run your updated script: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc ``` You will see: ```zsh Result: "1" ``` The number is now formatted as a string. Let's create a more impressive example and add a transaction that increments by 1000. ### Create a bulk increment transaction Generate a new transaction to demonstrate the NumberFormatter's power: ```zsh flow generate transaction IncrementBy1000 ``` Open `cadence/transactions/IncrementBy1000.cdc` and replace the content with: ```cadence import "Counter" transaction { prepare(acct: &Account) { // Authorization handled automatically } execute { // Increment the counter 1000 times var i = 0 while i < 1000 { Counter.increment() i = i + 1 } // Retrieve the new count and log it let newCount = Counter.getCount() log("New count after incrementing by 1000: ".concat(newCount.toString())) } } ``` Execute the transaction: ```zsh flow transactions send cadence/transactions/IncrementBy1000.cdc --signer test-account ``` Now run your formatted script to see the NumberFormatter in action: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc ``` Result: ```zsh Result: "1,001" ``` Perfect! The NumberFormatter automatically adds commas to make large numbers readable. :::info **The Power of Composability**: Notice what just happened—you enhanced your Counter contract's functionality **without modifying the original contract**. This is the power of Flow's composability: you can extend functionality by combining contracts, which allows rapid development and code reuse. Even more importantly, we did this **without the need for access or permission.** ::: ## Build transactions Transactions are the foundation of blockchain state changes. Unlike scripts (which only read data), transactions can modify contract state, transfer tokens, and emit events. Let's master advanced transaction patterns. ### Understand transaction anatomy Every Cadence transaction has the same basic structure: ```cadence import "OtherContract" transaction { // Optional: Declare variables available throughout the transaction let initialCount: Int // This phase has access to account storage and capabilities // Used for authorization and accessing private data prepare(acct: &Account) { } // This phase contains the main transaction logic // No access to account storage, only to data from prepare phase execute { } // Optional: Conditions that must be true after execution // Used for verification and ensuring transaction success post { } } ``` ### Transaction phases explained 1. **Import Phase**: declare contract dependencies. 2. **Parameter Declaration**: define inputs the transaction accepts. 3. **Variable Declaration**: declare transaction-scoped variables. 4. **Prepare Phase**: access account storage and capabilities (authorized). 5. **Execute Phase**: main logic execution (no storage access). 6. **Post Phase**: verify transaction success conditions. #### Transaction with parameters Create a transaction that accepts a custom increment value: ```zsh flow generate transaction IncrementByAmount ``` Open `cadence/transactions/IncrementByAmount.cdc`: ```cadence import "Counter" transaction(amount: Int) { // Store initial value let initialCount: Int prepare(acct: &Account) { // Verify the account is authorized to make this change log("Account ".concat(acct.address.toString()).concat(" is incrementing by ").concat(amount.toString())) prepare(acct: &Account) { self.initialCount = Counter.getCount() // Capture initial state log("Account".concat(acct.address.toString()).concat(" is incrementing by").concat(amount.toString())) } execute { // Validate input if amount <= 0 { panic("Amount must be positive") } // Increment the specified number of times var i = 0 while i < amount { Counter.increment() i = i + 1 } let newCount = Counter.getCount() log("Counter incremented by ".concat(amount.toString()).concat(", new value: ").concat(newCount.toString())) } post { // Verify the counter increased correctly Counter.getCount() == (self.initialCount + amount): "Counter must equal initial count plus increment amount" } } } ``` Execute with a parameter: ```zsh flow transactions send cadence/transactions/IncrementByAmount.cdc --network emulator --signer test-account ``` ## Test your code Testing is crucial for smart contract development. Flow provides powerful testing capabilities built into the CLI that allow comprehensive test coverage and test-driven development workflows. Execute the test suite: ```zsh flow test ``` You will see output that confirms the tests pass: ```zsh Test results: "Counter_test.cdc" - PASS: testContract All tests passed ``` ### Understand current tests Open `cadence/tests/Counter_test.cdc` to see the existing test: ```cadence import Test access(all) let account = Test.createAccount() access(all) fun testContract() { let err = Test.deployContract( name: "Counter", path: "../contracts/Counter.cdc", arguments: [] ) Test.expect(err, Test.beNil()) } ``` This basic test: 1. **Creates a test account** with `Test.createAccount()`. 2. **Deploys the Counter contract** to the test environment. 3. **Verifies deployment succeeded** by checking that no error occurred. ### Test integration with dependencies Test the NumberFormatter integration: ```cadence import Test access(all) let account = Test.createAccount() access(all) fun testNumberFormatterLogic() { // Test NumberFormatter logic inline without contract deployment // Test small number (under 1000) - should have no comma let smallNumberScript = Test.executeScript( "access(all) fun formatWithCommas(number: Int): String { let isNegative = number < 0; let absNumber = number < 0 ? -number : number; let numberString = absNumber.toString(); var formatted = \"\"; var count = 0; let numberLength = numberString.length; var i = numberLength - 1; while i >= 0 { let digit = numberString.slice(from: i, upTo: i + 1); formatted = digit.concat(formatted); count = count + 1; if count % 3 == 0 && i != 0 { formatted = \",\".concat(formatted) }; i = i - 1 }; if isNegative { formatted = \"-\".concat(formatted) }; return formatted }; access(all) fun main(): String { return formatWithCommas(number: 123) }", [] ) Test.expect(smallNumberScript, Test.beSucceeded()) let smallResult = smallNumberScript.returnValue! as! String Test.expect(smallResult, Test.equal("123")) // Test large number (over 999) - should have comma let largeNumberScript = Test.executeScript( "access(all) fun formatWithCommas(number: Int): String { let isNegative = number < 0; let absNumber = number < 0 ? -number : number; let numberString = absNumber.toString(); var formatted = \"\"; var count = 0; let numberLength = numberString.length; var i = numberLength - 1; while i >= 0 { let digit = numberString.slice(from: i, upTo: i + 1); formatted = digit.concat(formatted); count = count + 1; if count % 3 == 0 && i != 0 { formatted = \",\".concat(formatted) }; i = i - 1 }; if isNegative { formatted = \"-\".concat(formatted) }; return formatted }; access(all) fun main(): String { return formatWithCommas(number: 1234) }", [] ) Test.expect(largeNumberScript, Test.beSucceeded()) let largeResult = largeNumberScript.returnValue! as! String Test.expect(largeResult, Test.equal("1,234")) } ``` The `Formatter_test.cdc` test validates that number formatting with commas works correctly by testing two scenarios: numbers under 1,000 (which should have no commas) and numbers over 999 (which should have commas). The test is constructed with two main assertions - first testing that 123 formats as "123" without commas, and second testing that 1234 formats as "1,234" with a comma. ### Run your enhanced test suite Execute the complete test suite with your new comprehensive tests: ```zsh flow test ``` You should see output like: ```zsh Running tests... Test results: "cadence/tests/Formatter_test.cdc" - PASS: testNumberFormatterLogic Test results: "cadence/tests/Counter_test.cdc" - PASS: testContract All tests passed ``` :::tip For a more detailed guide on Cadence testing patterns and advanced techniques, check out the [tests documentation]. ::: --- ## Conclusion Through this tutorial, you've accomplished: ✅ **Dependency management** - Successfully integrated the NumberFormatter contract from testnet. - Learned about Flow's dependency management system and automatic address resolution. - Demonstrated contract composability by enhancing functionality without modifying source code. - Configured multi-contract deployments across different environments. ✅ **Transaction development** - Understood transaction anatomy including prepare, execute, and post phases. - Implemented proper input validation and error handling patterns. ✅ **Testing** - Implemented test coverage for contract functionality - Created integration tests that verify multi-contract interactions ### What you've learned You have learned how to use Flow's dependency management system to install and integrate external contracts (like NumberFormatter), understand the structure of Cadence transactions including their prepare, execute, and post phases, and implement basic testing for contract functionality. You can now work with multi-contract applications and understand how contracts can be composed together to extend functionality. ### Next steps With these skills, you're ready to: - Build frontend applications that interact with your smart contracts. - Deploy contracts to live networks (testnet and mainnet). - Explore advanced Flow patterns and ecosystem contracts. - Contribute to the growing Flow developer community. You've made significant progress in becoming a proficient Flow developer! ### Resources for continued learning Continue your Flow mastery with these advanced resources: - **[Flow Discord Community]**: Connect with other developers building sophisticated Flow applications. - **[Cadence Language Reference]**: Master advanced language features including resources, capabilities, and access control. - **[Flow GitHub]**: Explore production contract examples and contribute to the ecosystem. [tests documentation]: ../../../build/tools/flow-cli/tests.md [Flow Discord Community]: https://discord.gg/flow-blockchain [Flow Documentation]: https://developers.flow.com [Cadence Language Reference]: https://cadence-lang.org [Flow GitHub]: https://github.com/onflow [Flow DevEX]: https://flowdevx.com [Dependency Manager]: https://developers.flow.com/build/tools/flow-cli/dependency-manager [`NumberFormatter`]: https://contractbrowser.com/A.8a4dce54554b225d.NumberFormatter [`flow.json`]: https://developers.flow.com/build/tools/flow-cli/flow.json/configuration --- # File: /md/blockchain-development-tutorials/cadence/mobile.md --- title: Mobile Development on Flow sidebar_position: 4 description: Discover Flow's mobile development capabilities for building native blockchain applications. Learn about Flow's unique features for mobile apps, including secure key management, wallet integration, and progressive onboarding. keywords: - Flow mobile - mobile development - blockchain apps - native applications - mobile SDK - secure enclave - wallet integration - WalletConnect - account linking - mobile security - Flow features - mobile wallets - Cadence mobile - user experience - blockchain mobile --- # Mobile Development on Flow When you build mobile native applications that interact with the blockchain, it allows a much richer end user experience and provides access to OS capabilities. With Flow Mobile, developers can build native applications for iOS and Android with SDKs and mobile wallets. ## Why Flow Millions of users with Flow accounts explore the ecosystem and look for applications. Most of these users purchased Flow NFTs and are comfortable with web3 principles. In addition to the current user base, developers can tap into smart contracts deployed on the Flow blockchain. These contracts, which includes their onchain state, provide unique possibilities to build experiences that enrich currently-used applications. The following key capabilities make Flow a standout choice for mobile applications: - On-device key encryption via Secure Enclave & Keychain. - Mobile wallet compatibility and support for WalletConnect 2.0. - Simple, progressive onboarding experience with postponed account linking. - Seamless in-app experience with onchain interactions without constant signing requests. - Account flexibility enabling secure account recovery and sharing. ## Why Flow Mobile ### Proven Flow is built with mainstream adoption in mind. Mobile applications can leverage the best-in-class user experiences millions of users have enjoyed on the web, through applications like NBA TopShot or NFL AllDay. ### Best-in-class UX Flow's Client Library makes it very intuitive to sign up and sign in with their wallet of choice. For transaction signing, Flow offers human readable security, so users get a clear understanding of what they approve. An increased sense of trust for Flow applications is the outcome. Furthermore, Flow's powerful account model allows for seamless user flows of onchain operations. Apps can perform transactions on behalf of the users (with their approval) in the background, without the need to switch between apps. The account model also allows apps to pay for transactions to postpone fiat on-ramps to get them to experience the value of an application before they commit to buy tokens. Last, but not least, developers can leverage progressive web3 onboarding, in which you can use any identity provider authenticate users, but don't have to deal with keys. Developers can create Flow accounts for the users and link them to a wallet at a later point in time. ### Security first Flow's mobile SDKs use on-device key encryption via Apple's Secure Enclave and Android's Keystore. The flexible account model makes it possible for an account to have multiple keys with different weights, which allows secure social recovery, account sharing, and much more. ## Smart contract language inspired by mobile languages Cadence, Flow's smart contract language, will look and feel very familiar to mobile languages developers are already familiar with. Cadence was inspired by Move, Swift, and Kotlin. This reduces the ramp-up period to develop mobile applications leveraging onchain logic. ## What is available Developers can leverage the following features to get productive quickly: - Swift & Kotlin FCL SDKs to authenticate and interact with the Flow blockchain (query + execute scripts). - FCL-compatible mobile wallets. - User authentication with WalletConnect 2.0. - Basic mobile sample application (MonsterMaker). ## Guides **[iOS Development]** - Learn native iOS development on Flow through the Monster Maker sample project, which demonstrates wallet integration, transaction signing, and NFT management. The tutorial covers FCL Swift SDK integration, WalletConnect 2.0 for wallet connectivity, and essential blockchain interactions like querying and mutating data. **[React Native Development]** - Build cross-platform mobile dApps using React Native and Flow Client Library (FCL). This guide walks through how to set up authentication, query the blockchain, and execute transactions while interacting with the Profile Contract on Flow's testnet to create and edit user profiles. **[Build a Walletless Mobile App (PWA)]** - Create an accessible Progressive Web App with seamless onboarding with Magic integration for walletless authentication. The tutorial covers how to build a balloon inflation game that demonstrates Magic SDK integration, hybrid custody features, and account linking to transition from custodial to non-custodial ownership. ## Conclusion Flow Mobile empowers developers to create native blockchain applications that deliver best-in-class user experiences and maintain the security and flexibility that mainstream adoption demands. Whether you build with native SDKs or create Progressive Web Apps, Flow's mobile development capabilities provide the tools needed to bring web3 to millions of users through intuitive, secure, and feature-rich mobile experiences. [iOS Development]: ./ios-quickstart.md [React Native Development]: ./react-native-quickstart.md [Build a Walletless Mobile App (PWA)]: ./walletless-pwa.md --- # File: /md/blockchain-development-tutorials/cadence/mobile/ios-quickstart.md --- title: IOS Development sidebar_label: IOS Development sidebar_position: 1 description: Learn how to build native iOS applications on Flow blockchain using the Monster Maker sample project. Understand wallet integration, transaction signing, and NFT management in mobile apps. keywords: - iOS development - Flow mobile - Monster Maker - mobile dApp - FCL Swift - wallet integration - NFT mobile - iOS blockchain - mobile wallet - transaction signing - native app - Flow SDK - mobile development - iOS sample - blockchain mobile --- # IOS Development The following tutorial aims to educate you on how to build a native mobile application on Flow. It first presents Monster Maker, a starter project we've built to represent simple Flow mobile concepts. Next, it presents various developer resources related to building mobile native Flow applications. ## Monster Maker ![monster_maker_logo.png](./imgs/monster_maker_logo.png) Monster Maker is a native iOS app that allows users to connect a wallet, sign a transaction to mint an NFT (a monster) and display their collection of NFTs (their monsters) within the app. It's meant to be a lightweight sample project to exemplify how to build a mobile native Flow project. If want to build a native mobile application for Flow, explore the Monster Maker code base first, or you can even build off of it! ## Github repo The Monster Maker Github Repo can be found here: https://github.com/onflow/monster-maker ## Build to device Before you run Monster Maker on your device, make sure you've installed the [Xcode14] from Mac App Store. After you clone the repo, open the [MonsterMaker.xcodeproj] under the iOS folder. Xcode automatically sets up the project for you. If you see any errors related to dependencies, run `Xcode Menu -> File -> Packages -> Reset Package Cache` to resolve the issue. In the meantime, you can choose a simulator or your iPhone to run. For more detail, read the [official doc]. For run in real device, there are a few steps to deal with for signing: 1. Add your apple account to the Xcode which you can access from `Xcode Menu -> Settings -> Add account`. 2. Change the Team to your Personal Apple account from the **Signing & Capabilities** under the project target menu. For more detail, check the screenshot below. ![XCode Target Setup](./imgs/xcode_setup.png) ## Connect to a wallet To connect with wallets, there is native wallet discovery in the app. After you click connect, it brings out the list of the wallets which support `HTTP/POST` or `WC/RPC` method. ### FCL config To make sure the wallet can recognise your dApp, there are a few fields you need to configure before you connect to a wallet. The account proof config is optional. In addition, you'll need to create a project id from [walletconnect] cloud before you can connect to the `WC/RPC` compatible wallet such as [Flow Wallet]. ```swift import FCL // Config the App let defaultProvider: FCL.Provider = .dapperPro let defaultNetwork: Flow.ChainID = .testnet // or .mainnet // Optinal: Config for account proof let accountProof = FCL.Metadata.AccountProofConfig(appIdentifier: "Monster Maker") // Config for WC/RPC compatible wallet let walletConnect = FCL.Metadata.WalletConnectConfig(urlScheme: "monster-maker://", projectID: "12ed93a2aae83134c4c8473ca97d9399") // Config basic dApp info let metadata = FCL.Metadata(appName: "Monster Maker", appDescription: "Monster Maker Demo App for mobile", appIcon: URL(string: "https://i.imgur.com/jscDmDe.png")!, location: URL(string: "https://monster-maker.vercel.app/")!, accountProof: accountProof, walletConnectConfig: walletConnect) fcl.config(metadata: metadata, env: defaultNetwork, provider: defaultProvider) // Import keywords replacement for cadence query and transaction fcl.config .put("0xFungibleToken", value: "0x631e88ae7f1d7c20") .put("0xMonsterMaker", value: "0xfd3d8fe2c8056370") .put("0xMetadataViews", value: "0x631e88ae7f1d7c20") .put("0xTransactionGeneration", value: "0x44051d81c4720882") ``` ### Open wallet discovery ![In Monster Maker, the Connect button triggers opening of Wallet Discovery](./imgs/connect.png) In Monster Maker, click `Connect` to open Wallet Discovery For the wallet support `HTTP/POST`, it uses webview to show the following actions. For the wallet support `WC/RPC`, it uses deep-link to the wallet for actions. You can open the native wallet discovery to make the selection, but you can also connect to the specific wallet. Here is the code snippet of it: ```swift import FCL // Open discovery view fcl.openDiscovery() // Or manual connect to specific wallet try fcl.changeProvider(provider: provider, env: .testnet) try await fcl.authenticate() ``` ## Sign a transaction ![In Monster Maker, Initializing the NFT collection with the Initialize button triggers a transaction.](./imgs/initialize.png) In Monster Maker, click `Initialize` to initialize the NFT colelction. This triggers a transaction. Similar to what we have on fcl-js, native sdk also use `query` and `mutate` for onchain interactions. To request a signature from user, you can simply use `fcl.mutate` method. By default, the user is the payer, proposer and authorizer. If you want to add custom authorizer refer to the code from [Server] and [iOS] end. ```swift guard let user = fcl.currentUser else { // Not signin return } let txId = try await fcl.mutate( cadence: """ transaction(test: String, testInt: Int) { prepare(signer: &Account) { log(signer.address) log(test) log(testInt) } } """, args: [ .string("Hello"), .int(10) ], gasLimit: 999, authorizors: [user]) print("txId -> \(txId)") ``` ## View NFT ![The View page in Monster Maker exemplifies showing Monster Maker NFTs held by the connected wallet](./imgs/collection.png) The View page in Monster Maker exemplifies showing Monster Maker NFTs held by the connected wallet. During development, you always can query your NFT with `fcl.query`. Here is an example: - Query cadence ```cadence import NonFungibleToken from 0xNonFungibleToken import MonsterMaker from 0xMonsterMaker import MetadataViews from 0xMetadataViews access(all) struct Monster { access(all) let name: String access(all) let description: String access(all) let thumbnail: String access(all) let itemID: UInt64 access(all) let resourceID: UInt64 access(all) let owner: Address access(all) let component: MonsterMaker.MonsterComponent init( name: String, description: String, thumbnail: String, itemID: UInt64, resourceID: UInt64, owner: Address, component: MonsterMaker.MonsterComponent ) { self.name = name self.description = description self.thumbnail = thumbnail self.itemID = itemID self.resourceID = resourceID self.owner = owner self.component = component } } access(all) fun getMonsterById(address: Address, itemID: UInt64): Monster? { if let collection = getAccount(address).capabilities.get<&MonsterMaker.Collection>(MonsterMaker.CollectionPublicPath).borrow() { if let item = collection.borrowMonsterMaker(id: itemID) { if let view = item.resolveView(Type()) { let display = view as! MetadataViews.Display let owner: Address = item.owner!.address! let thumbnail = display.thumbnail as! MetadataViews.HTTPFile return Monster( name: display.name, description: display.description, thumbnail: thumbnail.url, itemID: itemID, resourceID: item.uuid, owner: address, component: item.component ) } } } return nil } access(all) fun main(address: Address): [Monster] { let account = getAccount(address) let collectionRef = account.capabilities.get<&{NonFungibleToken.Collection}>(MonsterMaker.CollectionPublicPath).borrow() ?? panic("The account with address " .concat(address.toString) .concat(" does not have a NonFungibleToken Collection at ") .concat(MonsterMaker.CollectionPublicPath.toString()) .concat(". Make sure the account address is correct and is initialized their account with a MonsterMaker Collection!")) let ids = collectionRef.getIDs() let monsters : [Monster] = [] for id in ids { if let monster = getMonsterById(address: address, itemID: id) { monsters.append(monster) } } return monsters } ``` ```swift let nftList = try await fcl.query(script: cadenceScript, args: [.address(address)]) .decode([NFTModel].self) ``` # External resources **FCL Swift** FCL Swift is the iOS native SDK for FCL. This SDK is integrated into the Monster Maker sample. https://github.com/Outblock/fcl-swift **FCL Android** FCL Android is the Android native SDK for FCL. https://github.com/Outblock/fcl-android **FCL Wallet Connect 2.0** One of the easiest ways to connect to a wallet via a mobile native dApp is through Flow's new support for Wallet Connect 2.0. This is the pattern that Monster Maker uses to connect to the [Flow Wallet]. For more information on FCL Wallet Connect 2.0, check out this page: [FCL Wallet Discovery] **How to build a native iOS dapp** The Agile Monkeys wrote a very comprehensive guide on how to build a native mobile application on iOS and interface with fcl-swift, which you can view with the link below: [How to Build a Native iOS Dapper] [Source Code] [Xcode14]: https://apps.apple.com/au/app/xcode/id497799835?mt=12 [MonsterMaker.xcodeproj]: https://github.com/onflow/monster-maker/tree/main/iOS/MonsterMaker.xcodeproj [official doc]: https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device [walletconnect]: https://cloud.walletconnect.com/app [Flow Wallet]: https://wallet.flow.com/ [Server]: https://github.com/onflow/monster-maker/blob/main/server/pages/api/signAsMinter/index.ts [iOS]: https://github.com/onflow/monster-maker/blob/main/iOS/MonsterMaker/Flow/MintHelper.swift) [FCL Wallet Discovery]: ../../../build/tools/clients/fcl-js/discovery.md [How to Build a Native iOS Dapper]: https://dev.to/theagilemonkeys/how-to-buid-a-native-ios-dapp-that-uses-the-flow-blockchain-as-the-backend-n9k [Source Code]: https://github.com/jfsagasti/FlowNotes --- # File: /md/blockchain-development-tutorials/cadence/mobile/react-native-quickstart.md --- title: React Native Development sidebar_label: React Native Development sidebar_position: 2 description: Learn how to build decentralized applications using React Native and Flow Client Library (FCL). Follow this guide to set up authentication, query the blockchain, and execute transactions in a React Native app. keywords: - React Native - Flow FCL - mobile dApp - blockchain development - FCL integration - React Native Flow - mobile blockchain - smart contracts - wallet integration - transaction signing - blockchain queries - dApp development - Flow mobile - React Native tutorial - mobile development --- # React Negative Development **Last Updated:** January 11th 2022 :::info This page will walk you through a very bare bones project to get started building a web3 dapp with the Flow Client Library (FCL). If you want a clonable repo, Flow community members created quickstart templates for different JavaScript frameworks (for example, [Next.js], [SvelteKit], [Nuxt]. You can consult the complete list [here]. ::: FCL-JS is the easiest way to start to build decentralized applications. Flow Client Library (FCL) wraps much of the logic you'd have to write yourself on other blockchains. Follow this quick start and you'll have a solid overview of how to build a shippable dapp on Flow. We're going to make an assumption that you know or understand React; however, the concepts should be easy to understand and transfer to another framework. While this tutorial uses Cadence (Flow's smart contract language), you do not need to know it. Instead, we recommend that you later [learn the Cadence language] after you've gotten the core FCL concepts down. In this tutorial, we are going to interact with an current smart contract on Flow's testnet known as the [Profile Contract]. With this contract, we will create a new profile and edit the profile information, both via a wallet. To do this, the FCL concepts we'll cover are: - [Installation] - [Configuration] - [Authentication] - [Query the Blockchain] - [Initialize an Account] - [Mutate the Blockchain] If you ever have any questions, we're always happy to help on [Discord](https://discord.gg/flowblockchain). There are also links at the end of this article for diving deeper into how to build on Flow. ## Installation The first step is to generate a React app using Next.js and [create-expo-app](https://docs.expo.dev/get-started/create-a-project/). From your terminal, run the following: ```sh npx create-expo-app flow-react-native cd flow-react-native ``` Next, install FCL so we can use it in our app. ```sh npm install @onflow/fcl@alpha @react-native-async-storage/async-storage expo-web-browser --save ``` Now run the app with the following command in your terminal. ```sh npm run start ``` Your app is now running. ## Configuration Now that your app is running, you can configure FCL. Within the main project directory, create a folder called `flow` and create a file called `config.js`. This file contains configuration information for FCL, such as what Access Node and wallet discovery endpoint to use (such as testnet or a local emulator). Add the following contents to the file: :::info These values are required to use FCL with your app. ::: > **Create file:** `./flow/config.js` ```javascript ./flow/config.js import { config } from '@onflow/fcl'; config({ 'accessNode.api': 'https://rest-testnet.onflow.org', // Mainnet: "https://rest-mainnet.onflow.org" 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', // Mainnet: "https://fcl-discovery.onflow.org/authn" 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', // Mainnet: "https://fcl-discovery.onflow.org/api/authn" }); ``` 📣 **Tip**: We recommend that you replace these values with environment variables for easy deployments across different environments like development/production or Testnet/Mainnet. - The `accessNode.api` key specifies the address of a Flow access node. Flow provides these, but in the future, third parties ay provide access to Flow through their own access nodes. - `discovery.wallet` and `discovery.authn.endpoint` are addresses that point to a service that lists FCL compatible wallets. Flow's FCL Discovery service is a service that FCL wallet providers can be added to, and be made 'discoverable' to any application that uses the `discovery.wallet` and `discovery.authn.endpoint`. > Learn more about [how to configure Discovery] or [how to set configuration values]. > If you run a Wallet Discovery locally and want to use it in the React Native app, change `https://fcl-discovery.onflow.org/` to `http://:/` > For Example: > use local [Wallet Discovery] and local [Dev Wallet]: > > ```javascript ./flow/config.js > import { config } from "@onflow/fcl"; > > config({ > ... > "discovery.wallet": "http://10.0.0.1:3002/local/authn", > "discovery.authn.endpoint": "http://10.0.0.1:3002/api/local/authn", > ... > }) > ``` The main screen for React Native apps is located in `./App.js` or in `./App.tsx`. So, to finish configuring our dApp, let's go into the root directory and import the config file into the top of our `App.js` file. We'll then swap out the default component in `App.js` to look like this: > **Replace file:** `./App.js` ```jsx ./App.js import { StatusBar } from 'expo-status-bar'; import { StyleSheet, Text, View } from 'react-native'; import './flow/config'; export default function App() { return ( Open up App.js to start working on your app! ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }, }); ``` Now we're ready to talk to Flow! ## Authentication To authenticate a user, you'll need to render a `ServiceDiscovery` component provided by `fcl-react-native`. Alternatively, you can build your own component with `useServiceDiscovery`. To unauthenticate, you can simply call `fcl.unauthenticate()`. After you're authenticated, FCL sets an object called `fcl.currentUser` which exposes methods to watch for changes in user data, signing transactions, and more. Let's add in a few components and buttons for sign up, login, and to subscribe to changes on the `currentUser`. When the user updates (which happens after authentication), we'll set the user state in our component to reflect this. To demonstrate user authenticated sessions, we'll conditionally render a component based on if the user is or is not logged in. This is what your file should look like now: > **Replace file:** `./App.js` ```jsx ./App.js import { Text, View, Button } from 'react-native'; import './flow/config'; import { useState, useEffect } from 'react'; import * as fcl from '@onflow/fcl/dist/fcl-react-native'; export default function App() { const [user, setUser] = useState({ loggedIn: null }); useEffect(() => fcl.currentUser.subscribe(setUser), []); const AuthedState = () => { return ( Address: {user?.addr ?? 'No Address'} {

{JSON.stringify({ isPending, isError, txId, results })}

} ); } export default Page; ``` ## Step 5: Test Your application 1. Start your development server: ```bash npm run dev ``` 2. Connect your wallet with the RainbowKit `ConnectButton` - Make sure to use a Cadence-compatible wallet like Flow Wallet 3. Click "Send Batch Transaction" - You'll be prompted to approve the Cadence transaction. - This transaction will execute multiple EVM calls in a single atomic operation. 4. Observe the results - The Cadence transaction ID will be displayed. - The results of each EVM transaction will be shown. ## How It Works When you call `sendBatchTransaction`, the following happens: 1. A Cadence transaction is created that includes all your EVM calls. 2. The transaction is executed with FCL's `mutate` function. 3. The Cadence transaction calls each EVM transaction in sequence. 4. If any transaction fails and `mustPass` is true, the entire batch is rolled back. 5. The results of each EVM transaction are returned. This approach gives you several advantages: - **Atomic Operations**: All transactions succeed or fail together. - **Single Signature**: Users only need to sign one transaction. - **Gas Efficiency**: Reduced gas costs compared to separate transactions. - **Simplified UX**: Users don't need to approve multiple transactions. ## Conclusion You've successfully integrated Flow Cadence with your wagmi/rainbowkit application! This integration allows you to leverage the power of Cadence and maintain the familiar EVM development experience. ## Reference implementation For a complete reference implementation, check out the [FCL + RainbowKit + wagmi Integration Demo] repository. [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [npm]: https://www.npmjs.com/ [create an issue]: https://github.com/onflow/docs/issues/new/choose [Cadence]: https://cadence-lang.org [Solidity]: https://soliditylang.org/ [native VRF]: ../../blockchain-development-tutorials/native-vrf/vrf-in-solidity.md [structure and call EVM transactions]: ./batched-evm-transactions.md [FLIP 316]: https://github.com/onflow/flips/pull/317 [Flow Client Library (FCL)]: ../../build/tools/clients/fcl-js [wagmi]: https://wagmi.sh/ [viem]: https://viem.sh/ [RainbowKit]: https://www.rainbowkit.com/ [wallet]: ../../ecosystem/wallets.md [Discord]: https://discord.com/channels/613813861610684416/1162086721471647874 [FCL + RainbowKit + wagmi Integration Demo]: https://github.com/jribbink/cross-vm-app [FCL-JS]: https://github.com/onflow/fcl-js [Testnet Cadence Flowscan]: https://testnet.flowscan.io [Cadence Owned Accounts]: ../../build/cadence/basics/accounts.md [Testnet EVM Flowscan]: https://evm-testnet.flowscan.io [pre-built utility from the `@onflow/react-sdk`]: ../../build/tools/react-sdk#usecrossvmbatchtransaction --- # File: /md/blockchain-development-tutorials/cross-vm-apps/batched-evm-transactions.md --- title: Batched EVM Transactions Using Cadence sidebar_label: Batched EVM Transactions sidebar_position: 3 --- # Batched EVM transactions using Cadence Developers who integrate Cadence into EVM applications on Flow lets them leverage the best of both worlds. This guide demonstrates how to batch EVM transactions with Cadence, which applications to embed multiple EVM transactions in a single Cadence transaction while conditioning final execution on the success of all EVM transactions. This feature can unlock experiences otherwise impossible on traditional EVM platforms, which supercharges your EVM application. ## Objectives After you complete this guide, you'll be able to - Construct a Cadence transaction that executes several EVM transactions such that if any EVM transaction fails, the entire set will revert. - Read and write from smart contract functions on [EVM Flowscan]. - Run a Cadence transaction from the browser with [Flow Runner]. - Install conceptual understanding of Cadence X EVM interactions. - Inspect multiple EVM transactions embedded in a Cadence transaction with [Flowscan] block explorer. - Write code that interacts with the EVM via a CadenceOwnedAccount (COA). ## Prerequisites Before you dive in, make sure you have the following configured: - [MetaMask] installed in your browser with an active account. - [Flow Wallet extension] installed in your browser with an active account. - Both wallets funded with Testnet FLOW. See the [Faucet guide] for more information. ## Overview For the purposes of demonstration, this walkthrough will focus on relatively simple EVM operations in addition to first creating a [Cadence-controlled EVM account (COA)]. Specifically, we will: - Wrap FLOW as WFLOW. - Approve an ERC721 to transfer WFLOW in exchange for an NFT mint. - Mint an ERC721 token - this ERC721 has a 50% chance of failure (via [onchain VRF] to determine success). These operations let us focus on the **core concepts** of this guide: 1. **Batching EVM transactions** with Cadence. 2. **Conditioning execution** on the results of those EVM transactions. However, with these same principles, you'll have the power to address more complex use cases. For instance, replace wrapping FLOW with a DEX swap. Or instead of minting an ERC721, purchase an NFT listing from a marketplace. Combine these two, and suddenly you can purchase NFTs with any ERC20 token, all in a single Cadence transaction, and revert everything if a single step fails. The point is, while a simple use case, this guide will give you the tools to build much more complex and interesting applications. So let's get started! ## Components As mentioned in the [Overview], this guide involves three main actions: - Wrap FLOW as WFLOW. - Approve an ERC721 to transfer WFLOW in exchange for an NFT mint. - Mint an ERC721 token. Before we interact with these contracts, let's dig bit more into the components of this guide. ### Wrap FLOW as WFLOW On Flow EVM, FLOW is the native currency and similar to other EVM platforms, the native currency is not accessible as an ERC20 token. To interact with ERC20 contracts, you need to wrap FLOW as WFLOW (Wrapped FLOW). This is Flow's equivalent of WETH on Ethereum. :::tip You can find WFLOW deployed to `0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` on Flow [Testnet] & [Mainnet] and source code in the [`@onflow/flow-sol-utils` repository]. ::: ### Approve ERC721 Transfer Our example `MaybeMintERC721` contract accepts WFLOW in exchange for minting an NFT. However, the contract cannot move WFLOW without your permission. To allow the contract to move your WFLOW, you must approve the contract to transfer enough of your WFLOW to mint the NFT. ### Mint ERC721 token Finally, we'll mint an ERC721 token using the `MaybeMintERC721` contract. This contract has a 50% chance of failure, which simpluates a real-world scenario where an NFT purchase might fail - say a listing was purchased before your transaction was processed. Importantly, if this transaction fails, we want to revert the entire sequence of transactions. After all, you wrapped FLOW to WFLOW and approved the ERC721 transfer specifically to mint this NFT. If the mint fails, you want to unwind everything. As we'll see shortly, this is where batching EVM transactions with Cadence is extremely powerful. ## Interact with the contracts Before we take the easy route, let's first interact with the contracts individually to better understand the process and status quo user experience. Realistically, this is your only option for completing the whole process on other EVM platforms. :::tip Recall in [Prerequisites] that you need to have both [MetaMask] and [Flow Wallet extension] installed and funded with Testnet FLOW. Make sure you've done so before you continue. ::: ### Use MetaMask #### 1. Wrap FLOW Our first action will be to wrap enough FLOW to cover the cost of minting the `MaybeMintERC721` token. To do this, we'll interact with the `WFLOW` contract on Testnet. There are a number of ways we could interact with this contract - Remix IDE, Foundry's CLI, Hardhat, and so on. For the purposes of this guide, we'll use the [Flowscan EVM block explorer]. Navigate to the WFLOW Testnet contract on Flowscan: [WFLOW]. Make sure you're on the `Write Contract` tab, which allows you to interact with the contract's mutating functions. Before you can interact with the contract, you need to connect your MetaMask wallet to the [Flowscan EVM block explorer]. Click `Connect` in the top right corner and follow the prompts to connect your MetaMask wallet. :::warning There are two **separate** block explorers for Flow - one for Cadence activity and another for EVM activity. This is unique to Flow and is due to the fact that Cadence and EVM are separate runtimes, with EVM effectively emulated within Cadence. This orientation - that of EVM running within Cadence - means that the Cadence-side explorer has visibility to EVM transactions embedded within a Cadence transaction. Practically, this means that you can view any transactions run with a Flow native account on the Cadence explorer, while you can view any transactions run with an EVM account on the EVM explorer. ::: ![Connect wallet to Flowscan](./imgs/flowscan-connect.png) After you connect, you will see your address in the top right corner and above the contract's functions. Now we can wrap FLOW. Click the `deposit` method which will drop down an input field for the amount of FLOW you want to wrap. The mint amount for the `MaybeMintERC721` contract is one whole FLOW which in EVM terms is `1e18 wei` - `wei` is the smallest unit of an EVM's native currency (inherited from Ethereum's units - more on Ether units [here]). As shown below, put `1 000 000 000 000 000 000` in the input field for `deposit`. ![Deposit 1 FLOW to WFLOW contract](./imgs/wflow-deposit.png) You can now click `Write` to submit the transaction. When MetaMask prompts you to sign the transaction, click `Confirm` and give it a few seconds to process.
![Confirm WFLOW deposit in MetaMask](./imgs/wflow-deposit-confirm.png)
When confirmed, you can see WFLOW balance in your tokens list in MetaMask - if not, click `Import Tokens` and paste the WFLOW contract address found on the Flowscan page and refresh your list.
![WFLOW in MetaMask](./imgs/wflow-in-metamask-tokens.png)
#### 2. Approve WFLOW transfer Now that you have your WFLOW, you'll need to approve the `MaybeMintERC721` contract to transfer your WFLOW. From the same WFLOW page in Flowscan, click the `approve` method. This time, you'll need to input the `MaybeMintERC721` contract address - `0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2` - and the amount of WFLOW you want to approve - again `1 000 000 000 000 000 000` WFLOW. ![Approve MaybeMintERC721 for 1 WFLOW in Flowscan](./imgs/wflow-approve.png) Click `Write` to submit the transaction. To be clear, this does not complete a transfer, but allows the `MaybeMintERC721` contract to transfer your WFLOW on your behalf which will execute in the next step. #### 3. Mint ERC721 token Finally, we'll attempt to mint the ERC721 token with the `MaybeMintERC721` contract. Navigate to the `MaybeMintERC721` contract on Flowscan: [MaybeMintERC721]. Again, you'll see the contract functions on the `Write Contract` tab. Click the `mint` function which takes no arguments - just click `Write` and then `Confirm` in the MetaMask window. This contract has a 50% chance of failue on mint with onchain randomness. If it fails, simply mint again until it succeeds. When it succeeds, you can click your NFTs in MetaMask to see your newly minted token.
![MaybeMintERC721 in MetaMask NFT list](./imgs/maybe-mint-in-metamask.png)
#### Recap This process is cumbersome and requires multiple transactions, each of which could fail. Given the intent of the process - to mint an NFT - if this were a case where the NFT was a limited edition or time-sensitive, you'd be left with WFLOW wrapped and approved for transfer, but no NFT and would need to manually unwind the process. Or you could just use Cadence to batch these transactions and revert everything if the mint fails. Let's do that. ### Use Flow Wallet Before we dive into the "how", let's execute the batched version of everything we just did with Flow Wallet. This will give you a sense of Cadence's power and the Flow blockchain. The transaction below, like all Cadence transactions, is scripted, which allows us to execute a series of actions. It may look like a lot at first, but we will break it down step by step in the following sections.
wrap_and_mint.cdc ```cadence // TESTNET IMPORTS import FungibleToken from 0x9a0766d93b6608b7 import FlowToken from 0x7e60df042a9c0868 import EVM from 0x8c5303eaa26202d6 /// This transaction demonstrates how multiple EVM calls can be batched in a single Cadence transaction via /// CadenceOwnedAccount (COA), performing the following actions: /// /// 1. Configures a COA in the signer's account if needed /// 2. Funds the signer's COA with enough FLOW to cover the WFLOW cost of minting an ERC721 token /// 3. Wraps FLOW as WFLOW - EVM call 1 /// 4. Approves the example MaybeMintERC721 contract which accepts WFLOW to move the mint amount - EVM call 2 /// 5. Attempts to mint an ERC721 token - EVM call 3 /// /// Importantly, the transaction is reverted if any of the EVM interactions fail returning the account to the original /// state before the transaction was executed across Cadence & EVM. /// /// For more context, see https://github.com/onflow/batched-evm-exec-example /// /// @param wflowAddressHex: The EVM address hex of the WFLOW contract as a String /// @param maybeMintERC721AddressHex: The EVM address hex of the ERC721 contract as a String /// transaction(wflowAddressHex: String, maybeMintERC721AddressHex: String) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount let mintCost: UFix64 let wflowAddress: EVM.EVMAddress let erc721Address: EVM.EVMAddress prepare(signer: auth(SaveValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, UnpublishCapability) &Account) { /* COA configuration & assigment */ // let storagePath = /storage/evm let publicPath = /public/evm // Configure a COA if one is not found in storage at the default path if signer.storage.type(at: storagePath) == nil { // Create & save the CadenceOwnedAccount (COA) Resource let newCOA <- EVM.createCadenceOwnedAccount() signer.storage.save(<-newCOA, to: storagePath) // Unpublish any existing Capability at the public path if it exists signer.capabilities.unpublish(publicPath) // Issue & publish the public, unentitled COA Capability let coaCapability = signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath) signer.capabilities.publish(coaCapability, at: publicPath) } // Assign the COA reference to the transaction's coa field self.coa = signer.storage.borrow(from: storagePath) ?? panic("A CadenceOwnedAccount (COA) Resource could not be found at path ".concat(storagePath.toString()) .concat(" - ensure the COA Resource is created and saved at this path to enable EVM interactions")) /* Fund COA with cost of mint */ // // Borrow authorized reference to signer's FlowToken Vault let sourceVault = signer.storage.borrow( from: /storage/flowTokenVault ) ?? panic("The signer does not store a FlowToken Vault object at the path " .concat("/storage/flowTokenVault. ") .concat("The signer must initialize their account with this vault first!")) // Withdraw from the signer's FlowToken Vault self.mintCost = 1.0 let fundingVault <- sourceVault.withdraw(amount: self.mintCost) as! @FlowToken.Vault // Deposit the mint cost into the COA self.coa.deposit(from: <-fundingVault) /* Set the WFLOW contract address */ // // View the cannonical WFLOW contract at: // https://evm-testnet.flowscan.io/address/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e self.wflowAddress = EVM.addressFromString(wflowAddressHex) /* Assign the ERC721 EVM Address */ // // Deserialize the provided ERC721 hex string to an EVM address self.erc721Address = EVM.addressFromString(maybeMintERC721AddressHex) } pre { self.coa.balance().inFLOW() >= self.mintCost: "CadenceOwnedAccount holds insufficient FLOW balance to mint - " .concat("Ensure COA has at least ".concat(self.mintCost.toString()).concat(" FLOW")) } execute { /* Wrap FLOW in EVM as WFLOW */ // // Encode calldata & set value let depositCalldata = EVM.encodeABIWithSignature("deposit()", []) let value = EVM.Balance(attoflow: 0) value.setFLOW(flow: self.mintCost) // Call the WFLOW contract, wrapping the sent FLOW let wrapResult = self.coa.call( to: self.wflowAddress, data: depositCalldata, gasLimit: 15_000_000, value: value ) assert( wrapResult.status == EVM.Status.successful, message: "Wrapping FLOW as WFLOW failed: ".concat(wrapResult.errorMessage) ) /* Approve the ERC721 address for the mint amount */ // // Encode calldata approve(address,uint) calldata, providing the ERC721 address & mint amount let approveCalldata = EVM.encodeABIWithSignature( "approve(address,uint256)", [self.erc721Address, UInt256(1_000_000_000_000_000_000)] ) // Call the WFLOW contract, approving the ERC721 address to move the mint amount let approveResult = self.coa.call( to: self.wflowAddress, data: approveCalldata, gasLimit: 15_000_000, value: EVM.Balance(attoflow: 0) ) assert( approveResult.status == EVM.Status.successful, message: "Approving ERC721 address on WFLOW contract failed: ".concat(approveResult.errorMessage) ) /* Attempt to mint ERC721 */ // // Encode the mint() calldata let mintCalldata = EVM.encodeABIWithSignature("mint()", []) // Call the ERC721 contract, attempting to mint let mintResult = self.coa.call( to: self.erc721Address, data: mintCalldata, gasLimit: 15_000_000, value: EVM.Balance(attoflow: 0) ) // If mint fails, all other actions in this transaction are reverted assert( mintResult.status == EVM.Status.successful, message: "Minting ERC721 token failed: ".concat(mintResult.errorMessage) ) } } ```
You can run the transaction at the following link with the community-developed Flow Runner tool: [`wrap_and_mint.cdc`]. This transaction takes two arguments: - WFLOW contract address: `0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` - MaybeMintERC721 contract address: `0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2` Before you run the tool, ensure that the network section - bottom right corner - displays testnet. If not, click and select `Testnet` as your network and refresh. After you've confirmed your Flow Runner targets testnet, copy these addresses and paste them into the respective fields on the Flow Runner page. Click `Run` on the top left and follow the prompts to connect your Flow Wallet and sign the transaction. :::warning Although we ran a manual transaction for the purposes of this walkthrough, always be careful to review the transaction details before you sign and submit. ::: Again, since the ERC721 has a 50% chance of failure, you may need to run the transaction multiple times until it succeeds. However, note that if the mint fails, the entire transaction will revert, unwinding the wrapped FLOW and approval. Again, since the ERC721 has a 50% chance of failure and the success of the transaction is conditioned on a successful mint, your transaction may fail. If it does fail, importantly the entire transaction reverts, which unwinds the wrapped FLOW deposit and approval - the wrap and approval transactions **do not execute** in the event of mint failure! This is the main takeaway of this guide, that you embed a whole sequence of EVM transactions into one atomic operation with Cadence and if the primary intent (or intents) does not execute, everything else is reverted as well. In our case, you'll want to submit a transaction until one succeeds. After you submit a successful transaction, you'll see a transaction ID with event logs in the Flow Runner output. Let's take a closer look at the transaction and its results in the Flowscan block explorer. ![Flow Runner output on successful transaction execution](./imgs/flow-runner-successful-output.png) Copy your transaction ID and go to the Flowscan Testnet Cadence block explorer: [Flowscan Cadence]. When you paste your transaction ID into the search bar, you'll see the transaction details, such as the Cadence script, execution status, and event logs. Click the `EVM` tab to view the EVM transactions batched in the Cadence transaction. ![Embedded EVM transactions on Flowscan](./imgs/evm-embed-flowscan.png) Click the transactions to open up the EVM transaction in Flowscan's EVM block explorer. If you view the EVM transactions in order, you'll notice that they aggregate the same actions we took manually in the MetaMask section, but this time in a single Cadence transaction! ## Breaking it down Now that we can relate to the pain of manually executing these transactions and we've seen the magic you can work with Cadence, let's understand what's going on under the hood. To recap, our Cadence transaction does the following, reverting if any step fails: 1. Wraps FLOW as WFLOW. 2. Approves the `MaybeMintERC721` contract to move WFLOW. 3. Attempts to mint a `MaybeMintERC721` token. But how does our Flow account interact with EVM from the Cadence runtime? As you'll recall from the [Interacting with COA](./interacting-with-coa.md) guide, we use a Cadence-owned account (COA) to interact with EVM contracts from Cadence. A COA is a [resource] that provides an interface through which Cadence can interact with the EVM runtime. This is **_in addition to_** to the traditional routes you'd normally access normal EVMs - such as via the JSON-RPC API. And with this interface, we can take advantage of all of the benefits of Cadence - namely here scripted transactions and conditional execution. So, in addition to the above steps, our transaction first configures a COA in the signer's account if one doesn't already exist. It then funds the COA with enough FLOW to cover the mint cost, and sources the funds from the signing Flow account's Cadence Vault. Finally, it wraps FLOW as WFLOW, approves the ERC721 contract to move the mint amount, and attempts to mint the ERC721 token. Let's see what each step looks like in the transaction code. ### COA Configuration The first step in our transaction is to configure a COA in the signer's account if one doesn't already exist. To do this, create a new COA resource and save it to the signer account's storage. A public Capability on the COA is then issued and published on the signer's account, which allows anyone to deposit FLOW into the COA and affects its EVM balance. ```cadence /* COA configuration & assignment */ // let storagePath = /storage/evm let publicPath = /public/evm // Configure a COA if one is not found in storage at the default path if signer.storage.type(at: storagePath) == nil { // Create & save the CadenceOwnedAccount (COA) Resource let newCOA <- EVM.createCadenceOwnedAccount() signer.storage.save(<-newCOA, to: storagePath) // Unpublish any existing Capability at the public path if it exists signer.capabilities.unpublish(publicPath) // Issue & publish the public, unentitled COA Capability let coaCapability = signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath) signer.capabilities.publish(coaCapability, at: publicPath) } // Assign the COA reference to the transaction's coa field self.coa = signer.storage.borrow(from: storagePath) ?? panic("A CadenceOwnedAccount (COA) Resource could not be found at path ".concat(storagePath.toString()) .concat(" - ensure the COA Resource is created and saved at this path to enable EVM interactions")) ``` At the end of this section, the transaction now has an reference authorized with the `EVM.Call` [entitlement] to use in the `execute` block which can be used call into EVM. You can run a transaction that does just this step here: [`setup_coa.cdc`] Since you ran the all-in-one transaction previously, your account already has a COA configured in which case the linked transaction won't do anything. You can lookup your Testnet account's EVM address with the script below to confirm you have a COA configured. Simply input your Testnet Flow address and click `Run`. ### Fund the COA Next, we fund the COA with enough FLOW to cover the mint cost. To do this, withdraw FLOW from the signer's FlowToken Vault and deposit it into the COA. ```cadence /* Fund COA with cost of mint */ // // Borrow authorized reference to signer's FlowToken Vault let sourceVault = signer.storage.borrow( from: /storage/flowTokenVault ) ?? panic("The signer does not store a FlowToken Vault object at the path " .concat("/storage/flowTokenVault. ") .concat("The signer must initialize their account with this vault first!")) // Withdraw from the signer's FlowToken Vault self.mintCost = 1.0 let fundingVault <- sourceVault.withdraw(amount: self.mintCost) as! @FlowToken.Vault // Deposit the mint cost into the COA self.coa.deposit(from: <-fundingVault) ``` When we take a look at the full transaction, we can see an explicit check that the COA has enough FLOW to cover the mint cost before it proceedes into the transaction's `execute` block. ```cadence pre { self.coa.balance().inFLOW() >= self.mintCost: "CadenceOwnedAccount holds insufficient FLOW balance to mint - " .concat("Ensure COA has at least ".concat(self.mintCost.toString()).concat(" FLOW")) } ``` This isn't absolutely necessary as successive steps would fail on this condition, but helps provide enhanced error messages in the event of insufficient funds. You can run the above block in a transaction here which will move one FLOW from your account's Cadence FLOW balance to your account's EVM balance and deposit it directly to your pre-configured COA: [`fund_coa.cdc`] After you run the linked transaction, you can check your COA's FLOW balance with the script below, just enter your COA's EVM address (which you can get from the previous script). The balance should be 1.0 (unless you've funded your COA prior to this walkthrough). ### Setting our EVM contract argets The last step in our transaction's `prepare` block is to deserialize the provided WFLOW and ERC721 contract addresses from hex strings to EVM addresses. ```cadence /* Set the WFLOW contract address */ // // View the cannonical WFLOW contract at: // https://evm-testnet.flowscan.io/address/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e self.wflowAddress = EVM.addressFromString(wflowAddressHex) /* Assign the ERC721 EVM Address */ // // Deserialize the provided ERC721 hex string to an EVM address self.erc721Address = EVM.addressFromString(maybeMintERC721AddressHex) ``` ### Wrap FLOW as WFLOW Next, we're on to the first EVM interaction - to wrap FLOW as WFLOW. To do this, encode the `deposit()` function call and set the call value to the mint cost. The COA then calls the WFLOW contract with the encoded calldata, gas limit, and value. ```cadence /* Wrap FLOW in EVM as WFLOW */ // // Encode calldata & set value let depositCalldata = EVM.encodeABIWithSignature("deposit()", []) let value = EVM.Balance(attoflow: 0) value.setFLOW(flow: self.mintCost) // Call the WFLOW contract, wrapping the sent FLOW let wrapResult = self.coa.call( to: self.wflowAddress, data: depositCalldata, gasLimit: 15_000_000, value: value ) assert( wrapResult.status == EVM.Status.successful, message: "Wrapping FLOW as WFLOW failed: ".concat(wrapResult.errorMessage) ) ``` When you set the value of the call, it transmits FLOW along with the call to the contract, accessible in solidity as `msg.value`. :::tip You'll notice a general pattern among all EVM calls in this transaction: 1. Encodes the calldata. 2. Calls the contract. 3. Asserts the call was successful. Here we're just interested in a successful call, but we could access return data if it were expected and relevant for our Cadence transaction. This returned data is accessible from the `data` field on the `EVM.Result` object returned from `coa.call(...)`. This data would then be decoded with `EVM.decodeABI(...)`. More on this in later guides. ::: You can run the above code as a transaction here: [`wrap_flow.cdc`] After you run the transaction, your COA should have a WFLOW balance of 1.0 WFLOW. To confirm your WFLOW balance, run the script below, and provide your Flow account address, the WFLOW address of `0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` and your COA's EVM address (retrieved from a previous script): Since Solidity does not support decimal precision, the returned balance will look like a large number. In the case of WFLOW, to recover the decimals, shift the decimal place 18 digits to the left. Your account should have `1` WFLOW or `1000000000000000000` as returned. :::warning The number of places to shift varies by ERC20 implementation -- the default value is 18, but it's not safe to assume this value. To check a token's decimal places, call `ERC20.decimals()(uint8)`. ::: ### Approve the ERC721 contract After the FLOW is wrapped as WFLOW, we approve the ERC721 contract to move the mint amount. To do this, encode the `approve(address,uint256)` calldata and call the WFLOW contract with the encoded calldata. ```cadence /* Approve the ERC721 address for the mint amount */ // // Encode calldata approve(address,uint) calldata, providing the ERC721 address & mint amount let approveCalldata = EVM.encodeABIWithSignature( "approve(address,uint256)", [self.erc721Address, UInt256(1_000_000_000_000_000_000)] ) // Call the WFLOW contract, approving the ERC721 address to move the mint amount let approveResult = self.coa.call( to: self.wflowAddress, data: approveCalldata, gasLimit: 15_000_000, value: EVM.Balance(attoflow: 0) ) assert( approveResult.status == EVM.Status.successful, message: "Approving ERC721 address on WFLOW contract failed: ".concat(approveResult.errorMessage) ) ``` You can run this approval with the transaction. Pass the WFLOW address of `0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` and MaybeMintERC721 address of `0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2` : [`approve_maybe_mint_erc721.cdc`] The linked transaction will perform the approval step, which authorizes the ERC721 to transfer WFLOW to cover the mint cost when `mint()` is called. To confirm the contract allowance, run the script below. Pass your Flow address, WFLOW address, ERC721 address, and your COA's EVM address. The result is the amount of your WFLOW balance the ERC721 is allowed to transfer, which after the transaction should be `1` WFLOW, or `1000000000000000000` as returned. ### Mint the ERC721 token Finally, we attempt to mint the ERC721 token. To do this, encode the `mint()` calldata and call the ERC721 contract with the encoded calldata. If the mint fails, the entire transaction is reverted. ```cadence /* Attempt to mint ERC721 */ // // Encode the mint() calldata let mintCalldata = EVM.encodeABIWithSignature("mint()", []) // Call the ERC721 contract, attempting to mint let mintResult = self.coa.call( to: self.erc721Address, data: mintCalldata, gasLimit: 15_000_000, value: EVM.Balance(attoflow: 0) ) // If mint fails, all other actions in this transaction are reverted assert( mintResult.status == EVM.Status.successful, message: "Minting ERC721 token failed: ".concat(mintResult.errorMessage) ) ``` You can run the minting transaction here. Pass the ERC721 address of `0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2`: [`mint.cdc`] Again, this transaction may fail. But if you executed all the prior stepwise transactions according to the walkthrough, you can try again until the mint succeeds. Recall that you can view your transaction details with Cadence [Flowscan] which will also let you view the embedded EVM transactions in the `EVM` tab. Try it out, and see if you can figure out how to get your minted NFT's URI with the script below. ### Recap All of the stepwise transactions you just executed are compiled in the first Cadence transaction we ran. Hopefully, going through the process step by step illuminates the power and flexibility of Cadence and allows you to write transactions as simple or as complex as you want. While lengthy transactions can be intimidating and even a bit verbose at times, the flexibility afforded by the language means you are only limited by your imagination. Cadence transactions allow you to support the most streamlined of experiences and incorporate as many contracts as needed to support your use case. ## Conclusion In this guide, we've demonstrated how to batch EVM transactions using Cadence, which allows you to conditionally execute multiple EVM transactions in a single Cadence transaction. While this guide focused on relatively simple EVM operations, the principles can be applied to much more complex and interesting applications. In the process, you learned how to: - Read and write from smart contract functions on EVM Flowscan. - Run a Cadence transaction from the browser with [Flow Runner]. - Execute batched EVM transactions via a COA in a Cadence transaction. - Condition final transaction execution on success of all EVM transactions. - Inspect multiple EVM transactions embedded in a Cadence transaction with [Flowscan] block explorer. The biggest takeaway here isn't the specific actions taken in this walkthrough, but the overarching concept that you can use **Cadence as an orchestration layer** to **extend existing EVM contracts**, which allows you to create unique user experiences with the power **to differentiate your Web3 application**. With these basics in hand, you're ready to build more complex applications that leverage the power of Cadence and the Flow blockchain. How will you use these features to build Web3's next killer app? ## Further reading Now that you've experienced the power of Cadence and EVM interactions firsthand, we recommend you check out the following guides to deepen your understanding: - [How Flow EVM Works] - Learn more about the Flow EVM and how it differs from traditional EVM platforms. - [Interacting with COAs] - Get a fuller picture of how Cadence interacts with EVM contracts via Cadence-owned accounts. - [Cadence Transactions] - Learn more about the Cadence transaction model. Ready to level up your Cadence skills? Take a look at [these Cadence tutorials]. [EVM Flowscan]: https://evm.flowscan.io/ [Flow Runner]: https://run.dnz.dev/ [Flowscan]: https://www.flowscan.io/ [MetaMask]: https://metamask.io/download/ [Flow Wallet extension]: https://wallet.flow.com/download [Faucet guide]: ../../ecosystem/faucets.md [Cadence-controlled EVM account (COA)]: ./interacting-with-coa.md [onchain VRF]: ../native-vrf/vrf-in-solidity.md [Overview]: #overview [Testnet]: https://evm-testnet.flowscan.io/token/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e?tab=contract [Mainnet]: https://evm.flowscan.io/token/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e?tab=contract [`@onflow/flow-sol-utils` repository]: https://github.com/onflow/flow-sol-utils [Prerequisites]: #prerequisites [Flowscan EVM block explorer]: https://www.evm-testnet.flowscan.io/ [WFLOW]: https://evm-testnet.flowscan.io/token/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e?tab=write_contract [here]: https://docs.soliditylang.org/en/v0.8.28/units-and-global-variables.html#ether-units [MaybeMintERC721]: https://evm-testnet.flowscan.io/address/0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2?tab=write_contract [`wrap_and_mint.cdc`]: https://run.dnz.dev/snippet/c99b25e04a2d1f28 [Flowscan Cadence]: https://testnet.flowscan.io/ [resource]: https://cadence-lang.org/docs/solidity-to-cadence#resources [entitlement]: https://cadence-lang.org/docs/language/access-control#entitlements [How Flow EVM Works]: ../../build/evm/how-it-works.md [Interacting with COAs]: ./interacting-with-coa.md [Cadence Transactions]: ../../build/cadence/basics/transactions.md [these Cadence tutorials]: https://cadence-lang.org/docs/tutorial/first-steps [`setup_coa.cdc`]: https://run.dnz.dev/snippet/4ec75e1f4165fa05 [`fund_coa.cdc`]: https://run.dnz.dev/snippet/0e7370601bd9123b [`wrap_flow.cdc`]: https://run.dnz.dev/snippet/9dbfb784da5300fb [`approve_maybe_mint_erc721.cdc`]: https://run.dnz.dev/snippet/1b503d82f9a2c5a7 [`mint.cdc`]: https://run.dnz.dev/snippet/fd7c4dda536d006e --- # File: /md/blockchain-development-tutorials/cross-vm-apps/direct-calls.md --- title: Direct Calls from Cadence to Flow EVM sidebar_label: Direct Calls to Flow EVM sidebar_position: 4 --- # Direct Calls from Cadence to Flow EVM Direct calls from Cadence to Flow EVM are essential to allow Cadence smart contracts to interact seamlessly with the EVM environment hosted on the Flow blockchain. These calls facilitate a range of functionalities including state queries and transaction initiations, allowing Cadence contracts to leverage EVM-based tools and assets. ## Make direct calls ### Access Flow EVM To interact with Flow EVM, Cadence contracts must first import `EVM` from its service address: ```js import EVM from ``` Next, create an `EVMAddress` with a sequence of 20 bytes that represents the EVM address: ```js let addr = EVM.EVMAddress(bytes: bytes) ``` After you can access an `EVMAddress`, you can query various pieces of state information such as: - `balance() EVM.Balance` provides the balance of the address. It returns a balance object rather than a basic type to avoid errors when it converts from flow to atto-flow. - `nonce() UInt64` retrieves the nonce associated with the address. - `code(): [UInt8]` fetches the code at the address; it returns the smart contract code if applicable, and is empty otherwise. ```cadence import EVM from access(all) fun main(bytes: [UInt8; 20]): EVM.Balance { let addr = EVM.EVMAddress(bytes: bytes) let bal = addr.balance() return bal } ``` Alternatively, you can use the EVM contract's native deserialization to access the balance provided a hex string representing the address: ```cadence import EVM from access(all) fun main(addressHex: String): UFix64 { let addr = EVM.addressFromString(addressHex) return addr.balance().inFLOW() } ``` ### Send transactions to Flow EVM To send transactions to Flow EVM, use the `run` function which executes RLP-encoded transactions. RLP (Recursive Length Prefix) encoding is used to efficiently encode data into a byte-array format, suitable for Ethereum-based environments. Here's an example of how to wrap and send a transaction: ```cadence import EVM from transaction(rlpEncodedTransaction: [UInt8], coinbaseBytes: [UInt8; 20]) { prepare(signer: &Account) { let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) let result = EVM.run(tx: rlpEncodedTransaction, coinbase: coinbase) assert( runResult.status == EVM.Status.successful, message: "tx was not executed successfully." ) } } ``` When you `run`, it restricts an EVM block to a single EVM transaction, while a future `batchRun` will offer the capability to execute multiple EVM transactions in a batch. ### Handle transaction responses It's crucial that your function handles responses correctly to manage the state changes or errors that occur during `EVM` transactions: When you call `EVM.run`, it's important to understand that this method does not revert the outer Flow transaction. Developers must therefore carefully handle the response based on the `result.Status` of the EVM transaction execution. There are three main outcomes to consider: - `Status.invalid`: This status indicates that the transaction or call failed at the validation step, such as due to a nonce mismatch. Transactions with this status are not executed or included in a block, which means no state change occurs. - `Status.failed`: This status is assigned when the transaction has technically succeeded in terms of being processable, but the EVM reports an error as the outcome, such as running out of gas. Importantly, a failed transaction or call is still included in a block. Any attempt to resubmit a failed transaction results in an `invalid` status on the second try due to a now incorrect nonce. - `Status.successful`: This status appears when the transaction or call is successfully executed and the EVM doesn't report errors. For scenarios where transaction validity is critical, developers may choose to use the `mustRun` variation, which reverts the transaction in the case of a validation failure. This provides an added layer of error handling. ### Understanding gas usage in EVM transactions Direct calls to Flow EVM require gas. It's important to understand how gas usage is calculated and billed. During the execution of methods that interact with the EVM: - **Gas Aggregation**: The gas that each call uses is aggregated throughout the transaction. - **Gas Adjustment**: The total gas used is then adjusted based on a multiplier. This multiplier is determined by the network and the service account can adjust it to reflect operational costs and network conditions. - **Payment of Gas Fees**: The adjusted total gas amount is added to the overall computation fees of the Flow transaction. The transaction initiator, commonly referred to as the payer, pays these fees. ## Keep learning For more information and a deeper dive into the `EVMAddress`, `Result`, and `Status` objects, see [the contract here](https://github.com/onflow/flow-go/blob/master/fvm/evm/stdlib/contract.cdc). --- # File: /md/blockchain-development-tutorials/cross-vm-apps/interacting-with-coa.md --- title: Interacting with COAs from Cadence sidebar_label: Interacting with COAs sidebar_position: 5 --- # Interacting with COAs from Cadence [Cadence Owned Accounts (COAs)] are EVM accounts that a Cadence resouce owns, and are used to interact with Flow EVM from Cadence. COAs expose two interfaces for interaction: one on the Cadence side and one on the EVM side. In this guide, we focuses on how to interact with COAs with Cadence. In this guide, we will walk through some basic examples that create and and interact with a COA in Cadence. Your specific usage of the COA resource will depend on your own application's requirements (for example, the COA resource may not live directly in `/storage/evm` as in these examples, but may instead be a part of a more complex resource structure). ## COA Interface To begin, we can take a look at a simplified version of the `EVM` contract, and highlight parts specific to COAs. You can learn more about the `EVM` contract [here] and find the full contract code on [GitHub]. ```cadence EVM.cdc access(all) contract EVM { //... access(all) resource CadenceOwnedAccount: Addressable { /// The EVM address of the cadence owned account /// -> could be used to query balance, code, nonce, etc. access(all) view fun address(): EVM.EVMAddress /// Get balance of the cadence owned account /// This balance access(all) view fun balance(): EVM.Balance /// Deposits the given vault into the cadence owned account's balance access(all) fun deposit(from: @FlowToken.Vault) /// The EVM address of the cadence owned account behind an entitlement, acting as proof of access access(EVM.Owner | EVM.Validate) view fun protectedAddress(): EVM.EVMAddress /// Withdraws the balance from the cadence owned account's balance /// Note that amounts smaller than 10nF (10e-8) can't be withdrawn /// given that Flow Token Vaults use UFix64s to store balances. /// If the given balance conversion to UFix64 results in /// rounding error, this function would fail. access(EVM.Owner | EVM.Withdraw) fun withdraw(balance: EVM.Balance): @FlowToken.Vault /// Deploys a contract to the EVM environment. /// Returns the address of the newly deployed contract access(EVM.Owner | EVM.Deploy) fun deploy( code: [UInt8], gasLimit: UInt64, value: Balance ): EVM.EVMAddress /// Calls a function with the given data. /// The execution is limited by the given amount of gas access(EVM.Owner | EVM.Call) fun call( to: EVMAddress, data: [UInt8], gasLimit: UInt64, value: Balance ): EVM.Result } // Create a new CadenceOwnedAccount resource access(all) fun createCadenceOwnedAccount(): @EVM.CadenceOwnedAccount // ... } ``` ## Import the EVM contract The `CadenceOwnedAccount` resource is a part of the `EVM` system contract, so to use any of these functions, you will need to import the `EVM` contract into your Cadence code. To import the `EVM` contract into your Cadence code with the simple import syntax, you can use the following format (learn more about how to configure contracts in `flow.json` [here]): ```cadence // This assumes you are working in the in the Flow CLI, FCL, or another tool that supports this syntax // The contract address should be configured in your project's `flow.json` file import "EVM" // ... ``` However, if you wish to use manual address imports instead, you can use the following format: ```cadence // Must use the correct address based on the network you are interacting with import EVM from 0x1234 // ... ``` To find the deployment addresses of the `EVM` contract, you can refer to the [EVM contract documentation]. ## Create a COA To create a COA, we can use the `createCadenceOwnedAccount` function from the `EVM` contract. This function takes no arguments and returns a new `CadenceOwnedAccount` resource which represents this newly-created EVM account. For example, we can create this COA in a transaction, save it to the user's storage, and publish a public capability to its reference: ```cadence create_coa.cdc import "EVM" // Note that this is a simplified example & will not handle cases where the COA already exists transaction() { prepare(signer: auth(SaveValue, IssueStorageCapabilityController, PublishCapability) &Account) { let storagePath = /storage/evm let publicPath = /public/evm // Create account & save to storage let coa: @EVM.CadenceOwnedAccount <- EVM.createCadenceOwnedAccount() signer.storage.save(<-coa, to: storagePath) // Publish a public capability to the COA let cap = signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath) signer.capabilities.publish(cap, at: publicPath) } } ``` ### Create a Cadence account and COA together It is possible to create a new Cadence account and COA within the same transaction. Another account will need to sign and pay for this transaction, but any account will do. A common process is to set up a backend service to handle this function. :::info During the singular transaction in which an account is created, the `AuthAccount` object for the newly-created account is present. As a result, the creating account can access and modify the new account's storage **only** during this transaction. ::: First, you'll need to use the CLI to [generate keys] for the new account. Then, run the following transaction to create the Cadence Account and COA at one time. :::warning This is a very minimal example. You may wish to set up vaults and perform other actions during account creation. ::: ```cadence import Crypto transaction(publicKeys: [Crypto.KeyListEntry]) { prepare(signer: auth(BorrowValue) &Account) { let newAccount = Account(payer: signer) for key in publicKeys { newAccount.keys.add(publicKey: key.publicKey, hashAlgorithm: key.hashAlgorithm, weight: key.weight) } let coa <- EVM.createCadenceOwnedAccount() let coaPath = /storage/evm newAccount.storage.save(<-coa, to: coaPath) let coaCapability = newAccount.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(coaPath) newAccount.capabilities.publish(coaCapability, at: /public/evm) } } ``` ## Retrieve the EVM Address of a COA To get the EVM address of a COA, you can use the `address` function from the `EVM` contract. This function returns the EVM address of the COA as an `EVM.Address` struct. This struct is used to represent addresses within Flow EVM and you can also use it to query the balance, code, nonce, and so on of an account. For our example, we could query the address of the COA we just created with the following script: ```cadence get_coa_address.cdc import "EVM" access(all) fun main(address: Address): EVM.EVMAddress { // Get the desired Flow account holding the COA in storage let account = getAuthAccount(address) // Borrow a reference to the COA from the storage location we saved it to let coa = account.storage.borrow<&EVM.CadenceOwnedAccount>( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) // Return the EVM address of the COA return coa.address() } ``` If you'd prefer the hex representation of the address, you instead return with the `EVMAddress.toString()` function: ```cadence return coa.address().toString() ``` The above will return the EVM address as a string; however, Cadence does not prefix hex strings with `0x`. ## Retrieve the Flow balance of a COA Like any other Flow EVM or Cadence account, COAs possess a balance of FLOW tokens. To get the current balance of our COA, we can use the COA's `balance` function. It will return a `EVM.Balance` struct for the account - these are used to represent balances within Flow EVM. This script will query the current balance of our newly created COA: ```cadence get_coa_balance.cdc import "EVM" access(all) fun main(address: Address): EVM.Balance { // Get the desired Flow account holding the COA in storage let account = getAuthAccount(address) // Borrow a reference to the COA from the storage location we saved it to let coa = account.storage.borrow<&EVM.CadenceOwnedAccount>( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) // Get the current balance of this COA return coa.balance() } ``` You can also easily get the `UFix64` FLOW balance of any EVM address with this script: ```cadence get_coa_balance_as_ufix64.cdc import "EVM" access(all) fun main(addressHex: String): UFix64 { let addr = EVM.addressFromString(addressHex) return addr.balance().inFLOW() } ``` The above script is helpful if you already know the COA address and can provide the hex representation directly. ## Deposit and withdraw Flow tokens You can seamlessly transfer tokens between the Flow EVM and Cadence environment with the `deposit` and `withdraw` functions that the COA resource provides. Anybody with a valid reference to a COA may deposit Flow tokens into a it, however only someone with the `Owner` or `Withdraw` entitlements can withdraw tokens. ### Deposit Flow tokens The `deposit` function takes a `FlowToken.Vault` resource as an argument, which represents the tokens to deposit. It will transfer the tokens from the vault into the COA's balance. This transaction will withdraw Flow tokens from a user's Cadence vault and deposit them into their COA: ```cadence deposit_to_coa.cdc import "EVM" import "FungibleToken" import "FlowToken" transaction(amount: UFix64) { let coa: &EVM.CadenceOwnedAccount let sentVault: @FlowToken.Vault prepare(signer: auth(BorrowValue) &Account) { // Borrow the public capability to the COA from the desired account // This script could be modified to deposit into any account with a `EVM.CadenceOwnedAccount` capability self.coa = signer.capabilities.borrow<&EVM.CadenceOwnedAccount>(/public/evm) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) // Withdraw the balance from the COA, we will use this later to deposit into the receiving account let vaultRef = signer.storage.borrow( from: /storage/flowTokenVault ) ?? panic("Could not borrow reference to the owner's FlowToken Vault") self.sentVault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault } execute { // Deposit the withdrawn tokens into the COA self.coa.deposit(from: <-self.sentVault) } } ``` :::info This is a basic example which only transfers tokens between a single user's COA & Flow account. You can easily modify it to transfer these tokens between any arbitrary accounts. You can also deposit tokens directly into other types of EVM accounts with the `EVM.EVMAddress.deposit` function. See the [EVM contract documentation] for more information. ::: ### Withdraw Flow tokens The `withdraw` function takes a `EVM.Balance` struct as an argument, which represents the amount of Flow tokens to withdraw, and returns a `FlowToken.Vault` resource with the withdrawn tokens. We can run the following transaction to withdraw Flow tokens from a user's COA and deposit them into their Flow vault: ```cadence withdraw_from_coa.cdc import "EVM" import "FungibleToken" import "FlowToken" transaction(amount: UFix64) { let sentVault: @FlowToken.Vault let receiver: &{FungibleToken.Receiver} prepare(signer: auth(BorrowValue) &Account) { // Borrow a reference to the COA from the storage location we saved it to with the `EVM.Withdraw` entitlement let coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) // We must create a `EVM.Balance` struct to represent the amount of Flow tokens to withdraw let withdrawBalance = EVM.Balance(attoflow: 0) withdrawBalance.setFLOW(flow: amount) // Withdraw the balance from the COA, we will use this later to deposit into the receiving account self.sentVault <- coa.withdraw(balance: withdrawBalance) as! @FlowToken.Vault // Borrow the public capability to the receiving account (in this case the signer's own Vault) // This script could be modified to deposit into any account with a `FungibleToken.Receiver` capability self.receiver = signer.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! } execute { // Deposit the withdrawn tokens into the receiving vault self.receiver.deposit(from: <-self.sentVault) } } ``` :::info This is a basic example which only transfers tokens between a single user's COA and Flow account. It can be easily modified to transfer these tokens between any arbitrary accounts. ::: ## Direct Calls to Flow EVM To interact with smart contracts on the EVM, you can use the `call` function the COA resource provides. This function takes the EVM address of the contract you want to call, the data you want to send, the gas limit, and the value you want to send. It will return a `EVM.Result` struct with the result of the call - you will need to handle this result in your Cadence code. This transaction will use the signer's COA to call a contract method with the defined signature and args at a given EVM address, and executes with the provided gas limit and value: ```cadence call.cdc import "EVM" /// Calls the function with the provided signature and args at the target contract address using /// the defined gas limit and transmitting the provided value. transaction(evmContractHex: String, signature: String, args: [AnyStruct], gasLimit: UInt64, flowValue: UInt) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { // Borrow an entitled reference to the COA from the storage location we saved it to self.coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { // Deserialize the EVM address from the hex string let contractAddress = EVM.addressFromString(evmContractHex) // Construct the calldata from the signature and arguments let calldata = EVM.encodeABIWithSignature( signature, args ) // Define the value as EVM.Balance struct let value = EVM.Balance(attoflow: flowValue) // Call the contract at the given EVM address with the given data, gas limit, and value // These values could be configured through the transaction arguments or other means // however, for simplicity, we will hardcode them here let result: EVM.Result = self.coa.call( to: contractAddress, data: calldata, gasLimit: gasLimit, value: value ) // Revert the transaction if the call was not successful // Note: a failing EVM call will not automatically revert the Cadence transaction // and it is up to the developer to use this result however it suits their application assert( result.status == EVM.Status.successful, message: "EVM call to ".concat(evmContractHex) .concat(" and signature ").concat(signature) .concat(" failed with error code ").concat(result.errorCode.toString()) .concat(": ").concat(result.errorMessage) ) } } ``` :::info Notice that the calldata is encoded in the scope of the transaction. While developers can encode the calldata outside the scope of the transaction and pass the encoded data as an argument, this compromises the human-readability of Cadence transactions. It's encouraged to either define transactions for each COA call and encoded the hardcoded EVM signature and arguments, or to pass in the human-readable arguments and signature and encode the calldata within the transaction. This ensures a more interpretable and therefore transparent transaction. ::: ### Transfer FLOW in EVM Similar to when you trasnfer ETH and other native value in other EVMs, you'll want to call to the target EVM address with empty calldata and provide the transfer value. ```cadence transfer_evm_flow.cdc import "EVM" /// Transfers FLOW to another EVM address from the signer's COA /// /// @param to: the serialized EVM address of the recipient /// @param amount: the amount of FLOW to send transaction(to: String, amount: UInt) { let recipient: EVM.EVMAddress let recipientPreBalance: UInt let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { self.recipient = EVM.addressFromString(to) self.recipientPreBalance = self.recipient.balance().attoflow self.coa = signer.storage.borrow(from: /storage/evm) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { let res = self.coa.call( to: self.recipient, data: [], gasLimit: 100_000, value: EVM.Balance(attoflow: amount) ) assert( res.status == EVM.Status.successful, message: "Failed to transfer FLOW to EVM address with error code ".concat(res.errorCode.toString()) .concat(": ").concat(res.errorMessage) ) } post { self.recipient.balance().attoflow == self.recipientPreBalance + amount: "Expected final balance ".concat((self.recipientPreBalance + amount).toString()) .concat(" but found actual balance ").concat(self.recipient.balance().attoflow.toString()) .concat(" after deposit of ").concat(amount.toString()) } } ``` ### Transfer ERC20 Below is an example transaction that demonstrates the common ERC20 transfer. You can use a similar pattern for other arbitrary EVM calls. ```cadence erc20_transfer_from.cdc import "EVM" /// Transfers ERC20 tokens from the signer's COA to the named recipient in the amount provided /// /// @param erc20AddressHex: the serialized EVM address of the ERC20 contract /// @param to: the serialized EVM address of the recipient /// @param amount: the amount of tokens to send transaction(erc20AddressHex: String, to: String, amount: UInt256) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { // Borrow an entitled reference to the COA from the canonical storage location self.coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { // Encode the calldata for the ERC20 transfer let calldata = EVM.encodeABIWithSignature( "transfer(address,uint256)", // function signature [EVM.addressFromString(to), amount] // function args ) // Call the contract at the given ERC20 address with encoded calldata and 0 value let result: EVM.Result = self.coa.call( to: EVM.addressFromString(erc20AddressHex), // deserialized address data: calldata, // encoded calldata gasLimit: 100_000, // 100k gas should cover most erc20 transfers value: EVM.Balance(attoflow: UInt(0)) // no value required in most cases ) // Revert the transaction if the call was not successful // Note: a failing EVM call will not automatically revert the Cadence transaction // and it is up to the developer to use this result however it suits their application assert( result.status == EVM.Status.successful, message: "ERC20.transfer call failed with error code: ".concat(result.errorCode.toString()) .concat(": ").concat(result.errorMessage) ) } } ``` ### Transfer ERC721 The example transaction below demonstrates a common ERC721 transfer. ```cadence erc721_transfer.cdc import "EVM" /// Transfers an ERC721 token from the signer's COA to the named recipient /// /// @param erc721AddressHex: the serialized EVM address of the ERC721 contract /// @param to: the serialized EVM address of the recipient /// @param id: the token ID to send from the signer's COA to the recipient transaction(erc721AddressHex: String, to: String, id: UInt256) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { // Borrow an entitled reference to the COA from the canonical storage location self.coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { let calldata = EVM.encodeABIWithSignature( "safeTransferFrom(address,address,uint256)", [self.coa.address(), EVM.addressFromString(to), id] ) // Call the contract at the given ERC721 address with encoded calldata and 0 value let result: EVM.Result = self.coa.call( to: EVM.addressFromString(erc721AddressHex), // deserialized address data: calldata // previously encoded calldata gasLimit: 100_000, // 100k gas should cover most erc721 transfers value: EVM.Balance(attoflow: UInt(0)) // no value required in most cases ) // Revert the transaction if the call was not successful // Note: a failing EVM call will not automatically revert the Cadence transaction // and it is up to the developer to use this result however it suits their application assert( result.status == EVM.Status.successful, message: "ERC721.safeTransferFrom call failed with error code: ".concat(result.errorCode.toString()) .concat(": ").concat(result.errorMessage) ) } } ``` #### Bulk transfer ERC721 As covered in the [Batched EVM transactions walkthrough], you can script multiple EVM calls in a single Cadence transaction. Compared to the single ERC721 transfer, to bulk send multiple tokens isn't much more code and allows for greater utility out of a single transaction. Below is an example of a bulk ERC721 token transfer. ```cadence erc721_bulk_transfer.cdc import "EVM" /// Bulk transfers ERC721 tokens from the signer's COA to the named recipient. All tokens must be from /// the same collection and sent to the same recipient. /// /// @param erc721AddressHex: the serialized EVM address of the ERC721 contract /// @param to: the serialized EVM address of the recipient /// @param ids: an array of IDs to send from the signer's COA to the recipient transaction(erc721AddressHex: String, to: String, ids: [UInt256]) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { // Borrow an entitled reference to the COA from the canonical storage location self.coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { // Iterate over provided IDs. Note the whole transaction fails if a single transfer fails, // so ownership validation is recommended before executing. Alternatively, you could remove // the assertion on success below and continue iteration on call failure. for id in ids { let calldata = EVM.encodeABIWithSignature( "safeTransferFrom(address,address,uint256)", [self.coa.address(), EVM.addressFromString(to), id] ) // Call the contract at the given ERC721 address with encoded calldata and 0 value let result: EVM.Result = self.coa.call( to: EVM.addressFromString(erc721AddressHex), // deserialized address data: calldata // previously encoded calldata gasLimit: 100_000, // 100k gas should cover most erc721 transfers value: EVM.Balance(attoflow: UInt(0)) // no value required in most cases ) // Revert the transaction if the transfer was not successful // Note: a failing EVM call will not automatically revert the Cadence transaction // and it is up to the developer to use this result however it suits their application assert( result.status == EVM.Status.successful, message: "ERC721.safeTransferFrom call failed on id ".concat(id.toString()) .concat(" with error code: ").concat(result.errorCode.toString()) .concat(": ").concat(result.errorMessage) ) } } } ``` ## Deploy a contract to Flow EVM To deploy a contract to the EVM, you can use the `deploy` function that the COA resource provides. This function takes the contract code, gas limit, and value you want to send. It will return the EVM address of the newly deployed contract. This transaction will deploy a contract with the given code with the signer's COA: ```cadence deploy_evm_contract.cdc import "EVM" transaction(bytecode: String) { let coa: auth(EVM.Deploy) &EVM.CadenceOwnedAccount prepare(signer: auth(BorrowValue) &Account) { // Borrow an entitled reference to the COA from the storage location we saved it to self.coa = signer.storage.borrow( from: /storage/evm ) ?? panic("Could not borrow reference to the signer's CadenceOwnedAccount (COA). " .concat("Ensure the signer account has a COA stored in the canonical /storage/evm path")) } execute { // Deploy the contract with the given compiled bytecode, gas limit, and value self.coa.deploy( code: bytecode.decodeHex(), gasLimit: 15_000_000, // can be adjusted as needed, hard coded here for simplicity value: EVM.Balance(attoflow: 0) ) } } ``` ## More information For more information about Cadence-owned Accounts, see [Flow EVM Accounts]. Other useful snippets to use when you interact with COAs can be found [here]. Check out the [Batched EVM Transactions walkthrough] for details on transaction batching with Cadence. [Flow EVM Accounts]: ../../build/evm/accounts.md [here]: https://fw-internal-doc.gitbook.io/evm [Batched EVM Transactions walkthrough]: ./batched-evm-transactions.md [EVM contract documentation]: ../../build/cadence/core-contracts/13-evm.md [generate keys]: ../../build/tools/flow-cli/keys/generate-keys.md [GitHub]: https://github.com/onflow/flow-go/tree/master/fvm/evm/stdlib/contract.cdc [Cadence Owned Accounts (COAs)]: ../../build/evm/accounts.md#cadence-owned-accounts --- # File: /md/blockchain-development-tutorials/cross-vm-apps/introduction.md --- title: Batched Tx From Scaffold description: Learn how to use FCL with wagmi and rainbowkit to create a cross-vm app - one that is simultaneously connected to Flow EVM and Flow Cadence. sidebar_position: 1 keywords: - hybrid apps - cross-vm apps - FCL - wagmi - RainbowKit - viem - Flow EVM - Flow Cadence - cross-VM - multi-call - batch transactions - web3 - dapp development - wallet integration - smart contracts - blockchain development - supercharge your EVM app with Cadence --- # Batched Tx From Scaffold Ever since we launched Flow EVM, you can _supercharge_ your EVM apps with Flow Cadence features and contracts. Some benefits, such as [native VRF] and inexpensive gas that won't compromise security, are built in and either easy to use or automatic. Others, such as the ability to use [Cadence] to [structure and call EVM transactions], are powerful but complicated to configure and use. They also require developers to manage concurrent connections to both networks. [FLIP 316] improves the [Flow Client Library (FCL)] to support cross-VM functionality between Flow EVM and Flow Cadence. For EVM developers, this means that you can use the familiar [wagmi], [viem], and [RainbowKit] stack you're used to, add FCL, and get features like **multi-call write** with one signature for users with a Cadence-compatible [wallet]. In this tutorial, you'll learn how to create [Click to Mint], a simple game that allows players to click a button to mint an ERC-20 token. With the power of Flow, they can also click a button and **complete 10 separate transactions with just one approval!** ![Click to Mint](./imgs/click-to-mint.png) :::warning The FCL functionality described in this tutorial is in alpha. Some steps may change. We'll keep the tutorial updated, but please [create an issue] or let us know on [Discord] if something doesn't ork for you. ::: ## Objectives After you complete this guide, you'll be able to: - Build an app that seamlessly integrates Flow Cadence and Flow EVM connections. - Add Cadence features to your [Rainbowkit]/[wagmi]/[viem] app. - Use [Flow Client Library (FCL)] to turn on multi-call contract writes to Flow EVM. ## Prerequisites ### Next.js and modern frontend development This tutorial uses [Next.js]. You don't need to be an expert, but it's helpful to be comfortable with development with a current React framework. You'll be on your own to select and use a package manager, manage Node versions, and other frontend environment tasks. If you don't have your own preference, you can just follow along with us and use [npm]. ### Solidity and Cadence smart contract development Apps that use the hybrid approach can interact with both [Cadence] and [Solidity] smart contracts. You don't need to be an expert in either of these, but it's helpful to be familiar with how smart contracts work in at least one of these languages. ### Onchain app frontends We assume you're familiar with [wagmi], [viem], and [RainbowKit]. If you come from the Cadence, you might want to take a quick look at the getting started guides for these platforms. They're all excellent and will rapidly get you up to speed on how the EVM world commonly connects their apps to their contracts. ## Get started For this tutorial, we'll start from a fork of the [FCL + RainbowKit + Wagmi Integration Demo] that the team built. Fork the repo so you can push your work freely to your own copy, then follow the setup instructions. ## Project overview Open the cross-vm app scaffold in your editor, run it, and view the site in your browser: ```bash npm run dev ``` You'll see: ![Hybrid App Demo](./imgs/hybrid-app-demo.png) Connect with a Cadence-compatible [wallet]. :::warning In a production app, you'll want to manage this process carefully. Non-Cadence EVM wallets may be able to connect, but they will **not** be able to use any Cadence features. ::: ## Send batch transactions The first demo built into this scaffold is **multi-call contract write**. On Flow, this isn't an unstable experimental feature - it's a demonstration of the power of EVM + Cadence. Click `Send Batch Transaction Example` and approve the transaction. You'll see three lines appear on the page, similar to: ``` {"isPending":false,"isError":false,"txId":"b3c2b8c86e68177af04324152d45d9de9c2a118ff8f090476b3a07e0c9554912","results":[{"hash":"0x46e923a08d9008632e3782ea512c4c590d4650ba58b3e8b49628f58e6adddaa9","status":"passed","errorMessage":""},{"hash":"0x52c82dc689cd5909519f8a90d0a1ec2e74192d7603fd3b5d33f7f4d54a618a84","status":"passed","errorMessage":""}]} ``` :::tip Currently, the Flow wallet sponsors the fees for the compute units (equivalent of gas) for all transactions signed with the wallet on both testnet **and mainnet!** ::: ### Cadence parent transaction The first line is the transaction ID of the Flow Cadence transaction that calls **both** of the EVM transactions. Search for it in [Testnet Cadence Flowscan]. Cadence transactions are more complicated than those in Solidity contracts. Rather than being restricted to run functions present on the contract, they can run arbitrary code as long as the caller has access to all of the resources the transaction requires. You can see the code of the transaction in the `Script` tab, but we've included it here for convenience: ```cadence import EVM from 0x8c5303eaa26202d6 transaction(calls: [{String: AnyStruct}], mustPass: Bool) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount // Borrow a reference to the EVM account that has the ability to sign transactions prepare(signer: auth(BorrowValue) & Account) { let storagePath = /storage/evm self.coa = signer.storage.borrow(from: storagePath) ?? panic("No CadenceOwnedAccount (COA) found at ".concat(storagePath.toString())) } // Iterate through the list of provided EVM transactions execute { for i, call in calls { let to = call["to"] as! String let data = call["data"] as! String let gasLimit = call["gasLimit"] as! UInt64 let value = call["value"] as! UInt let result = self.coa.call( to: EVM.addressFromString(to), data: data.decodeHex(), gasLimit: gasLimit, value: EVM.Balance(attoflow: value) ) if mustPass { assert( result.status == EVM.Status.successful, message: "Call index ".concat(i.toString()).concat(" to ").concat(to) .concat(" with calldata ").concat(data).concat(" failed: ") .concat(result.errorMessage) ) } } } ``` In this case, it checks that the caller of the Cadence transaction has permission to control to the EVM account, which is built in for [Cadence Owned Accounts]. The `execute` phase then iterates through the EVM transactions and uses the Cadence accounts own permissions to sign the EVM transactions. The loop also handles a check for the optional flag to cancel all of the transactions if any one of them fails. **In other words, you could set up a 20 transaction arbitrage attempt and unwind everything if it fails at any step!** ### EVM child transactions The next two lines show the transaction hashes for the EVM transactions. To view this in [Testnet EVM Flowscan], search for the transaction hashes, the same as any other. Look up both transactions. The first calls the `deposit()` function to wrap FLOW and move it to EVM. The second calls the ERC-20 `approve()` function to give another address the authority to spend those tokens. For the demo, the code for this is hard-coded into `src/app/page.tsx`: ```tsx const calls: EVMBatchCall[] = [ { // Call deposit() function (wrap FLOW) on the token contract. address: '0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e', // Replace with your actual token contract address. abi: [ { inputs: [], name: 'deposit', outputs: [], stateMutability: 'payable', type: 'function', }, ], functionName: 'deposit', args: [], // deposit takes no arguments; value is passed with the call. }, { // Call approve() function (ERC20 style) on the same token contract. address: '0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e', // Replace with your actual token contract address if needed. abi: [ { inputs: [ { name: 'spender', type: 'address' }, { name: 'value', type: 'uint256' }, ], name: 'approve', outputs: [{ name: '', type: 'bool' }], stateMutability: 'nonpayable', type: 'function', }, ], functionName: 'approve', args: [ '0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2', // Spender address. BigInt('1000000000000000000'), // Approve 1 token (assuming 18 decimals). ], }, ]; ``` It's called with the `useBatchTransaction` hook via the `sendBatchTransaction(calls)` function. ## Code evaluator The demo also has an embedded code evaluator that you can use to experiment with snippets of code from `fcl` or `wagmi`. For example: ```tsx const user = await fcl.currentUser().snapshot(); return user.addr; ``` Will return your Cadence address. This snippet: ```tsx const block = await fcl.block(); return block.height; ``` Returns the current Cadence VM block number. ## Call your own contract Next, we'll update the starter to connect to and call functions in our own contract. For this, we'll use a simple [Button Clicker Contract]. You can deploy your own copy, or use the one deployed at [`0xA7Cf2260e501952c71189D04FAd17c704DFB36e6`]. ## Set Up contract imports :::info The following steps assume deployment with Hardhat Ignition. If you use a different deployment method, import the contract address and abi as appropriate. ::: In your fork of the app, add a folder called `contracts` to the `src` folder. In it, copy over ['deployed_addresses.json`] from `ignition/deployments/chain-545` in the Button Clicker repo, and `ignition/deployments/chain-545/ClickTokenModule#ClickToken.json`. Next, create a folder called `constants` and add a file called `contracts.ts` to it. In it, import the contract artifact and addresses file, and create export a constant with this information. ```tsx import ClickToken from '../contracts/ClickTokenModule#ClickToken.json'; import deployedAddresses from '../contracts/deployed_addresses.json'; export const clickToken = { abi: ClickToken.abi, address: deployedAddresses['ClickTokenModule#ClickToken'] as `0x${string}`, }; ``` ## Build traditional functionality This isn't a wagmi tutorial, so we'll give you some components to speed up the process. Add a folder called `components` inside `src` and add the following files: `TheButton.tsx` ```tsx 'use client'; import { useAccount } from 'wagmi'; import { clickToken } from '../constants/contracts'; interface theButtonProps { // eslint-disable-next-line writeContract: Function; awaitingResponse: boolean; setAwaitingResponse: (value: boolean) => void; } export default function TheButton({ writeContract, awaitingResponse, setAwaitingResponse, }: theButtonProps) { const account = useAccount(); function handleClick() { setAwaitingResponse(true); writeContract({ abi: clickToken.abi, address: clickToken.address, functionName: 'mintTo', args: [account.address], gas: 45000, }); } return ( <> {!awaitingResponse && ( )} {awaitingResponse && ( )} ); } ``` `TopTenDisplay.tsx` ```tsx import { useAccount, useReadContract } from 'wagmi'; import { clickToken } from '../constants/contracts'; import { useEffect, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { formatUnits } from 'viem'; type scoreBoardEntry = { user: string; value: bigint; }; interface TopTenDisplayProps { reloadScores: boolean; setReloadScores: (value: boolean) => void; } export default function TopTenDisplay({ reloadScores, setReloadScores, }: TopTenDisplayProps) { const [scores, setScores] = useState([]); const account = useAccount(); const queryClient = useQueryClient(); const { data: scoresData, queryKey: getAllScoresQueryKey } = useReadContract({ abi: clickToken.abi, address: clickToken.address as `0x${string}`, functionName: 'getAllScores', }); useEffect(() => { if (scoresData) { const sortedScores = scoresData as scoreBoardEntry[]; // Sort scores in descending order sortedScores.sort((a, b) => Number(b.value) - Number(a.value)); setScores(sortedScores); } }, [scoresData]); useEffect(() => { if (reloadScores) { console.log('Reloading scores...'); queryClient.invalidateQueries({ queryKey: getAllScoresQueryKey }); setReloadScores(false); } }, [reloadScores]); function renderAddress(address: string) { return address?.slice(0, 5) + '...' + address?.slice(-3); } function renderTopTen() { if (scores.length === 0 || !account) { return (
  1. Loading...
); } // Only display the top 10 scores. If the user is in the top 10, bold the item with their score. If not, show it at the bottom with their ranking number const topTen = scores.length > 10 ? scores.slice(0, 10) : scores; // myRank is my address's position in the array of scores, +1. If it's not present, my rank is the length of the array const myRank = scores.findIndex((entry) => entry.user === account?.address) + 1 || scores.length + 1; const topTenList = topTen.map((entry, index) => { return (
  • {entry.user === account.address ? ( {index + 1} -- {renderAddress(entry.user)} --{' '} {formatUnits(entry.value, 18)} ) : ( <> {index + 1} -- {renderAddress(entry.user)} --{' '} {formatUnits(entry.value, 18)} )}
  • ); }); // Append my score if myRank is > 10 if (account?.address && (myRank > 10 || myRank > scores.length)) { topTenList.push(
  • {myRank} -- {renderAddress(account.address.toString())} --{' '} {myRank > scores.length ? 0 : formatUnits(scores[myRank - 1].value, 18)}
  • , ); } return
      {topTenList}
    ; } return (

    Top 10 Scores

    {renderTopTen()}
    ); } ``` `Content.tsx` ```tsx 'use client'; import { useEffect, useState } from 'react'; import TopTenDisplay from './TopTenDisplay'; import { useWaitForTransactionReceipt, useWriteContract, useAccount, } from 'wagmi'; import TheButton from './TheButton'; export default function Content() { const [reload, setReload] = useState(false); const [awaitingResponse, setAwaitingResponse] = useState(false); const account = useAccount(); const { data, writeContract, error: writeError } = useWriteContract(); const { data: receipt, error: receiptError } = useWaitForTransactionReceipt({ hash: data, }); useEffect(() => { if (receipt) { console.log('Transaction receipt:', receipt); setReload(true); setAwaitingResponse(false); } }, [receipt]); useEffect(() => { if (writeError) { console.error(writeError); setAwaitingResponse(false); } }, [writeError]); useEffect(() => { if (receiptError) { console.error(receiptError); setAwaitingResponse(false); } }, [receiptError]); return (
    {account.address && (
    )}
    {}
    ); } ``` Then, import and add `` to `page.tsx`: ```tsx return ( <>

    Flow Address: {flowAddress}

    EVM Address: {coa?.address}


    {

    {JSON.stringify({ isPending, isError, txId, results })}

    } ); ``` You'll now see the button and scoreboard from the contract. Test it out and earn a few points! ![scores](./imgs/scores.png) ## Supercharge your EVM app With Cadence Now let's supercharge it. With the power of Cadence, you can use multi-call write and give your users way more tokens with a single click and single signature! For the first pass, we'll skip some organization best practices. Import `clickToken` into `page.tsx` and update `calls` to instead call the `mint` function from the Button Clicker contract. ```tsx const calls: EVMBatchCall[] = [ { address: clickToken.address, abi: clickToken.abi as Abi, functionName: 'mintTo', args: [coa?.address], }, ]; ``` Click `Send Batch Transaction Example` again. You'll have to **manually refresh** the page when the EVM transaction hash appears to see the score update. We haven't wired in the query invalidation yet. Next, use some JavaScript to put 10 copies of the transaction call into the array: ```tsx const calls: EVMBatchCall[] = Array.from({ length: 10 }, () => ({ address: clickToken.address, abi: clickToken.abi as Abi, functionName: 'mintTo', args: [coa?.address], })); ``` Click the button again and **manually** refresh page after the transaction hashes appear. **You just minted 10 tokens from 10 transactions with one signature!** ## Improve the UI/UX While the batched transactions feature works, we've got a few flaws in the user experience that we'll need to resolve, and we should make this look a bit nicer. ### Install Tailwind :::warning We initially tried to get an AI friend to install this for us and it got very confused. `Next.js` and Tailwind have both had a lot of change recently. As a result, the LLMs don't seem to have caught up just yet. Do this part the old-fashioned way. ::: The components we borrowed already use [Tailwind], so install it: ```bash npm install tailwindcss @tailwindcss/postcss postcss ``` Then, in the root of the project, add `postcss.config.mjs` and add: ```tsx const config = { plugins: { '@tailwindcss/postcss': {}, }, }; export default config; ``` Then, add the following to the top of `src/styles/global.css`: ```css @import 'tailwindcss'; ``` Run the app and make sure you see some styling. It won't look nice yet. We'll help you reorganize the components and hook up state monitoring, but it will be up to you to style the app how you'd like. You can check out the [reference repo] for inspiration, but it's far from perfect or beautiful. ### Update state display The first thing we'll need to fix is that the user has to refresh the window manually to see the results of the batched transaction in the scoreboard. To start, move the functionality in `page.tsx` into a new component, called `SuperButton.tsx`. We mimic the pattern in `TheButton.tsx` where the blockchain state is managed in `Content.tsx`, and we pass in the relevant information and functions as props: ```tsx 'use client'; import { useAccount } from 'wagmi'; import { clickToken } from '../constants/contracts'; import { CallOutcome, EVMBatchCall } from '../hooks/useBatchTransaction'; import { Abi } from 'viem'; interface SuperButtonProps { flowAddress: string | null; awaitingResponse: boolean; setAwaitingResponse: (value: boolean) => void; sendBatchTransaction: (calls: EVMBatchCall[]) => void; isPending: boolean; isError: boolean; txId: string; results: CallOutcome[]; } export default function SuperButton({ flowAddress, awaitingResponse, setAwaitingResponse, sendBatchTransaction, isPending, isError, txId, results, }: SuperButtonProps) { const account = useAccount(); const calls: EVMBatchCall[] = Array.from({ length: 10 }, () => ({ address: clickToken.address, abi: clickToken.abi as Abi, functionName: 'mintTo', args: [account?.address], })); function handleClick() { setAwaitingResponse(true); sendBatchTransaction(calls); } return (
    With the{' '} Flow Wallet , you can sign 10 mint transactions at once!
    {!awaitingResponse && ( )} {awaitingResponse && ( )} {

    {JSON.stringify({ isPending, isError, txId, results })}

    }
    ); } ``` You will end up with a vastly simplified `page.tsx`: ```tsx import Content from '../components/Content'; function Page() { return ( <> ); } export default Page; ``` Next, update `Content.tsx`. First, add the decomposition of the `useBatchTransactions` hook that used to be in `page.tsx`. You'll keep blockchain-state-related code here, in a similar pattern to `useWriteTransaction`. ```tsx import { useBatchTransaction } from '../hooks/useBatchTransaction'; ``` ```tsx const { sendBatchTransaction, isPending, isError, txId, results } = useBatchTransaction(); ``` You'll also need to move the `useEffect` that subscribes to the current user on the Cadence side: ```tsx useEffect(() => { const unsub = fcl.currentUser().subscribe((user: CurrentUser) => { setFlowAddress(user.addr ?? null); }); return () => unsub(); }, []); ``` Then, update the `useEffect` that waits for a `receipt` to also trigger if `results` is updated with the result of a batched transaction: ```tsx useEffect(() => { if (receipt || results.length > 0) { console.log('Transaction receipt:', receipt); setReload(true); setAwaitingResponse(false); } }, [receipt, results]); ``` Finally, reorganize the `return` into two side-by-side cards and put the new component in the right card: ```tsx return (

    Flow Address: {flowAddress}

    EVM Address: {account?.address}


    {account.address && (
    )}
    {}
    ); ``` ### Tests Run the app and make sure it works as expected, even if in a rather ugly fashion. ### Add UI hints With this kind of app, you're likely to have two types of users. Those that have upgraded to the [Flow Wallet] can take advantage of advanced features such as batched transactions, and those who haven't cannot. It's up to you do design a comprehensive strategy for your app, but here, we can at least let users know what's going on. Add some explainer text, and configure the button to show an appropriate message and disable itself if the wallet won't support it. ```tsx
    With the Flow Wallet, you can sign 10 mint transactions at once!
    ``` ### Style It's up to you to make the app pretty. If you need inspiration, you can always check the [reference repo]. ## Conclusion In this tutorial, you reviewed the demo starter to build hybrid applications that use a common EVM stack and integrate with Flow Cadence. You then added functionality to interface with another contract that mints ERC-20 tokens. Finally, you supercharged your app with the power of Cadence for EVM multi-call contract writes. Now that you have completed the tutorial, you will be able to: - Build an app that seamlessly integrates Flow Cadence and Flow EVM connections. - Add Cadence features to your [Rainbowkit]/[wagmi]/[viem] app. - Use [Flow Client Library (FCL)] to enable multi-call contract writes to Flow EVM. [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [npm]: https://www.npmjs.com/ [Click to Mint]: https://clicktomint.vercel.app/ [create an issue]: https://github.com/onflow/docs/issues/new/choose [Cadence]: https://cadence-lang.org [Solidity]: https://soliditylang.org/ [native VRF]: ../native-vrf/vrf-in-solidity.md [structure and call EVM transactions]: ./batched-evm-transactions.md [FLIP 316]: https://github.com/onflow/flips/pull/317 [Flow Client Library (FCL)]: ../../build/tools/clients/fcl-js [wagmi]: https://wagmi.sh/ [viem]: https://viem.sh/ [RainbowKit]: https://www.rainbowkit.com/ [wallet]: ../../ecosystem/wallets.md [Discord]: https://discord.com/channels/613813861610684416/1162086721471647874 [FCL + RainbowKit + Wagmi Integration Demo]: https://github.com/jribbink/cross-vm-app [FCL-JS]: https://github.com/onflow/fcl-js [Testnet Cadence Flowscan]: https://testnet.flowscan.io [Cadence Owned Accounts]: ../../build/cadence/basics/accounts.md [Testnet EVM Flowscan]: https://evm-testnet.flowscan.io [Button Clicker Contract]: https://github.com/briandoyle81/button-clicker-contract/blob/main/contracts/ClickToken.sol [`0xA7Cf2260e501952c71189D04FAd17c704DFB36e6`]: https://evm-testnet.flowscan.io/address/0xA7Cf2260e501952c71189D04FAd17c704DFB36e6?tab=contract [Tailwind]: https://tailwindcss.com/ [reference repo]: https://github.com/briandoyle81/cross-vm-app-1/tree/main --- # File: /md/blockchain-development-tutorials/cross-vm-apps/vm-bridge.md --- title: Cross-VM Bridge sidebar_label: Cross-VM Bridge sidebar_position: 6 --- # Cross-VM Bridge Flow provides the [Cross-VM Bridge] which allows the movement of fungible and non-fungible tokens between Flow-Cadence & Flow-EVM. The Cross-VM Bridge is a contract-based protocol which allows the automated and atomic bridging of tokens from Cadence into EVM with their corresponding ERC-20 and ERC-721 token types. In the opposite direction, it supports bridging of arbitrary ERC-20 and ERC-721 tokens from EVM to Cadence as their corresponding FT or NFT token types. By default, when a user onboards a new token to the bridge, the bridge will deploy a standard token contract in the other VM that only the core bridge protocol contracts retain limited control over. This bridge-deployed contract handles basic minting and metadata operations that are required for usage in the needed environment. If a developer wants to define and connect the NFT contracts on both sides of the bridge, they can have each contract point to each other to indicate that they are associated and then register that association with the bridge so the token moves between VMs as either definition. The Cross-VM Bridge internalizes the capabilities to deploy new token contracts in either VM state as needed, resolves access to and maintains links between associated contracts. It also automates account and contract calls to enforce source VM asset burn or lock, and target VM token mint or unlock. Developers who want to use the Cross-VM Bridge must use a Cadence transaction. Cross-VM bridging functionality is not currently available natively in Flow EVM. By extension, this means that the EVM account bridging from EVM to Cadence must be a [`CadenceOwnedAccount` (COA)] as this is the only EVM account type that can be controlled from the Cadence runtime. This [FLIP-233] outlines the architecture and implementation of the VM bridge. An additional [FLIP-318] describes how developers can create custom associations between NFTs they define and control in each VM. This document will focus on how to use the Cross-VM Bridge and considerations for fungible and non-fungible token projects that deploy to either Cadence or EVM. ## Deployments You can find the core bridge contracts at the following addresses: | Contracts | Testnet | Mainnet | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | All Cadence Bridge contracts | [`0xdfc20aee650fcbdf`] | [`0x1e4aa0b87d10b141`] | | `FlowEVMBridgeFactory.sol` | [`0xf8146b4aef631853f0eb98dbe28706d029e52c52`] | [`0x1c6dea788ee774cf15bcd3d7a07ede892ef0be40`] | | `FlowEVMBridgeDeploymentRegistry.sol` | [`0x8781d15904d7e161f421400571dea24cc0db6938`] | [`0x8fdec2058535a2cb25c2f8cec65e8e0d0691f7b0`] | | `FlowEVMBridgedERC20Deployer.sol` | [`0x4d45CaD104A71D19991DE3489ddC5C7B284cf263`] | [`0x49631Eac7e67c417D036a4d114AD9359c93491e7`] | | `FlowEVMBridgedERC721Deployer.sol` | [`0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56`] | [`0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df`] | And below are the bridge escrow's EVM addresses. These addresses are COAs and are stored stored in the same Flow account as you'll find the Cadence contracts (see above). | Network | Address | | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Testnet | [`0x0000000000000000000000023f946ffbc8829bfd`] | | Mainnet | [`0x00000000000000000000000249250a5c27ecab3b`] | ## Interact With the Bridge :::info All bridging activity in either direction is orchestrated via Cadence on COA EVM accounts. This means that all bridging activity must be initiated via a Cadence transaction, not an EVM transaction, regardless of the directionality of the bridge request. For more information on the interplay between Cadence and EVM, see [How Flow EVM Works]. ::: ## Overview The Flow EVM bridge allows both fungible and non-fungible tokens to move atomically between Cadence and EVM. In EVM, fungible tokens are defined as ERC20 tokens, and non-fungible tokens as ERC721 tokens. In Cadence, fungible tokens are defined by contracts that implement [the `FungibleToken` interface] and non-fungible tokens implement [the `NonFungibleToken` interface]. You can find full guides to create these projects [here]. Like all operations on Flow, there are native fees associated with both computation and storage. To prevent spam and sustain the bridge account's storage consumption, fees are charged to both onboard assets and bridge assets. In the case where storage consumption is expected, fees are charged based on the storage consumed at the current network storage rate. ## Onboard your token to the bridge For the purpose of this guide, we assume that the developer already deployed a token smart contract to their preferred VM (Flow-Cadence or Flow-EVM) and wants to bridge it to the other (target) VM. For the developer to use their token in the target VM, there must be a contract that defines the asset and how it behaves in the target VM that also allows the bridge to fulfill the asset from Cadence to EVM and vice versa. This contract is separate from the contract in the native VM, but they are "associated" with each other by the mechanisms of the Flow VM bridge. To create this association, the asset must be "onboarded" to the bridge before bridging operations can be fulfilled. This can happen in two ways: ### Option 1: automatic onboarding Any user registers the native token contract with the bridge and the bridge deploys a basic templated version of the contract in the target VM. This basic contract is automatically associated with the native contract and is used for bridging. The developer has no direct control over this bridge-deployed contract because the bridge controls it. This method is covered in the [Automatic Onboarding Section]. ### Option 2: custom association onboarding With this option (available for only for NFTs), developers can deploy their own contract to the target VM and declare a custom association between it and the native contract. This allows them to have more control over both contracts, which lets them to include more sophisticated features and mechanisms in their bridged token contracts such as ERC-721C, unique metadata views, and more that aren't included in the default bridged template versions. This method is covered in the [Custom Association Section]. :::info Before you continue to onboard your token, review the [Prep Your Assets for Bridging] section of this document. This describes some steps you should follow to make sure that your native asset or bridged asset are properly set up for you to register them with the bridge. ::: ## Automatic onboarding To move from a Cadence-native asset to EVM, automatic onboarding can occur on the fly, where a template contract deploys in the same transaction as the asset is bridged to EVM if the transaction so specifies. To move from EVM to Cadence, however, requires that onboarding occur in a separate transaction due to the fact that a Cadence contract is initialized at the end of a transaction and isn't available in the runtime until after the transaction has executed. Below are transactions relevant to automatically onboarding assets native to either VM: **Automatically Onboard a Cadence-native asset:**
    onboard_by_type.cdc ```cadence onboard_by_type.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/onboarding/onboard_by_type.cdc ```
    **Automatically Onboard an EVM-native asset:**
    onboard_by_evm_address.cdc ```cadence onboard_by_evm_address.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc ```
    ## Custom association onboarding With [Custom Associations], developers can deploy NFT contracts in both VMs and associate them with each other, which allows them to retain control of the contracts in both VMs as well as implement custom use-case specific functionality. To do this, each contract must implement a special interface that tells the bridge what the associated contract is in the other VM. The fact that both point to each other validates the intended association, which prevents spoofing. If the contracts do not point to each other this way, you can't register them as a custom association. Review the [Preparing Custom Associations] section to learn how to set up each of your contracts for a custom association. Below is the transaction for onboarding NFTs for a custom association. Remember that both the Cadence and the Solidity contract need to be deployed and include the special interface conformances to point to each other before registration! **Onboard an NFT Custom Association:**
    register_cross_vm_nft.cdc ```cadence onboard_by_type.cdc !from https://github.com/onflow/flow-evm-bridge/blob/flip-318/cadence/transactions/bridge/onboarding/register_cross_vm_nft.cdc ```
    ## Bridging After an asset gets onboarded, either by automatic or custom association, it can be bridged in either direction, referred to by its Cadence type. For Cadence-native assets, this is simply its native type. For EVM-native assets, this is in most cases a templated Cadence contract deployed to the bridge account, the name of which is derived from the EVM contract address. For instance, an ERC721 contract at address `0x1234` would be onboarded to the bridge as `EVMVMBridgedNFT_0x1234`, making its type identifier `A..EVMVMBridgedNFT_0x1234.NFT`. To get the type identifier for a given NFT, you can use the following code: ```cadence // Where `nft` is either a @{NonFungibleToken.NFT} or &{NonFungibleToken.NFT} nft.getType().identifier ``` You may also retrieve the type associated with a given EVM contract address with the following script:
    get_associated_type.cdc ```cadence get_associated_type.cdc !from https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_type.cdc ```
    Alternatively, given some onboarded Cadence type, you can retrieve the associated EVM address with the following script:
    get_associated_address.cdc ```cadence get_associated_address.cdc !from https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_evm_address.cdc ```
    #### NFTs Any Cadence NFTs that bridge to EVM are escrowed in the bridge account and either minted in a bridge-deployed ERC721 contract or transferred from escrow to the calling COA in EVM. On the return trip, NFTs are escrowed in EVM - owned by the bridge's COA - and either unlocked from escrow if locked or minted from a bridge-owned NFT contract. Below are transactions relevant to bridging NFTs:
    bridge_nft_to_evm.cdc ```cadence bridge_nft_to_evm.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc ```
    bridge_nft_from_evm.cdc ```cadence bridge_nft_from_evm.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/nft/bridge_nft_from_evm.cdc ```
    #### Fungible tokens Any Cadence fungible tokens that bridge to EVM are escrowed in the bridge account only if they are Cadence-native. If the bridge defines the tokens, they are burned. On the return trip the pattern is similar, as the bridge burns bridge-defined tokens or escrows them if they are EVM-native. In all cases, if the bridge has authority to mint on one side, it must escrow on the other as the native VM contract is owned by an external party. With fungible tokens in particular, there may be some cases where the Cadence contract is not deployed to the bridge account, but the bridge still follows a mint/burn pattern in Cadence. These cases are handled via [`TokenHandler`] implementations. Also know that moving $FLOW to EVM is built into the `EVMAddress` object so any requests to bridge $FLOW to EVM will simply leverage this interface; however, you must move $FLOW from EVM to Cadence through the COA resource. Below are transactions relevant to bridging fungible tokens:
    bridge_tokens_to_evm.cdc ```cadence bridge_tokens_to_evm.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/tokens/bridge_tokens_to_evm.cdc ```
    bridge_tokens_from_evm.cdc ```cadence bridge_tokens_from_evm.cdc !from https://raw.githubusercontent.com/onflow/flow-evm-bridge/main/cadence/transactions/bridge/tokens/bridge_tokens_from_evm.cdc ```
    ## Prep your assets for bridging ### Context To maximize utility to the ecosystem, this bridge is permissionless and open to any fungible or non-fungible token as defined by the respective Cadence standards and limited to ERC20 and ERC721 Solidity standards. Ultimately, a project does not have to do anything for users to bridge their assets between VMs. However, there are some considerations developers may take to enhance the representation of their assets in non-native VMs. These largely relate to asset metadata and ensuring that bridging does not compromise critical user assumptions about asset ownership. ### EVMBridgedMetadata Proposed in [@onflow/flow-nft/pull/203], the `EVMBridgedMetadata` view presents a mechanism to both represent metadata from bridged EVM assets as well as allow Cadence-native projects to specify the representation of their assets in EVM. It isn't required to implement this view to bridge asets, but the bridge does default to it when available as a way to provide projects greater control over their EVM asset definitions within the scope of ERC20 and ERC721 standards. The interface for this view is as follows: ```cadence access(all) struct URI: MetadataViews.File { /// The base URI prefix, if any. Not needed for all URIs, but helpful /// for some use cases For example, updating a whole NFT collection's /// image host easily access(all) let baseURI: String? /// The URI string value /// NOTE: this is set on init as a concatenation of the baseURI and the /// value if baseURI != nil access(self) let value: String access(all) view fun uri(): String } access(all) struct EVMBridgedMetadata { access(all) let name: String access(all) let symbol: String access(all) let uri: {MetadataViews.File} } ``` This uri value could be a pointer to some offchain metadata if you expect your metadata to be static. Or you could couple the `uri()` method with the utility contract below to serialize the onchain metadata on the fly. Alternatively, you may choose to host a metadata proxy which serves the requested token URI content. ### SerializeMetadata The key consideration with respect to metadata is the distinct metadata storage patterns between ecosystem. It's critical for NFT utility that the metadata be bridged in addition to the representation of the NFTs ownership. However, it's commonplace for Cadence NFTs to store metadata onchain while EVM NFTs often store an onchain pointer to metadata stored offchain. For Cadence NFTs to be properly represented in EVM platforms, the metadata must be bridged in a format expected by those platforms and be done in a manner that also preserves the atomicity of bridge requests. The path forward on this was decided to be a commitment of serialized Cadence NFT metadata into formats popular in the EVM ecosystem. For assets that do not implement `EVMBridgedMetadata`, the bridge will attempt to serialize the metadata of the asset as a JSON data URL string. This is done via the [`SerializeMetadata` contract] which serializes metadata values into a JSON blob compatible with the OpenSea metadata standard. The serialized metadata is then committed as the ERC721 `tokenURI` upon bridging Cadence-native NFTs to EVM. Since Cadence NFTs can easily update onchain metadata either by field or by the ownership of sub-NFTs, this serialization pattern allows token URI updates on subsequent bridge requests. ### Prepar custom associations If you are a developer who wants to deploy and manage NFT contracts in both VMs and have tokens from each be exchangable for each other, you'll have to add some code to your contracts which indicate that they each represent the same token in their respective VMs so they point to each other. For the purposes of these instructions, an NFT is native to a VM if that VM is the main source of truth for the contracts and where they are originally minted. This feature is not available for Fungible Tokens at the moment, but may be in the future. :::warning The bridge only supports a single custom association declaration. This means that once you register an association between your Cadence NFT & EVM contract, the association cannot be updated. If you wish to retain some upgradeability to your registered implementations, we recommend that you both retain keys on your Cadence NFT contract account **and ** implement an upgradeable Solidity pattern when deploying your ERC721, then register the association between your Cadence NFT Type & ERC721 proxy (not the implementation address). ::: #### Cadence All Cadence NFT contracts implement [Metadata Views] that return metadata about their NFTs in standard ways via the `{Contract}.resolveContractView()` and `{NFT}.resolveView()` methods. The following new view (`CrossVMMetadataViews.EVMPointer`) **must** be resolved at the contract level (`ViewResolver.resolveContractView()`) for a given Type **and** at the NFT level (`ViewResolver.Resolver.resolveView()`) ```cadence /// View resolved at contract & resource level pointing to the associated EVM implementation access(all) struct EVMPointer { /// The associated Cadence Type access(all) let cadenceType: Type /// The defining Cadence contract address access(all) let cadenceContractAddress: Address /// The associated EVM contract address access(all) let evmContractAddress: EVM.EVMAddress /// Whether the asset is Cadence- or EVM-native access(all) let isCadenceNative: Bool } ``` This view allows a Cadence contract to specify which Solidity contract it is associated with. You can see an example of how this view is implemented in [the `ExampleNFT` contract] in the Flow Non-Fungible Token repo. If your EVM contract expects metadata to be passed from Cadence at the time of bridging, you must implement the `CrossVMMetadataViews.EVMBytesMetadata` view. You'll find this useful for Cadence-native NFTs with dynamic metadata. This view will be resolved by the bridge and passed to your EVM contract when the `fulfillToEVM` method is called. How you handle the bridged bytes in your ERC721 implementation will be a matter of overriding the `_beforeFulfillment` and/or `_afterFulfillment` hooks included in the `CrossVMBridgeERC721Fulfillment` base contract. **Flow EVM-Native NFTs** If the NFT being onboarded to the bridge is native to Flow-EVM, then the associated contract's minter resource must implement the `FlowEVMBridgeCustomAssociationTypes.NFTFulfillmentMinter` interface: ```cadence /// Resource interface used by EVM-native NFT collections allowing for the fulfillment of NFTs from EVM into Cadence /// access(all) resource interface NFTFulfillmentMinter { /// Getter for the type of NFT that's fulfilled by this implementation /// access(all) view fun getFulfilledType(): Type /// Called by the VM bridge when moving NFTs from EVM into Cadence if the NFT is not in escrow. Since such NFTs /// are EVM-native, they are distributed in EVM. On the Cadence side, those NFTs are handled by a mint & escrow /// pattern. On moving to EVM, the NFTs are minted if not in escrow at the time of bridging. /// /// @param id: The id of the token being fulfilled from EVM /// /// @return The NFT fulfilled from EVM as its Cadence implementation /// access(FulfillFromEVM) fun fulfillFromEVM(id: UInt256): @{NonFungibleToken.NFT} { pre { id <= UInt256(UInt64.max): "The requested ID \(id.toString()) exceeds the maximum assignable Cadence NFT ID \(UInt64.max.toString())" } post { UInt256(result.id) == id: "Resulting NFT ID \(result.id.toString()) does not match requested ID \(id.toString())" result.getType() == self.getFulfilledType(): "Expected \(self.getFulfilledType().identifier) but fulfilled \(result.getType().identifier)" } } } ``` You can see an example of an implementation of this interface in the [Flow EVM bridge repo ExampleNFT contract]. A Capability with the `FulfillFromEVM` entitlement is required at the time of registration so the bridge can fulfill NFTs bridged from EVM for the first time. #### Solidity For custom associations, the following interface **must** be implemented in the IERC721-conforming Solidity contract. This provides functionality to point to the address and type of the associated Cadence NFT. ```solidity interface ICrossVM { /** * Returns the Cadence address defining the associated type */ function getCadenceAddress() external view returns (string memory); /** * Returns the Cadence Type identifier associated with the EVM contract */ function getCadenceIdentifier() external view returns (string memory); } ``` As an example, [`ICrossVM` is already implemented] and in use in the bridged [ERC721] and [ERC20] templates. If you want to register a custom association for an NFT that is native to Cadence, which means that your project distributes NFTs to users on the Cadence side, then your ERC721 contract must implement the `CrossVMBridgeERC721Fulfillment` contract. This is a required conformance that does three primary things: 1. Implements the mint/escrow pattern expected by the VM bridge. 2. Allows for the passing of arbitrary abi-encodable metadata from the Cadence NFT at the time of bridging. 3. Exposes two optional hooks, which allows you to update the fulfilled token's URI with the provided metadata at the time of bridging. Here is the Solidity contract to implement: ```solidity abstract contract CrossVMBridgeERC721Fulfillment is ICrossVMBridgeERC721Fulfillment, CrossVMBridgeCallable, ERC721 { /** * Initializes the bridge EVM address such that only the bridge COA can call privileged methods */ constructor(address _vmBridgeAddress) CrossVMBridgeCallable(_vmBridgeAddress) {} /** * @dev Fulfills the bridge request, minting (if non-existent) or transferring (if escrowed) the * token with the given ID to the provided address. For dynamic metadata handling between * Cadence & EVM, implementations should override and assign metadata as encoded from Cadence * side. If overriding, be sure to preserve the mint/escrow pattern as shown in the default * implementation. See `_beforeFulfillment` and `_afterFulfillment` hooks to enable pre-and/or * post-processing without the need to override this function. * * @param _to address of the token recipient * @param _id the id of the token being moved into EVM from Cadence * @param _data any encoded metadata passed by the corresponding Cadence NFT at the time of * bridging into EVM */ function fulfillToEVM(address _to, uint256 _id, bytes memory _data) external onlyVMBridge { _beforeFulfillment(_to, _id, _data); // hook allowing implementation to perform pre-fulfillment validation if (_ownerOf(_id) == address(0)) { _mint(_to, _id); // Doesn't exist, mint the token } else { // Should be escrowed under vm bridge - transfer from escrow to recipient _requireEscrowed(_id); safeTransferFrom(vmBridgeAddress(), _to, _id); } _afterFulfillment(_to, _id, _data); // hook allowing implementation to perform post-fulfillment processing emit FulfilledToEVM(_to, _id); } /** * @dev Returns whether the token is currently escrowed under custody of the designated VM bridge * * @param _id the ID of the token in question */ function isEscrowed(uint256 _id) public view returns (bool) { return _ownerOf(_id) == vmBridgeAddress(); } /** * @dev Returns whether the token is exists or not defined positively by whether the owner of * the token is 0x0. * * @param _id the ID of the token in question */ function exists(uint256 _id) public view returns (bool) { return _ownerOf(_id) != address(0); } /** * @dev Allows a caller to determine the contract conforms to implemented interfaces */ function supportsInterface(bytes4 interfaceId) public view virtual override(CrossVMBridgeCallable, ERC721, IERC165) returns (bool) { return interfaceId == type(ICrossVMBridgeERC721Fulfillment).interfaceId || interfaceId == type(ICrossVMBridgeCallable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Internal method that reverts with FulfillmentFailedTokenNotEscrowed if the provided * token is not escrowed with the assigned vm bridge address as owner. * * @param _id the token id that must be escrowed */ function _requireEscrowed(uint256 _id) internal view { if (!isEscrowed(_id)) { revert FulfillmentFailedTokenNotEscrowed(_id, vmBridgeAddress()); } } /** * @dev This internal method is included as a step implementations can override and have * executed in the default fullfillToEVM call. * * @param _to address of the pending token recipient * @param _id the id of the token to be moved into EVM from Cadence * @param _data any encoded metadata passed by the corresponding Cadence NFT at the time of * bridging into EVM */ function _beforeFulfillment(address _to, uint256 _id, bytes memory _data) internal virtual { // No-op by default, meant to be overridden by implementations } /** * @dev This internal method is included as a step implementations can override and have * executed in the default fullfillToEVM call. * * @param _to address of the pending token recipient * @param _id the id of the token to be moved into EVM from Cadence * @param _data any encoded metadata passed by the corresponding Cadence NFT at the time of * bridging into EVM */ function _afterFulfillment(address _to, uint256 _id, bytes memory _data) internal virtual { // No-op by default, meant to be overridden by implementations for things like processing // and setting metadata } } ``` The `_beforeFulfillment()` and `_afterFulfillment()` hooks are `virtual`, which allows implementations to optionally override the methods and handle the provided metadata passed from your NFT if `EVMBytesMetadata` is resolved at the time of bridging. Also, notice that the `fulfillToEVM` method is `onlyVMBridge`, which allows the VM bridge to call the method either minting the NFT if it does not exist or transfer the NFT from escrow in a manner consistent with the bridge's mint/escrow pattern. ### Opt Out It's also recognized that the logic of some use cases may actually be compromised by the act of bridging, particularly in such a unique partitioned runtime environment. Such cases might include those that do not maintain ownership assumptions implicit to ecosystem standards. For instance, an ERC721 implementation may reclaim a user's assets after a month of inactivity. In such a case, bridging that ERC721 to Cadence would decouple the representation of ownership of the bridged NFT from the actual ownership in the defining ERC721 contract after the token had been reclaimed - there would be no NFT in escrow for the bridge to transfer on fulfillment of the NFT back to EVM. In such cases, projects may choose to opt-out of bridging, but **importantly must do so before the asset has been onboarded to the bridge**. For Solidity contracts, opting out is as simple as extending the [`BridgePermissions.sol` abstract contract] which defaults `allowsBridging()` to `false`. The bridge explicitly checks for the implementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not opted out of bridging. Similarly, Cadence contracts can implement the [`IBridgePermissions.cdc` contract interface]. This contract has a single method `allowsBridging()` with a default implementation returning `false`. Again, the bridge explicitly checks for the implementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not opted out of bridging. Should you later choose to turn on bridging, you can simply override the default implementation and return `true`. In both cases, `allowsBridging()` gates onboarding to the bridge. After the onboard occurs - **a permissionless operation anyone can execute** - the value of `allowsBridging()` is irrelevant and assets can move between VMs permissionlessly. ## Under the hood For an in-depth look at the high-level architecture of the bridge, see [FLIP #237] ### Additional resources For the current state of Flow EVM across various task paths, see the following resources: - [Flow EVM Equivalence forum post] - [EVM Integration FLIP #223] - [Gateway & JSON RPC FLIP #235] [Flow EVM Equivalence forum post]: https://forum.flow.com/t/evm-equivalence-on-flow-proposal-and-path-forward/5478 [EVM Integration FLIP #223]: https://github.com/onflow/flips/pull/225/files [Gateway & JSON RPC FLIP #235]: https://github.com/onflow/flips/pull/235) [FLIP #237]: https://github.com/onflow/flips/blob/main/application/20231222-evm-vm-bridge.md [`IBridgePermissions.cdc` contract interface]: https://github.com/onflow/flow-evm-bridge/blob/main/cadence/contracts/bridge/interfaces/IBridgePermissions.cdc [`BridgePermissions.sol` abstract contract]: https://github.com/onflow/flow-evm-bridge/blob/main/solidity/src/interfaces/BridgePermissions.sol [`ICrossVM` is already implemented]: https://github.com/onflow/flow-evm-bridge/blob/main/solidity/src/interfaces/ICrossVM.sol [ERC721]: https://github.com/onflow/flow-evm-bridge/blob/flip-318/solidity/src/templates/FlowEVMBridgedERC721.sol#L37-L43 [ERC20]: https://github.com/onflow/flow-evm-bridge/blob/flip-318/solidity/src/templates/FlowEVMBridgedERC20.sol#L13-L40 [Flow EVM bridge repo ExampleNFT contract]: https://github.com/onflow/flow-evm-bridge/blob/flip-318/cadence/contracts/example-assets/cross-vm-nfts/ExampleEVMNativeNFT.cdc#L352-L377 [the `ExampleNFT` contract]: https://github.com/onflow/flow-nft/blob/master/contracts/ExampleNFT.cdc#L173-L195 [Metadata Views]: ../../build/cadence/advanced-concepts/metadata-views.md [`SerializeMetadata` contract]: https://github.com/onflow/flow-evm-bridge/blob/main/cadence/contracts/utils/SerializeMetadata.cdc [@onflow/flow-nft/pull/203]: https://github.com/onflow/flow-nft/pull/203 [`TokenHandler`]: https://github.com/onflow/flow-evm-bridge/blob/main/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc [Preparing Custom Associations]: #preparing-custom-associations [Custom Associations]: https://github.com/onflow/flips/blob/main/application/20250131-cross-vm-nft-support.md [Automatic Onboarding Section]: #automatic-onboarding [Custom Association Section]: #custom-association-onboarding [Prep Your Assets for Bridging]: #prep-your-assets-for-bridging [How Flow EVM Works]: ../../build/evm/how-it-works.md [the `FungibleToken` interface]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc [the `NonFungibleToken` interface]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc) [here]: ../tokens/nft-cadence.md) [`0xdfc20aee650fcbdf`]: https://contractbrowser.com/account/0xdfc20aee650fcbdf/contracts [`0x1e4aa0b87d10b141`]: https://contractbrowser.com/account/0x1e4aa0b87d10b141/contracts) [`0xf8146b4aef631853f0eb98dbe28706d029e52c52`]: https://evm-testnet.flowscan.io/address/0xF8146B4aEF631853F0eB98DBE28706d029e52c52) [`0x1c6dea788ee774cf15bcd3d7a07ede892ef0be40`]: https://evm.flowscan.io/address/0x1C6dEa788Ee774CF15bCd3d7A07ede892ef0bE40) [`0x8781d15904d7e161f421400571dea24cc0db6938`]: https://evm-testnet.flowscan.io/address0x8781d15904d7e161f421400571dea24cc0db6938 [`0x8fdec2058535a2cb25c2f8cec65e8e0d0691f7b0`]: https://evm.flowscan.io/address/0x8FDEc2058535A2Cb25C2f8ceC65e8e0D0691f7B0) [`0x4d45CaD104A71D19991DE3489ddC5C7B284cf263`]: https://evm-testnet.flowscan.io/address/0x4d45CaD104A71D19991DE3489ddC5C7B284cf263 [`0x49631Eac7e67c417D036a4d114AD9359c93491e7`]: https://evm.flowscan.io/address/0x49631Eac7e67c417D036a4d114AD9359c93491e7 [`0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56`]: https://evm-testnet.flowscan.io/address/0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56 [`0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df`]: https://evm.flowscan.io/address/0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df [`0x0000000000000000000000023f946ffbc8829bfd`]: https://evm-testnet.flowscan.io/address/0x0000000000000000000000023f946FFbc8829BFD [`0x00000000000000000000000249250a5c27ecab3b`]: https://evm.flowscan.io/address/0x00000000000000000000000249250a5C27Ecab3B [`CadenceOwnedAccount` (COA)]: interacting-with-coa.md [FLIP-233]: https://github.com/onflow/flips/pull/233 [FLIP-318]: https://github.com/onflow/flips/blob/main/application/20250131-cross-vm-nft-support.md [Cross-VM Bridge]: https://www.github.com/onflow/flow-evm-bridge --- # File: /md/blockchain-development-tutorials/evm.md --- title: Flow EVM Guides description: Tutorials and guides for building on Flow EVM, integrating with popular Ethereum tools, and leveraging Flow's unique features. sidebar_position: 5 keywords: - Flow EVM - guides - tutorials - Ethereum - Solidity - Foundry - Hardhat - Remix - wagmi - RainbowKit - web3.js - ethers.js --- # Flow EVM Guides Flow EVM provides full Ethereum Virtual Machine (EVM) compatibility, which allows developers to deploy current Solidity contracts and use familiar Ethereum development tools on Flow's high-performance blockchain. These guides demonstrate how to leverage popular Ethereum tools and frameworks to build, test, and deploy smart contracts on Flow EVM while taking advantage of Flow's unique features like built-in randomness and efficient transaction processing. ## [Setup] Essential setup guides for Flow EVM development, which includes MetaMask integration and wallet configuration. Learn how to connect popular Ethereum tools to Flow's EVM-compatible network and prepare your development environment to build on Flow. ## [Frameworks] Modern JavaScript and React frameworks to build Flow EVM applications. These guides cover popular blockchain libraries like `ethers.js`, `web3.js`, `wagmi`, and RainbowKit. They provide practical implementation patterns for frontend development on Flow. ## [Development Tools] Professional Solidity development tools adapted for Flow EVM. Master Foundry's testing suite, Hardhat's TypeScript environment, and Remix's browser-based IDE for comprehensive smart contract development workflows on Flow. ## [Build a Fully-Onchain Image Gallery] Create a decentralized image gallery that stores images directly on the blockchain with Flow's efficient gas pricing. This comprehensive tutorial demonstrates how to build smart contracts for onchain image storage, implement factory patterns for user galleries, and create a modern React frontend with wallet integration. Learn how Flow's low gas costs allow applications that would be prohibitively expensive on other chains. ## Conclusion These EVM guides provide comprehensive coverage of the most popular Ethereum development tools and frameworks, adapted for Flow EVM development. Whether you want to migrate Ethereum applications or build new projects, these tutorials offer practical implementation patterns for wallet integration, contract deployment, and blockchain interaction on Flow's high-performance EVM-compatible network. [Setup]: /blockchain-development-tutorials/evm/setup [Frameworks]: /blockchain-development-tutorials/evm/frameworks [Development Tools]: /blockchain-development-tutorials/evm/development-tools [Build a Fully-Onchain Image Gallery]: ./image-gallery.md --- # File: /md/blockchain-development-tutorials/evm/development-tools.md --- title: Flow EVM Development Tools description: Comprehensive guides for using popular Solidity development tools and IDEs to build, test, and deploy smart contracts on Flow EVM. sidebar_position: 3 keywords: - Flow EVM - development tools - Solidity development - smart contract deployment - Foundry - Hardhat - Remix IDE - contract testing - blockchain development --- # Flow EVM Development Tools Professional smart contract development requires robust tools to write, test, deploy, and manage Solidity contracts. This section covers the most popular and powerful development tools in the Ethereum ecosystem, adapted for Flow EVM development. From command-line frameworks to browser-based IDEs, these tools provide everything you need for comprehensive smart contract development workflows. Each tool offers unique strengths: Foundry excels at testing and gas optimization, Hardhat provides extensive plugin ecosystems and TypeScript support, while Remix allows you to rapidly prototype without local setup. These tutorials demonstrate how to configure and use each tool effectively with Flow EVM, which allows you to choose the right development environment for your project needs. ## [Foundry] Master Foundry's powerful suite of development tools for advanced Solidity contract development, testing, and deployment on Flow EVM. This comprehensive guide covers how to set up Foundry projects, write and test ERC-20 contracts with extensive test coverage, and deploy to Flow networks with proper configuration and verification. Learn to leverage Foundry's forge, cast, and anvil tools for complete development workflows, which include contract interaction and state management. ## [Hardhat] Build and deploy Solidity smart contracts on Flow EVM with Hardhat's comprehensive development environment with TypeScript support and extensive plugins. This tutorial walks through complete project setup, network configuration for Flow testnet and mainnet, contract deployment with custom scripts, and contract verification on Flow's block explorer. You'll implement end-to-end workflows for HelloWorld contract development, testing, and deployment using Hardhat's powerful plugin ecosystem. ## [Remix] Develop and deploy smart contracts directly in the browser with Remix IDE with Flow EVM integration for rapid prototyping and development. This guide demonstrates how to set up Remix for Flow development, deploying contracts through MetaMask integration, and how to interact with deployed contracts with Remix's built-in debugging and interaction tools. Learn to use Remix for quick contract development, testing, and deployment without local development environment setup requirements. ## Conclusion These development tool guides provide comprehensive coverage of the most popular Solidity development environments, each optimized for Flow EVM development. Whether you prefer command-line tools like Foundry, comprehensive frameworks like Hardhat, or browser-based development with Remix, these tutorials offer practical workflows to build, test, and deploy professional smart contracts on Flow's high-performance EVM network. [Foundry]: ./foundry.md [Hardhat]: ./hardhat.md [Remix]: ./remix.md --- # File: /md/blockchain-development-tutorials/evm/development-tools/foundry.md --- title: Using Foundry with Flow description: 'Using Foundry to deploy a Solidity contract to Flow EVM.' sidebar_label: Foundry sidebar_position: 5 --- # Using Foundry with Flow Foundry is a suite of development tools that simplifies the process to develop and deploy Solidity contracts to EVM networks. This guide will walk you through how to deploy a Solidity contract to Flow EVM with the Foundry development toolchain. You can check out the official [Foundry docs]. In this guide, we'll deploy an ERC-20 token contract to Flow EVM using Foundry. We'll cover: - How to develop and test a basic ERC-20 contract - Deploy the contract to Flow EVM with Foundry tools - How to query the Testnet state - How to mutate Testnet state by sending transactions ## Overview To use Flow across all Foundry tools you need to: 1. Provide the Flow EVM RPC URL to the command you are using: ```shell --rpc-url https://testnet.evm.nodes.onflow.org ``` 2. Use the `--legacy` flag to turn off [EIP-1559] style transactions. Flow will support EIP-1559 soon and this flag won't be needed. As an example, we'll show you how to deploy a fungible token contract to Flow EVM with Foundry. You will see how the above flags are used in practice. ## Example: Deploy an ERC-20 Token Contract to Flow EVM ERC-20 tokens are the most common type of tokens on Ethereum. We'll use [OpenZeppelin] starter templates with Foundry on Flow Testnet to deploy our own token called `MyToken`. ### Installation The best way to install Foundry, is to use the `foundryup` CLI tool. You can get it with the following command: ```shell curl -L https://foundry.paradigm.xyz | bash ``` Install the tools: ```shell foundryup ``` This will install the Foundry tool suite: `forge`, `cast`, `anvil`, and `chisel`. You may need to reload your shell after `foundryup` installation. Check out the official [Installation Guide] for more information about different platforms or how to install specific versions. ### Wallet setup We first need to generate a key pair for our EVM account. We can do this with the `cast` tool: ```shell cast wallet new ``` `cast` will print the private key and address of the new account. We can then paste the account address into the [Faucet] to fund it with some Testnet FLOW tokens. You can verify the balance of the account after funding. Replace `$YOUR_ADDRESS` with the address of the account you funded: ```shell cast balance --ether --rpc-url https://testnet.evm.nodes.onflow.org $YOUR_ADDRESS ``` ### Project setup First, create a new directory for your project: ```shell mkdir mytoken cd mytoken ``` We can use `init` to initialize a new project: ```shell forge init ``` This will create a contract called `Counter` in the `contracts` directory with associated tests and deployment scripts. We can replace this with our own ERC-20 contract. To verify the initial setup, you can run the tests for `Counter`: ```shell forge test ``` The tests should pass. ### Write the ERC-20 token contract We'll use the OpenZeppelin ERC-20 contract template. To start, we'll add OpenZeppelin to our project: ```shell forge install OpenZeppelin/openzeppelin-contracts ``` Rename `src/Counter.sol` to `src/MyToken.sol` and replace the contents with the following: ```solidity pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MyToken is ERC20 { constructor(uint256 initialMint_) ERC20("MyToken", "MyT") { _mint(msg.sender, initialMint_); } } ``` The above is a basic ERC-20 token with the name `MyToken` and symbol `MyT`. It also mints the specified amount of tokens to the contract deployer. The amount is passed as a constructor argument during deployment. Before we comnpile, we also need to update the test file. ### Testing Rename `test/Counter.t.sol` to `test/MyToken.t.sol` and replace the contents with the following: ```solidity pragma solidity ^0.8.20; import {Test, console2, stdError} from "forge-std/Test.sol"; import {MyToken} from "../src/MyToken.sol"; contract MyTokenTest is Test { uint256 initialSupply = 420000; MyToken public token; address ownerAddress = makeAddr("owner"); address randomUserAddress = makeAddr("user"); function setUp() public { vm.prank(ownerAddress); token = new MyToken(initialSupply); } /* Test general ERC-20 token properties */ function test_tokenProps() public view { assertEq(token.name(), "MyToken"); assertEq(token.symbol(), "MyT"); assertEq(token.decimals(), 18); assertEq(token.totalSupply(), initialSupply); assertEq(token.balanceOf(address(0)), 0); assertEq(token.balanceOf(ownerAddress), initialSupply); } /* Test Revert transfer to sender with insufficient balance */ function test_transferRevertInsufficientBalance() public { vm.prank(randomUserAddress); vm.expectRevert(abi.encodeWithSignature("ERC20InsufficientBalance(address,uint256,uint256)", randomUserAddress, 0, 42)); token.transfer(ownerAddress, 42); } /* Test transfer */ function test_transfer() public { vm.prank(ownerAddress); assertEq(token.transfer(randomUserAddress, 42), true); assertEq(token.balanceOf(randomUserAddress), 42); assertEq(token.balanceOf(ownerAddress), initialSupply - 42); } /* Test transferFrom with approval */ function test_transferFrom() public { vm.prank(ownerAddress); token.approve(randomUserAddress, 69); uint256 initialRandomUserBalance = token.balanceOf(randomUserAddress); uint256 initialOwnerBalance = token.balanceOf(ownerAddress); vm.prank(randomUserAddress); assertEq(token.transferFrom(ownerAddress, randomUserAddress, 42), true); assertEq(token.balanceOf(randomUserAddress), initialRandomUserBalance + 42); assertEq(token.balanceOf(ownerAddress), initialOwnerBalance - 42); assertEq(token.allowance(ownerAddress, randomUserAddress), 69 - 42); } } ``` To make sure everything is okay, compile the contracts: ```shell forge compile ``` Run the tests: ```shell forge test ``` They should all succeed. ### Deploy to Flow Testnet We can now deploy `MyToken` with the `forge create` command. We need to provide the RPC URL, private key from a funded account with the faucet, and constructor arguments that is the initial mint amount in this case. We need to use the `--legacy` flag to turn off EIP-1559 style transactions. Replace `$DEPLOYER_PRIVATE_KEY` with the private key of the account you created earlier: ```shell forge create --broadcast src/MyToken.sol:MyToken \ --rpc-url https://testnet.evm.nodes.onflow.org \ --private-key $DEPLOYER_PRIVATE_KEY \ --constructor-args 42000000 \ --legacy ``` The above will print the deployed contract address. We'll use it in the next section to interact with the contract. ### Verify a smart contract After you deploy the contract, you can verify it so that others can see the source code and interact with it from Flow's block explorer. You can use the [`forge verify-contract`] command: ```shell forge verify-contract --rpc-url https://testnet.evm.nodes.onflow.org/ \ --verifier blockscout \ --verifier-url https://evm-testnet.flowscan.io/api \ $DEPLOYED_MYTOKEN_ADDRESS \ src/MyToken.sol:MyToken ``` :::info When you verify a Mainnet contract, be sure to use the Mainnet [RPC] and block explorer URLs. ::: ### Query Testnet state Based on the given constructor arguments, the deployer should own `42,000,000 MyT`. We can check the `MyToken` balance of the contract owner. Replace `$DEPLOYED_MYTOKEN_ADDRESS` with the address of the deployed contract and `$DEPLOYER_ADDRESS` with the address of the account you funded earlier: ```shell cast balance \ --rpc-url https://testnet.evm.nodes.onflow.org \ --erc20 $DEPLOYED_MYTOKEN_ADDRESS \ $DEPLOYER_ADDRESS ``` This will return the amount specified during deployment. We can also call the associated function directly in the contract: ```shell cast call $DEPLOYED_MYTOKEN_ADDRESS \ --rpc-url https://testnet.evm.nodes.onflow.org \ "balanceOf(address)(uint256)" \ $DEPLOYER_ADDRESS ``` We can query other data like the token symbol: ```shell cast call $DEPLOYED_MYTOKEN_ADDRESS \ --rpc-url https://testnet.evm.nodes.onflow.org \ "symbol()(string)" ``` ### Send Transactions Let's create a second account and move some tokens with a transaction. You can use `cast wallet new` to create a new test account. You don't need to fund it to receive tokens. Replace `$NEW_ADDRESS` with the address of the new account: ```shell cast send $DEPLOYED_MYTOKEN_ADDRESS \ --rpc-url https://testnet.evm.nodes.onflow.org \ --private-key $DEPLOYER_PRIVATE_KEY \ --legacy \ "transfer(address,uint256)(bool)" \ $NEW_ADDRESS 42 ``` We can check the balance of the new account: ```shell cast balance \ --rpc-url https://testnet.evm.nodes.onflow.org \ --erc20 $DEPLOYED_MYTOKEN_ADDRESS \ $NEW_ADDRESS ``` The deployer will also own fewer tokens now: ```shell cast balance \ --rpc-url https://testnet.evm.nodes.onflow.org \ --erc20 $DEPLOYED_MYTOKEN_ADDRESS \ $DEPLOYER_ADDRESS ``` [Foundry docs]: https://book.getfoundry.sh/) [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 [OpenZeppelin]: https://www.openzeppelin.com/ [Installation Guide]: https://book.getfoundry.sh/getting-started/installation [Faucet]: https://faucet.flow.com/fund-account [`forge verify-contract`]: https://book.getfoundry.sh/reference/forge/forge-verify-contract [RPC]: ../../../build/evm/networks.md --- # File: /md/blockchain-development-tutorials/evm/development-tools/hardhat.md --- title: Flow Hardhat Guide description: 'Using Hardhat to deploy a Solidity contract to Flow EVM.' sidebar_label: Hardhat sidebar_position: 2 --- # Flow Hardhat Guide Hardhat is an Ethereum development tool designed to facilitate the deployment, testing, and debugging of Solidity smart contracts. It provides a streamlined experience for developers who work with with Solidity contracts. ## Prerequisites ### Node Node v18 or higher, available for [download here]. For those new to Hardhat, we recommend that you exploare the [official Hardhat documentation] to get acquainted. The following instructions use `npm` to initialize a project and install dependencies: ### Wallet You'll also need a wallet that supports EVM. For this guide, a MetaMask account and its corresponding private key will work. ```shell mkdir hardhat-example cd hardhat-example npm init npm install --save-dev hardhat npx hardhat init ``` > When prompted, select TypeScript and to use `@nomicfoundation/hardhat-toolbox` to follow along with this guide. ### Fund Your wallet To deploy smart contracts, ensure your wallet has **$FLOW**. To obtain funds, navigate to the Flow [Faucet] and enter your wallet address. ## Deploy a smart contract with Hardhat This section guides you through the process of how to deploy smart contracts on the Flow network with Hardhat. ### Configuration First, incorporate the Testnet network into your `hardhat.config.ts`: ```javascript import { HardhatUserConfig } from 'hardhat/config'; import '@nomicfoundation/hardhat-toolbox'; const config: HardhatUserConfig = { solidity: '0.8.24', networks: { testnet: { url: 'https://testnet.evm.nodes.onflow.org', accounts: [``], // In practice, this should come from an environment variable and not be commited gas: 500000, // Example gas limit }, }, }; export default config; ``` To keep this example straightforward, we've included the account's private key directly in `hardhat.config.ts`. However, it is crucial to not commit private keys to your Git repository for security reasons. Instead, use environment variables to safely handle sensitive information. ### Deploy HelloWorld smart contract ## HelloWorld smart contract ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract HelloWorld { // Declare a public field of type string. string public greeting; // Constructor to initialize the greeting. // In Solidity, the constructor is defined with the "constructor" keyword. constructor() { greeting = "Hello, World!"; } // Public function to change the greeting. // The "public" keyword makes the function accessible from outside the contract. function changeGreeting(string memory newGreeting) public { greeting = newGreeting; } // Public function that returns the greeting. // In Solidity, explicit return types are declared. function hello() public view returns (string memory) { return greeting; } } ``` Deploy: 1. Create a file named `HelloWorld.sol` under `contracts` directory. 2. Add above `HelloWorld.sol` contract code to new file. 3. Create a `deploy.ts` file in `scripts` directory. 4. Paste in the following TypeScript code. ```javascript import { ethers } from 'hardhat'; async function main() { const [deployer] = await ethers.getSigners(); console.log('Deploying contracts with the account:', deployer.address); const deployment = await ethers.deployContract('HelloWorld'); console.log('HelloWorld address:', await deployment.getAddress()); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` 5. Run `npx hardhat run scripts/deploy.ts --network testnet` in the project root. 6. Copy the deployed `HelloWorld` address. This address will be used in other scripts. Output will look like this (with the exception that your address will be different): ```shell ❯ npx hardhat run scripts/deploy.ts --network testnet Deploying contracts with the account: ... HelloWorld address: 0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb ``` You can now search for your deployed contract on the [Flowscan] block explorer! ### Get HelloWorld contract greeting Now, we want to get the greeting from the deployed `HelloWorld` smart contract. ```javascript import { ethers } from 'hardhat'; import HelloWorldABI from '../artifacts/contracts/HelloWorld.sol/HelloWorld.json'; async function main() { // Replace with your contract's address const contractAddress = '0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb'; // Get hardhat provider const provider = ethers.provider; // Create a new contract instance const helloWorldContract = new ethers.Contract( contractAddress, HelloWorldABI.abi, provider, ); // Call the greeting function const greeting = await helloWorldContract.hello(); console.log('The greeting is:', greeting); } main().catch((error) => { console.error(error); process.exit(1); }); ``` Steps: 1. Create a `getGreeting.ts` file in the `scripts` directory. 2. Paste contents of script above. Make sure to update the contract address with the one from deployment in an earlier step. 3. Call script to get the greeting, `npx hardhat run scripts/getGreeting.ts --network testnet` 4. The output will be as follows: ```shell ❯ npx hardhat run scripts/getGreeting.ts --network testnet The greeting is: Hello, World! ``` ### Update greeting on HelloWorld smart contract Next, we'll add a script to update the greeting and log it. ```javascript import { ethers } from 'hardhat'; import HelloWorldABI from '../artifacts/contracts/HelloWorld.sol/HelloWorld.json'; async function main() { const contractAddress = '0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb'; const newGreeting = process.env.NEW_GREETING; if (!newGreeting) { console.error('Please set the NEW_GREETING environment variable.'); process.exit(1); } // Signer to send the transaction (e.g., the first account from the hardhat node) const [signer] = await ethers.getSigners(); // Contract instance with signer const helloWorldContract = new ethers.Contract( contractAddress, HelloWorldABI.abi, signer, ); console.log('The greeting is:', await helloWorldContract.hello()); // Create and send the transaction const tx = await helloWorldContract.changeGreeting(newGreeting); console.log('Transaction hash:', tx.hash); // Wait for the transaction to be mined await tx.wait().catch((error: Error) => {}); console.log('Greeting updated successfully!'); console.log('The greeting is:', await helloWorldContract.hello()); } main().catch((error) => { console.error(error); process.exit(1); }); ``` Here are the steps to follow: 1. Create an `updateGreeting.ts` script in the `scripts` directory. 2. Paste in the TypeScript above, make sure to update the contract address with the one from deployment in earlier step. 3. Call the new script, `NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network testnet` 4. The output will be: ```shell ❯ NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network testnet The greeting is: Hello, World! Transaction hash: 0x03136298875d405e0814f54308390e73246e4e8b4502022c657f04f3985e0906 Greeting updated successfully! The greeting is: Howdy! ``` ### Verify contract To verify your contract on [Flowscan], you can update your Hardhat config file. To do this, include the correct chainID, apiURL and browserURL: ```javascript import { HardhatUserConfig } from 'hardhat/config'; import '@nomicfoundation/hardhat-toolbox'; import "@nomicfoundation/hardhat-verify"; const PRIVATE_KEY = vars.get("EVM_PRIVATE_KEY"); const config: HardhatUserConfig = { solidity: '0.8.24', networks: { testnet: { url: 'https://testnet.evm.nodes.onflow.org', accounts: [PRIVATE_KEY], // In practice, this should come from an environment variable and not be commited gas: 500000, // Example gas limit }, }, etherscan: { apiKey: { // Is not required by blockscout. Can be any non-empty string 'testnet': "abc" }, customChains: [ { network: "testnet", chainId: 545, urls: { apiURL: "https://evm-testnet.flowscan.io/api", browserURL: "https://evm-testnet.flowscan.io/", } } ] }, sourcify: { enabled: false } }; export default config; ``` The [verify] plugin requires you to include constructor arguments with the verify task and ensures that they correspond to expected ABI signature. However, Blockscout ignores those arguments, so you may specify any values that correspond to the ABI. Execute the following command to verify the contract: ```shell npx hardhat verify --network testnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument 1" ``` [download here]: https://nodejs.org/en/download [official Hardhat documentation]: https://hardhat.org/tutorial/creating-a-new-hardhat-project [Faucet]: https://faucet.flow.com/fund-account [Flowscan]: https://evm-testnet.flowscan.io/ [verify]: https://docs.blockscout.com/developer-support/verifying-a-smart-contract/hardhat-verification-plugin --- # File: /md/blockchain-development-tutorials/evm/development-tools/remix.md --- title: Flow Remix Guide description: 'Using Remix to deploy a solidity contract to Flow EVM.' sidebar_label: Remix sidebar_position: 3 --- # Using Remix Remix is an open-source, web-based development environment tailored for EVM smart contract development. It offers developers a comprehensive suite of tools to write, deploy, and test smart contracts in Solidity. For more information, visit [Remix]. ## Add the Flow network to MetaMask ![Add Flow Network](./imgs/Remix-adding-metamask-network.gif) Navigate to the [Using EVM] page to find the button to add the Flow network information to your metamask. ## Fund Your Flow Account Navigate to the [Flow Testnet Faucet] to obtain FLOW tokens necessary to deploy a smart contract. ## Deploy a smart contract with Remix ![Deploy smart contract](./imgs/Remix-deploy-contract-flowevm.gif) ### HelloWorld smart contract ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract HelloWorld { // Declare a public field of type string. string public greeting; // Constructor to initialize the greeting. // In Solidity, the constructor is defined with the "constructor" keyword. constructor() { greeting = "Hello, World!"; } // Public function to change the greeting. // The "public" keyword makes the function accessible from outside the contract. function changeGreeting(string memory newGreeting) public { greeting = newGreeting; } // Public function that returns the greeting. // In Solidity, explicit return types are declared. function hello() public view returns (string memory) { return greeting; } } ``` ### Steps to deploy the HelloWorld smart contract 1. Create a file named `HelloWorld.sol`. 2. Select Solidity Compiler and compile `HelloWorld.sol`. 3. Select Deploy & Run Transactions. 4. Make sure to select `Injected Provider - Metamask` in Environment dropdown. 5. Deploy the `HelloWorld` smart contract. ## Call the Deployed Smart Contract ![Call Smart Contract](./imgs/Remix-call-getGreeting.gif) ### Use Ethers.js to Call the HelloWorld Smart Contract 1. Create a new `get-greeting.js` file under `scripts`. 2. Paste in the JavaScript code below. 3. Click on green play button to run. 4. Verify the greeting is "Hello World!". ```javascript // Import ethers from the ethers.js library const { ethers } = require('ethers'); // Define the contract ABI const contractABI = ['function hello() public view returns (string memory)']; // Define the contract address const contractAddress = '0x8a120383e6057b1f3aef4fa9b89c2f1b0a695926'; // Connect to the Ethereum network // This example uses the default provider from ethers.js, which connects to the Ethereum mainnet. // For a testnet or custom RPC, use ethers.getDefaultProvider('networkName') or new ethers.providers.JsonRpcProvider(url) const provider = new ethers.providers.Web3Provider(window?.ethereum); // Create a new contract instance const contract = new ethers.Contract(contractAddress, contractABI, provider); // Call the hello function of the contract async function getGreeting() { const greeting = await contract.hello(); console.log(greeting); } // Execute the function getGreeting(); ``` Follow the steps below to change the greeting and retrieve the new greeting. ## Updating the deployed smart contract ![Update smart contract](./imgs/Remix-update-greeting.gif) 1. Select the `HelloWorld.sol` file. 2. Select the `Deploy and Run Transaction` page. 3. Make sure to select `Injected Provider - Metamask` in Environment dropdown. 4. Type a new greeting in the text input next to `changeGreeting`. 5. Click `changeGreeting`. 6. Sign the Metamask transaction. 7. To verify the greeting changed, re-run the `get-greeting.js` script above. [Remix]: https://remix.ethereum.org/ [Flow Testnet Faucet]: https://faucet.flow.com/fund-account [Using EVM]: ../../../build/evm/using.mdx --- # File: /md/blockchain-development-tutorials/evm/frameworks.md --- title: Flow EVM Frameworks description: Learn how to integrate popular JavaScript and React frameworks with Flow EVM for building modern blockchain applications with familiar tools. sidebar_position: 2 keywords: - Flow EVM - JavaScript frameworks - React frameworks - ethers.js - web3.js - wagmi - viem - RainbowKit - blockchain libraries - frontend development --- # Flow EVM Frameworks Modern blockchain development relies on powerful JavaScript and React frameworks that simplify smart contract interactions and provide seamless user experiences. This section covers the most popular frontend frameworks and libraries that you can use to build Flow EVM applications, from low-level blockchain interaction libraries to high-level React components for wallet management and user interfaces. These frameworks allow developers to build sophisticated decentralized applications with familiar JavaScript and React patterns while leveraging Flow's high-performance EVM environment. Whether you want to build simple contract interfaces or complex multi-wallet applications, these tutorials provide practical implementation guidance for you to integrate proven blockchain development frameworks with Flow EVM. ## [Ethers.js] Discover how to use `ethers.js`, the most popular JavaScript library for blockchain interactions, to connect with Flow EVM and manage smart contracts. This guide covers provider setup, how to read blockchain data, how to write transactions with proper signers, and how to implement both read-only queries and state-changing operations. Learn to leverage ethers.js's intuitive interface for comprehensive Flow EVM contract interactions. ## [Web3.js] Master `web3.js` integration with Flow EVM for comprehensive smart contract development and blockchain interaction capabilities. This tutorial demonstrates how to initialize web3 instances with Flow endpoints, how to query blockchain state, how to manage accounts with private keys, and how to execute contract transactions. You'll work through practical examples, such as a complete Storage contract implementation with read and write operations. ## [Viem & Wagmi] Build modern React applications on Flow EVM with wagmi and viem with built-in Flow network configurations and React hooks. This comprehensive guide walks through how to create `Next.js` applications with wallet connection capabilities, smart contract interactions with React hooks, and proper error handling for blockchain operations. Learn to implement complete dApps with wagmi's powerful React integration and viem's efficient blockchain interactions. ## [RainbowKit] Integrate advanced wallet connection experiences into your Flow EVM applications using RainbowKit with custom Flow Wallet support. This tutorial demonstrates how to create custom wallet connectors, how to configure WalletConnect integration for seamless mobile and desktop connections, and how to implement comprehensive wallet onboarding flows. You'll build complete wallet connection interfaces that support Flow Wallet alongside other popular Ethereum wallets. ## Conclusion These framework guides provide comprehensive coverage of the most popular JavaScript and React tools for Flow EVM development. From low-level blockchain interactions with ethers.js and web3.js to sophisticated React applications with wagmi and RainbowKit, these tutorials offer practical implementation patterns for you to build modern decentralized applications on Flow's EVM-compatible network. [Ethers.js]: ./ethers.md [Web3.js]: ./web3-js.md [Viem & Wagmi]: ./wagmi.md [RainbowKit]: ./rainbowkit.md --- # File: /md/blockchain-development-tutorials/evm/frameworks/ethers.md --- title: Ethers.js on Flow Blockchain sidebar_label: Ethers sidebar_position: 1 --- # Ethers.js [ethers.js] is a powerful JavaScript library for interacting with Ethereum and other EVM-compatible blockchain networks. In this guide, we'll walk you through how to use `ethers.js` to interact with smart contracts on the Flow Blockchain. --- ## Installation To use `ethers.js` in your project, you'll first need to install the package. To do this, run the following command: ```bash bashCopy code npm install --save ethers ``` ## Setup After you install `ethers.js`, the next step is to import it into your project. To do this, add the following line of code at the beginning of your JavaScript file: ```jsx const ethers = require('ethers'); ``` ## Connect to Flow To connect to the Flow Blockchain with `ethers.js`, you need to create a new `JsonRpcProvider` instance with the appropriate RPC URL for Flow: ```jsx const ethers = require('ethers'); const url = 'https://testnet.evm.nodes.onflow.org/'; const provider = new ethers.providers.JsonRpcProvider(url); ``` **Note:** If you want to connect to the Flow mainnet, replace the above URL with `https://mainnet.evm.nodes.onflow.org`. ## Read data from the Blockchain After you set up your provider, you can start reading data from the Flow Blockchain. For instance, to retrieve the latest block number, you can use the `getBlockNumber` method: ```jsx async function getLatestBlock() { const latestBlock = await provider.getBlockNumber(); console.log(latestBlock); } ``` ## Write data to the Blockchain To send transactions or write data to the Flow Blockchain, you need to create a `Signer`. To do this, initialize a new `Wallet` object with your private key and the previously created `Provider`: ```jsx const privateKey = 'YOUR_PRIVATE_KEY'; const signer = new ethers.Wallet(privateKey, provider); ``` **Note:** Replace `'YOUR_PRIVATE_KEY'` with the actual private key of the wallet you want to use. ## Interact with smart contracts ethers.js also allows interaction with smart contracts on the Flow Blockchain. To do this, create a `Contract` object using the Application Binary Interface (ABI) and the address of the deployed contract: ```jsx const abi = [ // ABI of deployed contract ]; const contractAddress = 'CONTRACT_ADDRESS'; // read-only contract instance const contract = new ethers.Contract(contractAddress, abi, provider); ``` For contracts that require writing, you'll need to provide a `Signer` object instead of a `Provider`: ```jsx // write-enabled contract instance const contract = new ethers.Contract(contractAddress, abi, signer); ``` **Note:** Replace `'CONTRACT_ADDRESS'` with the actual address of your deployed contract. After you set up your `Contract` object, you can call methods on the smart contract as needed: ```jsx async function setValue(value) { const tx = await contract.set(value); console.log(tx.hash); } async function getValue() { const value = await contract.get(); console.log(value.toString()); } ``` [ethers.js]: https://docs.ethers.org/v5/ --- # File: /md/blockchain-development-tutorials/evm/frameworks/rainbowkit.md --- title: Using Rainbowkit with Flow Wallet description: A step-by-step guide on adding Flow Wallet as a custom Wallet to RainbowKit. sidebar_position: 4 sidebar_label: Rainbowkit --- # Using Rainbow Kit with FLow Wallet When you integrate Flow Wallet with [RainbowKit][1], you can seamlessly connect your Flow accounts through one of the most popular wallet connection interfaces. This guide walks you through how to define Flow Wallet as a custom wallet in RainbowKit and testing the integration. As you follow along, you can set up up a new RainbowKit project or use the code in this guide to integrate these steps into your current dApp. ## Objectives After you complete this guide, you'll be able to: - Create a custom Flow Wallet connector compatible with RainbowKit's interface. - Configure your Wagmi setup to support Flow Wallet connections. - Implement a complete wallet connection flow for Flow blockchain users. - Test and verify the Flow Wallet integration in your dApp. ## Prerequisites ### Next.js and modern frontend development The RainbowKit starter is built on `Next.js`, so familiarity with React, hooks, and modern frontend development will help you follow along. ## A Flow Wallet To use Flow Wallet with RainbowKit, install the Flow Wallet browser extension from the [Chrome Web Store][2]. After you install it, create or import an account to set up your wallet. For quick access, pin the extension to your browser toolbar. ## Set up your environment ### Initial setup The RainbowKit starter is built on`Next.js` and follows its standard project structure and conventions. Create a new project or ensure your current one has the necessary dependencies: ```bash $ npm init @rainbow-me/rainbowkit@latest $ cd my-rainbowkit-app $ npm run dev ``` The [RainbowKit] components are available throughout your application via the provided wrapper components. ### Create the Flow Wallet connector The first major step is to define the Flow Wallet connector. Create a new file called `flowWallet.ts` in `src/flowWallet.ts` to house the wallet configuration: ```tsx /* src/flowWallet.ts */ import { Wallet, getWalletConnectConnector } from '@rainbow-me/rainbowkit'; export interface MyWalletOptions { projectId: string; } export const flowWallet = ({ projectId }: MyWalletOptions): Wallet => ({ id: 'flow-wallet', name: 'Flow Wallet', rdns: 'com.flowfoundation.wallet', iconUrl: 'https://lilico.app/logo_mobile.png', iconBackground: '#41CC5D', downloadUrls: { android: 'https://play.google.com/store/apps/details?id=com.flowfoundation.wallet', ios: 'https://apps.apple.com/ca/app/flow-wallet-nfts-and-crypto/id6478996750', chrome: 'https://chromewebstore.google.com/detail/flow-wallet/hpclkefagolihohboafpheddmmgdffjm', qrCode: 'https://link.lilico.app', }, mobile: { getUri: (uri: string) => `https://fcw-link.lilico.app/wc?uri=${encodeURIComponent(uri)}`, }, qrCode: { getUri: (uri: string) => uri, instructions: { learnMoreUrl: 'https://wallet.flow.com', steps: [ { description: 'We recommend putting Flow Wallet on your home screen for faster access to your wallet.', step: 'install', title: 'Open the Flow Wallet app', }, { description: 'You can find the scan button on home page, a connection prompt will appear for you to connect your wallet.', step: 'scan', title: 'Tap the scan button', }, ], }, }, extension: { instructions: { learnMoreUrl: 'https://wallet.flow.com', steps: [ { description: 'We recommend pinning Flow Wallet to your taskbar for quicker access to your wallet.', step: 'install', title: 'Install the Flow Wallet extension', }, { description: 'Be sure to back up your wallet using a secure method. Never share your secret phrase with anyone.', step: 'create', title: 'Create or Import a Wallet', }, { description: 'Once you set up your wallet, click below to refresh the browser and load up the extension.', step: 'refresh', title: 'Refresh your browser', }, ], }, }, createConnector: getWalletConnectConnector({ projectId }), }); ``` ### Configure your Wagmi integration Next, update your Wagmi configuration to include Flow Wallet support. Modify your `wagmi.ts` file: ```tsx /* src/wagmi.ts */ 'use client'; import { connectorsForWallets } from '@rainbow-me/rainbowkit'; import { createConfig, http } from 'wagmi'; import { mainnet, flowMainnet } from 'viem/chains'; import { flowWallet } from './flowWallet'; /* We can leave this as is for the tutorial but it should be replaced with your own project ID for production use. */ const projectId = 'YOUR_PROJECT_ID'; const connectors = connectorsForWallets( [ { groupName: 'Recommended', wallets: [flowWallet] }, ], { appName: 'RainbowKit App', projectId, } ); export const config = createConfig({ connectors, chains: [flowMainnet, mainnet], ssr: true, transports: { [flowMainnet.id]: http(), [mainnet.id]: http(), }, }); ``` :::info WalletConnect Project ID Every dApp that relies on WalletConnect now needs to obtain a projectId from [WalletConnect Cloud (now rebranded as reown)]. This is absolutely free and only takes a few minutes. To get a Project ID, sign up at WalletConnect Cloud, create a new project, and copy the generated ID into the `projectId` variable in the `wagmi.ts` file. ::: ## Test Your Integration After you implement the Flow Wallet connector and configure Wagmi, follow these steps to verify that the integration works correctly in your dApp: 1. **Click "Connect Wallet"** – Open your application and click "Connect Wallet." 2. **Check for Flow Wallet** – Ensure Flow Wallet appears as an option in the RainbowKit wallet selection modal. If you haven't installed the browser extension and set up your wallet yet, you can find install it via the [Chrome Web Store][2]. 3. **Connect the Wallet** – Click on Flow Wallet in the selection modal. If you use the browser extension, open it and press "Connect." ![Rainbowkit dAPP UI](./imgs/rainbowkit-1.png) 4. **Verify Connection** – Confirm that your Flow Wallet is now connected and visible in your dApp's UI. ![Rainbowkit dAPP UI](./imgs/rainbowkit-2.png) ## Conclusion In this tutorial, you learned how to integrate Flow Wallet with [RainbowKit] and create a seamless wallet connection experience for your users. You should now be able to: - Create a custom Flow Wallet connector compatible with RainbowKit's interface. - Configure your Wagmi setup to support Flow Wallet connections. - Implement a complete wallet connection flow for Flow blockchain users. - Test and verify the Flow Wallet integration in your dApp. Now that you've completed this tutorial, you're ready to enhance your dApp with additional Flow blockchain features such as token transfers, NFT minting, and smart contract interactions. [1]: https://www.rainbowkit.com/ [2]: https://chromewebstore.google.com/detail/flow-wallet/hpclkefagolihohboafpheddmmgdffjm?hl=en [RainbowKit]: https://www.rainbowkit.com/ [WalletConnect Cloud (now rebranded as reown)]: https://cloud.reown.com/sign-in) --- # File: /md/blockchain-development-tutorials/evm/frameworks/wagmi.md --- title: Viem & Wagmi description: 'Using Wagmi to interact with Solidity contract to Flow EVM.' sidebar_label: Viem & Wagmi sidebar_position: 3 --- :::info Make sure to use `viem` version `2.9.6` or greater. This version contains flow EVM networks ::: # Viem & Wagmi Flow networks have been added to viem chain definitions [viem networks]. This allows for convenient flow network configuration when you use viem and wagmi. ## Viem Flow Config The configuration below uses Flow Testnet. Since this configuration is already in viem various properties are already set, like block explorer and json-rpc endpoint. See how this configuration is used in a nextjs wagmi web application below. ```javascript import { http, createConfig } from '@wagmi/core'; import { flowTestnet } from '@wagmi/core/chains'; import { injected } from '@wagmi/connectors'; export const config = createConfig({ chains: [flowTestnet], connectors: [injected()], transports: { [flowTestnet.id]: http(), }, }); ``` # Use Next.js and Wagmi This tutorial will guide you through how to create a simple web application, connect to an EVM capable wallet and interact with the "HelloWorld" smart contract to get and set greetings. We will not dive into how to manage transactions. ## Prerequisites - `Node.js` installed on your machine. - A code editor (such as Visual Studio Code). - Basic knowledge of React and `Next.js`. ## Step 1: Set up the Next.js project This tutorial will follow the [Wagmi getting-started manual tutorial]. First, let's create a Wagmi project named `flow-evm-wagmi`. We will use npm but you are welcome to use yarn or bun. ```bash npm create wagmi@latest # project name flow-evm-wagmi # Select 'React' then 'next' ``` After Wagmi automatic installation procedure. ```bash cd flow-evm-wagmi npm install ``` ## Step 2: Configure Wagmi and connect the Wallet Make sure you have Metamask installed and Flow network configured. For more information, see [Metamask and Flow blockchain]. Wagmi needs to know what networks to be aware of. Let's configure to use Flow Testnet and update the `config.ts` file with the following: ```javascript import { http, createConfig } from '@wagmi/core'; import { flowTestnet } from '@wagmi/core/chains'; import { injected } from '@wagmi/connectors'; export const config = createConfig({ chains: [flowTestnet], connectors: [injected()], transports: { [flowTestnet.id]: http(), }, }); ``` By default, Wagmi configures many wallets, MetaMask, Coinbase Wallet, and WalletConnect as wallet providers. Above, we simplify the code to only be interested in the Injected Provider, which we are interested in Metamask. Verify `page.tsx` code looks like the following. ```javascript 'use client'; import { useAccount, useConnect, useDisconnect } from 'wagmi'; function App() { const account = useAccount(); const { connectors, connect, status, error } = useConnect(); const { disconnect } = useDisconnect(); return ( <>

    Account

    status: {account.status}
    addresses: {JSON.stringify(account.addresses)}
    chainId: {account.chainId}
    {account.status === 'connected' && ( )}

    Connect

    {connectors.map((connector) => ( ))}
    {status}
    {error?.message}
    ); } export default App; ``` ![Connect Metamask](./imgs/Connect-Metamask.gif) This step relies on an already deployed HelloWorld contract. See [Using Remix] to deploy a smart contract on flow evm blockchain. Create or edit the simple `page.tsx` file in the app directory to have better styles, that's beyond this tutorial. We will modify `page.txs` to add a new `HelloWorld.tsx`. Replace `YOUR_CONTRACT_ADDRESS` with your deployed address. ## Step 3: Create the interface for HelloWorld contract Now, let's create a component to interact with the HelloWorld contract. Assume your contract is already deployed, and you have its address and ABI. - Create a new file, `HelloWorld.ts`, in the components directory. - Use Wagmi's hooks to read from and write to the smart contract: ```javascript import { useState } from 'react'; import { useContractRead, useContractWrite, useAccount, useConnect, } from 'wagmi'; import contractABI from './HelloWorldABI.json'; // Import your contract's ABI const contractAddress = 'YOUR_CONTRACT_ADDRESS'; const HelloWorld = () => { const [newGreeting, setNewGreeting] = useState(''); const { address, isConnected } = useAccount(); const { connect } = useConnect(); const { data: greeting } = useContractRead({ addressOrName: contractAddress, contractInterface: contractABI, functionName: 'hello', }); const { write: changeGreeting } = useContractWrite({ addressOrName: contractAddress, contractInterface: contractABI, functionName: 'changeGreeting', args: [newGreeting], }); if (!isConnected) { return ; } return (

    Current Greeting: {greeting}

    setNewGreeting(e.target.value)} placeholder="New greeting" />
    ); }; export default HelloWorld; ``` Replace YOUR_CONTRACT_ADDRESS with the actual address of your deployed HelloWorld contract. Also notice you need the HelloWorld contract ABI, save this to a new file called `HelloWorld.json` in the app directory. ```json { "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [ { "internalType": "string", "name": "newGreeting", "type": "string" } ], "name": "changeGreeting", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "greeting", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "hello", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" } ] } ``` ## Step 4: Integrate the HelloWorld component Finally, import and use the HelloWorld component in your `pages.tsx`, throw it at the bottom of the render section. ```javascript import HelloWorld from './helloWorld' // put at the bottom of the Connect section.

    Connect

    {connectors.map((connector) => ( ))}
    {status}
    {error?.message}
    // 👇👇👇👇👇👇👇👇👇👇👇
    ``` Now, you have a functional App that can connect to Metamask, display the current greeting from the "HelloWorld" smart contract, and update the greeting. To test it, update the greeting, sign a transaction in your Metamask, wait a minute, then refresh the website. Handling transactions are outside of this tutorial. We'll leave that as a future task. [Checkout Wagmi documentation] ![Update HelloWorld Greeting](./imgs/Update-HelloWorld-Greeting.gif) [viem networks]: https://github.com/wevm/viem/tree/main/src/chains/definitions [Wagmi getting-started manual tutorial]: https://wagmi.sh/react/getting-started [Metamask and Flow blockchain]: ../../../build/evm/using.mdx [Checkout Wagmi documentation]: https://wagmi.sh/react/getting-started [Using Remix]: ../development-tools/remix.md --- # File: /md/blockchain-development-tutorials/evm/frameworks/web3-js.md --- title: Web3.js on Flow Blockchain sidebar_label: Web3.js sidebar_position: 2 --- # Web3.js [Web3.js] is a Javascript library for building on EVM-compatible networks. It allows developers to interact with smart contracts, send transactions, and retrieve data from the network. ## Prerequisites :::info This guide assumes you have the latest version of [Node.js] installed. ::: To install `web3` in your project, run the following command: ```sh npm install web3 ``` ## Initialize Web3 with Flow To use `web3` in your project, first import the module and initialize your `Web3` instance with a Flow RPC endpoint. ```js const { Web3 } = require('web3'); const web3 = new Web3('https://testnet.evm.nodes.onflow.org'); ``` **Note:** If you want to connect to the Flow testnet, replace the above URL with `https://mainnet.evm.nodes.onflow.org`. ## Query The blockchain `web3` provides a number of methods for how to query the blockchain, such as to retrieve the latest block number, query account balances, and more. You can try with some of these methods to verify that your `web3` instance works correctly. ```js // Get the latest block number const blockNumber = await web3.eth.getBlockNumber(); console.log(blockNumber); // Latest block number // Get the balance of an account const balance = await web3.eth.getBalance('0x1234'); // Replace with any address console.log(balance); // Balance in attoFlow // Get the chain ID const chainId = await web3.eth.getChainId(); console.log(chainId); // Get the gas price const gasPrice = await web3.eth.getGasPrice(); console.log(gasPrice); // Gas price in attoFlow ``` For more information about other queries you can make `web3`, see the [Web3.js] official documentation. ## Interact with smart contracts The `web3` library allows developers to interact with smart contracts via the `web3.eth.Contract` API. For this example we will use the following `Storage` contract. We recommend that you deploy your own contract, which you can do with [Hardhat] or [Remix]. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Storage { uint256 public storedData; function store(uint256 x) public { storedData = x; } function retrieve() public view returns (uint256) { return storedData; } } ``` You can generate the ABI for this contract with the [`solc` compiler], or another tool such as [Hardhat]or [Remix]. Now that we have both the ABI and address of the contract, we can create a new `Contract` object for use in our application. ```js // Replace with the ABI of the deployed contract const abi = [ { inputs: [], stateMutability: 'nonpayable', type: 'constructor', }, { inputs: [ { internalType: 'uint256', name: 'x', type: 'uint256', }, ], name: 'store', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [], name: 'retrieve', outputs: [ { internalType: 'uint256', name: '', type: 'uint256', }, ], stateMutability: 'view', type: 'function', }, ]; // Replace with the address of the deployed contract const contractAddress = '0x4c7784ae96e7cfcf0224a95059573e96f03a4e70'; // Create a new contract object with the ABI and address const contract = new web3.eth.Contract(abi, contractAddress); ``` We can now interact with the contract on the network ith the `contract` object. ### Read state State can be read from the contract via the `call` function with one of the contract's methods. This will not change the state and will not send a transaction. ```js // Retrieve the current value stored in the contract // (this is using the `retrieve` method from the contract with no arguments) const result = await contract.methods.retrieve().call(); console.log(result); // Current value stored in the contract ``` ### Change state To mutate the state of the contract, we can send a transaction to the network. In order to send a transaction to the network, you will need an account with sufficient funds to pay for the transaction. :::info If you do not have an account yet, you can create one with the following command from your project's root directory: ```sh node -e "console.log(require('web3').eth.accounts.create())" ``` This is not a secure way to generate an account, and you should use a more secure method in a production environment. You can fund your account with the [Flow Faucet]. ::: We can use the `privateKeyToAccount` function to create an `Web3Account` object from our account's private key. ```js // You must replace this with the private key of the account you wish to use const account = web3.eth.accounts.privateKeyToAccount('0x1234'); ``` Then, we can sign a transaction with the user's account and send it to the network. ```js const newValue = 1337; // Replace with any value you want to store // Sign a transaction that stores a new value in the contract // (this is using the `store` method from the contract with the new value as an argument) let signed = await account.signTransaction({ from: account.address, to: contractAddress, data: contract.methods.store(newValue).encodeABI(), gas: 10000000n, // Replace with the gas limit you want to use gasPrice: await web3.eth.getGasPrice(), // Replace with the gas price you want to use }); // Send signed transaction to the network const result = await web3.eth.sendSignedTransaction(signed.rawTransaction); // { status: 1, transactionHash: '0x1234', ... } // status=1 means the transaction was successful console.log(result); ``` Now that the transaction was sent, the contract's state was updated. To verify this, we can query the contract's state again: ```js const result = await contract.methods.retrieve().call(); console.log(result); // New value stored in the contract ``` For more information about how to use smart contracts in web3.js, see the [official documentation]. [Web3.js]: https://web3js.org/ [Node.js]: https://nodejs.org/en [Flow Faucet]: https://faucet.flow.com/fund-account [`solc` compiler]: https://docs.soliditylang.org/en/latest/installing-solidity.html [Hardhat]: ../development-tools/hardhat.md) [Remix]: ../development-tools/remix.md). [official documentation]: https://docs.web3js.org/libdocs/Contract --- # File: /md/blockchain-development-tutorials/evm/image-gallery.md --- title: Build a Fully-Onchain Image Gallery description: Learn how to store images up to approximately 32kb onchain, on Flow EVM, easily - free with the Flow wallet, or sub-cent with any other wallet. sidebar_position: 4 keywords: - Flow - EVM, - Flow EVM, - storage, - Solidity, - Next.js, - React, - onchain storage, - base64, - image gallery, - smart contract, - blockchain, - gas efficiency, - web3, - dapp, - tutorial, - onchain app --- # Build a fully-onchain image gallery :::warning This tutorial has not been updated for the new [limit of 16 million gas per transaction] in Fusaka. It will still work, but you will need to resize the images to a maximum of about 160\*160 pixels. ::: :::info The [FlowtoBooth] tutorial series teaches you how to build a **fun benchmark app** and provides inspiration for the greater scope of possibilities to build on Flow since gas is so much less expensive. It is **not a production best-practice**. While everything in these tutorials works, you'll run into the following problems at production scale: - RPC Providers will likely rate-limit you for reading this much data at once. - NFT marketplaces may not display the images, likely due to the above. - 256\*256 images are huge by blockchain data standards, but too small for modern devices. ::: If you search for resources on how to store images of any significant size onchain, you'll be told it's either prohibitively expensive or even completely impossible. The reason for this is two-fold. First, the size limit for data on transactions is about 40kb. Second, to save 40kb takes almost all of the 30 million gas limit on most blockchains. The former constraint is immutable (though many chains have started to slowly increase this limit), which limits the app to images about 256\*256 pixels in size. The latter heavily depends on which chain you choose. At current gas prices on most chains, to use all 30 million gas in a block costs **several dollars** - or potentially **thousands** on ETH mainnet. At current prices on Flow, to spend 30 million gas costs **less than a penny**, usually one or two tenths of a cent. Much more computation is available at prices you or your users will want to pay for regular interactions. This includes, but isn't limited to: - Airdrop hundreds of NFTs with one transaction, for pennies. - Generation of large mazes. - Generation of large amounts of random numbers (with free [native VRF]). - Extensive string manipulation onchain. - Simple game AI logic. In this tutorial, we'll build a smart contract that can store and retrieve images onchain. We'll also build a simple frontend to interact with the contract on Flow and another chain. ![stage-1](./imgs/stage-1.png) ## Objectives After you complete this guide, you'll be able to: - Construct a composable onchain image gallery that can be used permissionlessly by onchain apps and other contracts to store and retrieve images. - Build an onchain app that can interact with this contract to save and display images. - Compare the price of when you spend 30 million gas on Flow with the price on other chains. ## Prerequisites ### Next.js and modern frontend development This tutorial uses [Next.js]. You don't need to be an expert, but it's helpful to be comfortable with development in a current React framework. You'll be on your own to select and use a package manager, manage Node versions, and other frontend environment tasks. ### Solidity You don't need to be an expert, but you should be comfortable enough to write code in [Solidity]. You can use [Hardhat], [Foundry], or even [Remix]. ## Build an image gallery contract Start a new smart contract project in the toolchain of your choice and install the [OpenZeppelin] contracts. In your project, stub out a new contract for your image gallery that inherits from the [Ownable] contract: ```solidity // ImageGallery.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; contract ImageGallery is Ownable { constructor(address _owner) Ownable(_owner) {} } ``` We pass the original owner of the contract as an argument in the constructor to give greater flexibility for ownership when this contract is deployed. ### Set up storage for images We'll store the images in a simple `struct` that holds the image as a `base64` encoded `string`and also contains a `string` for the description. Doing so allows the image to be directly used in HTML and makes it easier to test the contract directly with a block explorer, but it also makes the images 33% bigger. Another format is more efficient. These will be held in array: ```solidity struct Image { string description; string base64EncodedImage; } Image[] public images; ``` ### Construct functions to add and delete images Next, add a function that accepts a `_description` and `_base64EncodedImage` and adds them to the array. ```solidity function addImage( string memory _description, string memory _base64EncodedImage ) public onlyOwner { images.push(Image(_description, _base64EncodedImage)); } ``` Then, add one to delete the image at a given index: ```solidity function deleteImage(uint256 index) public onlyOwner { if (index >= images.length) { revert ImageIndexOutOfBounds(index, images.length); } for (uint256 i = index; i < images.length - 1; i++) { images[i] = images[i + 1]; } images.pop(); } ``` :::warning If the array gets big enough that for you to call `deleteImage` takes more than 30 million gas, it will brick this function. A safer and more gas-efficient method is to use a `mapping` with a counter as the index, and handling for the case where an index is empty. We do it this way to provide a way to delete accidentally uploaded images without making things too complex. ::: ### Retrieval functions Finally, add functions to get one image, get all of the images, and get the number of images in the collection. ```solidity function getImages() public view returns (Image[] memory) { return images; } function getImage(uint256 index) public view returns (Image memory) { if (index >= images.length) { revert ImageIndexOutOfBounds(index, images.length); } return images[index]; } function getImageCount() public view returns (uint256) { return images.length; } ``` ### Final contract After you complete the above, you'll end up with a contract similar to: ```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; contract ImageGallery is Ownable { struct Image { string description; string base64EncodedImage; } Image[] public images; error ImageIndexOutOfBounds(uint256 index, uint256 length); constructor(address _owner) Ownable(_owner) {} function addImage( string memory _description, string memory _base64EncodedImage ) public onlyOwner { images.push(Image(_description, _base64EncodedImage)); } function deleteImage(uint256 index) public onlyOwner { if (index >= images.length) { revert ImageIndexOutOfBounds(index, images.length); } for (uint256 i = index; i < images.length - 1; i++) { images[i] = images[i + 1]; } images.pop(); } function getImages() public view returns (Image[] memory) { return images; } function getImage(uint256 index) public view returns (Image memory) { if (index >= images.length) { revert ImageIndexOutOfBounds(index, images.length); } return images[index]; } function getImageCount() public view returns (uint256) { return images.length; } } ``` ### Create a factory The image gallery contract you've just constructed is intended to be a utility for other contracts and apps to use freely. You don't want just one gallery for everyone, you need to give the ability for any app or contract to create and deploy private galleries freely. Build a factory to deploy image galleries: ```solidity pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; import "./ImageGallery.sol"; contract ImageGalleryFactory { event ImageGalleryCreated(address indexed owner, address gallery); function createImageGallery(address _owner) public { ImageGallery gallery = new ImageGallery(_owner); emit ImageGalleryCreated(_owner, address(gallery)); } } ``` ### Track factories Some app designs may need multiple galleries for each user. For example, you might want to give users the ability to collect images in separate galleries for separate topics, dates, or events, similar to how many photo apps work on smartphones. To facilitate this feature, update your contract to keep track of which users created which galleries . You'll end up with: ```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; import "./ImageGallery.sol"; contract ImageGalleryFactory { event ImageGalleryCreated(address indexed owner, address gallery); mapping(address => address[]) userToGalleries; function createImageGallery(address _owner) public { ImageGallery gallery = new ImageGallery(_owner); emit ImageGalleryCreated(_owner, address(gallery)); userToGalleries[_owner].push(address(gallery)); } function getGalleries( address _owner ) public view returns (address[] memory) { return userToGalleries[_owner]; } } ``` ### Test the factory Write appropriate unit tests, then deploy and verify the factory on Flow Testnet. If you need help, check out: - Deploy and Verify with [Hardhat] - Deploy and Verify with [Foundry] - Deploy and Verify with [Remix] Navigate to [evm-testnet.flowscan.io], search for your contract, and navigate to the `contracts` tab, then `Read/Write contract`. You'll see something similar to: ![Factory on Flowscan](./imgs/factory-on-flowscan.png) `Connect` your wallet. Use the [Flow Wallet] if you want automatically sponsored gas on both mainnet and testnet, or use the [Flow Faucet] to grab some testnet funds if you prefer to use another wallet. Expand the `createImageGallery` function, click `self`, and then `Write` the function. ![createImageGallery](./imgs/create-image-gallery.png) Approve the transaction and wait for it to complete. Then, call `getGalleries` for your address to find the address of the gallery you've created. ### Test the image gallery Search for the address of your image gallery contract. It `won't` be verified, but if you use our exact contract, you will see a message from Flowscan that a verified contract with the same bytecode was found in the Blockscout DB. Click the provided link to complete the verification process. :::info The easiest way to get an ABI for the image gallery is to deploy one. You can do that now if you like. ::: If you're following along, but used your own contract, simply deploy and verify one copy of the contract directly, refresh the page, then complete the above. You could test `addImage` with a random string, but it's better to use a base64-encoded image. Search for and navigate to one of the many online tools that will base64 encode images. :::danger Most sites of this nature are free tools created by helpful programmers and are funded with ads, donations, or the generosity of the creator. But you never know who made them or what they're caching. **Never** upload or convert sensitive data on a free site. ::: Use the tool to convert an image that is ~30kb or smaller. Copy the string and paste it into the field in `addImage`. You can also add a `description`, but the bytes used will count towards the ~40kb limit. ![addImage](./imgs/add-image.png) Click `Write` and approve the transaction. Take note of the cost! You've saved an image onchain forever for just a little bit of gas! After the transaction goes through, call `getImage` with `0` as the index to retrieve your description and base64-encoded image. Paste your image string as the `src` for an `img` tag in an HTML snippet to confirm it worked. ```html
    ``` ## Build the frontend Now that your contracts are sorted and work, it's time to build an app to interact with it. We'll use [Next.js] for this, but the components we provide will be adaptable to other React frameworks. Run: ```bash npx create-next-app ``` We'll use' the default options. Next, install [rainbowkit], [wagmi], and their related dependencies: ```bash npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query ``` ### Provider setup Add a file called `providers` inside the `app` folder. In it, add your config and providers for [wagmi] and [rainbowkit]. You'll need to [add the Flow Wallet] as a custom wallet. It's not included by default because it has special features that aren't compatible with other blockchains. ```tsx 'use client'; import { connectorsForWallets } from '@rainbow-me/rainbowkit'; import { Wallet, getWalletConnectConnector } from '@rainbow-me/rainbowkit'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { createConfig, WagmiProvider } from 'wagmi'; import { RainbowKitProvider } from '@rainbow-me/rainbowkit'; import { flowTestnet } from 'viem/chains'; import { http } from 'wagmi'; const projectId = '51407fcf066d74968d9a1a4c6da0d994'; // Replace with your actual project ID export interface MyWalletOptions { projectId: string; } const flowWallet = ({ projectId }: MyWalletOptions): Wallet => ({ id: 'flow-wallet', name: 'Flow Wallet', rdns: 'com.flowfoundation.wallet', iconUrl: 'https://lilico.app/logo_mobile.png', iconBackground: '#41CC5D', downloadUrls: { android: 'https://play.google.com/store/apps/details?id=com.flowfoundation.wallet', ios: 'https://apps.apple.com/ca/app/flow-wallet-nfts-and-crypto/id6478996750', chrome: 'https://chromewebstore.google.com/detail/flow-wallet/hpclkefagolihohboafpheddmmgdffjm', qrCode: 'https://link.lilico.app', }, mobile: { getUri: (uri: string) => `https://fcw-link.lilico.app/wc?uri=${encodeURIComponent(uri)}`, }, qrCode: { getUri: (uri: string) => uri, instructions: { learnMoreUrl: 'https://wallet.flow.com', steps: [ { description: 'We recommend putting Flow Wallet on your home screen for faster access to your wallet.', step: 'install', title: 'Open the Flow Wallet app', }, { description: 'You can find the scan button on home page, a connection prompt will appear for you to connect your wallet.', step: 'scan', title: 'Tap the scan button', }, ], }, }, extension: { instructions: { learnMoreUrl: 'https://wallet.flow.com', steps: [ { description: 'We recommend pinning Flow Wallet to your taskbar for quicker access to your wallet.', step: 'install', title: 'Install the Flow Wallet extension', }, { description: 'Be sure to back up your wallet using a secure method. Never share your secret phrase with anyone.', step: 'create', title: 'Create or Import a Wallet', }, { description: 'Once you set up your wallet, click below to refresh the browser and load up the extension.', step: 'refresh', title: 'Refresh your browser', }, ], }, }, createConnector: getWalletConnectConnector({ projectId }), }); const connectors = connectorsForWallets( [ { groupName: 'Recommended', wallets: [flowWallet], }, ], { appName: 'Onchain Image Gallery', projectId: projectId, }, ); const wagmiConfig = createConfig({ connectors, chains: [flowTestnet], ssr: true, transports: { [flowTestnet.id]: http(), }, }); export default function Providers({ children }: { children: React.ReactNode }) { const queryClient = new QueryClient(); return ( {children} ); } ``` ### Add the connect button Open `page.tsx` and clear out the default content. Replace it with a message about what your app does and add the [rainbowkit] `Connect` button. Don't forget to import rainbowkit's `.css` file and the `ConnectButton` component: ```tsx import '@rainbow-me/rainbowkit/styles.css'; import { ConnectButton } from '@rainbow-me/rainbowkit'; export default function Home() { return (

    Image Gallery

    A decentralized image gallery built on Flow blockchain. All images saved directly onchain.

    A fun benchmark, not best practice for production!

    Free with gas sponsored by Flow with the Flow wallet. Sub-cent to save an image with other wallets.

    ); } ``` Test the app and make sure you can connect your wallet. ### Import Your contracts Next, you'll need to get your contract ABI and address into your frontend. If you use Hardhat, you can use the artifacts produced by the Ignition deployment process. If you use Foundry or Remix, you can adapt this process to the format of artifacts produced by those toolchains. :::tip If you didn't deploy the Image Gallery contract, do so now to generate an artifact that contains the ABI. ::: Add a folder in `app` called `contracts`. Copy the following files from your smart contract project, located in the `ignition` and `ignition/deployments/chain-545` folders: - `deployed_addresses.json` - `ImageGallery#ImageGallery.json` - `ImageGalleryFactory#ImageGalleryFactory.json` Additionally, add a file called `contracts.ts`. In it, create a hook to provide the ABI and addresses of your contracts conveniently: ```tsx import { useMemo } from 'react'; import { Abi } from 'viem'; import imageGalleryFactory from './ImageGalleryFactory#ImageGalleryFactory.json'; import imageGallery from './ImageGallery#ImageGallery.json'; import addresses from './deployed_addresses.json'; export default function useContracts() { return useMemo(() => { return { imageGalleryFactory: { address: addresses[ 'ImageGalleryFactory#ImageGalleryFactory' ] as `0x${string}`, abi: imageGalleryFactory.abi as Abi, }, imageGallery: { abi: imageGallery.abi as Abi, }, }; }, []); } ``` :::info Note that we **won't** include an `address` for the `imageGallery` itself. We'll need to set this dynamically as users might have more than one gallery. ::: ### Add content You can use a few strategies to organize the components that interact with the blockchain. One is to create a centralized component that stores all of the state related to smart contracts and uses a single instance of `useWriteContract`. This makes it easier to convey the transaction lifecycle to your users, at the cost of re-fetching all the data from your RPC provider after every transaction. This becomes sub-optimal if your app interacts with many contracts, or even different read functions within the same contract. Add a folder in `app` called `components`, and create a file called `Content.tsx`. In it, add the following: - Imports for React, wagmi, your contracts, and Tanstack - State variables for: - When a reload is needed - When you are waiting on a transaction response - The list of gallery addresses for the connected wallet - Hooks for: - `useAccount()` - `useQueryClient()` - `useContracts()` - `useWriteContract()` - `useWaitForTransactionReceipt()` - `useEffects` to: - Listen for a receipt and set `reload` to true and `awaitingResponse` false - Listen for needing a reload and invalidating the query for galleryAddresses - Error handling - Receipt of gallery addresses - A `useReadContract` to fetch the list of gallery addresses for this user - Frontend code to display the button to create a gallery if the user is signed in You'll end up with something similar to: ```tsx 'use client'; import { useEffect, useState } from 'react'; import { useAccount, useReadContract, useWaitForTransactionReceipt, useWriteContract, } from 'wagmi'; import useContracts from '../contracts/contracts'; import { useQueryClient } from '@tanstack/react-query'; export default function Content() { const [reload, setReload] = useState(false); const [awaitingResponse, setAwaitingResponse] = useState(false); const [galleryAddresses, setGalleryAddresses] = useState([]); const account = useAccount(); const queryClient = useQueryClient(); const { imageGalleryFactory } = useContracts(); const { data, writeContract, error: writeError } = useWriteContract(); const { data: receipt, error: receiptError } = useWaitForTransactionReceipt({ hash: data, }); useEffect(() => { if (receipt) { setReload(true); setAwaitingResponse(false); } }, [receipt]); useEffect(() => { if (reload) { setReload(false); queryClient.invalidateQueries({ queryKey: galleryAddressesQueryKey }); } }, [reload]); useEffect(() => { if (writeError) { console.error(writeError); setAwaitingResponse(false); } }, [writeError]); useEffect(() => { if (receiptError) { console.error(receiptError); setAwaitingResponse(false); } }, [receiptError]); const { data: galleryAddressesData, queryKey: galleryAddressesQueryKey } = useReadContract({ abi: imageGalleryFactory.abi, address: imageGalleryFactory.address, functionName: 'getGalleries', args: [account.address], }); useEffect(() => { if (galleryAddressesData) { const newAddresses = galleryAddressesData as string[]; newAddresses.reverse(); setGalleryAddresses(newAddresses); } }, [galleryAddressesData]); function handleCreateGallery() { setAwaitingResponse(true); writeContract({ abi: imageGalleryFactory.abi, address: imageGalleryFactory.address, functionName: 'createImageGallery', args: [account.address], }); } return (
    {account.isConnected && (
    )}
    ); } ``` **Don't forget** to add your `` component to `page.tsx`, below the `` component. Test the app and make sure you can complete the transaction to create a gallery. ### Gallery List Next, you'll need to display the list of a user's galleries and allow them to select which one they want to interact with. A dropdown list will serve this function well. Add a component called `AddressList.tsx`, and in it add: ```tsx import React, { useEffect, useState } from 'react'; type AddressDropdownProps = { addresses: string[]; // Array of EVM addresses handleSetActiveAddress: Function; }; const AddressDropdown: React.FC = ({ addresses, handleSetActiveAddress, }) => { const [selectedAddress, setSelectedAddress] = useState(''); useEffect(() => { if (selectedAddress) { console.log(selectedAddress); handleSetActiveAddress(selectedAddress); } }, [selectedAddress]); return (

    Select a Gallery

    ); }; export default AddressDropdown; ``` This component **doesn't** interact directly with the blockchain. It accepts the array of `addresses` and a function to handle setting the `activeAddress`. To use it in `Content.tsx`, you'll need to add a new state variable for the `activeAddress`: ```tsx const [activeAddress, setActiveAddress] = useState(null); ``` You'll also need a handler for when the `activeAddress` is set. You can't just use the `setActiveAddress()` function because you need to tell the app to reload if the user changes which gallery is active, so that the images in that gallery are loaded. ```tsx function handleSetActiveAddress(address: string) { setReload(true); setActiveAddress(address); } ``` Finally, add the new component under the `
    )} ); ``` Run the app, log in with your wallet **that has the gallery you created for testing**, and select the gallery. You'll now see an image that is stored onchain **forever**! ## Image uploader The last thing to do for this initial implementation is to add functionality so that users can upload their own images through the app and save them onchain without the need to do the base64 conversion on their own. For now, we'll just generate an error if the file is too big, but later on we can do that for the user as well. Add the `ImageUploader` component. This needs to be able to upload the image and display any errors. We'll keep the state for the image itself in `Content` so that it's accessible to other components: ```tsx import React, { useState } from 'react'; type ImageUploaderProps = { setUploadedBase64Image: (base64: string) => void; // Function to set the uploaded base64 image }; const ImageUploader: React.FC = ({ setUploadedBase64Image, }) => { const [error, setError] = useState(null); const handleImageUpload = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) { setError('No file selected'); return; } if (!file.type.startsWith('image/')) { setError('Only image files are allowed'); return; } if (file.size > 30 * 1024) { setError('Image size must be 30KB or smaller'); return; } const reader = new FileReader(); reader.onload = () => { const base64 = reader.result as string; setUploadedBase64Image(base64); setError(null); }; reader.onerror = () => { setError('Failed to read file'); }; reader.readAsDataURL(file); }; return (
    {error &&

    {error}

    }
    ); }; export default ImageUploader; ``` As before, we'll need to make some updates to `Content.tsx` to complete the implementation. First, add a state variable for the image: ```tsx const [uploadedBase64Image, setUploadedBase64Image] = useState(''); ``` Then add the `ImageUploader` to the `return`: ```tsx ``` Later on, you'll probably want to make a component that displays the uploaded image, but for now just add it below the uploader button component: ```tsx { uploadedBase64Image && (
    Uploaded
    ); } ``` Finally, you need to add a button and a handler to call the smart contract function to save the image onchain. ```tsx function handleSaveOnchain() { // console.log(uploadedBase64Image); setAwaitingResponse(true); writeContract({ abi: imageGallery.abi, address: activeAddress as `0x${string}`, functionName: 'addImage', args: ['', uploadedBase64Image], }); } ``` Add the button inside the check for an `uploadedBase64Image` so that it only displays when there is an image to upload: ```tsx { uploadedBase64Image && (
    Uploaded
    ); } ``` Test the app to save your new image, and make sure the error displays if you try to upload an image that is too large. ## Conclusion In this tutorial, you built a fully functional onchain image gallery with Flow EVM. You created smart contracts that can store images directly on the blockchain and a modern React frontend that allows users to interact with these contracts. The implementation demonstrates how Flow's efficient gas pricing makes operations that would be prohibitively expensive on other chains not just possible, but practical. Now that you have completed the tutorial, you should be able to: - Construct a composable onchain image gallery that can be used permissionlessly by onchain apps and other contracts to store and retrieve images. - Build an onchain app that can interact with this contract to save and display images. - Compare the price of spending 30 million gas on Flow with the price on other chains. Now that you've completed this tutorial, you're ready to explore more complex onchain storage patterns and build applications that take advantage of Flow's unique capabilities to store and process larger amounts of data than traditionally possible on other chains. [limit of 16 million gas per transaction]: https://ethereum.org/roadmap/fusaka/#how-does-new-16m-gas-limit-affects-contract-developers [FlowtoBooth]: https://flowtobooth.vercel.app/ [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [Solidity]: https://soliditylang.org/ [Hardhat]: ../../blockchain-development-tutorials/evm/development-tools/hardhat.md [Foundry]: ../../blockchain-development-tutorials/evm/development-tools/foundry.md [Remix]: ../../blockchain-development-tutorials/evm/development-tools/remix.md [native VRF]: ../native-vrf/vrf-in-solidity.md [OpenZeppelin]: https://www.openzeppelin.com/ [Ownable]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol [evm-testnet.flowscan.io]: https://evm-testnet.flowscan.io/ [Hardhat]: ../../blockchain-development-tutorials/evm/development-tools/hardhat.md [Foundry]: ../../blockchain-development-tutorials/evm/development-tools/foundry.md [Remix]: ../../blockchain-development-tutorials/evm/development-tools/remix.md [Flow Wallet]: https://wallet.flow.com/ [Flow Faucet]: https://faucet.flow.com/fund-account [add the Flow Wallet]: ./frameworks/rainbowkit.md [rainbowkit]: https://www.rainbowkit.com/ [wagmi]: https://wagmi.sh/ [viem]: https://viem.sh/ --- # File: /md/blockchain-development-tutorials/evm/setup.md --- title: Flow EVM Setup description: Essential setup guides for connecting wallets and configuring development environments for Flow EVM development. sidebar_position: 1 keywords: - Flow EVM - setup - MetaMask - wallet integration - network configuration - development environment - blockchain setup --- # Flow EVM setup Before you dive into Flow EVM development, you'll need to configure your development environment and connect the necessary tools. This section covers the essential setup steps required to build on Flow EVM, from wallet configuration to network integration. These foundational setup guides ensure you have the proper connections and configurations needed for seamless Flow EVM development. Whether you want to set up MetaMask for the first time or integrate Flow network support into your current app, these tutorials provide step-by-step instructions to establish a robust development environment on Flow's EVM-compatible blockchain. ## [Integrating MetaMask] Learn how to programmatically integrate the Flow network with MetaMask wallets with EIP-3035 and the MetaMask Custom Networks API. This guide demonstrates howw to create user-friendly network addition buttons that automatically configure Flow testnet and mainnet settings in MetaMask without manual user input. You'll implement proper error handling and user experience flows to ensure seamless wallet onboarding for your Flow EVM applications. ## Conclusion These setup guides provide the foundational knowledge needed to configure development environments and wallet connections for Flow EVM development. With proper MetaMask integration and network configuration, you'll be ready to build, test, and deploy applications on Flow's EVM-compatible blockchain with confidence and efficiency. [Integrating MetaMask]: ./integrating-metamask.mdx --- # File: /md/blockchain-development-tutorials/evm/setup/integrating-metamask.mdx --- title: Integrating Metamask sidebar_position: 1 --- import BrowserOnly from '@docusaurus/BrowserOnly'; import { AddNetworkButton } from '@site/src/components/addNetworkButton'; # Wallets & Configurations This document shows how to integrate the Flow Network programmatically with your app via MetaMask. If you want to add it to your wallet now, click the buttons below, or follow the [manual process]. {() => { // ******* If Chain Id changes, update the Chain ID in the AddNetworkButton component ******* return ; }} ## Metamask To integrate additional networks into MetaMask can pose challenges for users who lack technical expertise and may lead to errors. If you simplify this process, you can greatly enhance user onboarding for your application. This guide demonstrates how to create a straightforward button within your frontend application to streamline the addition of the Flow network to MetaMask. ### EIP-3035 & MetaMask [EIP-3035](https://eips.ethereum.org/EIPS/eip-3085) is an Ethereum Improvement Proposal that defines an RPC method for adding Ethereum-compatible chains to wallet applications. Since March 2021 MetaMask has implemented that EIP as part of their MetaMask [Custom Networks API](https://consensys.io/blog/connect-users-to-layer-2-networks-with-the-metamask-custom-networks-api). ### Flow Network configuration To add the Flow Testnet network to Metamask, add the following network configuration: ```js export const TESTNET_PARAMS = { chainId: '0x221', chainName: 'Flow', rpcUrls: ['https://testnet.evm.nodes.onflow.org'], nativeCurrency: { name: 'Flow', symbol: 'FLOW', decimals: 18, }, blockExplorerUrls: ['https://evm-testnet.flowscan.io/'] }; ``` ### Add Flow network To add this configuration to MetaMask, call the `wallet_addEthereumChain` method which is exposed by the web3 provider. ```js function addFlowTestnet() { injected.getProvider().then((provider) => { provider .request({ method: 'wallet_addEthereumChain', params: [TESTNET_PARAMS], }) .catch((error: any) => { console.log(error); }); }); } ``` The variable, `injected`, is initialized as a `web3-react/injected-connector` used to interface with MetaMask APIs. Usage for other popular web frameworks is similar. The typical usage would be to expose this button if you get errors when attempting to connect to MetaMask (i.e. `Wrong Network` or `Error Connecting`). ### User Experience Users of your app will need to first approve a connection to Metamask. After doing this, if you don't detect a successful Web3 network connection, you may present a dialog asking them to add the Flow network to their wallet. ![Metamask Network](./imgs/metamask-network.png) After they approve, your app will be connected to the Flow network. If you use this approach to add the Flow network to Metamask, you can avoid manual user data entry and ensure that users are ready to interact with your dApp! {/* Reference-style links, does not render on page */} [manual process]: ../../../build/evm/using.mdx --- # File: /md/blockchain-development-tutorials/flow-101.md --- title: Flow Blockchain 101 sidebar_position: 2 description: Learn why Flow blockchain is uniquely designed for consumer-scale decentralized applications. Understand Flow's multi-role architecture, native account abstraction, and EVM equivalence. keywords: - Flow blockchain - blockchain scaling - multi-role architecture - Cadence language - EVM equivalence - account abstraction - blockchain security - Flow features - blockchain trilemma - Flow EVM - smart contracts - Flow token - blockchain architecture - decentralization - consumer apps - MEV - miner-extractable value - maximum extractable value --- import { ActionCard } from '@site/src/components/ActionCard';
    ## What is Flow? Flow is a Layer 1 blockchain built from the ground up to support large-scale applications, especially in the world of consumer crypto. Originally developed by the team behind CryptoKitties, Flow was designed to address the limitations they experienced with other blockchains — particularly around scalability and user experience. At the heart of Flow's design is a modular architecture that separates the responsibilities of consensus, execution, verification, and collection across different node types. This allows the network to process many transactions in parallel without compromising decentralization or safety. Other defining features of Flow include: - **Modular architecture** that enables scalability without sharding. - **Fast finality**, making applications responsive and user-friendly. - **Resistance to Miner Extractable Value (MEV)**, protecting users from front-running. - **EVM equivalence**, allowing developers to deploy Solidity contracts on Flow EVM. - **Low compute fees (on the Cadence side) and low gas fees (on the EVM side)**, which make applications affordable and accessible to users. Flow has already powered some of the most successful Web3 products to date, including: - [NBA Top Shot]: One of the most widely adopted NFT applications in history. - [Disney Pinnacle]: A collectible platform with iconic Disney content. - [CryptoKitties: AllTheZen]: A spiritual successor to the original CryptoKitties. ### Flow EVM vs Cadence Flow supports two smart contract environments, giving builders the flexibility to use whichever tool fits best: - **Flow EVM** is fully compatible with the Ethereum Virtual Machine, allowing you to use Solidity, Hardhat, MetaMask, and other familiar tools with no changes. - **Cadence** is Flow's native smart contract language, purpose-built to handle digital assets safely and intuitively using a resource-oriented programming model. Developers can build entirely in one or mix both environments for hybrid applications. ## Flow features ### Onchain randomness Flow natively supports verifiable randomness through its built-in Verifiable Random Function (VRF), which developers can use directly in smart contracts. This removes the need for third-party randomness oracles in many cases. - [Learn more] ### Batch transactions Batching allows you to group multiple transactions together for atomic execution across both Flow EVM and Cadence-based contracts. This enables powerful cross-VM apps and composability. - [Explore the tutorial] ### Account linking Account linking is a unique feature that lets users connect different accounts — such as linking their Dapper Wallet to another address — without compromising control or security. This is particularly useful in onboarding flows and games. - [Read the guide] ## What Cadence enables Cadence is a smart contract language built specifically for digital assets. It uses a **resource-oriented programming model**, which ensures that assets like NFTs and tokens are treated as first-class citizens that can't be duplicated or accidentally lost. Key advantages of Cadence include: - A powerful **account model** that supports multiple keys and roles. - **Capability-based access control**, which lets users share or restrict access to resources with fine granularity. - A **resource-oriented system** that prevents bugs common in other ecosystems, like token duplication or loss due to programming mistakes. Cadence helps developers write safer code, faster — and is a great choice for apps where assets, ownership, and identity matter. ## Building on Flow ### Connecting to the network Flow provides robust support for both Cadence and EVM development. Here's how to get started: **For Flow EVM:** - [Connect to Testnet] - [Connect to Mainnet] **For Cadence:** - [Flow Network Overview and Setup] ### Developer tools **Cadence development:** - [Flow CLI]: Command-line tool for managing accounts, deploying contracts, and running scripts/transactions. - [@onflow/react-sdk]: A development toolkit to scaffold, simulate, and deploy Cadence apps quickly. **Flow EVM development:** - Fully compatible with Ethereum development tools like Hardhat, Foundry, MetaMask, and Ethers.js. If you know how to build for Ethereum, you'll feel right at home. ### Ecosystem partners and tools The Flow ecosystem includes infrastructure providers, wallet integrations, analytics tools, dev tooling, and much more. Browse the full list of partners and recommended resources: - [Explore the Ecosystem] ### Join the community Whether you're looking for support, feedback, or collaboration, Flow's community is active and welcoming: - **Discord**: [Connect] with other developers and get real-time help. - **Twitter/𝕏**: Follow [@flow_blockchain] for updates, announcements, and highlights. ### Flow Cadence Quickstart - [Contract Interaction]: Interact with your first Cadence smart contract on the Flow testnet. - [Local Development]: Set up your dev environment, run tests, add already deployed contracts to your environment with Dependency Manager, and deploy and use your first contract with the emulator. - [Simple Frontend]: Read and write from a smart contract using the hooks from [@onflow/react-sdk]. ### Flow EVM Quickstart - [EVM Quickstart]: Deploy a contract with Hardhat and interact with it using [Testnet Flowscan]. - [Foundry]: Build and deploy an ERC20 on Flow with Foundry. We also have guides for working with [Rainbowkit] and [wagmi]. [NBA Top Shot]: https://nbatopshot.com/ [Disney Pinnacle]: https://disneypinnacle.com/ [CryptoKitties: AllTheZen]: https://allthezen.cryptokitties.co/ [Learn more]: https://developers.flow.com/blockchain-development-tutorials/native-vrf [Explore the tutorial]: https://developers.flow.com/blockchain-development-tutorials/cross-vm-apps/introduction [Read the guide]: https://developers.flow.com/build/cadence/guides/account-linking-with-dapper [Connect to Testnet]: https://developers.flow.com/protocol/flow-networks/accessing-testnet [Connect to Mainnet]: https://developers.flow.com/protocol/flow-networks/accessing-mainnet [Flow Network Overview and Setup]: https://developers.flow.com/protocol/flow-networks [Flow CLI]: https://developers.flow.com/tools/flow-cli [@onflow/react-sdk]: https://developers.flow.com/tools/react-sdk [Explore the Ecosystem]: https://developers.flow.com/ecosystem [@flow_blockchain]: https://x.com/flow_blockchain [Connect]: https://discord.com/invite/flow [Contract Interaction]: ./cadence/getting-started/smart-contract-interaction.md [Local Development]: ./cadence/getting-started/cadence-environment-setup.md [Simple Frontend]: ./cadence/getting-started/building-a-frontend-app.md [EVM Quickstart]: ../build/evm/quickstart.md [Testnet Flowscan]: https://evm-testnet.flowscan.io/ [Foundry]: ./evm/development-tools/foundry.md [Rainbowkit]: ./evm/frameworks/rainbowkit.md [wagmi]: ./evm/frameworks/wagmi.md --- # File: /md/blockchain-development-tutorials/forte.md --- title: Forte Network Upgrade description: A series of tutorials covering new features and capabilities introduced in the Forte network upgrade for Flow blockchain. sidebar_position: 2 keywords: - Forte - network upgrade - Flow blockchain - Flow Actions - scheduled transactions - blockchain automation - DeFi workflows - time-based execution - composable protocols --- # Forte Network Upgrade Tutorials This series covers the new features and capabilities introduced in the Forte network upgrade for Flow blockchain. The Forte upgrade brings powerful new tools for building sophisticated decentralized finance (DeFi) applications, including automated DeFi workflows and time-based smart contract execution. ## What's new in Forte The Forte network upgrade introduces several features that expand Flow's capabilities: - **Flow Actions**: Standardized interfaces for building composable DeFi workflows. - **Scheduled Transactions**: Time-based smart contract execution and blockchain automation. - **Enhanced Composability**: New patterns for building complex, interconnected applications. ## Deployed Contract Addresses :::info Forte is **live** on emulator, testnet, and Mainnet. ::: import CopyButton from '@site/src/components/CopyButton'; | Contract | Testnet | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | Mainnet | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | DeFiActions | [0x0b11b1848a8aa2c0](https://testnet.flowscan.io/contract/A.0b11b1848a8aa2c0.DeFiActions?tab=deployments) | | [0x6d888f175c158410](https://flowscan.io/contract/A.6d888f175c158410.DeFiActions?tab=deployments) | | | DeFiActionsUtils | [0x0b11b1848a8aa2c0](https://testnet.flowscan.io/contract/A.0b11b1848a8aa2c0.DeFiActionsUtils?tab=deployments) | | [0x6d888f175c158410](https://flowscan.io/contract/A.6d888f175c158410.DeFiActionsUtils?tab=deployments) | | | FungibleTokenConnectors | [0x4cd02f8de4122c84](https://testnet.flowscan.io/contract/A.4cd02f8de4122c84.FungibleTokenConnectors?tab=deployments) | | [0x0c237e1265caa7a3](https://flowscan.io/contract/A.0c237e1265caa7a3.FungibleTokenConnectors?tab=deployments) | | | ERC4626Utils | [0x7014dcffa1f14186](https://testnet.flowscan.io/contract/A.7014dcffa1f14186.ERC4626Utils?tab=deployments) | | [0x04f5ae6bef48c1fc](https://flowscan.io/contract/A.04f5ae6bef48c1fc.ERC4626Utils?tab=deployments) | | | ERC4626PriceOracles | [0x7014dcffa1f14186](https://testnet.flowscan.io/contract/A.7014dcffa1f14186.ERC4626PriceOracles?tab=deployments) | | [0x04f5ae6bef48c1fc](https://flowscan.io/contract/A.04f5ae6bef48c1fc.ERC4626PriceOracles?tab=deployments) | | | ERC4626SinkConnectors | [0x7014dcffa1f14186](https://testnet.flowscan.io/contract/A.7014dcffa1f14186.ERC4626SinkConnectors?tab=deployments) | | [0x04f5ae6bef48c1fc](https://flowscan.io/contract/A.04f5ae6bef48c1fc.ERC4626SinkConnectors?tab=deployments) | | | ERC4626SwapConnectors | [0x7014dcffa1f14186](https://testnet.flowscan.io/contract/A.7014dcffa1f14186.ERC4626SwapConnectors?tab=deployments) | | [0x04f5ae6bef48c1fc](https://flowscan.io/contract/A.04f5ae6bef48c1fc.ERC4626SwapConnectors?tab=deployments) | | | EVMNativeFLOWConnectors | [0xbee3f3636cec263a](https://testnet.flowscan.io/contract/A.bee3f3636cec263a.EVMNativeFLOWConnectors?tab=deployments) | | [0x1a771b21fcceadc2](https://flowscan.io/contract/A.1a771b21fcceadc2.EVMNativeFLOWConnectors?tab=deployments) | | | EVMTokenConnectors | [0xbee3f3636cec263a](https://testnet.flowscan.io/contract/A.bee3f3636cec263a.EVMTokenConnectors?tab=deployments) | | [0x1a771b21fcceadc2](https://flowscan.io/contract/A.1a771b21fcceadc2.EVMTokenConnectors?tab=deployments) | | | SwapConnectors | [0xaddd594cf410166a](https://testnet.flowscan.io/contract/A.addd594cf410166a.SwapConnectors?tab=deployments) | | [0xe1a479f0cb911df9](https://flowscan.io/contract/A.e1a479f0cb911df9.SwapConnectors?tab=deployments) | | | IncrementFiSwapConnectors | [0x494536c102537e1e](https://testnet.flowscan.io/contract/A.494536c102537e1e.IncrementFiSwapConnectors?tab=deployments) | | [0xe844c7cf7430a77c](https://flowscan.io/contract/A.e844c7cf7430a77c.IncrementFiSwapConnectors?tab=deployments) | | | IncrementFiFlashloanConnectors | [0x494536c102537e1e](https://testnet.flowscan.io/contract/A.494536c102537e1e.IncrementFiFlashloanConnectors?tab=deployments) | | [0xe844c7cf7430a77c](https://flowscan.io/contract/A.e844c7cf7430a77c.IncrementFiFlashloanConnectors?tab=deployments) | | | IncrementFiPoolLiquidityConnectors | [0x494536c102537e1e](https://testnet.flowscan.io/contract/A.494536c102537e1e.IncrementFiPoolLiquidityConnectors?tab=deployments) | | [0xe844c7cf7430a77c](https://flowscan.io/contract/A.e844c7cf7430a77c.IncrementFiPoolLiquidityConnectors?tab=deployments) | | | IncrementFiStakingConnectors | [0x494536c102537e1e](https://testnet.flowscan.io/contract/A.494536c102537e1e.IncrementFiStakingConnectors?tab=deployments) | | [0xe844c7cf7430a77c](https://flowscan.io/contract/A.e844c7cf7430a77c.IncrementFiStakingConnectors?tab=deployments) | | | BandOracleConnectors | [0xbb76ea2f8aad74a0](https://testnet.flowscan.io/contract/A.bb76ea2f8aad74a0.BandOracleConnectors?tab=deployments) | | [0xe36ef556b8b5d955](https://flowscan.io/contract/A.e36ef556b8b5d955.BandOracleConnectors?tab=deployments) | | | UniswapV2Connectors | [0x5f1153f29b57747f](https://testnet.flowscan.io/contract/A.5f1153f29b57747f.UniswapV2Connectors?tab=deployments) | | [0xf94f371678513b2b](https://flowscan.io/contract/A.f94f371678513b2b.UniswapV2Connectors?tab=deployments) | | ## Tutorial series ### [Flow Actions] Learn how to build DeFi applications with the Flow Actions framework, which allows developers to create composable DeFi workflows. Flow Actions provide standardized interfaces that make it easy to combine different DeFi protocols and create sophisticated financial applications. ### [Scheduled Transactions] Discover how to implement scheduled transactions for time-based smart contract execution on Flow. These tutorials cover how to create automated workflows, cron-like functionality, and time-sensitive blockchain applications that can execute without manual intervention. ### [Passkeys] Implement device-backed passkeys with the Web Authentication API to register Flow account keys and sign transactions with secure, user-friendly authentication. For more information, see the [advanced concepts documentation](../../build/cadence/advanced-concepts/passkeys.md). ### [High-Precision Fixed-Point Math] Learn about Flow's high-precision mathematical utilities for DeFi applications using UInt128-based 24-decimal fixed-point arithmetic. This tutorial covers how to perform accurate financial calculations, handle rounding modes, and avoid precision loss in complex DeFi operations like liquidity pools, yield farming, and token swaps. ## Get started To begin with Forte tutorials, we recommend that you start with: 1. **[Introduction to Flow Actions]** - Understand the core concepts and architecture 2. **[Scheduled Transactions Introduction]** - Learn about time-based execution capabilities ## Key benefits - **Automation**: Build applications that can execute complex workflows automatically - **Composability**: Combine different protocols and services seamlessly - **Time-based Logic**: Implement sophisticated scheduling and automation features - **Developer Experience**: Simplified interfaces for complex blockchain operations ## Conclusion The Forte network upgrade represents a significant evolution of Flow's capabilities, with powerful new tools to build the next generation of decentralized applications. These tutorials provide the foundation for you to leverage these new features to create sophisticated, automated, and composable blockchain applications. [Flow Actions]: ./flow-actions/index.md [Scheduled Transactions]: ./scheduled-transactions/index.md [Passkeys]: ../../build/cadence/advanced-concepts/passkeys.md [High-Precision Fixed-Point Math]: ./fixed-point-128-bit-math.md [Introduction to Flow Actions]: ./flow-actions/intro-to-flow-actions.md [Scheduled Transactions Introduction]: ./scheduled-transactions/scheduled-transactions-introduction.md --- # File: /md/blockchain-development-tutorials/forte/fixed-point-128-bit-math.md --- title: High-Precision Fixed-Point Math description: Learn about Flow's high-precision mathematical utilities for DeFi applications using UInt128-based 24-decimal fixed-point arithmetic for accurate financial calculations. sidebar_position: 3 keywords: - DeFi math - fixed-point arithmetic - UInt128 - high precision - financial calculations - rounding modes - math utilities - UFix64 conversion - DeFi Actions - price calculations sidebar_label: DeFi Math Utils --- # High-precision fixed-point 128 bit math Dealing with decimals is a notorious issue for most developers on other chains, especially when working with decentralized finance (DeFi). Blockchains are deterministic systems and floating-point arithmetic is non-deterministic across different compilers and architectures, which is why blockchains use fixed-point arithmetic via integers (scaling numbers by a fixed factor). The issue with this is that these fixed-point integers tend to be very imprecise when using various mathematical operations on them. The more operations you apply to these numbers, the more imprecise these numbers become. However [`DeFiActionsMathUtils`] provides a standardized library for high-precision mathematical operations in DeFi applications on Flow. The contract extends Cadence's native 8-decimal precision (`UFix64`) to 24 decimals using `UInt128` for intermediate calculations, ensuring accuracy in complex financial computations while maintaining deterministic results across the network. Through integration of this math utility library, developers can ensure that their DeFi protocols perform precise calculations for liquidity pools, yield farming, token swaps, and other financial operations without accumulating rounding errors. :::info While this document focuses on DeFi use cases, you can use these mathematical utilities for any application requiring high-precision decimal arithmetic beyond the native 8-decimal limitation of `UFix64`. ::: ## The precision problem DeFi applications often require multiple sequential calculations, and each operation can introduce rounding errors. When these errors compound over multiple operations, they can lead to: - Price manipulation vulnerabilities - Incorrect liquidity calculations - Unfair token distributions - Arbitrage opportunities from precision loss Consider a simple example: ```cadence // Native UFix64 with 8 decimals let price: UFix64 = 1.23456789 // Actually stored as 1.23456789 let amount: UFix64 = 1000000.0 let fee: UFix64 = 0.003 // 0.3% // Multiple operations compound rounding errors let afterFee = amount * (1.0 - fee) // Some precision lost let output = afterFee * price // More precision lost let finalAmount = output / someRatio // Even more precision lost ``` After three-to-four sequential operations, significant cumulative rounding errors can occur, especially when dealing with large amounts. Assuming a rounding error with eight decimals (1.234567885 rounds up to 1.23456789, causing a rounding error of 0.000000005), then after 100 operations with this error and dealing with one million dollars USDF, the protocol loses $0.5 in revenue from this lack of precision. This might not seem like a lot, but if we consider the TVL of Aave, which is around 40 billion USD, then that loss results in $20,000 USD! ## The solution: 24-decimal precision [`DeFiActionsMathUtils`] solves this with `UInt128` to represent fixed-point numbers with 24 decimal places (scaling factor of 10^24). This provides 16 additional decimal places for intermediate calculations, dramatically reducing precision loss. :::warning There is still some precision loss occurring, but it is much smaller than with eight decimals. ::: ### The three-tier precision system The contract implements a precision sandwich pattern: 1. **Input Layer**: `UFix64` (8 decimals) - User-facing values 2. **Processing Layer**: `UInt128` (24 decimals) - Internal calculations 3. **Output Layer**: `UFix64` (8 decimals) - Final results with smart rounding ```cadence // Import the contract import DeFiActionsMathUtils from 'ContractAddress' // Convert UFix64 to high-precision UInt128 let inputAmount: UFix64 = 1000.12345678 let highPrecision = DeFiActionsMathUtils.toUInt128(inputAmount) // highPrecision now represents 1000.123456780000000000000000 (24 decimals) // Perform calculations at 24-decimal precision let result = DeFiActionsMathUtils.mul(highPrecision, anotherValue) // Convert back to UFix64 with rounding let output = DeFiActionsMathUtils.toUFix64Round(result) ``` ## Core constants The contract defines several key constants: ```cadence access(all) let e24: UInt128 // 10^24 = 1,000,000,000,000,000,000,000,000 access(all) let e8: UInt128 // 10^8 = 100,000,000 access(all) let decimals: UInt8 // 24 ``` These constants ensure consistent scaling across all operations. ## Rounding modes Smart rounding is the strategic selection of rounding strategies based on the financial context of your calculation. After performing high-precision calculations at 24 decimals, you must convert the final results back to `UFix64` (8 decimals). How you handle this conversion can protect your protocol from losses, ensure fairness to users, and reduce systematic bias. [`DeFiActionsMathUtils`] provides four rounding modes, each optimized for specific financial scenarios: ```cadence access(all) enum RoundingMode: UInt8 { /// Rounds down (floor) - use for payouts access(all) case RoundDown /// Rounds up (ceiling) - use for fees/liabilities access(all) case RoundUp /// Standard rounding: < 0.5 down, >= 0.5 up access(all) case RoundHalfUp /// Banker's rounding: ties round to even number access(all) case RoundEven } ``` ### When to use each mode **RoundDown** - Choose this when you calculate user payouts, withdrawals, or rewards. When you round down, your protocol retains any fractional amounts, which protects against losses from accumulated rounding errors. This is the conservative choice when funds leave your protocol. ```cadence // When you calculate how much to pay out to users let userReward = DeFiActionsMathUtils.toUFix64RoundDown(calculatedReward) ``` **RoundUp** - Use this for protocol fees, transaction costs, or amounts owed to your protocol. Rounding up ensures your protocol collects slightly more, compensating for precision loss and preventing systematic under-collection of fees over many transactions. ```cadence // When calculating fees the protocol collects let protocolFee = DeFiActionsMathUtils.toUFix64RoundUp(calculatedFee) ``` **RoundHalfUp** - Apply this for general-purpose calculations, display values, or when presenting prices to users. This is the familiar rounding method (values 0.5 and above round up, below 0.5 round down) that users expect in traditional finance. ```cadence // For display values or general calculations let displayValue = DeFiActionsMathUtils.toUFix64Round(calculatedValue) ``` **RoundEven** - Select this for scenarios with many repeated calculations where you want to minimize systematic bias. Also known as "[banker's rounding]", this mode rounds ties (exactly 0.5) to the nearest even number, which statistically balances out over many operations, making it ideal for large-scale distributions or statistical calculations. ```cadence // For repeated operations where bias matters let unbiasedValue = DeFiActionsMathUtils.toUFix64(calculatedValue, DeFiActionsMathUtils.RoundingMode.RoundEven) ``` ## Core functions ### Conversion functions **Convert UFix64 to UInt128** ```cadence access(all) view fun toUInt128(_ value: UFix64): UInt128 ``` Converts a `UFix64` value to `UInt128` with 24-decimal precision. **Example:** ```cadence import DeFiActionsMathUtils from 'ContractAddress' let price: UFix64 = 123.45678900 let highPrecisionPrice = DeFiActionsMathUtils.toUInt128(price) // highPrecisionPrice = 123456789000000000000000000 (represents 123.45678900... with 24 decimals) ``` **Convert UInt128 to UFix64** ```cadence access(all) view fun toUFix64(_ value: UInt128, _ roundingMode: RoundingMode): UFix64 access(all) view fun toUFix64Round(_ value: UInt128): UFix64 access(all) view fun toUFix64RoundDown(_ value: UInt128): UFix64 access(all) view fun toUFix64RoundUp(_ value: UInt128): UFix64 ``` Converts a `UInt128` value back to `UFix64`, applying the specified rounding strategy. **Example:** ```cadence let highPrecisionValue: UInt128 = 1234567890123456789012345678 let roundedValue = DeFiActionsMathUtils.toUFix64Round(highPrecisionValue) // roundedValue = 1234567.89012346 (rounded to 8 decimals using RoundHalfUp) let flooredValue = DeFiActionsMathUtils.toUFix64RoundDown(highPrecisionValue) // flooredValue = 1234567.89012345 (truncated to 8 decimals) let ceilingValue = DeFiActionsMathUtils.toUFix64RoundUp(highPrecisionValue) // ceilingValue = 1234567.89012346 (rounded up to 8 decimals) ``` ## High-precision arithmetic ### Multiplication ```cadence access(all) view fun mul(_ x: UInt128, _ y: UInt128): UInt128 ``` Multiplies two 24-decimal fixed-point numbers, maintaining precision. **Example:** ```cadence let amount = DeFiActionsMathUtils.toUInt128(1000.0) let price = DeFiActionsMathUtils.toUInt128(1.5) let totalValue = DeFiActionsMathUtils.mul(amount, price) let result = DeFiActionsMathUtils.toUFix64Round(totalValue) // result = 1500.0 ``` :::info **Important:** The multiplication uses `UInt256` internally to prevent overflow: ::: ```cadence // Internal implementation prevents overflow return UInt128(UInt256(x) * UInt256(y) / UInt256(e24)) ``` ### Division ```cadence access(all) view fun div(_ x: UInt128, _ y: UInt128): UInt128 ``` Divides two 24-decimal fixed-point numbers, maintaining precision. **Example:** ```cadence let totalValue = DeFiActionsMathUtils.toUInt128(1500.0) let shares = DeFiActionsMathUtils.toUInt128(3.0) let pricePerShare = DeFiActionsMathUtils.div(totalValue, shares) let result = DeFiActionsMathUtils.toUFix64Round(pricePerShare) // result = 500.0 ``` ### UFix64 division with rounding For convenience, the contract provides direct division functions that handle conversion and rounding in one call: ```cadence access(all) view fun divUFix64WithRounding(_ x: UFix64, _ y: UFix64): UFix64 access(all) view fun divUFix64WithRoundingUp(_ x: UFix64, _ y: UFix64): UFix64 access(all) view fun divUFix64WithRoundingDown(_ x: UFix64, _ y: UFix64): UFix64 ``` **Example:** ```cadence let totalAmount: UFix64 = 1000.0 let numberOfUsers: UFix64 = 3.0 // Standard rounding let perUserStandard = DeFiActionsMathUtils.divUFix64WithRounding(totalAmount, numberOfUsers) // perUserStandard = 333.33333333 // Round down (conservative for payouts) let perUserSafe = DeFiActionsMathUtils.divUFix64WithRoundingDown(totalAmount, numberOfUsers) // perUserSafe = 333.33333333 // Round up (conservative for fees) let perUserFee = DeFiActionsMathUtils.divUFix64WithRoundingUp(totalAmount, numberOfUsers) // perUserFee = 333.33333334 ``` ## Common DeFi use cases ### Liquidity pool pricing (constant product AMM) Automated Market Makers (AMM) like Uniswap use the formula `x * y = k`. Here's how to calculate swap outputs with high precision: ```cadence import DeFiActionsMathUtils from 'ContractAddress' import FungibleToken from 'FungibleTokenAddress' access(all) fun calculateSwapOutput( inputAmount: UFix64, inputReserve: UFix64, outputReserve: UFix64, feeBasisPoints: UFix64 // e.g., 30 for 0.3% ): UFix64 { // Convert to high precision let input = DeFiActionsMathUtils.toUInt128(inputAmount) let reserveIn = DeFiActionsMathUtils.toUInt128(inputReserve) let reserveOut = DeFiActionsMathUtils.toUInt128(outputReserve) let fee = DeFiActionsMathUtils.toUInt128(feeBasisPoints) let basisPoints = DeFiActionsMathUtils.toUInt128(10000.0) // Calculate: inputWithFee = inputAmount * (10000 - fee) let feeMultiplier = DeFiActionsMathUtils.div( basisPoints - fee, basisPoints ) let inputWithFee = DeFiActionsMathUtils.mul(input, feeMultiplier) // Calculate: numerator = inputWithFee * outputReserve let numerator = DeFiActionsMathUtils.mul(inputWithFee, reserveOut) // Calculate: denominator = inputReserve + inputWithFee let denominator = reserveIn + inputWithFee // Calculate output: numerator / denominator let output = DeFiActionsMathUtils.div(numerator, denominator) // Return with conservative rounding (round down for user protection) return DeFiActionsMathUtils.toUFix64RoundDown(output) } ``` ### Compound interest calculations Calculate compound interest for yield farming rewards: ```cadence import DeFiActionsMathUtils from 0xYourAddress access(all) fun calculateCompoundInterest( principal: UFix64, annualRate: UFix64, // e.g., 0.05 for 5% periodsPerYear: UInt64, numberOfYears: UFix64 ): UFix64 { // Convert to high precision let p = DeFiActionsMathUtils.toUInt128(principal) let r = DeFiActionsMathUtils.toUInt128(annualRate) let n = DeFiActionsMathUtils.toUInt128(UFix64(periodsPerYear)) let t = DeFiActionsMathUtils.toUInt128(numberOfYears) let one = DeFiActionsMathUtils.toUInt128(1.0) // Calculate: rate per period = r / n let ratePerPeriod = DeFiActionsMathUtils.div(r, n) // Calculate: (1 + rate per period) let onePlusRate = one + ratePerPeriod // Calculate: number of periods = n * t let totalPeriods = DeFiActionsMathUtils.mul(n, t) // Note: For production, you'd need to implement a power function // This is simplified for demonstration // Calculate final amount with rounding return DeFiActionsMathUtils.toUFix64Round(finalAmount) } ``` ### Proportional distribution Distribute rewards proportionally among stakeholders: ```cadence import DeFiActionsMathUtils from 0xYourAddress access(all) fun calculateProportionalShare( totalRewards: UFix64, userStake: UFix64, totalStaked: UFix64 ): UFix64 { // Convert to high precision let rewards = DeFiActionsMathUtils.toUInt128(totalRewards) let stake = DeFiActionsMathUtils.toUInt128(userStake) let total = DeFiActionsMathUtils.toUInt128(totalStaked) // Calculate: (userStake / totalStaked) * totalRewards let proportion = DeFiActionsMathUtils.div(stake, total) let userReward = DeFiActionsMathUtils.mul(proportion, rewards) // Round down for conservative payout return DeFiActionsMathUtils.toUFix64RoundDown(userReward) } ``` ### Price impact calculation Calculate the price impact of a large trade: ```cadence import DeFiActionsMathUtils from 0xYourAddress access(all) fun calculatePriceImpact( inputAmount: UFix64, inputReserve: UFix64, outputReserve: UFix64 ): UFix64 { // Convert to high precision let input = DeFiActionsMathUtils.toUInt128(inputAmount) let reserveIn = DeFiActionsMathUtils.toUInt128(inputReserve) let reserveOut = DeFiActionsMathUtils.toUInt128(outputReserve) // Calculate initial price: outputReserve / inputReserve let initialPrice = DeFiActionsMathUtils.div(reserveOut, reserveIn) // Calculate new reserves after trade let newReserveIn = reserveIn + input let k = DeFiActionsMathUtils.mul(reserveIn, reserveOut) let newReserveOut = DeFiActionsMathUtils.div(k, newReserveIn) // Calculate final price: newOutputReserve / newInputReserve let finalPrice = DeFiActionsMathUtils.div(newReserveOut, newReserveIn) // Calculate impact: (initialPrice - finalPrice) / initialPrice let priceDiff = initialPrice - finalPrice let impact = DeFiActionsMathUtils.div(priceDiff, initialPrice) return DeFiActionsMathUtils.toUFix64Round(impact) } ``` ## Benefits of high-precision math ### Precision preservation The 24-decimal precision provides headroom for complex calculations: ```cadence // Chain multiple operations without significant precision loss let step1 = DeFiActionsMathUtils.mul(valueA, valueB) let step2 = DeFiActionsMathUtils.div(step1, valueC) let step3 = DeFiActionsMathUtils.mul(step2, valueD) let step4 = DeFiActionsMathUtils.div(step3, valueE) // Still maintains 24 decimals of precision until final conversion ``` ### Overflow protection The contract uses `UInt256` for intermediate multiplication to prevent overflow: ```cadence // Internal implementation protects against overflow access(all) view fun mul(_ x: UInt128, _ y: UInt128): UInt128 { return UInt128(UInt256(x) * UInt256(y) / UInt256(self.e24)) } ``` And includes explicit bounds checking when converting to `UFix64`: ```cadence access(all) view fun assertWithinUFix64Bounds(_ value: UInt128) { let MAX_1E24: UInt128 = 184_467_440_737_095_516_150_000_000_000_000_000 assert( value <= MAX_1E24, message: "Value exceeds UFix64.max" ) } ``` ## Best practices Always Use High Precision for Intermediate Calculations. **❌ Low precision (loses ~$0.50 per 1M USDC):** ```cadence let fee: UFix64 = amount * 0.003 let afterFee: UFix64 = amount - fee let output: UFix64 = afterFee * price ``` **✅ High precision (safe and accurate):** ```cadence // Convert once at the start let amountHP = DeFiActionsMathUtils.toUInt128(amount) let feeRate = DeFiActionsMathUtils.toUInt128(0.003) let priceHP = DeFiActionsMathUtils.toUInt128(price) // Perform all calculations at high precision let afterFeeHP = DeFiActionsMathUtils.mul(amountHP, DeFiActionsMathUtils.toUInt128(1.0) - feeRate) let outputHP = DeFiActionsMathUtils.mul(afterFeeHP, priceHP) // Convert once at the end with smart rounding let output = DeFiActionsMathUtils.toUFix64RoundDown(outputHP) ``` The pattern is simple: **convert → calculate → convert back**. The extra lines give you production-grade precision that protects your protocol from financial losses. Always validate that inputs are within acceptable ranges: ```cadence access(all) fun swap(inputAmount: UFix64) { pre { inputAmount > 0.0: "Amount must be positive" inputAmount <= 1000000.0: "Amount exceeds maximum" } let inputHP = DeFiActionsMathUtils.toUInt128(inputAmount) // ... perform calculations } ``` ## More resources - [View the DeFiActionsMathUtils source code] - [Flow DeFi Actions Documentation] - [Cadence Fixed-Point Numbers] ## Key takeaways - Use high precision (24 decimals) for all intermediate calculations. - Convert to `UFix64` only for final results. - Choose appropriate rounding modes based on your use case. - Always validate inputs and test edge cases. - Document your rounding decisions for maintainability. ## Conclusion [`DeFiActionsMathUtils`] gives Flow developers a significant advantage in building DeFi applications. With 24-decimal precision, it is much more accurate than typical blockchain implementations (which use 6-18 decimals). The standardized library eliminates the need to build custom math implementations. The simple **convert → calculate → convert back** pattern, combined with strategic rounding modes and built-in overflow protection, means you can focus on your protocol's business logic instead of low-level precision handling. At scale, this protection prevents thousands of dollars in losses from accumulated rounding errors. [`DeFiActionsMathUtils`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc [banker's rounding]: https://learn.microsoft.com/en-us/openspecs/microsoft_general_purpose_programming_languages/ms-vbal/98152b5a-4d86-4acb-b875-66cb1f49433e [View the DeFiActionsMathUtils source code]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc [Flow DeFi Actions Documentation]: https://developers.flow.com/blockchain-development-tutorials/forte/flow-actions [Cadence Fixed-Point Numbers]: https://cadence-lang.org/docs/language/values-and-types/fixed-point-nums-ints --- # File: /md/blockchain-development-tutorials/forte/flow-actions.md --- title: Flow Actions description: A series of tutorials on building decentralized finance applications using the Flow Actions framework and scheduled transactions. sidebar_position: 1 keywords: - flow actions - cadence interfaces - cadence actions - Flow Actions - defi workflows - scheduled transactions - time-based execution - blockchain automation - flow blockchain - cadence smart contracts - defi protocols - composable defi --- # Flow Actions tutorials
    This series covers how to build decentralized finance (DeFi) applications using the Flow Actions framework, enabling developers to create composable DeFi workflows. These tutorials are part of the Forte network upgrade, which introduces new capabilities to the Flow blockchain. ## Tutorials - **[Introduction to Flow Actions]** - Learn about Flow Actions, a suite of standardized Cadence interfaces that enable developers to compose complex DeFi workflows using small, reusable components. - **[Flow Actions Transaction]** - Learn how to create and execute Flow Actions transactions for automated DeFi workflows. - **[Connectors]** - Understand how connectors bridge standardized Flow Actions interfaces with different DeFi protocols. - **[Basic Combinations]** - Learn how to combine Flow Actions to create new workflows. [FLIP 339]: https://github.com/onflow/flips/pull/339/files [Introduction to Flow Actions]: ./intro-to-flow-actions.md [Flow Actions Transaction]: ./flow-actions-transaction.md [Connectors]: ./connectors.md [Basic Combinations]: ./basic-combinations.md --- # File: /md/blockchain-development-tutorials/forte/flow-actions/basic-combinations.md --- title: Basic Combinations description: Learn how to combine Flow Actions primitives to create powerful DeFi workflows sidebar_position: 3 keywords: - combinations - workflows - strategies - examples --- # Composing workflows with Flow Actions :::warning We are reviewing and finalizing Flow Actions in [FLIP 339]. The specific implementation may change as a part of this process. We will update these tutorials, but you may need to refactor your code if the implementation changes. ::: Flow Actions are designed to be **composable**, which means you can chain them together like LEGO blocks to build complex strategies. Each primitive has a standardized interface that works consistently across all protocols and eliminates the need to learn multiple APIs. This composability allows atomic execution of multi-step workflows within single transactions, which ensures either complete success or safe failure. When developers combine these primitives, they create sophisticated decentralized finance (DeFi) strategies like automated yield farming, cross-protocol arbitrage, and portfolio rebalancing. The [5 Flow Actions Primitives] are: - **Source** → Provides tokens on demand by withdrawl from vaults or reward claims. Sources respect minimum balance constraints and return empty vaults gracefully when nothing is available. - **Sink** → Accepts token deposits up to a specified capacity limit. Sinks perform no-ops rather than revert when deposits exceed capacity, which allows smooth workflow execution. - **Swapper** → Exchanges one token type for another through DEX trades or cross-chain bridges. Swappers support bidirectional operations and provide quote estimation for slippage protection. - **PriceOracle** → Provides real-time price data for assets from external feeds or DEX prices. Oracles handle staleness validation and return nil for unavailable prices rather than failing. - **Flasher** → Issues flash loans that must be repaid within the same transaction via callback execution. Flashers allow capital-efficient strategies like arbitrage and liquidations without the need for upfront capital. ## Learning objectives After you complete this tutorial, you will be able to: - Understand the key features of Flow Actions, which includes atomic composition, weak guarantees, and event traceability. - Create and use Sources to provide tokens from various protocols and locations. - Create and use Sinks to accept tokens up to defined capacity limits. - Create and use Swappers to exchange tokens between different types with price estimation. - Create and use Price Oracles to get price data for assets with consistent denomination. - Create and use Flashers to provide flash loans with atomic repayment requirements. - Use UniqueIdentifiers to trace and correlate operations across multiple Flow Actions. - Compose complex DeFi workflows by connecting multiple Actions in a single atomic transaction. ## Core Flow patterns ### Linear Flow (Source → Swapper → Sink) The most common pattern 1. Get tokens 2. Convert them 3. Deposit them ![vault source](./imgs/vault-source.png) ![swap vault sink](./imgs/swap-vaultsink.png) **Example**: Claim rewards → Swap to different token → Stake in new pool. ### Bidirectional Flow (Source ↔ Sink) Two-way operations where you can both deposit and withdraw. ![vault source](./imgs/vault-source.png) ![vault sink](./imgs/vault-sink.png) **Example**: Vault operations with both deposit and withdrawal capabilities. ### Aggregated Flow (Multiple Sources → Aggregator → Sink) Combine multiple sources for optimal results. ``` Source A → Aggregator → Sink Source B ↗ Source C ↗ ``` **Example**: Multiple DEX aggregators find the best swap route. ## Common DeFi workflow combinations ### Single token to LP (Zapper) **Goal**: Convert a single token into liquidity provider (LP) tokens in one transaction. The **Zapper** is a specialized connector that combines swapper and sink functionality. It takes a single token input and outputs LP tokens by automatically handling the token splitting, swapping, and liquidity provision process. ![zapper](./imgs/zapper.png) **How it works:** 1. Takes single token A as input. 2. Splits it into two portions. 3. Swaps one portion to token B. 4. Provides liquidity with A + B to get LP tokens. 5. Returns LP tokens as output. ```cadence // Zapper: Convert single FLOW token to FLOW/USDC LP tokens let zapper = IncrementFiPoolLiquidityConnectors.Zapper( token0Type: Type<@FlowToken.Vault>(), // Input token type token1Type: Type<@USDC.Vault>(), // Paired token type stableMode: false, // Use volatile pricing uniqueID: nil ) // Execute: Input 100 FLOW → Output FLOW/USDC LP tokens let flowTokens <- flowVault.withdraw(amount: 100.0) let lpTokens <- zapper.swap(nil, inVault: <-flowTokens) // Now you have LP tokens ready to stake or for further use. ``` **Benefits:** - **Simplicity**: Single transaction converts any token to LP position. - **Efficiency**: Automatically calculates optimal split ratios. - **Composability**: Output LP tokens work with any sink connector. ### Reward harvesting and conversion **Goal**: Claim staking rewards and convert them to a stable token. This workflow automatically claims accumulated staking rewards and converts them to a stable asset like USDC. It combines a rewards source, token swapper, and vault sink to create a seamless reward collection and conversion process. **How it works:** 1. Claims pending rewards from a staking pool with user certificate. 2. Swaps the reward tokens (for example, FLOW) to stable tokens (for example, USDC). 3. Deposits the stable tokens to a vault with capacity limits. 4. Returns any unconverted tokens back to the user. ```cadence // 1. Source: Claim rewards from staking pool let rewardsSource = IncrementFiStakingConnectors.PoolRewardsSource( userCertificate: userCert, poolID: 1, vaultType: Type<@FlowToken.Vault>(), overflowSinks: {}, uniqueID: nil ) // 2. Swapper: Convert rewards to stable token let swapper = IncrementFiSwapConnectors.Swapper( path: ["A.FlowToken", "A.USDC"], inVault: Type<@FlowToken.Vault>(), outVault: Type<@USDC.Vault>(), uniqueID: nil ) // 3. Sink: Deposit stable tokens to vault let vaultSink = FungibleTokenConnectors.VaultSink( max: 1000.0, depositVault: vaultCap, uniqueID: nil ) // Execute the workflow let rewards = rewardsSource.withdrawAvailable(1000.0) let stableTokens = swapper.swap(nil, inVault: <-rewards) vaultSink.depositCapacity(from: &stableTokens) ``` **Benefits:** - **Risk Reduction**: converts volatile reward tokens to stable assets. - **Automation**: single transaction handles claim, swap, and storage. - **Capital Efficiency**: no manual intervention needed for reward management. ### Liquidity provision & yield farming **Goal**: Convert single token to LP tokens for yield farming This workflow takes a single token from your vault, converts it into liquidity provider (LP) tokens, and immediately stakes them for yield farming rewards. It combines vault operations, zapping functionality, and staking in one seamless transaction. **How it works:** 1. Withdraws single token (for example, FLOW) from vault with minimum balance protection. 2. Uses Zapper to split token and create LP position (FLOW/USDC pair). 3. Stakes the resulting LP tokens in a yield farming pool. 4. Begins to earn rewards on the staked LP position. ```cadence // 1. Source: Provide single token (e.g., FLOW) let flowSource = FungibleTokenConnectors.VaultSource( min: 100.0, withdrawVault: flowVaultCap, uniqueID: nil ) // 2. Zapper: Convert to LP tokens let zapper = IncrementFiPoolLiquidityConnectors.Zapper( token0Type: Type<@FlowToken.Vault>(), token1Type: Type<@USDC.Vault>(), stableMode: false, uniqueID: nil ) // 3. Sink: Stake LP tokens for rewards let stakingSink = IncrementFiStakingConnectors.PoolSink( staker: user.address, poolID: 2, uniqueID: nil ) // Execute the workflow let flowTokens = flowSource.withdrawAvailable(100.0) let lpTokens = zapper.swap(nil, inVault: <-flowTokens) stakingSink.depositCapacity(from: &lpTokens) ``` **Benefits:** - **Yield Optimization**: converts idle tokens to yield-generating LP positions. - **Single Transaction**: no need for multiple manual steps or approvals. - **Automatic Staking**: LP tokens immediately start earning rewards. ### Cross-VM bridge and swap **Goal**: Bridge tokens from Cadence to EVM, swap them, then bridge back. This workflow demonstrates Flow's unique cross-VM capabilities by bridging tokens from Cadence to Flow EVM, executes a swap with UniswapV2-style routing, and bridges the results back to Cadence. This allows access to EVM-based DEX liquidity and maintain Cadence token ownership. **How it works:** 1. Withdraws tokens from Cadence vault with minimum balance protection. 2. Bridges tokens from Cadence to Flow EVM environment. 3. Executes swap with UniswapV2 router on EVM side. 4. Bridges the swapped tokens back to Cadence environment. 5. Deposits final tokens to target Cadence vault. ```cadence // 1. Source: Cadence vault let cadenceSource = FungibleTokenConnectors.VaultSource( min: 50.0, withdrawVault: cadenceVaultCap, uniqueID: nil ) // 2. EVM Swapper: Cross-VM swap let evmSwapper = UniswapV2SwapConnectors.Swapper( routerAddress: EVM.EVMAddress(0x...), path: [tokenA, tokenB], inVault: Type<@FlowToken.Vault>(), outVault: Type<@USDC.Vault>(), coaCapability: coaCap, uniqueID: nil ) // 3. Sink: Cadence vault for swapped tokens let cadenceSink = FungibleTokenConnectors.VaultSink( max: nil, depositVault: usdcVaultCap, uniqueID: nil ) // Execute the workflow let cadenceTokens = cadenceSource.withdrawAvailable(50.0) let evmTokens = evmSwapper.swap(nil, inVault: <-cadenceTokens) cadenceSink.depositCapacity(from: &evmTokens) ``` **Benefits:** - **Extended Liquidity**: access to both Cadence and EVM DEX liquidity. - **Cross-VM Arbitrage**: exploit price differences between VM environments. - **Atomic Execution**: all bridges and swaps happen in single transaction. ### Flash loan arbitrage **Goal**: Borrow tokens, execute arbitrage, repay loan with profit. This advanced strategy uses flash loans to execute risk-free arbitrage. To do this, it borrows tokens, exploits price differences across multiple DEXs, and repays the loan with interest while it keeps the profit. The entire operation happens atomically within a single transaction. **How it works:** 1. Borrows tokens via flash loan without collateral requirements. 2. Uses multi-swapper to find optimal arbitrage routes across DEXs. 3. Executes trades to exploit price differences. 4. Repays flash loan with fees from arbitrage profits. 5. Keeps profits that remain profit after loan repayment. ```cadence // 1. Flasher: Borrow tokens for arbitrage let flasher = IncrementFiFlashloanConnectors.Flasher( pairAddress: pairAddress, type: Type<@FlowToken.Vault>(), uniqueID: nil ) // 2. Multi-swapper: Find best arbitrage route let multiSwapper = SwapConnectors.MultiSwapper( inVault: Type<@FlowToken.Vault>(), outVault: Type<@FlowToken.Vault>(), swappers: [swapper1, swapper2, swapper3], uniqueID: nil ) // 3. Execute arbitrage with callback flasher.flashLoan(1000.0, callback: arbitrageCallback) ``` **Benefits:** - **Zero Capital Required**: No upfront investment needed for arbitrage. - **Risk-Free Profit**: Transaction reverts if arbitrage isn't profitable. - **Market Efficiency**: Helps eliminate price discrepancies across DEXs. ## Advanced Workflow combinations ### VaultSource + Zapper integration **Goal**: Withdraw tokens from a vault and convert them to LP tokens in a single transaction. This advanced workflow demonstrates the power of combining VaultSource with Zapper functionality to seamlessly convert idle vault tokens into yield-generating LP positions. The Zapper handles the complex process of splitting the single token and creating balanced liquidity. ![vault source zapper](./imgs/vaultsource-zapper.png) **How it works:** 1. VaultSource withdraws tokens from vault while respecting minimum balance. 2. Zapper receives the single token and splits it optimally. 3. Zapper swaps a portion of token A to token B with internal DEX routing. 4. Zapper provides balanced liquidity (A + B) to the pool. 5. Returns LP tokens that represent the liquidity position. ```cadence // 1. Create VaultSource with minimum balance protection let vaultSource = FungibleTokenConnectors.VaultSource( min: 500.0, // Keep 500 tokens minimum in vault withdrawVault: flowVaultCapability, uniqueID: nil ) // 2. Create Zapper for FLOW/USDC pair let zapper = IncrementFiPoolLiquidityConnectors.Zapper( token0Type: Type<@FlowToken.Vault>(), // Input token (A) token1Type: Type<@USDC.Vault>(), // Paired token (B) stableMode: false, // Use volatile pair pricing uniqueID: nil ) // 3. Execute Vault Source → Zapper workflow let availableTokens <- vaultSource.withdrawAvailable(maxAmount: 1000.0) let lpTokens <- zapper.swap(quote: nil, inVault: <-availableTokens) // Result: LP tokens ready for staking or further DeFi strategies log("LP tokens created: ".concat(lpTokens.balance.toString())) ``` **Benefits:** - **Capital Efficiency**: converts idle vault tokens to yield-generating LP positions. - **Automated Balancing**: Zapper handles optimal token split calculations automatically - **Single Transaction**: complex multi-step process executed atomically. - **Minimum Protection**: VaultSource ensures vault never goes below safety threshold. ### Price-informed rebalancing **Goal**: Create autonomous rebalancing system based on price feeds. This sophisticated workflow creates an autonomous portfolio management system that monitors real-time price data to maintain target value ratios. The AutoBalancer combines price oracles, sources, and sinks to automatically rebalance positions when they deviate from target thresholds. **How it works:** 1. Price oracle provides real-time asset valuations with staleness protection. 2. AutoBalancer tracks historical deposit values vs current market values. 3. When portfolio value exceeds upper threshold (120%), excess is moved to rebalance sink. 4. When portfolio value falls below lower threshold (80%), additional funds are sourced. 5. System maintains target allocation automatically without manual intervention. ```cadence // Create autonomous rebalancing system let priceOracle = BandOracleConnectors.PriceOracle( unitOfAccount: Type<@FlowToken.Vault>(), staleThreshold: 3600, // 1 hour feeSource: flowTokenSource, uniqueID: nil ) let autoBalancer <- FlowActions.createAutoBalancer( vault: <-initialVault, lowerThreshold: 0.8, upperThreshold: 1.2, source: rebalanceSource, sink: rebalanceSink, oracle: priceOracle, uniqueID: nil ) autoBalancer.rebalance(force: false) // Autonomous rebalancing ``` **Benefits:** - **Autonomous Operation**: maintains portfolio balance without manual intervention. - **Risk Management**: prevents excessive exposure through automated position sizing. - **Market Responsive**: adapts to price movements with real-time oracle data. - **Threshold Flexibility**: configurable upper/lower bounds for different risk profiles. ### Restake and compound strategy **Goal**: Automatically compound staking rewards back into the pool. This advanced compounding strategy maximizes yield; it automatically claims staking rewards and converts them back into LP tokens for re-staking. The workflow combines rewards claims, zaps, and staking into a seamless compound operation that accelerates yield accumulation through reinvestment. **How it works:** 1. PoolRewardsSource claims accumulated staking rewards from the pool. 2. Zapper receives the reward tokens and converts them to LP tokens. 3. SwapSource orchestrates the rewards → LP token conversion process. 4. PoolSink re-stakes the new LP tokens back into the same pool. 5. Compound interest effect increases overall position size and future rewards. ```cadence // Restake rewards workflow let rewardsSource = IncrementFiStakingConnectors.PoolRewardsSource( poolID: 1, staker: userAddress, vaultType: Type<@FlowToken.Vault>(), overflowSinks: {}, uniqueID: nil ) let zapper = IncrementFiPoolLiquidityConnectors.Zapper( token0Type: Type<@FlowToken.Vault>(), token1Type: Type<@USDC.Vault>(), stableMode: false, uniqueID: nil ) let swapSource = SwapConnectors.SwapSource( swapper: zapper, source: rewardsSource, uniqueID: nil ) let poolSink = IncrementFiStakingConnectors.PoolSink( staker: userAddress, poolID: 1, uniqueID: nil ) // Execute compound strategy let lpTokens <- swapSource.withdrawAvailable(maxAmount: UFix64.max) poolSink.depositCapacity(from: lpTokens) ``` **Benefits:** - **Compound Growth**: exponential yield increase through automatic reinvestment. - **Gas Efficiency**: single transaction handles claim, convert, and re-stake operations. - **Set-and-Forget**: automated compounding without manual intervention required. - **Optimal Conversion**: Zapper ensures efficient reward token to LP token conversion. ## Safety best practices ### Always check capacity Prevents transaction failures and allows graceful handling when sinks reach their maximum capacity limits. This is crucial for automated workflows that might encounter varying capacity conditions. ```cadence // Check before depositing if sink.depositCapacity(from: &vault) { sink.depositCapacity(from: &vault) } else { // Handle insufficient capacity } ``` ### Validate balances Ensures operations behave as expected and helps detect unexpected token loss or gain during complex workflows. Balance validation is essential for financial applications where token accuracy is critical. ```cadence // Verify operations completed successfully let beforeBalance = vault.balance sink.depositCapacity(from: &vault) let afterBalance = vault.balance assert(afterBalance >= beforeBalance, message: "Balance should not decrease") ``` ### Use graceful degradation Prevents entire workflows from failure when individual components encounter issues. This approach allows robust strategies that can adapt to volatile market conditions or temporary protocol unavailability. ```cadence // Handle failures gracefully if let result = try? operation.execute() { // Success path } else { // Fallback or no-op log("Operation failed, continuing with strategy") } ``` ### Resource management Proper resource cleanup prevents token loss and ensures all vaults are properly handled, even when transactions partially fail. This is critical in Cadence where you must explicitly manage resources. ```cadence // Always clean up resources let vault = source.withdrawAvailable(amount) defer { // Ensure vault is properly handled if vault.balance > 0 { // Return unused tokens sourceVault.deposit(from: <-vault) } } ``` ## Test your combinations ### Unit tests Tests individual connectors in isolation to verify they respect their constraints and behave correctly under various conditions. This catches bugs early and ensures each component works as designed. ```cadence // Test individual components test("VaultSource should maintain minimum balance") { let source = VaultSource(min: 100.0, withdrawVault: vaultCap, uniqueID: nil) // Test minimum balance enforcement let available = source.minimumAvailable() assert(available >= 100.0, message: "Should maintain minimum balance") } ``` ### Integration tests Validates that multiple connectors work together correctly in complete workflows. This ensures the composition logic is sound and identifies issues that only appear when components interact. ```cadence // Test complete workflows test("Reward harvesting workflow should complete successfully") { let workflow = RewardHarvestingWorkflow( rewardsSource: rewardsSource, swapper: swapper, sink: sink ) let result = workflow.execute() assert(result.success, message: "Workflow should complete successfully") } ``` ### Simulation tests Tests strategies under various market conditions using mock data to verify they respond appropriately to price changes, liquidity variations, and other market dynamics. This is essential for strategies that rely on external market data. ```cadence // Test with simulated market conditions test("Strategy should handle price volatility") { let strategy = ArbitrageStrategy( priceOracle: mockPriceOracle, swapper: mockSwapper ) // Simulate price changes mockPriceOracle.setPrice(1.0) let result1 = strategy.execute() mockPriceOracle.setPrice(2.0) let result2 = strategy.execute() assert(result1 != result2, message: "Strategy should adapt to price changes") } ``` ## 📚 Next steps Now that you understand basic combinations, explore: 1. **Advanced Strategies**: complex multi-step workflows. 2. **Risk Management**: advanced safety and monitoring techniques. 3. **Custom Connectors**: build your own protocol adapters. ## Conclusion In this tutorial, you learned how to combine Flow Actions primitives to create sophisticated workflows that leverage atomic composition, weak guarantees, and event traceability. You can now create and use Sources, Sinks, Swappers, Price Oracles, and Flashers, while utilizing UniqueIdentifiers to trace operations and compose complex atomic transactions. Composability is the core strength of Flow Actions. These examples demonstrate how Flow Actions primitives can be combined to create powerful, automated workflows that integrate multiple protocols seamlessly. The framework's standardized interfaces enable developers to chain operations together like LEGO blocks, focusing on strategy implementation rather than protocol-specific integration details. [FLIP 339]: https://github.com/onflow/flips/pull/339/files [5 Flow Actions Primitives]: intro-to-flow-actions.md --- # File: /md/blockchain-development-tutorials/forte/flow-actions/connectors.md --- title: Connectors description: Build Flow Actions connectors that integrate protocols with Flow Actions primitives sidebar_position: 2 keywords: - Connectors - Flow Actions - Sink - Swap - Source - Oracles - Flashers - DeFi - Protocols --- # Connectors :::warning We are reviewing and finalizing Flow Actions in [FLIP 339]. The specific implementation may change as a part of this process. We will update these tutorials, but you may need to refactor your code if the implementation changes. ::: **Connectors** are the bridge between external DeFi protocols and the standardized Flow Actions primitive interfaces. They act as **protocol adapters** that translate protocol-specific APIs into the universal language of Flow Actions. Think of them as "drivers" that provide a connection between software and a piece of hardware without the software developer needing to know how the hardware expects to receive commands, or an MCP allowing an agent to use an API in a standardized manner. Flow Actions act as "money LEGOs" with which you can compose various complex operations with simple transactions. These are the benefits of connectors: - Abstraction Layer: Connectors act like a universal translator between your application and various decentralized finance (DeFi) protocols. - Standardized Interface: All connectors implement the same core methods, which makes them interchangeable. - Protocol Integration: They handle the complex interactions with different DeFi services (swaps, staking, lending, and so on). ## How connectors work ### Abstraction layer Connectors sit between your application logic and protocol-specific contracts: ``` Your DeFi Strategy → Flow Actions Connector → Protocol Contract → Blockchain State ``` ### Interface implementation Each connector implements one or more of the five primitive interfaces: ```cadence // Example: A connector implementing the Sink primitive access(all) struct MyProtocolSink: DeFiActions.Sink { // Protocol-specific configuration access(self) let protocolConfig: MyProtocol.Config // DeFiActions required methods access(all) fun getSinkType(): Type { ... } access(all) fun minimumCapacity(): UFix64 { ... } access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) { ... } } ``` All connectors implement these standard methods: ```cadence // Identity & Component Info fun getComponentInfo(): ComponentInfo fun copyID(): UniqueIdentifier? fun setID(_ id: UniqueIdentifier?) // Type-specific methods fun getSinkType(): Type // Sink only fun getSourceType(): Type // Source only fun inType() / outType(): Type // Swapper only // Core operations fun minimumCapacity(): UFix64 // Sink fun depositCapacity(from: &Vault) // Sink fun minimumAvailable(): UFix64 // Source fun withdrawAvailable(maxAmount: UFix64): @Vault // Source fun swap(quote: Quote?, inVault: @Vault): @Vault // Swapper fun getPrice(baseAsset: Type, quoteAsset: Type): UFix64 // PriceOracle fun flashLoan(amount: UFix64, callback: Function) // Flasher ``` ### Composition pattern You can combine Connetors to create sophisticated workflows: ```cadence // Claim rewards → Swap to different token → Stake in new pool ProtocolA.RewardsSource → SwapConnectors.SwapSource → ProtocolB.StakingSink ``` ## Connector library 🔄 SOURCE Primitive Implementations | Connector | Location | Protocol | Purpose | | ------------------ | ------------------------------ | -------------------------------- | ----------------------------------------------------- | | VaultSource | [FungibleTokenConnectors] | Generic FungibleToken | Withdraw from vaults with minimum balance protection. | | VaultSinkAndSource | [FungibleTokenConnectors] | Generic FungibleToken | Combined vault operations (dual interface). | | SwapSource | [SwapConnectors] | Generic (composes with Swappers) | Source tokens then swap before returning. | | PoolRewardsSource | [IncrementFiStakingConnectors] | IncrementFi Staking | Claim staking rewards from pools. | ⬇️ SINK Primitive Implementations | Connector | Location | Protocol | Purpose | | ------------------ | ------------------------------ | -------------------------------- | -------------------------------------------- | | VaultSink | [FungibleTokenConnectors] | Generic FungibleToken | Deposit to vaults with capacity limits. | | VaultSinkAndSource | [FungibleTokenConnectors] | Generic FungibleToken | Combined vault operations (dual interface). | | SwapSink | [SwapConnectors] | Generic (composes with Swappers) | Swap tokens before depositing to inner sink. | | PoolSink | [IncrementFiStakingConnectors] | IncrementFi Staking | Stake tokens in staking pools. | 🔀 SWAPPER Primitive Implementations | Connector | Location | Protocol | Purpose | | ------------------- | ------------------------------------ | ------------------------- | ------------------------------------------------ | | MultiSwapper | [SwapConnectors] | Generic (DEX aggregation) | Aggregate multiple swappers for optimal routing. | | Swapper | [IncrementFiSwapConnectors] | IncrementFi DEX | Token swapping through SwapRouter. | | Zapper | [IncrementFiPoolLiquidityConnectors] | IncrementFi Pools | Single-token liquidity provision. | | UniswapV2EVMSwapper | [UniswapV2SwapConnectors] | Flow EVM Bridge | Cross-VM UniswapV2-style swapping. | 💰 PRICEORACLE Primitive Implementations | Connector | Location | Protocol | Purpose | | ----------- | ---------------------- | ------------- | ----------------------------------------------- | | PriceOracle | [BandOracleConnectors] | Band Protocol | External price feeds with staleness validation. | ⚡ FLASHER Primitive Implementations | Connector | Location | Protocol | Purpose | | --------- | -------------------------------- | --------------- | --------------------------------------- | | Flasher | [IncrementFiFlashloanConnectors] | IncrementFi DEX | Flash loans through SwapPair contracts. | ## Guide to building connectors ### Choose your primitive First, determine which Flow Actions primitive(s) your connector will implement: | Primitive | When to Use | Example Use Cases | | --------------- | --------------------------------- | ---------------------------------------------- | | **Source** | Your protocol provides tokens | Vault withdrawals, reward claiming, unstaking. | | **Sink** | Your protocol accepts tokens | Vault deposits, staking, loan repayments. | | **Swapper** | Your protocol exchanges tokens | DEX trades, cross-chain bridges, LP provision. | | **PriceOracle** | Your protocol provides price data | Oracle feeds, TWAP calculations. | | **Flasher** | Your protocol offers flash loans | Arbitrage opportunities, liquidations. | ### Analyze your protocol Study your target protocol to understand: - **Contract interfaces** and method signatures. - **Required parameters** and data structures. - **Error conditions** and failure modes. - **Fee structures** and payment mechanisms. - **Access controls** and permissions. ### Design your connector Plan your connector implementation: - **Configuration parameters** needed for initialization. - **Capability requirements** for protocol access. - **Error handling strategy** for graceful failures. - **Resource management** for token handling. - **Event emission** for traceability. ### Implement the interface Create your connector struct to implement the chosen primitive interface(s). ### Add safety features Implement safety mechanisms: - **Capacity checking** before operations. - **Balance validation** after operations. - **Graceful error handling** with no-ops. - **Resource cleanup** for empty vaults. ### Support Flow Actions standards Add required Flow Actions support: - **IdentifiableStruct** implementation. - **UniqueIdentifier** management. - **ComponentInfo** for introspection. - **Event emission** integration. ## Best practices ### **Error handling** - **Graceful Failures**: return empty results instead of panicking. - **Validation**: check all inputs and preconditions. - **Resource Safety**: properly handle vault resources in all paths. ```cadence // Good: Graceful failure access(all) fun minimumCapacity(): UFix64 { if let pool = self.poolCapability.borrow() { return pool.getAvailableCapacity() } return 0.0 // Graceful failure } // Bad: Panics on failure access(all) fun minimumCapacity(): UFix64 { let pool = self.poolCapability.borrow()! // Will panic if invalid return pool.getAvailableCapacity() } ``` ### **Capacity and balance checks** - **Always Check First**: validate capacity/availability before operations. - **Respect Limits**: work within available constraints. - **Handle Edge Cases**: zero amounts, maximum values, empty vaults. ```cadence access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) { // Check capacity first let capacity = self.minimumCapacity() if capacity == 0.0 { return } // Calculate actual deposit amount let availableAmount = from.balance let depositAmount = capacity < availableAmount ? capacity : availableAmount // Handle edge case if depositAmount == 0.0 { return } // Proceed with deposit... } ``` ### **Type safety** - **Validate Types**: ensure vault types match expected types. - **Early Returns**: fail fast on type mismatches. - **Clear Error Messages**: help developers understand issues. ```cadence access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) { // Type validation if from.getType() != self.getSinkType() { return // No-op for wrong token type } // Continue with deposit... } ``` ### **Event integration** - **Leverage Post-conditions**: Flow Actions interfaces emit events automatically. - **Provide Context**: include relevant information in events. - **Support Traceability**: use UniqueIdentifiers consistently. ### **Resource management** - **Handle Empty Vaults**: use `DeFiActionsUtils.getEmptyVault()` for consistent empty vault creation. - **Destroy Properly**: clean up resources in all code paths. - **Avoid Resource Leaks**: ensure all vaults are handled appropriately. ### **Capability management** - **Validate Capabilities**: check capabilities before using them. - **Handle Revocation**: gracefully handle revoked capabilities. - **Proper Entitlements**: use correct entitlement levels (auth vs unauth). ### **Documentation** - **Clear Comments**: explain protocol-specific logic. - **Usage Examples**: show how to use your connectors. - **Integration Patterns**: demonstrate composition with other connectors. ## Integration into Flow Actions We will now go over how to build a connector and integrate it with Flow Actions. Specifically, we will showcase the process of using the **VaultSink** connector in the [FungibleTokenConnectors]. It only performs basic token deposits to a vault with capacity limits, implements the Sink interface, has minimal external dependencies (only FungibleToken standard), and requires simple configuration (max balance, deposit vault capability,and unique ID). The `VaultSink` connector is already deployed and working in Flow Actions. Let's examine how it's integrated: **Location**: `cadence/contracts/connectors/FungibleTokenConnectors.cdc` **Contract**: `FungibleTokenConnectors` **Connector**: `VaultSink` struct that defines the interaction with the connector. ### Deploy Your connector contract Deploy your connector contract with the following command: ```bash flow project deploy ``` In your 'flow.json' you will find: ```json { "contracts": { "FungibleTokenConnectors": { "source": "./cadence/contracts/connectors/FungibleTokenConnectors.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "testnet": "...", "mainnet": "..." } } } } ``` ### Create usage transactions Create transaction templates for use with your connectors: ```cadence // Transaction: save_vault_sink.cdc import "FungibleTokenConnectors" import "DeFiActions" import "FungibleToken" transaction(maxBalance: UFix64) { prepare(signer: auth(Storage, Capabilities) &Account) { // Get vault capability for deposits let vaultCap = signer.capabilities.get<&{FungibleToken.Receiver}>( /public/flowTokenReceiver ) // Create the VaultSink connector let vaultSink = FungibleTokenConnectors.VaultSink( max: maxBalance, depositVault: vaultCap, uniqueID: nil ) // Save to storage for later use signer.storage.save(vaultSink, to: /storage/FlowTokenVaultSink) } } ``` ### Real usage transaction: VaultSink Here's the actual working transaction that creates a VaultSink: ```cadence // File: cadence/transactions/fungible-token-stack/save_vault_sink.cdc import "FungibleToken" import "FungibleTokenMetadataViews" import "FlowToken" import "FungibleTokenConnectors" transaction(receiver: Address, vaultPublicPath: PublicPath, sinkStoragePath: StoragePath, max: UFix64?) { let depositVault: Capability<&{FungibleToken.Vault}> let signer: auth(SaveValue) &Account prepare(signer: auth(SaveValue) &Account) { // Get the receiver's vault capability self.depositVault = getAccount(receiver).capabilities.get<&{FungibleToken.Vault}>(vaultPublicPath) self.signer = signer } pre { self.signer.storage.type(at: sinkStoragePath) == nil: "Collision at sinkStoragePath \(sinkStoragePath.toString())" self.depositVault.check(): "Invalid deposit vault capability" } execute { // Create the VaultSink connector let sink = FungibleTokenConnectors.VaultSink( max: max, // Maximum capacity (nil = unlimited) depositVault: self.depositVault, // Where tokens will be deposited uniqueID: nil // No unique ID for this example ) // Save the connector for later use self.signer.storage.save(sink, to: sinkStoragePath) log("VaultSink created and saved!") log("Max capacity: ".concat(max?.toString() ?? "unlimited")) log("Receiver: ".concat(receiver.toString())) } post { self.signer.storage.type(at: sinkStoragePath) == Type(): "VaultSink was not stored correctly" } } ``` Execute this transaction: ```bash flow transactions send cadence/transactions/fungible-token-stack/save_vault_sink.cdc \ --arg Address:0x01cf0e2f2f715450 \ --arg PublicPath:"/public/FlowTokenReceiver" \ --arg StoragePath:"/storage/FlowTokenSink" \ --arg "UFix64?":1000.0 \ --signer emulator ``` ### Create combinations examples Show how your connectors work with existing Flow Actions components: ```cadence // Example: Using VaultSink in a real deposit workflow import "FungibleTokenConnectors" import "FlowToken" transaction(depositAmount: UFix64) { prepare(signer: auth(BorrowValue) &Account) { // 1. Load the saved VaultSink let sink = signer.storage.borrow<&FungibleTokenConnectors.VaultSink>( from: /storage/FlowTokenSink ) ?? panic("VaultSink not found - create one first!") // 2. Create a simple source (your own vault) let flowVault = signer.storage.borrow( from: /storage/FlowTokenVault ) ?? panic("FlowToken vault not found") // 3. Check sink capacity before depositing let capacity = sink.minimumCapacity() log("Sink capacity: ".concat(capacity.toString())) if capacity >= depositAmount { // 4. Execute Source → Sink workflow let tokens <- flowVault.withdraw(amount: depositAmount) sink.depositCapacity(from: tokens) log("Deposited ".concat(depositAmount.toString()).concat(" FLOW through VaultSink!")) } else { log("Insufficient sink capacity: ".concat(capacity.toString())) } } } ``` ### Add to current workflows You can use VaultSink in advanced Flow Actions workflows: ```cadence // Example: VaultSink in AutoBalancer (real integration pattern) import "DeFiActions" import "FungibleTokenConnectors" import "BandOracleConnectors" transaction() { prepare(signer: auth(SaveValue, BorrowValue, IssueStorageCapabilityController) &Account) { // 1. Create rebalancing sink using VaultSink pattern let rebalanceCap = getAccount(signer.address) .capabilities.get<&{FungibleToken.Receiver}>(/public/FlowTokenReceiver) let rebalanceSink = FungibleTokenConnectors.VaultSink( max: nil, // No limit for rebalancing depositVault: rebalanceCap, uniqueID: nil ) // 2. Create rebalancing source let sourceCap = signer.capabilities.storage.issue( /storage/FlowTokenVault ) let rebalanceSource = FungibleTokenConnectors.VaultSource( min: 100.0, // Keep 100 FLOW minimum withdrawVault: sourceCap, uniqueID: nil ) // 3. Create price oracle let priceOracle = BandOracleConnectors.PriceOracle( unitOfAccount: Type<@FlowToken.Vault>(), staleThreshold: 3600, feeSource: rebalanceSource, uniqueID: nil ) // 4. Create AutoBalancer using VaultSink pattern let autoBalancer <- DeFiActions.createAutoBalancer( oracle: priceOracle, vaultType: Type<@FlowToken.Vault>(), lowerThreshold: 0.9, upperThreshold: 1.1, rebalanceSink: rebalanceSink, // Uses VaultSink! rebalanceSource: rebalanceSource, // Uses VaultSource! uniqueID: nil ) signer.storage.save(<-autoBalancer, to: /storage/FlowAutoBalancer) log("AutoBalancer created using VaultSink/VaultSource pattern!") } } ``` ### For your own connectors When building your own connectors, follow the VaultSink pattern: 1. **Keep constructors simple** - minimal required parameters. 2. **Validate inputs** - check capabilities and preconditions. 3. **Handle errors gracefully** - no-ops instead of panics. 4. **Support Flow Actions standards** - UniqueIdentifier, ComponentInfo. 5. **Test thoroughly** - create usage transactions like the ones shown. 6. **Document clearly** - show real integration examples. ## Conclusion The Flow Actions framework provides a comprehensive set of connectors that successfully implement the five fundamental DeFi primitives across multiple protocols: - **20+ Connector Implementations** spanning basic vault operations to complex cross-VM swapping. - **4 Protocol Integrations**: generic FungibleToken, IncrementFi, Band Oracle, Flow EVM. - **Composable Architecture**: combine Connectors to create sophisticated financial workflows. - **Safety-First Design**: graceful error handling and resource safety throughout. - **Event-Driven Traceability**: full workflow tracking and debugging capabilities. This framework allows developers to build sophisticated DeFi strategies while maintaining the simplicity and reliability of standardized primitive interfaces. The modular design allows for easy extension to additional protocols while preserving composability and atomic execution guarantees. [FLIP 339]: https://github.com/onflow/flips/pull/339/files [FungibleTokenConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/FungibleTokenConnectors.cdc [SwapConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/SwapConnectors.cdc [IncrementFiStakingConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiStakingConnectors.cdc [IncrementFiSwapConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiSwapConnectors.cdc [IncrementFiPoolLiquidityConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiPoolLiquidityConnectors.cdc [UniswapV2SwapConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc [BandOracleConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/band-oracle/BandOracleConnectors.cdc [IncrementFiFlashloanConnectors]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiFlashloanConnectors.cdc --- # File: /md/blockchain-development-tutorials/forte/flow-actions/flow-actions-transaction.md --- title: Flow Actions Transaction description: Build your first transaction that uses Flow actions on Flow Cadence. sidebar_position: 1 keywords: - Flow Actions - flow actions - atomic transactions - DeFi - defi - staking - LP - Liquidity Pools - Swap - Sink --- # Flow Actions Transaction :::warning We are reviewing and finalizing Flow Actions in [FLIP 339]. The specific implementation may change as a part of this process. We will update these tutorials, but you may need to refactor your code if the implementation changes. ::: [Staking] is a simple way to participate in the blockchain process. You supply tokens to help with governance and, in return, you earn a share of the network's rewards. It's a way to grow unused assets and provides a much higher rate of return than a savings account. :::warning Make certain you understand how [slashing] works and assess your risk tolerance before you stake. ::: To stake directly, lock up your tokens with [Flow Port]. You can also use other platforms and protocols that have a different strategy for participating in this process. [IncrementFi] offers a Liquid Staking Protocol (LSP) they describe as: > LSP allows users to earn staking rewards without locking $flow tokens or running node softwares. Users can deposit $flow tokens and receive transferrable $stFlow tokens in return. Liquid staking combines the benefits of staking (earning rewards) and brings liquidity, as well as additional possibilities to increase your assets or hedge your positions by participating in Flow's DeFi ecosystem. Participation in staking comes with a tedious chore - you'll need to regularly complete one or more transactions to claim your rewards and restake them to compound your earnings. Flow Actions simplifies this task. It gives you a suite of blocks that, after instantiation, perform actions in the same way from one protocol to another. In this tutorial, you'll learn how to build a transaction that simplifies restaking on [IncrementFi], and that you can adapt with different connectors to work on other protocols as well. :::tip If you combine this transaction with [scheduled transactions], you can automate it completely! ::: ## Learning objectives After you complete this tutorial, you will be able to: - Chain multiple decentralized finance (DeFi) operations atomically - Handle token type mismatches automatically - Build safe, validated transactions with proper error handling - Create reusable, protocol-agnostic DeFi building blocks ## Prerequisites - Flow CLI: install from the [Flow CLI docs] - Cursor + [Cadence Extension] (recommended) ## Cadence programming language This tutorial assumes you have a modest knowledge of [Cadence]. If you don't, you can still follow along, but we recommend that you complete our series of [Cadence] tutorials. Most developers find it more pleasant than other blockchain languages and it's easy to pick up. ## Get started on mainnet This demo uses **mainnet** and a real DeFi protocol. Before you write any code, set up your staking position. :::danger This tutorial uses a real protocol with real funds. Only work with funds your comfortable losing in the event of an error or mistake. Cadence is much safer than Solidity, you can make a mistake and all investment involves risk. ::: ### Stake with IncrementFi To complete this tutorial, set up a staking position in Increment Fi. If you already have LP tokens, skip to the **Staking LP Token** step. **Create an LP position** First, go to the [Increment Fi Liquidity Pool] and select 'Single Asset' to provide liquidity with your FLOW tokens. ![single asset](./imgs/single-asset.png) Then, enter the amount of FLOW you want to add as liquidity. Confirm the transaction and continue to the next step. **Staking LP token** Now that you have LP tokens from the FLOW-stFLOW pool, you can stake these tokens to receive rewards from them. To do this, go to the [IncrementFi Farms] page and look for the `Flow-stFlow Pool #199` pool. Note that the #199 is the Pool ID (pid). You might need to select the list view first (the middle button in the upper-right section of the LP pools page) in order to properly see the pid. ![list view](./imgs/list-view-farms.png) This pid is necessary to execute the restaking transaction later, so make certain you know which pid to use. ![pid](./imgs/pid.png) Then, select `Stake LP` and enter the amount of LP tokens to stake into the pool. After the transaction is approved and confirmed, you will see the total stake position and claimable rewards in the pool card. ![pool card](./imgs/pool-card.png) Now our staking position generates rewards as time passes by. We use Flow Actions to execute a single transaction that can claim the rewards (stFLOW), convert the optimal amount into FLOW, increase the LP position (thus getting more LP tokens), and restake them into the farm. ### Initialize Your Staking.UserCertificate IncrementFi uses a `Staking.UserCertificate` internally for some actions, and you'll need this certificate to complete this tutorial. While the platform automatically creates it when you perform other actions on the platform, you can explicitly set it up with the [script on Flow Runner]. When the transaction succeeds, you'll see output similar to: ```zsh Transaction ID: 7d3efabb98d3fed69aabf8fa9007fa11571b70300cbd641120271bbfa8e932f5 Transaction Result: {6 items "blockId":string"1206e0a1e6f16098e8d3555f7568f7f14e8e6df1983946408627a964dd87d69d" "status":int4 "statusString":string"SEALED" "statusCode":int0 "errorMessage":string"" # Remaining details omitted for brevity ``` The UserCertificate is a resource stored in your account's private storage that: 1. Proves your identity for IncrementFi staking operations. 2. Allows you to claim rewards from staking pools. ## Set up the project To start, use the [Flow Actions Scaffold] repo as a template to create a new repository. Clone your new repository and open it in your editor. Follow the instructions in the README for **mainnet**. ### Start With the scaffold Create a new repo with the [Flow Actions Scaffold] as a template. Clone your new repo locally and open it in your editor. Run `flow deps install` to install dependencies. :::note This Scaffold repo is a minimal Flow project with dependencies for Flow Actions and Increment Fi connectors. It only has support for the specific transaction that we execute in this demo (Claim → Zap → Restake for IncrementFi LP rewards) ::: ### Export Your wallet key :::danger Never use a wallet with with a large amount of funds for development! If you download a malicious VS Code extension, your funds could be stolen. Never put a wallet key directly in `flow.json`. ::: :::warning Transactions on mainnet incur fees and affect onchain balances. We recommend that you create a new Flow Wallet account with limited funds. ::: [Export] the key for the wallet you want to use for this exercise. It needs to have some funds in it, but you shouldn't do development with the same wallet you keep any valuable assets in. Create a `.pkey` file for your wallet key, **add it to `.gitignore`**, then add the account to `flow.json`: ```json "accounts": { "my-testing-account": { "address": "", "key": { "type": "file", "location": "./my-testing-account.pkey" } } } ``` ## Build the transaction Now that the dependencies have been properly setup and we have made sure that our account is properly setup, the staking position is established as well as the `Staking.UserCertificate`; we are now ready to finally build the restaking transaction We will be duplicating the transaction provided in the scaffold `cadence/transactions/increment_fi_restake.cdc` The key pattern we need to create is: **Source** → **Swap** → **Sink** - **Source**: Provides tokens (rewards, vaults, etc.) - **Swap**: Converts tokens (swapping to zap input then zapping to LP tokens) - **Sink**: Receives and deposits tokens (staking pools, vaults) ### Import required contracts First, import all the contracts you need to build the transaction: ```cadence import "FungibleToken" import "DeFiActions" import "SwapConnectors" import "IncrementFiStakingConnectors" import "IncrementFiPoolLiquidityConnectors" import "Staking" ``` - `FungibleToken`: Standard token interface for Flow - `DeFiActions`: Core Flow Actions framework for composability - `SwapConnectors`: Wraps swap operations as Flow Actions - `IncrementFiStakingConnectors`: Flow Actions connectors for IncrementFi staking - `IncrementFiPoolLiquidityConnectors`: LP token creation (zapping) - `Staking`: Core staking contract for user certificates ### Define transaction parameters We will specify the `pid` (Pool ID) as the transaction parameter because it identifies which IncrementFi staking pool to interact with ```cadence transaction( pid: UInt64 ) { ``` ### Declare transaction properties Then, declare all the properties needed for the transaction. Here is where you'll use the `Staking.UserCertificate` for authentication staking operations. The `pool` is used to reference the staking pool for validation. The starting balance for post-condition verification is the `startingStake`. The composable source that provides LP tokens is the `swapSource`. The `expectedStakeIncrease` is the minimum expected increase for safety. Finally, the `operationID` serves as the unique identifier for tracing the operation across Flow Actions. ```cadence let userCertificateCap: Capability<&Staking.UserCertificate> let pool: &{Staking.PoolPublic} let startingStake: UFix64 let swapSource: SwapConnectors.SwapSource let expectedStakeIncrease: UFix64 let operationID: DeFiActions.UniqueIdentifier ``` ### Prepare phase The `prepare` phase runs first in the transaction. You use it to set up and validate a Cadence transaction. It's also the only place where a transaction can interact with a user's account and the [resources] within. **Pool Validation** verifies that the specified pool exists and is accessible. ```cadence // Get pool reference and validate it exists self.pool = IncrementFiStakingConnectors.borrowPool(pid: pid) ?? panic("Pool with ID \(pid) not found or not accessible") ``` **User State Validation** records current staking balance to verify the transaction worked correctly. ```cadence // Get starting stake amount for post-condition validation self.startingStake = self.pool.getUserInfo(address: acct.address)?.stakingAmount ?? panic("No user info for address \(acct.address)") ``` **User Authentication** creates a capability to access your UserCertificate (required for staking operations) ```cadence // Issue capability for user certificate self.userCertificateCap = acct.capabilities.storage .issue<&Staking.UserCertificate>(Staking.UserCertificateStoragePath) ``` **Operation Tracking** creates a unique ID to trace this operation through all Flow Actions components ```cadence // Create unique identifier for tracing this composed operation self.operationID = DeFiActions.createUniqueIdentifier() ``` ### Token type detection and configuration Use the `pid` from the pool we staked the LP tokens to get the liquidity pair information (what tokens make up this pool). We also convert token identifiers to actual Cadence types and determines if this is a stableswap pool or a regular AMM. ```cadence // Get pair info to determine token types and stable mode let pair = IncrementFiStakingConnectors.borrowPairPublicByPid(pid: pid) ?? panic("Pair with ID \(pid) not found or not accessible") // Derive token types from the pair let token0Type = IncrementFiStakingConnectors.tokenTypeIdentifierToVaultType(pair.getPairInfoStruct().token0Key) let token1Type = IncrementFiStakingConnectors.tokenTypeIdentifierToVaultType(pair.getPairInfoStruct().token1Key) ``` ### Build the Flow Actions chain We need to create the `RewardsSource` so that we can claim the available rewards from the staking pool. ```cadence // Create rewards source to claim staking rewards let rewardsSource = IncrementFiStakingConnectors.PoolRewardsSource( userCertificate: self.userCertificateCap, pid: pid, uniqueID: self.operationID ) ``` In case the reward token might not match the pool's `token0`, we check if we need to reverse the order to account for this mismatch. This helps us verify that the zapper can function properly. ```cadence // Check if we need to reverse token order: if reward token doesn't match token0, we reverse // so that the reward token becomes token0 (the input token to the zapper) let reverse = rewardsSource.getSourceType() != token0Type ``` Now the zapper can function properly and it takes the reward token as an input (regardless of token ordering). The zapper swaps half to the other token pair to combine them into LP tokens. ```cadence // Create zapper to convert rewards to LP tokens let zapper = IncrementFiPoolLiquidityConnectors.Zapper( token0Type: reverse ? token1Type : token0Type, // input token (reward token) token1Type: reverse ? token0Type : token1Type, // other pair token stableMode: pair.getPairInfoStruct().isStableswap, uniqueID: self.operationID ) ``` Here is where the true **composition** of Flow Actions come into place. The `lpSource` creates a single source that claims rewards from the staking pool. automatically converts them into LP tokens, and provides LP tokens as output. ```cadence // Wrap rewards source with zapper to convert rewards to LP tokens let lpSource = SwapConnectors.SwapSource( swapper: zapper, source: rewardsSource, uniqueID: self.operationID ) ``` Then the minimum LP tokens we expect to receive are calculated for safety validation. ```cadence // Calculate expected stake increase for post-condition self.expectedStakeIncrease = zapper.quoteOut( forProvided: lpSource.minimumAvailable(), reverse: false ).outAmount ``` ### Post-condition safety check This phase runs at the end for condition verification. We verify that the transaction actually increased your staking balance as expected. ```cadence post { // Verify that staking amount increased by at least the expected amount self.pool.getUserInfo(address: self.userCertificateCap.address)!.stakingAmount >= self.startingStake + self.expectedStakeIncrease: "Restake below expected amount" } ``` ### Execute the transaction `poolSink` creates the staking pool sink in which the LP tokens are deposited. ```cadence // Create pool sink to receive LP tokens for staking let poolSink = IncrementFiStakingConnectors.PoolSink( pid: pid, staker: self.userCertificateCap.address, uniqueID: self.operationID ) ``` Now we have all the components ready for the full flow of transactions. `swapSource.withdrawAvailable()` triggers the entire Source → Transformer chain. This claims rewards, swaps to LP tokens and withdraws LP tokens. The `poolSink.depositCapacity()` deposits LP tokens into the staking pool. And finally, we verify that all tokens were properly deposited (no dust left behind) and destroy the empty vault. ```cadence // Withdraw LP tokens from swap source (sized by sink capacity) let vault <- self.swapSource.withdrawAvailable(maxAmount: poolSink.minimumCapacity()) // Deposit LP tokens into pool for staking poolSink.depositCapacity(from: &vault as auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) // Ensure no residual tokens remain assert(vault.balance == 0.0, message: "Residual after deposit") destroy vault ``` See what happened? We executed this whole (and quite complex) flow in an atomic manner with a single transaction! ## Run the transaction We are now ready to restake the position with a single transaction! ```bash flow transactions send cadence/transactions/increment_fi_restake.cdc \ --network mainnet \ --signer my-testing-account \ --args-json '[{"type":"UInt64","value":""}]' ``` Replace `` with your actual pool ID (PID) from the IncrementFi Farms page, in this case it is 1999. The PID changes over time. ### Interpret the results After you complete the transaction, you see that the following events occurred: - The rewards (stFLOW) were claimed from pool #199 (or the current pool number if you run this exercise yourself) and the reward balance was updated properly. - The stFLOW was converted to FLOW. - FLOW and stFLOW was used to add liquidity to the liquidity pool. - LP tokens were received. - LP tokens were staked back into the #199 pool causing the staking balance to increase. ## Run the transaction on emulator You can run this whole transaction on Emulator as well. Although this example used a real pool to demonstrate a real-world use case, we recommend you start any real projects by testing on the Emulator. After cloning the [Flow Actions Scaffold] and installing the dependencies you can run: ```bash make start ``` The `make start` command handles all setup automatically using the built-in emulator service account, so no manual configuration is needed. This starts the Flow Emulator and deploys Increment FI dependencies, creates test tokens (1M each), sets up the liquidity pool, sets up the staking pool #0 and displays the complete environment summary. The `pid` is `0` because the automated setup creates the first staking pool with ID `0` containing your staked LP tokens and active rewards Now you can test the restake workflow: ```bash # Check available rewards flow scripts execute cadence/scripts/get_available_rewards.cdc \ --network emulator \ --args-json '[{"type":"Address","value":"0xf8d6e0586b0a20c7"},{"type":"UInt64","value":"0"}]' # Run the restake transaction flow transactions send cadence/transactions/increment_fi_restake.cdc \ --signer emulator-account \ --network emulator \ --args-json '[{"type":"UInt64","value":"0"}]' # Verify rewards were claimed and restaked flow scripts execute cadence/scripts/get_available_rewards.cdc \ --network emulator \ --args-json '[{"type":"Address","value":"0xf8d6e0586b0a20c7"},{"type":"UInt64","value":"0"}]' ``` If you want to run Cadence tests, then use the following commands: ```bash make test # or directly: flow test ``` ## Conclusion This transaction demonstrates how to chain multiple DeFi operations atomically, handle token type mismatches automatically, build safe validated transactions with proper error handling, and create reusable protocol-agnostic DeFi building blocks. You can apply these patterns to build yield farming, arbitrage, and portfolio management strategies across Flow's DeFi ecosystem. Flow Actions allow sophisticated DeFi strategies, that are complex in nature and dependant on various protocols, to execute in a single atomic transaction. [FLIP 339]: https://github.com/onflow/flips/pull/339/files [Staking]: ../../../protocol/staking/index.md [slashing]: ../../../protocol/staking/04-stake-slashing.md [Flow CLI docs]: ../../../build/tools/flow-cli/install.md [Cadence Extension]: https://marketplace.visualstudio.com/items?itemName=onflow.cadence [Flow Port]: https://port.flow.com/ [IncrementFi]: https://app.increment.fi/ [Flow Actions Scaffold]: https://github.com/onflow/flow-actions-scaffold [Increment Fi Liquidity Pool]: https://app.increment.fi/liquidity/add?in=A.1654653399040a61.FlowToken&out=A.d6f80565193ad727.stFlowToken&stable=true [zap]: ./breakthislinkfornow [zapper]: ./breakthislinkfornow [`/cadence/transactions/increment_fi_restake.cdc`]: https://github.com/onflow/flow-actions-scaffold/blob/main/cadence/transactions/increment_fi_restake.cdc [scheduled transactions]: ../scheduled-transactions/scheduled-transactions-introduction.md [Export]: https://docs.wallet.flow.com/tutorial/extension-private-key-and-seed-phrase-guide [Cadence]: https://cadence-lang.org/docs/ [staking app]: https://app.increment.fi/staking [script on Flow Runner]: https://run.dnz.dev/snippet/d1bf715483551879 [IncrementFi Farms]: https://app.increment.fi/farm --- # File: /md/blockchain-development-tutorials/forte/flow-actions/intro-to-flow-actions.md --- title: Introduction to Flow Actions description: Learn about Flow Actions, a suite of standardized Cadence interfaces that enable developers to compose complex DeFi workflows using small, reusable components. sidebar_position: 0 keywords: - Flow Actions - cadence interfaces - cadence actions - flow actions - defi workflows - source - sink - swapper - price oracle - flasher - flash loans - atomic transactions - composable defi - flow blockchain - cadence smart contracts - defi protocols - token vaults - unique identifiers - event traceability - weak guarantees - fungible tokens - connectors --- # Introduction to Flow Actions :::warning We are reviewing and finalizing Flow Actions in [FLIP 339]. The specific implementation may change as a part of this process. We will update these tutorials, but you may need to refactor your code if the implementation changes. ::: _Actions_ are a suite of standardized Cadence interfaces that allow developers to compose complex workflows, starting with decentralized finance (DeFi) workflows, by connecting small, reusable components. Actions provide a "LEGO" framework of blocks where each component performs a single operation (deposit, withdraw, swap, price lookup, flash loan) while maintaining composability with other components. This creates sophisticated workflows executable in a single atomic transaction. By using Flow Actions, developers can remove large amounts of tailored complexity from building DeFi apps and can instead focus on business logic using nouns and verbs. ## Key features - **Atomic Composition** - All operations complete or fail together. - **Weak Guarantees** - Flexible error handling, no-ops when conditions aren't met. - **Event Traceability** - UniqueIdentifier system for tracking operations. - **Protocol Agnostic** - Standardized interfaces across different protocols. - **Struct-based** - Lightweight, copyable components for efficient composition. ## Learning Objectives After you complete this tutorial, you will be able to: - Understand the key features of Flow Actions including atomic composition, weak guarantees, and event traceability - Create and use Sources to provide tokens from various protocols and locations - Create and use Sinks to accept tokens up to defined capacity limits - Create and use Swappers to exchange tokens between different types with price estimation - Create and use Price Oracles to get price data for assets with consistent denomination - Create and use Flashers to provide flash loans with atomic repayment requirements - Use UniqueIdentifiers to trace and correlate operations across multiple Flow Actions - Compose complex DeFi workflows by connecting multiple Actions in a single atomic transaction # Prerequisites ## Cadence programming language This tutorial assumes you have a modest knowledge of [Cadence]. If you don't, you can follow along, but you'll get more out of it if you complete our [Cadence] tutorials. Most developers find it easier than other blockchain languages and it's not hard to pick up. ## Flow Action types The first five Flow Actions implement five core primitives to integrate external DeFi protocols. 1. **Source**: Provides tokens on demand (for example, withdraw from vault, claim rewards, pull liquidity) ![source](./imgs/source.png) 2. **Sink**: Accepts tokens up to capacity (for example, deposit to vault, repay loan, add liquidity) ![sink](./imgs/sink.png) 3. **Swapper**: Exchanges one token type for another (for example, targeted DEX trades, multi-protocol aggregated swaps) ![swapper](./imgs/swapper.png) 4. **PriceOracle**: Provides price data for assets (for example, external price feeds, DEX prices, price caching) ![price oracle](./imgs/price-oracle.png) 5. **Flasher**: Provides flash loans with atomic repayment (for example, arbitrage, liquidations) ![flasher](./imgs/flasher.png) ## Connectors [Connectors] create the bridge between the standardized interfaces of Flow Actions and the often customized and complicated mechanisms of different DeFi protocols. You can use existing connectors that other developers wrote, or create your own. To instantiate Flow Actions, create an instance of the appropriate [struct] from a connector that provides the desired type of action connected to the desired DeFi protocol. For more information, read the [connectors article]. ## Token types In Cadence, tokens that adhere to the [Fungible Token Standard] have types that work with type safety principles. For example, you can find the type of $FLOW by running this script: ```cadence import "FlowToken" access(all) fun main(): String { return Type<@FlowToken.Vault>().identifier } ``` You'll get: ```zsh A.1654653399040a61.FlowToken.Vault ``` Many Flow Actions use these types to provide a safer method of working with tokens than an arbitrary address that may or may not be a token. ## Flow Actions The following Flow Actions standardize **usage** patterns for common defi-related tasks. By working with them, you - or Artificial Intelligence (AI) agents - can more easily write transactions and functionality regardless of the myriad of different ways each protocol works to accomplish these tasks. :::info Defi protocols and tools operate very differently, which means the calls to instantiate the same kind of action connected to different protocols will vary by protocol and connector. ::: ### Source A source is a primitive component that can supply a [vault] which contains the requested type and amount of tokens from something the user controls, or has authorized access to. This includes, but isn't limited to, personal vaults, accounts in protocols, and rewards. ![source](./imgs/source.png) You'll likely use one or more sources in any transactions using actions if the user needs to pay for something or otherwise provide tokens. Sources conform to the `Source` [interface]: ```cadence access(all) struct interface Source : IdentifiableStruct { /// Returns the Vault type provided by this Source access(all) view fun getSourceType(): Type /// Returns an estimate of how much can be withdrawn access(all) fun minimumAvailable(): UFix64 /// Withdraws up to maxAmount, returning what's actually available access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} } ``` Every source is guaranteed to have the above functions and return types that allow you to get the type of vault that the source returns, get an estimate of how many tokens users may currently withdraw, and actually withdraw those tokens, up to the amount available. Sources _degrade gracefully_ - If the requested amount of tokens is not available, they return the available amount. They always return a vault, even if that vault is empty. To create a source, instantiate a struct that conforms to the `Source` interface corresponding to a given protocol [connector]. For example, to create a source from a generic vault, create a `VaultSource` from [`FungibleTokenConnectors`]: ```cadence import "FungibleToken" import "FungibleTokenConnectors" transaction { prepare(acct: auth(BorrowValue) { let withdrawCap = acct.storage.borrow( /storage/flowTokenVault ) let source = FungibleTokenConnectors.VaultSource( min: 0.0, withdrawVault: withdrawCap, uniqueID: nil ) // Note: Logs are only visible in the emulator console log("Source created for vault type: ".concat(source.withdrawVaultType.identifier)) } } ``` ### Sink A sink is the opposite of a source - it's a place to send tokens, up to the limit of the capacity defined in the sink. As with any [resource], this process is non-destructive. Any remaining tokens remain in the vault that the source provides. They also have flexible limits, meaning the capacity can be dynamic. ![sink](./imgs/sink.png) Sinks adhere to the `Sink` [interface]. ```cadence access(all) struct interface Sink : IdentifiableStruct { /// Returns the Vault type accepted by this Sink access(all) view fun getSinkType(): Type /// Returns an estimate of remaining capacity access(all) fun minimumCapacity(): UFix64 /// Deposits up to capacity, leaving remainder in the referenced vault access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) } ``` You create a sink similar how you create a source, which is to instantiate an instance of the appropriate `struct` from the [connector]. For example, to create a sink in a generic vault from, instantiate a `VaultSink` from [`FungibleTokenConnectors`]: ```cadence import "FungibleToken" import "FungibleTokenConnectors" transaction { prepare(acct: &Account) { // Public, non-auth capability to deposit into the vault let depositCap = acct.capabilities.get<&{FungibleToken.Vault}>( /public/flowTokenReceiver ) // Optional: specify a max balance the user's Flow Token vault should hold let maxBalance: UFix64? = nil // or UFix64(1000.0) // Optional: for aligning with Source in a stack let uniqueID = nil let sink = FungibleTokenConnectors.VaultSink( max: maxBalance, depositVault: depositCap, uniqueID: uniqueID ) // Note: Logs are only visible in the emulator console log("VaultSink created for deposit type: ".concat(sink.depositVaultType.identifier)) } } ``` ### Swapper A swapper exchanges tokens between different types with support for bidirectional swaps and price estimation. Bi-directional means that they support swaps in both directions, which is necessary if an inner connector can't accept the full swap output balance. ![swapper](./imgs/swapper.png) They also contain price discovery to provide estimates for the amounts in and out via the [`{Quote}`] object, and the [quote system] allows price caching and execution parameter optimization. Swappers conform to the `Swapper` [interface]: ```cadence access(all) struct interface Swapper : IdentifiableStruct { /// Input and output token types - in and out token types via default `swap()` route access(all) view fun inType(): Type access(all) view fun outType(): Type /// Price estimation methods - quote required amount given some desired output & output for some provided input access(all) fun quoteIn(forDesired: UFix64, reverse: Bool): {Quote} access(all) fun quoteOut(forProvided: UFix64, reverse: Bool): {Quote} /// Swap execution methods access(all) fun swap(quote: {Quote}?, inVault: @{FungibleToken.Vault}): @{FungibleToken.Vault} access(all) fun swapBack(quote: {Quote}?, residual: @{FungibleToken.Vault}): @{FungibleToken.Vault} } ``` To create a swapper, instantiate the appropriate `struct` from the appropriate connector. To create a swapper for [IncrementFi] with the [`IncrementFiSwapConnectors`], instantiate `Swapper`: ```cadence import "FlowToken" import "USDCFlow" import "IncrementFiSwapConnectors" import "SwapConfig" transaction { prepare(acct: &Account) { // Derive the path keys from the token types let flowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@FlowToken.Vault>().identifier) let usdcFlowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@USDCFlow.Vault>().identifier) // Minimal path Flow -> USDCFlow let swapper = IncrementFiSwapConnectors.Swapper( path: [ flowKey, usdcFlowKey ], inVault: Type<@FlowToken.Vault>(), outVault: Type<@USDCFlow.Vault>(), uniqueID: nil ) // Example: quote how much USDCFlow you'd get for 10.0 FLOW let qOut = swapper.quoteOut(forProvided: 10.0, reverse: false) // Note: Logs are only visible in the emulator console log(qOut) // Example: quote how much FLOW you'd need to get 25.0 USDCFlow let qIn = swapper.quoteIn(forDesired: 25.0, reverse: false) // Note: Logs are only visible in the emulator console log(qIn) } } ``` ### Price oracle A price [oracle] provides price data for assets with a consistent denomination. All prices are returned in the same unit and will return `nil` rather than reverting in the event that a price is unavailable. Prices are indexed by [Cadence type], requiring a specific Cadence-based token type for which to serve prices, as opposed to looking up an asset by a generic address. ![price oracle](./imgs/price-oracle.png) You can pass an argument this `Type`, or any conforming fungible token type conforming to the interface to the `price` function to get a price. The full [interface] for `PriceOracle` is: ```cadence access(all) struct interface PriceOracle : IdentifiableStruct { /// Returns the denomination asset (e.g., USDCf, FLOW) access(all) view fun unitOfAccount(): Type /// Returns current price or nil if unavailable, conditions for which are implementation-specific access(all) fun price(ofToken: Type): UFix64? } ``` To create a `PriceOracle` from [Band] with [`BandOracleConnectors`]: :::info You need to pay the oracle to get information from it. Here, we're using another Flow Action - a source - to fund getting a price from the oracle. ::: ```cadence import "FlowToken" import "FungibleToken" import "FungibleTokenConnectors" import "BandOracleConnectors" transaction { prepare(acct: auth(IssueStorageCapabilityController) &Account) { // Ensure we have an authorized capability for FlowToken (auth Withdraw) let storagePath = /storage/flowTokenVault let withdrawCap = acct.capabilities.storage.issue(storagePath) // Fee source must PROVIDE FlowToken vaults (per PriceOracle preconditions) let feeSource = FungibleTokenConnectors.VaultSource( min: 0.0, // keep at least 0.0 FLOW in the vault withdrawVault: withdrawCap, // auth withdraw capability uniqueID: nil ) // unitOfAccount must be a mapped symbol in BandOracleConnectors.assetSymbols. // The contract's init already maps FlowToken -> "FLOW", so this is valid. let oracle = BandOracleConnectors.PriceOracle( unitOfAccount: Type<@FlowToken.Vault>(), // quote token (e.g. FLOW in BASE/FLOW) staleThreshold: 600, // seconds; nil to skip staleness checks feeSource: feeSource, uniqueID: nil ) // Note: Logs are only visible in the emulator console log("Created PriceOracle; unit: ".concat(oracle.unitOfAccount().identifier)) } } ``` ### Flasher A flasher provides flash loans with atomic repayment requirements. ![flasher](./imgs/flasher.png) If you're not familiar with flash loans, imagine a scenario where you discovered an NFT listed for sale one one marketplace for 1 million dollars, then noticed an open bid to buy that same NFT for 1.1 million dollars on another marketplace. In theory, you could make an easy 100k by buying the NFT on the first marketplace and then fulfilling the open buy offer on the second marketplace. There's just one big problem - You might not have 1 million dollars liquid just laying around for you to purchase the NFT! Flash loans allow you to create one transaction during which you: 1. Borrow 1 million dollars. 2. Purchase the NFT. 3. Sell the NFT. 4. Repay 1 million dollars plus a small fee. :::warning This scenario may be a scam. A scammer could set up this situation as bait and cancel the buy order the instant someone purchases the NFT that is for sale. You'd have paid a vast amount of money for something worthless. The great thing about Cadence transactions, with or without Actions, is that you can set up an atomic transaction where everything either works, or is reverted. Either you make 100k, or nothing happens except a tiny expenditure of compute units. ::: Flashers adhere to the `Flasher` interface: ```cadence access(all) struct interface Flasher : IdentifiableStruct { /// Returns the asset type this Flasher can issue as a flash loan access(all) view fun borrowType(): Type /// Returns the estimated fee for a flash loan of the specified amount access(all) fun calculateFee(loanAmount: UFix64): UFix64 /// Performs a flash loan of the specified amount. The callback function is passed the fee amount, a loan Vault, /// and data. The callback function should return a Vault containing the loan + fee. access(all) fun flashLoan( amount: UFix64, data: AnyStruct?, callback: fun(UFix64, @{FungibleToken.Vault}, AnyStruct?): @{FungibleToken.Vault} // fee, loan, data ) } ``` You create a flasher the same way as the other actions, but you'll need the address for a `SwapPair`. You can get that onchain at runtime. For example, to borrow $FLOW from [IncrementFi]: ```cadence import "FungibleToken" import "FlowToken" import "USDCFlow" import "SwapInterfaces" import "SwapConfig" import "SwapFactory" import "IncrementFiFlashloanConnectors" transaction { prepare(_ acct: &Account) { // Increment uses token *keys* like "A.1654653399040a61.FlowToken" (mainnet FlowToken) // and "A.f1ab99c82dee3526.USDCFlow" (mainnet USDCFlow). let flowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@FlowToken.Vault>().identifier) let usdcFlowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@USDCFlow.Vault>().identifier) // Ask the factory for the pair's public capability (or address), then verify it. // Depending on the exact factory interface you have, one of these will exist: // - getPairAddress(token0Key: String, token1Key: String): Address // - getPairPublicCap(token0Key: String, token1Key: String): Capability<&{SwapInterfaces.PairPublic}> // - getPair(token0Key: String, token1Key: String): Address // // Try address first; if your factory exposes a different helper, swap it in. let pairAddr: Address = SwapFactory.getPairAddress(flowKey, usdcFlowKey) // Sanity-check: borrow PairPublic and verify it actually contains FLOW/USDCFlow let pair = getAccount(pairAddr) .capabilities .borrow<&{SwapInterfaces.PairPublic}>(SwapConfig.PairPublicPath) ?? panic("Could not borrow PairPublic at resolved address") let info = pair.getPairInfoStruct() assert( (info.token0Key == flowKey && info.token1Key == usdcFlowKey) || (info.token0Key == usdcFlowKey && info.token1Key == flowKey), message: "Resolved pair does not match FLOW/USDCFlow" ) // Instantiate the Flasher to borrow FLOW (switch to USDCFlow if you want that leg) let flasher = IncrementFiFlashloanConnectors.Flasher( pairAddress: pairAddr, type: Type<@FlowToken.Vault>(), uniqueID: nil ) // Note: Logs are only visible in the emulator console log("Flasher ready on mainnet FLOW/USDCFlow at ".concat(pairAddr.toString())) flasher.flashloan( amount: 100.0 data: nil callback: flashloanCallback ) } } // Callback function passed to flasher.flashloan access(all) fun flashloanCallback(fee: UFix64, loan: @{FungibleToken.Vault}, data: AnyStruct?): @{FungibleToken.Vault} { log("Flashloan with balance of \(loan.balance) \(loan.getType().identifier) executed") return <-loan } ``` ## Identification and traceability The `UniqueIdentifier` allows protocols to trace stack operations via Flow Actions interface-level events, which identifies them by IDs. `IdentifiableResource` implementations should verify that access to the identifier is encapsulated by the structures they identify. While you can create Cadence struct types in any context (such as passed in as transaction parameters), the authorized `AuthenticationToken` [capability] verifies that only those issued by the Flow Actions contract can be used in connectors, preventing forgery. For example, to use a `UniqueIdentifier` in a source->swap->sink: ```cadence import "FungibleToken" import "FlowToken" import "USDCFlow" import "FungibleTokenConnectors" import "IncrementFiSwapConnectors" import "SwapConfig" import "DeFiActions" transaction { prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) { // Standard token paths let storagePath = /storage/flowTokenVault let receiverStoragePath = USDCFlow.VaultStoragePath let receiverPublicPath = USDCFlow.VaultPublicPath // Ensure private auth-withdraw (for Source) let withdrawCap = acct.capabilities.storage.issue(storagePath) // Ensure public receiver Capability (for Sink) - configure receiving Vault is none exists if acct.storage.type(at: receiverStoragePath) == nil { // Save the USDCFlow Vault acct.storage.save(<-USDCFlow.createEmptyVault(vaultType: Type<@USDCFlow.Vault>()), to: USDCFlow.VaultStoragePath) // Issue and publish public Capabilities to the token's default paths let publicCap = acct.capabilities.storage.issue<&USDCFlow.Vault>(storagePath) ?? panic("failed to link public receiver") acct.capabilities.unpublish(receiverPublicPath) acct.capabilities.unpublish(USDCFlow.ReceiverPublicPath) acct.capabilities.publish(cap, at: receiverPublicPath) acct.capabilities.publish(cap, at: USDCFlow.ReceiverPublicPath) } let depositCap = acct.capabilities.get<&{FungibleToken.Vault}>(receiverPublicPath) // Initialize shared UniqueIdentifier - passed to each connector on init let uniqueIdentifier = DeFiActions.createUniqueIdentifier() // Instantiate: Source, Swapper, Sink let source = FungibleTokenConnectors.VaultSource( min: 5.0, withdrawVault: withdrawCap, uniqueID: uniqueIdentifier ) // Derive the IncrementFi token keys from the token types let flowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@FlowToken.Vault>().identifier) let usdcFlowKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(vaultTypeIdentifier: Type<@USDCFlow.Vault>().identifier) // Replace with a real Increment path when swapping tokens (e.g., FLOW → USDCFlow) // e.g. ["A.1654653399040a61.FlowToken", "A.f1ab99c82dee3526.USDCFlow"] let swapper = IncrementFiSwapConnectors.Swapper( path: [flowKey, usdcFlowKey], inVault: Type<@FlowToken.Vault>(), outVault: Type<@USDCFlow.Vault>(), uniqueID: uniqueIdentifier ) let sink = FungibleTokenConnectors.VaultSink( max: nil, depositVault: depositCap, uniqueID: uniqueIdentifier ) // ----- Real composition (no destroy) ----- // 1) Withdraw from Source let tokens <- source.withdrawAvailable(maxAmount: 100.0) // 2) Swap with Swapper from FLOW → USDCFlow let swapped <- swapper.swap(quote: nil, inVault: <-tokens) // 3) Deposit into Sink (consumes by reference via withdraw()) sink.depositCapacity(from: &swapped as auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) // 4) Return any residual by depositing the *entire* vault back to user's USDCFlow vault // (works even if balance is 0; deposit will still consume the resource) depositCap.borrow().deposit(from: <-swapped) // Optional: inspect that all three share the same ID log(source.id()) log(swapper.id()) log(sink.id()) } } ``` ## Why `UniqueIdentifier` matters in FlowActions The `UniqueIdentifier` is used to tag multiple FlowActions connectors as part of the **same logical operation**. By aligning the same ID across connectors (for example, Source → Swapper → Sink), you can: ### 1. Event correlation - Every connector emits events tagged with its `UniqueIdentifier`. - Shared IDs let you filter and group related events in the chain's event stream. - Makes it easy to see that a withdrawal, swap, and deposit were part of **one workflow**. ### 2. Stack tracing - When you use composite connectors (for example, `SwapSource`, `SwapSink`, `MultiSwapper`), IDs allow you to trace the complete path through the stack. - Helpful to heklp you debug and understand the flow of operations inside complex strategies. ### 3. Analytics and attribution - Allows measuring usage of specific strategies or routes. - Lets you join data from multiple connectors into a single logical "transaction" for reporting. - Supports fee attribution and performance monitorsacross multi-step workflows. ### Without a shared `UniqueIdentifier` - Events from different connectors appear unrelated, even if they occurred in the same transaction. - Harder to debug, track, or analyze multi-step processes. ## Conclusion In this tutorial, you learned about Flow Actions, a suite of standardized Cadence interfaces that enable developers to compose complex DeFi workflows using small, reusable components. You explored the five core Flow Action types - Source, Sink, Swapper, PriceOracle, and Flasher - and learned how to create and use them with various connectors. Now that you have completed this tutorial, you can: - Understand the key features of Flow Actions including atomic composition, weak guarantees, and event traceability - Create and use Sources to provide tokens from various protocols and locations - Create and use Sinks to accept tokens up to defined capacity limits - Create and use Swappers to exchange tokens between different types with price estimation - Create and use Price Oracles to get price data for assets with consistent denomination - Create and use Flashers to provide flash loans with atomic repayment requirements - Use UniqueIdentifiers to trace and correlate operations across multiple Flow Actions - Compose complex DeFi workflows by connecting multiple Actions in a single atomic transaction [FLIP 339]: https://github.com/onflow/flips/pull/339/files [Cadence]: https://cadence-lang.org/docs [Fungible Token Standard]: https://developers.flow.com/build/cadence/guides/fungible-token [Connectors]: ./connectors.md [connector]: ./connectors.md [connectors article]: ./connectors.md [vault]: https://developers.flow.com/build/cadence/guides/fungible-token#vaults-on-flow [interface]: https://cadence-lang.org/docs/language/interfaces [resource]: https://cadence-lang.org/docs/language/resources [IncrementFi]: https://app.increment.fi/swap?in=A.1654653399040a61.FlowToken&out= [`IncrementFiSwapConnectors`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiSwapConnectors.cdc [`IncrementFiFlashloanConnectors`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/increment-fi/IncrementFiFlashloanConnectors.cdc [`FungibleTokenConnectors`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/FungibleTokenConnectors.cdc [oracle]: https://developers.flow.com/defi/defi-contracts-mainnet#oracles [Band]: https://blog.bandprotocol.com/ [`BandOracleConnectors`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/connectors/band-oracle/BandOracleConnectors.cdc [Cadence type]: https://cadence-lang.org/docs/language/types-and-type-system/type-safety [capability]: https://cadence-lang.org/docs/language/capabilities --- # File: /md/blockchain-development-tutorials/forte/scheduled-transactions.md --- title: Scheduled Transactions description: A series of tutorials on implementing scheduled transactions for time-based smart contract execution on Flow. sidebar_position: 2 keywords: - scheduled transactions - time-based execution - blockchain automation - flow blockchain - cadence smart contracts - automated workflows - cron jobs - blockchain scheduling --- # Scheduled Transactions Tutorials This series covers how to implement scheduled transactions for time-based smart contract execution on Flow, which allows developers to create automated workflows and time-sensitive blockchain applications. These tutorials are part of the Forte network upgrade, which introduces new capabilities to the Flow blockchain. ## Tutorials - **[Scheduled Transactions Introduction]** - Learn how to implement scheduled transactions for time-based smart contract execution on Flow. [Scheduled Transactions Introduction]: ./scheduled-transactions-introduction.md --- # File: /md/blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md --- title: Introduction to Scheduled Transactions description: Learn how to implement scheduled transactions for time-based smart contract execution on Flow sidebar_position: 5 keywords: - scheduled transactions - flow transaction scheduler - time-based execution - blockchain automation - deferred actions - recurring jobs - smart contract scheduling - flow blockchain - cadence smart contracts - defi automation --- # Introduction to Scheduled Transactions Flow, EVM, and other blockchains are a form of a **single** shared computer that anyone can use, with no admin privileges, super user roles, or complete control. For this to work, it must be impossible for any user to freeze the computer, on purpose or by accident. As a result, most blockchain computers, including EVM and Solana, aren't [Turing Complete], because they can't run an unbounded loop. Each transaction must occur within one block, and can't consume more compute units, or gas, than the limit. While this limitation prevents infinite loops, it makes it so that you can't do anything 100% onchain if you need it to happen at a later time or after a trigger. As a result, developers must often build products that involve a fair amount of traditional infrastructure and requires users to give those developers a great amount of trust that their backend will execute the promised task. Flow fixes this problem with _scheduled transactions_. Scheduled Transactions let smart contracts execute code at, or after, a chosen time without an external transaction. You schedule work now and the network executes it later. This allows recurring jobs, deferred actions, and autonomous workflows. ## Learning objectives After you complete this tutorial, you will be able to: - Understand the concept of scheduled transactions and how they solve blockchain limitations. - Explain the key components of the `FlowTransactionScheduler` system. - Implement a basic scheduled transaction using the provided scaffold. - Analyze the structure and flow of scheduled transaction transactions. - Create custom scheduled transaction contracts and handlers. - Evaluate the benefits and use cases of scheduled transactions in DeFi applications. # Prerequisites ## Cadence programming language This tutorial assumes you have a modest knowledge of [Cadence]. If you don't, you can follow along, but you'll get more out of it if you complete our series of [Cadence] tutorials. Most developers find it more pleasant than other blockchain languages, and it's not hard to pick up. ## Get started To start, run `flow init` and select `Scheduled Transactions project`. Open the project. The `readme` file has a robust getting started guide. Complete that to set up and run the demo scheduled transaction. It doesn't seem like much at first. The counter was at `0`, you ran a transaction, now it's at `1`. What's the big deal? Let's try again to make it clearer what's happening. Open `cadence/transactions/ScheduleIncrementIn.cdc` and look at the arguments for the transaction: ```cadence transaction( delaySeconds: UFix64, priority: UInt8, executionEffort: UInt64, transactionData: AnyStruct? ) ``` The first parameter is the delay in seconds for the scheduled transaction. Let's try running it again. You'll need to be quick on the keyboard, so feel free to use a higher number of `delaySeconds` if you need to. You're going to: 1. Call the script to view the counter. 2. Call the transaction to schedule the counter to increment after 10 seconds. 3. Call the script to view the counter again and verify that it hasn't changed yet. 4. Wait 10 seconds, call it again, and confirm the counter incremented. For your convenience, the updated transaction call is: ```zsh flow transactions send cadence/transactions/ScheduleIncrementIn.cdc \ --network emulator --signer emulator-account \ --args-json '[ {"type":"UFix64","value":"20.0"}, {"type":"UInt8","value":"1"}, {"type":"UInt64","value":"1000"}, {"type":"Optional","value":null} ]' ``` And the call to run the script to get the count is: ```zsh flow scripts execute cadence/scripts/GetCounter.cdc --network emulator ``` The result in your terminal should be similar to: ```zsh briandoyle@Mac scheduled-transactions-scaffold % flow scripts execute cadence/scripts/GetCounter.cdc --network emulator Result: 2 briandoyle@Mac scheduled-transactions-scaffold % flow transactions send cadence/transactions/ScheduleIncrementIn.cdc \ --network emulator --signer emulator-account \ --args-json '[ {"type":"UFix64","value":"10.0"}, {"type":"UInt8","value":"1"}, {"type":"UInt64","value":"1000"}, {"type":"Optional","value":null} ]' Transaction ID: 61cc304cee26ad1311cc1b0bbcde23bf2b3a399485c2b6b8ab621e429abce976 Waiting for transaction to be sealed...⠹ Block ID 6b9f5138901cd0d299adea28e96d44a6d8b131ef58a9a14a072a0318da0ad16b Block Height 671 Status ✅ SEALED ID 61cc304cee26ad1311cc1b0bbcde23bf2b3a399485c2b6b8ab621e429abce976 Payer f8d6e0586b0a20c7 Authorizers [f8d6e0586b0a20c7] # Output omitted for brevity briandoyle@Mac scheduled-transactions-scaffold % flow scripts execute cadence/scripts/GetCounter.cdc --network emulator Result: 2 briandoyle@Mac scheduled-transactions-scaffold % flow scripts execute cadence/scripts/GetCounter.cdc --network emulator Result: 2 briandoyle@Mac scheduled-transactions-scaffold % flow scripts execute cadence/scripts/GetCounter.cdc --network emulator Result: 3 ``` ### Review the current contract and transactions If you're not familiar with `cadence/contracts/Counter.cdc` review it. This is the standard contract created by default when you run `flow init`. It's very simple, with a counter and public functions to increment or decrement it. ### Transaction handler Next, open `cadence/contracts/CounterTransactionHandler.cdc` ```cadence import "FlowTransactionScheduler" import "Counter" access(all) contract CounterTransactionHandler { /// Handler resource that implements the Scheduled Transaction interface access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { Counter.increment() let newCount = Counter.getCount() log("Transaction executed (id: ".concat(id.toString()).concat(") newCount: ").concat(newCount.toString())) } access(all) view fun getViews(): [Type] { return [Type(), Type()] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return /storage/CounterTransactionHandler case Type(): return /public/CounterTransactionHandler default: return nil } } } /// Factory for the handler resource access(all) fun createHandler(): @Handler { return <- create Handler() } } ``` This contract is simple. It contains a [resource] that has a function with the `FlowTransactionScheduler.Execute` [entitlement]. This function contains the code that the scheduled transaction calls. It: 1. Calls the `increment` function in the `Counter` contract. 2. Fetches the current value in the counter. 3. Logs that value to the console **for the emulator**. It also contains functions to get metadata about the handler and a function, `createHandler`, which creates and returns an instance of the `Handler` resource. There are other metadata views that could be good to include in your Handler, but we're sticking to the basic ones for now. ### Initialize the transaction handler Next, take a look at `cadence/transactions/InitCounterTransactionHandler.cdc`: ```cadence import "CounterTransactionHandler" import "FlowTransactionScheduler" transaction() { prepare(signer: auth(Storage, Capabilities) &Account) { // Save a handler resource to storage if not already present if signer.storage.borrow<&AnyResource>(from: /storage/CounterTransactionHandler) == nil { let handler <- CounterTransactionHandler.createHandler() signer.storage.save(<-handler, to: /storage/CounterTransactionHandler) } // Validation/example that we can create an issue a handler capability with correct entitlement for FlowTransactionScheduler let _ = signer.capabilities.storage .issue(/storage/CounterTransactionHandler) // Issue a non-entitled public capability for the handler that is publicly accessible let publicCap = signer.capabilities.storage .issue<&{FlowTransactionScheduler.TransactionHandler}>(/storage/CounterTransactionHandler) // publish the capability signer.capabilities.publish(publicCap, at: /public/CounterTransactionHandler) } } ``` This transaction saves an instance of the `Handler` resource to the user's [storage]. It also tests out/demonstrates how to issue the handler [capability] with the `FlowTransactionScheduler.Execute` [entitlement] and how to publish an un-entitled capability to the handler so it can be publicly accessible. The use of the name `_` is convention to name a variable we don't intend to use for anything. ### Schedule the transaction Finally, open `cadence/transactions/ScheduleIncrementIn.cdc` again. This is the most complicated transaction, so we'll break it down. The final call other than the `log` is what actually schedules the transaction: ```cadence manager.schedule( handlerCap: handlerCap, data: transactionData, timestamp: future, priority: pr, executionEffort: executionEffort, fees: <-fees ) ``` It calls the `schedule` function from the `FlowTransactionSchedulerUtils.Manager` contract. This function has parameters for: - `handlerCap`: The handler [capability] for the code that should execute. This was created above during the previous transaction with: ```cadence let handlerCap = signer.capabilities.storage .issue(/storage/CounterTransactionHandler) ``` That line creates a capability with the `FlowTransactionScheduler.Execute` entitlement. That entitlement permits calling the function (`executeTransaction()`) from the `Handler` resource in `CounterTransactionHandler.cdc` that you created and stored an instance of in the `InitCounterTransactionHandler` transaction. Then, in the schedule transaction, we retrieve the handler capability that we created before. We created two separate handlers, a public and a private one, so we have to make sure we're getting the private one: ```cadence // Get the entitled capability that will be used to create the transaction // Need to check both controllers because the order of controllers is not guaranteed var handlerCap: Capability? = nil if let cap = account.capabilities.storage .getControllers(forPath: /storage/CounterTransactionHandler)[0] .capability as? Capability { handlerCap = cap } else { handlerCap = account.capabilities.storage .getControllers(forPath: /storage/CounterTransactionHandler)[1] .capability as! Capability } ``` - `data`: The arguments required by the transaction function. In this example, `transactionData` is passed in as a prop on the transaction and is `null`. - `timestamp`: The timestamp for the time in the `future` that this transaction should be run. The transaction call has an argument for `delaySeconds`, which is then converted to a `future` timestamp: ```cadence let future = getCurrentBlock().timestamp + delaySeconds ``` - `priority`: The priority this transaction is given in the event of network congestion. A higher priority means a higher fee for higher precedence. The `priority` argument is supplied in the transaction as a `UInt8` for convenience, then converted into the appropriate [enum] type: ```cadence let pr = priority == 0 ? FlowTransactionScheduler.Priority.High : priority == 1 ? FlowTransactionScheduler.Priority.Medium : FlowTransactionScheduler.Priority.Low ``` The `executionEffort` is also supplied as an argument in the transaction. This represents the compute unit limit for your transaction and used to prepare the estimate for the compute unit fees that must be paid for the transaction, and directly in the call to `schedule()` the transaction. - `fees`: A [vault] containing the appropriate amount of compute unit fees needed to pay for the execution of the scheduled transaction. To create the vault, the `calculateFee()` function calculates the amount needed: ```cadence // Calculates the estimated fee for the provided execution effort, priority, and transaction data size (in megabytes) // If the scheduled transaction has no data for its `data` argument, then the data size can be zero let est = FlowTransactionScheduler.calculateFee( executionEffort: executionEffort, priority: pr, dataSizeMB: 0 ) ``` Then, an [authorized reference] to the signer's vault is created and used to `withdraw()` the needed funds and [move] them into the `fees` variable, which is then sent in the `schedule()` function call. ## Use the FlowTransactionSchedulerUtils.Manager The `FlowTransactionSchedulerUtils.Manager` resource provides a safer and more convenient way to manage scheduled transactions. Instead of directly calling the `FlowTransactionScheduler` contract, you can use the Manager resource that manages all your scheduled transactions from a single place and handles many of the common patterns to reduce boilerplate code. It also provides many convenient functions to get detailed information about all the transactions you have scheduled by timestamp, handler, and so on. When setting up a manager, you also publish a capability for it so it is easy for scripts to query your account and also see what transactions are scheduled! ### Set up the Manager First, you need to create and store a Manager resource in your account: ```cadence import "FlowTransactionSchedulerUtils" import "FlowToken" import "FungibleToken" transaction() { prepare(signer: auth(Storage, Capabilities) &Account) { // Create and save the Manager resource let manager <- FlowTransactionSchedulerUtils.createManager() signer.storage.save(<-manager, to: FlowTransactionSchedulerUtils.managerStoragePath) // Create a capability for the Manager let managerCap = signer.capabilities.storage.issue<&FlowTransactionSchedulerUtils.Manager>(FlowTransactionSchedulerUtils.managerStoragePath) signer.capabilities.publish(managerCap, at: FlowTransactionSchedulerUtils.managerPublicPath) } } ``` ### Schedule transactions with the Manager The Manager provides a `schedule` method that simplifies the scheduling process: ```cadence manager.schedule( handlerCap: handlerCap, data: transactionData, timestamp: future, priority: priority, executionEffort: executionEffort, fees: <-fees ) ``` The Manager also provides utility methods for: - Scheduling another transaction with a previously used handler. - Getting scheduled transaction information in many different ways. - Canceling scheduled transactions. - Managing transaction handlers. - Querying transaction status. ## Write a new scheduled transaction With this knowledge, we can create our own scheduled transaction. For this demo, we'll simply display a hello from an old friend in the emulator's console logs. ### Create the contracts To start, use the [Flow CLI] to create a new contract called `RickRoll.cdc` and one called `RickRollTransactionHandler.cdc`: ```zsh flow generate contract RickRoll flow generate contract RickRollTransactionHandler ``` Open the `RickRoll` contract and add functions to log a fun message to the emulator console, and a variable to track which message to call: ```cadence access(all) contract RickRoll { access(all) var messageNumber: UInt8 init() { self.messageNumber = 0 } // Reminder: Anyone can call these functions! access(all) fun message1() { log("Never gonna give you up") self.messageNumber = 1 } access(all) fun message2() { log("Never gonna let you down") self.messageNumber = 2 } access(all) fun message3() { log("Never gonna run around and desert you") self.messageNumber = 3 } access(all) fun resetMessageNumber() { self.messageNumber = 0 } } ``` Next, open `RickRollTransactionHandler.cdc`. Import the `RickRoll` contract, `FlowToken`, `FungibleToken`, and `FlowTransactionScheduler`, and stub out the `Handler` and factory: ```cadence import "FlowTransactionScheduler" import "RickRoll" import "FlowToken" import "FungibleToken" access(all) contract RickRollTransactionHandler { /// Handler resource that implements the Scheduled Transaction interface access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { // TODO } /// Factory for the handler resource access(all) fun createHandler(): @Handler { return <- create Handler() } } ``` Next, add a switch to call the appropriate function based on what the current `messageNumber` is and add the metadata getters: ```cadence access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { switch (RickRoll.messageNumber) { case 0: RickRoll.message1() case 1: RickRoll.message2() case 2: RickRoll.message3() case 3: return default: panic("Invalid message number") } } access(all) view fun getViews(): [Type] { return [Type(), Type()] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return /storage/RickRollTransactionHandler case Type(): return /public/RickRollTransactionHandler default: return nil } } } ``` We could move forward with this, but it would be more fun to have each transaction schedule the follow transaction to share the next message. To do this, move most of the code found in the transaction to the handler. Start with configuring the `delay`, `future`, `priority`, and `executionEffort`. We'll hardcode these for simplicity: ```cadence var delay: UFix64 = 5.0 let future = getCurrentBlock().timestamp + delay let priority = FlowTransactionScheduler.Priority.Medium let executionEffort: UInt64 = 1000 ``` Next, add the `calculateFee()` call to calculate the fee for the scheduled transaction and ensure that a handler for the scheduled transaction exists. Your transaction does not provide and accompanying data, so your `dataSizeMB` argument can be zero. ```cadence let estimate = FlowTransactionScheduler.calculateFee( executionEffort: executionEffort, priority: priority, dataSizeMB: 0 ) // Ensure a handler resource exists in the contract account storage if RickRollTransactionHandler.account.storage.borrow<&AnyResource>(from: /storage/RickRollTransactionHandler) == nil { let handler <- RickRollTransactionHandler.createHandler() RickRollTransactionHandler.account.storage.save(<-handler, to: /storage/RickRollTransactionHandler) // Issue a non-entitled public capability for the handler that is publicly accessible let publicCap = RickRollTransactionHandler.account.capabilities.storage .issue<&{FlowTransactionScheduler.TransactionHandler}>(/storage/RickRollTransactionHandler) // publish the capability RickRollTransactionHandler.capabilities.publish(publicCap, at: /public/RickRollTransactionHandler) } ``` Then withdraw the necessary funds: ```cadence let vaultRef = CounterLoopTransactionHandler.account.storage .borrow(from: /storage/flowTokenVault) ?? panic("missing FlowToken vault on contract account") let fees <- vaultRef.withdraw(amount: estimate ?? 0.0) as! @FlowToken.Vault ``` Finally, schedule the transaction: ```cadence // borrow a reference to the scheduled transaction manager let manager = RickRollTransactionHandler.account.storage.borrow(from: FlowTransactionSchedulerUtils.managerStoragePath) ?? panic("Could not borrow a Manager reference from \(FlowTransactionSchedulerUtils.managerStoragePath)") let handlerTypeIdentifier = manager.getHandlerTypes().keys[0]! manager.scheduleByHandler( handlerTypeIdentifier: handlerTypeIdentifier, handlerUUID: nil, data: data, timestamp: future, priority: priority, executionEffort: executionEffort, fees: <-fees ) ``` As you can see, this time, we didn't have to get the handler capability. This is because the manager stores a history of handlers that you have used in the past so that you can easily just specify the type of the handler that you want to schedule for and it will schedule it for you. ### Set Up the transactions Next, you need to add transactions to initialize the new transaction handler, and another to fire off the sequence. To start, add `InitRickRollHandler.cdc`: ```zsh flow generate transaction InitRickRollHandler ``` The transaction itself is nearly identical to the one we reviewed: ```cadence import "RickRollTransactionHandler" import "FlowTransactionScheduler" transaction() { prepare(signer: auth(Storage, Capabilities) &Account) { // Save a handler resource to storage if not already present if signer.storage.borrow<&AnyResource>(from: /storage/RickRollTransactionHandler) == nil { let handler <- RickRollTransactionHandler.createHandler() signer.storage.save(<-handler, to: /storage/RickRollTransactionHandler) // Validation/example that we can create an issue a handler capability with correct entitlement for FlowTransactionScheduler signer.capabilities.storage .issue(/storage/RickRollTransactionHandler) // Issue a non-entitled public capability for the handler that is publicly accessible let publicCap = signer.capabilities.storage .issue<&{FlowTransactionScheduler.TransactionHandler}>(/storage/RickRollTransactionHandler) // publish the capability signer.capabilities.publish(publicCap, at: /public/RickRollTransactionHandler) } } } ``` Next, add `ScheduleRickRoll`: ```zsh flow generate transaction ScheduleRickRoll ``` This transaction is essentially identical as well, it just uses the `handlerCap` stored in `RickRollTransaction`: ```cadence import "FlowTransactionScheduler" import "FlowToken" import "FungibleToken" /// Schedule a Rick Roll with a delay of delaySeconds transaction( delaySeconds: UFix64, priority: UInt8, executionEffort: UInt64, transactionData: AnyStruct? ) { prepare(signer: auth(Storage, Capabilities) &Account) { let future = getCurrentBlock().timestamp + delaySeconds let pr = priority == 0 ? FlowTransactionScheduler.Priority.High : priority == 1 ? FlowTransactionScheduler.Priority.Medium : FlowTransactionScheduler.Priority.Low let est = FlowTransactionScheduler.calculateFee( executionEffort: executionEffort, priority: pr, dataSizeMB: 0 ) let vaultRef = signer.storage .borrow(from: /storage/flowTokenVault) ?? panic("missing FlowToken vault") let fees <- vaultRef.withdraw(amount: est ?? 0.0) as! @FlowToken.Vault // if a transaction scheduler manager has not been created for this account yet, create one if !signer.storage.check<@{FlowTransactionSchedulerUtils.Manager}>(from: FlowTransactionSchedulerUtils.managerStoragePath) { let manager <- FlowTransactionSchedulerUtils.createManager() signer.storage.save(<-manager, to: FlowTransactionSchedulerUtils.managerStoragePath) // create a public capability to the scheduled transaction manager let managerRef = signer.capabilities.storage.issue<&{FlowTransactionSchedulerUtils.Manager}>(FlowTransactionSchedulerUtils.managerStoragePath) signer.capabilities.publish(managerRef, at: FlowTransactionSchedulerUtils.managerPublicPath) } // Get a capability to the handler stored in this contract account // Get the entitled capability that will be used to create the transaction // Need to check both controllers because the order of controllers is not guaranteed var handlerCap: Capability? = nil if let cap = signer.capabilities.storage .getControllers(forPath: /storage/RickRollTransactionHandler)[0] .capability as? Capability { handlerCap = cap } else { handlerCap = signer.capabilities.storage .getControllers(forPath: /storage/RickRollTransactionHandler)[1] .capability as! Capability } // borrow a reference to the scheduled transaction manager let manager = signer.storage.borrow(from: FlowTransactionSchedulerUtils.managerStoragePath) ?? panic("Could not borrow a Manager reference from \(FlowTransactionSchedulerUtils.managerStoragePath)") manager.schedule( handlerCap: handlerCap, data: transactionData, timestamp: future, priority: pr, executionEffort: executionEffort, fees: <-fees ) log("Scheduled transaction at \(future)") } } ``` ### Deployment and testing It's now time to deploy and test the new scheduled transaction! First, add the new contracts to the emulator account in `flow.json` (other contracts may be present): ```json "deployments": { "emulator": { "emulator-account": [ "RickRoll", "RickRollTransactionHandler" ] } } ``` Then, deploy the contracts to the emulator: ```zsh flow project deploy --network emulator ``` Next, execute the transaction to initialize the new scheduled transaction handler: ```zsh flow transactions send cadence/transactions/InitRickRollHandler.cdc \ --network emulator --signer emulator-account ``` Finally, **get ready to quickly switch to the emulator console** and call the transaction to schedule the transaction: ```zsh flow transactions send cadence/transactions/ScheduleRickRoll.cdc \ --network emulator --signer emulator-account \ --args-json '[ {"type":"UFix64","value":"2.0"}, {"type":"UInt8","value":"1"}, {"type":"UInt64","value":"1000"}, {"type":"Optional","value":null} ]' ``` In the logs, you'll see content similar to: ```zsh 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "Scheduled transaction at 1755099632.00000000" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.execute_transaction] executing transaction 4" 11:40AM INF LOG: "Never gonna give you up" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.execute_transaction] executing transaction 5" 11:40AM INF LOG: "Never gonna let you down" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.execute_transaction] executing transaction 6" 11:40AM INF LOG: "Never gonna run around and desert you" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" 11:40AM INF LOG: "[system.process_transactions] processing transactions" ``` The last case `return`s the function, so it doesn't set a new scheduled transaction. ## Conclusion In this tutorial, you learned about scheduled transactions, a powerful feature that enables smart contracts to execute code at future times without external transactions. You explored how scheduled transactions solve the fundamental limitation of blockchain computers being unable to run unbounded loops or execute time-delayed operations. Now that you have completed this tutorial, you should be able to: - Understand the concept of scheduled transactions and how they solve blockchain limitations. - Explain the key components of the FlowTransactionScheduler system. - Understand the benefits of the Transaction Scheduler Manager. - Implement a basic scheduled transaction using the provided scaffold. - Analyze the structure and flow of scheduled transaction transactions. - Create custom scheduled transaction contracts and handlers. - Evaluate the benefits and use cases of scheduled transactions in DeFi applications. Scheduled transactions open up new possibilities for DeFi applications, enabling recurring jobs, deferred actions, and autonomous workflows that were previously impossible on blockchain. This feature represents a significant step forward in making blockchain more practical for real-world applications that require time-based execution. [FLIP 330]: https://github.com/onflow/flips/pull/331/files [Turing Complete]: https://en.wikipedia.org/wiki/Turing_completeness [Scheduled Transactions Scaffold]: https://github.com/onflow/scheduledtransactions-scaffold [Cadence]: https://cadence-lang.org/docs [resource]: https://cadence-lang.org/docs/language/resources [entitlement]: https://cadence-lang.org/docs/language/access-control#entitlements [storage]: https://cadence-lang.org/docs/language/accounts/storage [enum]: https://cadence-lang.org/docs/language/enumerations [vault]: https://developers.flow.com/build/cadence/guides/fungible-token#vaults-on-flow [authorized reference]: https://cadence-lang.org/docs/language/references#authorized-references [move]: https://cadence-lang.org/docs/language/operators/assign-move-force-swap#move-operator-- [Flow CLI]: https://developers.flow.com/tools/flow-cli --- # File: /md/blockchain-development-tutorials/gasless-transactions.md --- title: Gasless Transactions description: Build on a blockchain with no gas fees. Flow enables gasless transactions natively, letting developers build apps without user transaction costs. sidebar_position: 9 keywords: - Backend - Flow - Flow EVM - Flow Node - Gas Free - EVM Gateway - RPC Endpoint --- # Gasless Transactions on Flow Flow is one of the easiest platforms for developers to onboard new users. Currently, the Flow Wallet automatically sponsors transactions on **both testnet and mainnet**. This allows developers to build seamless Web3 applications without the need for users to manage gas tokens or pay transaction fees. In addition to native sponsorship, Flow also supports multiple methods for gas sponsorship that you can tailor to your application’s needs. You can learn about these approaches in more detail [here]. The [Flow Wallet] currently sponsors all transactions - on testnet and mainnet! This is possible because [sponsored transactions] are a native feature of the Flow Protocol. Additional methods for gas sponsorship are available and are described here. ## What you'll learn In this tutorial series, you’ll discover how to: - Configure and deploy a **gas free EVM endpoint** for your backend. - Allow **gasless transactions** so that users can interact with your app and never gas fees. - Use Flow’s EVM Gateway service account to automatically cover gas fees for transactions, which ensures a smooth experience for your users. ## Tutorial for how to build on an EVM blockchain without Gas fees Learn how to set up a gas free EVM endpoint for your backend. All transactions sent through this endpoint aren't charged gas fees from the sender’s account. Instead, the EVM Gateway’s service account will sponsor the gas, which makes transactions completely **gasless for end users**. Tutorial: [Gas Free EVM Endpoint] [Flow Wallet]: https://wallet.flow.com/ [sponsored transactions]: ../../build/cadence/advanced-concepts/account-abstraction#sponsored-transactions [Gas Free EVM Endpoint]: ./sponsored-transactions-evm-endpoint.md [here]: https://developers.flow.com/build/cadence/advanced-concepts/account-abstraction#sponsored-transactions --- # File: /md/blockchain-development-tutorials/gasless-transactions/sponsored-transactions-evm-endpoint.md --- title: Sponsored Transactions EVM Endpoint description: Learn how to set up a gas-free EVM RPC endpoint that sponsors all transaction fees for your users, eliminating gas fees as a barrier to adoption. sidebar_position: 1 keywords: - gas free - sponsored transactions - EVM Gateway - RPC endpoint - backend development - user experience - transaction fees - Flow EVM - service accounts - embedded wallets - gas sponsorship - custom gateway - account management - concurrent transactions - web3 adoption - user onboarding --- # Gas Free EVM Endpoint Gas fees are one of the biggest barriers to user adoption in blockchain applications. While Flow's native Cadence transactions support a separate fee payer role, EVM transactions require the sender to pay their own gas fees. This creates friction for users who need to acquire tokens before they can interact with your application. Flow EVM's architecture provides a unique solution: you can set up a **gas-free EVM endpoint** that sponsors all transaction fees on behalf of your users. This allows seamless user experiences similar to traditional web applications, where users don't need to worry about transaction costs. :::warning This method only works in situations where you can control the RPC node used to **send** transactions, such as your backend, or if your frontend uses embedded wallets. It will **not** work on the frontend with traditional wallets, such as MetaMask. ::: ## Objectives After you complete this guide, you'll be able to: - Understand how Flow EVM's transaction model allows gas-free transactions. - Set up a custom EVM Gateway that sponsors all transaction fees. - Configure your application to use a gas-free RPC endpoint. - Implement sponsored transactions for various business scenarios. - Manage service accounts and keys for concurrent transaction processing. ## Prerequisites ### Flow EVM and RPC endpoints This tutorial assumes you're familiar with [Flow EVM] and how RPC endpoints work. You should understand the difference between read and write operations, and how transactions are sent through RPC endpoints. ## Get started This tutorial will guide you through how to set up a gas-free EVM endpoint that sponsors all transaction fees for your users. You'll: 1. Create a service account to act as the fee payer. 2. Configure a custom EVM Gateway with gas sponsorship turned on. 3. Sett up your application to use the custom RPC endpoint. ## Overview If you've read [Using Flow EVM], you know that you use the RPC endpoint to send EVM transactions to the Flow EVM. The default RPC endpoint is actually the [EVM Gateway]. EVM transactions sent through the EVM Gateway are wrapped in a Flow Cadence transaction and sent to the Flow network. The Flow EVM Gateway's service account is the payer of that Cadence transaction. :::info In Cadence, the proposer, payer, and signer of a transaction are natively separate. ::: For EVM transactions inside this Cadence transaction, the transaction fees of the EVM transaction are sent from the sender's account and applied to the EVM Gateway's service account. The EVM Gateway pays the Cadence transaction fee, but is reimbursed via the EVM transaction's gas fee, which is directed to its own EVM address as the _coinbase_. Here are the key points of the Flow EVM Gateway: - The EVM Gateway works as a proxy to send EVM transactions. - The EVM Gateway wraps EVM transactions into native Flow Cadence transactions. - The EVM Gateway has a service account, which is the payer and sender of all native transactions sent through the EVM Gateway. - The sender of the EVM transaction is the fee payer of its EVM transaction to the EVM Gateway's service account. ### Why do we need a gas-free EVM endpoint? From the Flow [transaction model], we know that there is actually a Fee Payer role in native Flow transactions. When the transaction is executed, the fees for the transaction are entirely borne by the Fee Payer role. However, for EVM, the transaction model doesn't separate the signer from the payer. Therefore, when we execute transactions on the EVM, the sender of the transaction **must** cover the transaction fees. If someone wants to send an EVM transaction through the default EVM Gateway, the sender's account must have enough balance to cover the transaction fees, unless they use the Flow wallet, which also sponsors gas. Developers who use embedded wallets, or hold user keys on the backend, may prefer to sponsor the transaction fees to create a more user-friendly experience, just as can be done in native Cadence transactions or paymaster solutions on other networks. Here, we provide a solution: you can set up a sponsored transaction EVM endpoint for your backend service, and all transactions sent through this endpoint are sponsored by your account. ### Who needs the gas-free EVM endpoint? Here are several typical business scenarios suitable for its use: - Centralized exchanges that wish to improve internal transaction performance for users' deposits and withdrawals. - Apps with embedded wallets with social login that don't want to require users to deposit money in a new wallet before they can interact with the app. - Games that need to complete onchain tasks to onboard new players who wish to try the game. - Backends that create and manage large amounts of wallets on behalf of their users. - Businesses that wish to provide sponsored transactions as a service. As long as you can ensure that all your users' transactions are sent through a custom RPC endpoint, you can set up a gas-free EVM Gateway to allow gas-free transactions for your users. ## How to set up a gas-free EVM endpoint Most of the tasks you need to complete are in the guide to set up your own [Custom EVM Gateway]. In addition to those tasks, you'll need to: 1. Prepare a Service Account for the EVM Gateway to cover all transaction fees. Refer to [Account Creation] for more details. 2. Add enough identical keys to the Service Account to support the concurrent signing of EVM transactions. This is very important because the EVM Gateway uses the Service Account to pay for the gas fees of the EVM transactions, so the Service Account needs to have enough keys to support the concurrent signing of EVM transactions. Refer to [Account and Key Management] for more details. 3. Set the correct environment variables for the EVM Gateway to turn on the gas-free feature. Adjust the environment variables in the [Run the gateway] section as follows: - `COINBASE`: The address used to accept EVM transaction fees. In this case, there won't be fees to accept because the service account covers all fees. Regardless, you need to set it with a valid address to ensure the EVM Gateway can start. - `COA_ADDRESS`: This is the service account address, which will be used to pay for the gas fees of the EVM transactions. Enter the address of the Service Account you created in step 1, but without the `0x` prefix. - **Fund this address** to cover transaction fees. - `COA_KEY`: You need to set the private key of the Service Account you created in step 1. - `GAS_PRICE`: **Critical**: set this to `0` to ensure the linked service account will pay for transactions on users' behalf. 4. Follow the full guide of [Custom EVM Gateway] to complete the EVM gateway setup with these adjustments, and you will get a custom RPC endpoint that will sponsor 100% of the gas fees for any EVM transaction sent through it. ## Configure the RPC gateway If you use an embedded wallet solution, such as [Dynamic] or [Privy], you can set which RPC endpoint your app uses to read **and write** transactions. For your users with browser extension wallets, your selected RPC is **only** used for read calls. With [Wagmi], you can configure a custom endpoint in your `config`: ```tsx const config = createConfig({ chains: [flowMainnet, flowTestnet], transports: { [flowMainnet.id]: http('https://your-evm-endpoint.com'), [flowTestnet.id]: http('https://your-testnet-evm-endpoint.com'), }, }); ``` ## Conclusion In this tutorial, we've explored how to set up a gas-free EVM endpoint for your backend service. This solution provides several significant benefits: 1. **Enhanced User Experience**: Users can execute EVM transactions and not worry about gas fees, which makes the platform more accessible and user-friendly. 2. **Business Flexibility**: Service providers can cover transaction costs on behalf of their users. 3. **Cost Management**: When businesses centralize gas fee payments through a service account, they can better manage and control their transaction costs. The implementation requires careful setup of a service account with sufficient keys for concurrent transactions and proper configuration of the EVM Gateway environment variables. While this solution requires more initial setup compared to if you use the default EVM Gateway, the benefits of providing a gas-free experience to users can significantly enhance your platform's usability and adoption. Remember that this solution is only effective when you can ensure all user transactions are routed through your custom RPC endpoint. This makes it particularly suitable for centralized services and applications where you control the transaction routing. [Flow EVM]: ../../build/evm/using.mdx [Using Flow EVM]: ../../build/evm/using.mdx [EVM Gateway]: https://github.com/onflow/flow-evm-gateway [transaction model]: ../../build/cadence/basics/transactions.md#Payer [Custom EVM Gateway]: ../../protocol/node-ops/evm-gateway/evm-gateway-setup.md [Account Creation]: ../../protocol/node-ops/evm-gateway/evm-gateway-setup.md#step-1---account-creation [Account and Key Management]: ../../protocol/node-ops/evm-gateway/evm-gateway-setup.md#account-and-key-management [Run the gateway]: ../../protocol/node-ops/evm-gateway/evm-gateway-setup.md#run-the-gateway [Dynamic]: https://www.dynamic.xyz/ [Privy]: https://www.privy.io/ [Wagmi]: https://wagmi.sh/ --- # File: /md/blockchain-development-tutorials/integrations.md --- title: Third-Party Integrations description: Comprehensive guides for integrating popular blockchain infrastructure platforms with Flow to enhance user experience and reduce development complexity. sidebar_position: 10 keywords: - Crossmint - integrations - infrastructure - payments - wallets - authentication --- # Third-Party integrations Flow's developer-friendly ecosystem extends beyond core blockchain functionality through strategic integrations with leading infrastructure platforms. These integrations eliminate common Web3 friction points, which allows you to build sophisticated applications with traditional Web2 user experiences and leverage Flow's unique blockchain capabilities. This section provides comprehensive integration guides for platforms that enhance Flow development by addressing key challenges like payment processing, user onboarding, and wallet management. Each integration tutorial provides step-by-step implementation guidance, best practices, and real-world examples to help you quickly integrate these powerful services into your Flow applications. ## Available integrations ### [Crossmint Integration Platform] Build enterprise-grade Web3 applications on Flow with Crossmint's comprehensive blockchain infrastructure platform. This extensive integration guide covers four key areas: - Authentication with email, Social logins, and wallet connections. - Fiat payment processing supporting credit cards, Apple Pay, and Google Pay - NFT and token minting platform with no-code deployment tools - Access to real-world goods through crypto payments. Crossmint allows you to create complete blockchain applications that feel familiar to Web2 users and leverage Flow's advanced capabilities. ## Conclusion These third-party integrations demonstrate Flow's commitment to provide developers with the tools they need to build mainstream-ready blockchain applications. When you combine Flow's innovative architecture with best-in-class infrastructure platforms, you can eliminate traditional Web3 barriers and create user experiences that drive adoption. Whether you want to build decentralized finance (DeFi) protocols, NFT marketplaces, or consumer applications, these integrations provide the foundation for scalable, user-friendly products that bridge the gap between Web2 expectations and Web3 capabilities. [Crossmint Integration Platform]: ./crossmint/index.md --- # File: /md/blockchain-development-tutorials/integrations/crossmint.md --- sidebar_position: 2 title: Crossmint Integration Guide description: Learn how to integrate Crossmint's comprehensive blockchain infrastructure platform with Flow for wallets, payments, NFTs, and more. keywords: - tutorials - guides - Flow - crossmint - wallets - payments - nft - minting - stablecoins - agentic finance - checkout - embedded wallets --- # Crossmint Integration Guide Crossmint is an all-in-one platform that brings enterprise-grade blockchain infrastructure to Flow developers. Build complete Web3 applications without a need for your users to understand crypto - from embedded wallets and gasless payments to AI agent commerce and stablecoin integration. **Why this matters:** - **Eliminate barriers**: No gas fees, seed phrases, or complex wallet setup for your users. - **Enterprise ready**: Bank-grade security trusted by Fortune 500 companies. With Crossmint on Flow, you can create comprehensive blockchain applications that feel like traditional Web2 apps and leverage Flow's unique capabilities. **Core features:** Gasless transactions, fiat payments, token minting ## 🎯 Available Features ### 1. Minting platform Create and distribute tokens at scale via API and no-code tools. You'll be able to: - Deploy secure smart contracts on Flow. - Mint, update, burn, and airdrop tokens at scale. - Manage metadata and collections. - API and no-code collection creation. ### 2. Authentication Create wallets for users with seamless authentication. This unlocks: - Authentication with email, social logins, wallets, and passkeys. - Smart wallets with custodial and non-custodial options. - Gasless transactions and improved user experience. - Data APIs to fetch balances and activity. ### 3. Fiat and cross-chain payment checkout Digital Asset Checkout supports fiat and cross-chain payments. Allow your users to buy onchain assets with any of the following payment methods: - Credit card, Apple Pay, Google Pay support. - Cross-chain crypto payments (40+ tokens). - No KYC required for most transactions. ### 4. World store Access to real-world goods and services via crypto payments such as: - Over one billion products from Amazon, Shopify, flights, and more. - Pay with FLOW, USDF, and other supported tokens. - Perfect for expanding crypto utility to real-world commerce. - API access to global commerce platforms. ## 🛠 Prerequisites Make sure you have: - **Crossmint account:** - [Crossmint Console] account. - API keys configured for your project. - **Flow development environment:** - Flow CLI installed and configured. - **Technical knowledge:** - Basic JavaScript/TypeScript, React hooks. - Understanding of Flow (Cadence or EVM). - **Setup:** 1. Clone or create your Flow project. 2. Install Crossmint SDK: `npm i @crossmint/client-sdk-react-ui` 3. Configure environment variables for API keys. 4. Onboard thousands of users seamlessly. ## Guides Get started with Crossmint on Flow in under 15 minutes: 1. **[Set up authentication]** to seamlessly onboard users. 2. **[Enable fiat payments]** for your Flow assets. 3. **[Minting Platform]** to create and distribute tokens at scale. If you have trouble during the integrations process, refer to these documentation sites: - **[Crossmint Documentation]** - Complete platform docs. - **[Flow Developer Portal]** - Flow-specific resources. [Crossmint Console]: https://staging.crossmint.com [Contact Sales]: https://crossmint.com/contact [Set up authentication]: ./authentication.md [Enable fiat payments]:./payment-checkout.md [Minting Platform]:./minting-platform.md [Crossmint Documentation]: https://docs.crossmint.com/ [Flow Developer Portal]: https://developers.flow.com/ --- # File: /md/blockchain-development-tutorials/integrations/crossmint/authentication.md --- sidebar_position: 2 title: Authentication Integration Guide description: Set up user authentication for your Flow application using Crossmint's integrated authentication system with email, social logins, and wallet connections. keywords: - authentication - user management - email otp - social login - wallet auth - farcaster - jwt - flow - crossmint --- # Authentication Integration Guide Crossmint provides a comprehensive user management solution tightly integrated with all other Crossmint products. Authenticate users with Web3 or traditional sign-in methods, with seamless wallet creation and unified identity management. **Why this matters:** - **Unified identity system**: Single user account across your backend and Web3 app. - **Multiple auth methods**: Email OTP, social logins, wallet connections, and Farcaster. - **Automatic wallet creation**: Optionally create or link wallets with user accounts. - **Drag and drop integration**: Setup in under five minutes. ## 🎯 Available authentication methods ### 1. Email OTP authentication Passwordless sign-in with one-time codes delivered to the user's email. - No passwords required - Secure and user-friendly - Automatic account creation ### 2. Social account authentication Sign in with popular social platforms: - Google - Apple - X (Twitter) - And more ### 3. Farcaster integration Use the [Sign In With Farcaster (SIWF) standard] - Web3-native authentication - Decentralized identity support ### 4. External wallet authentication Connect with crypto wallets for Web3 authentication: - MetaMask - WalletConnect - Flow wallets - And other Web3 wallets ## Prerequisites Make sure you have: **Crossmint account:** - [Crossmint Console](https://staging.crossmint.com) account - Client API key with authentication scopes **React/Next.js project:** - React 16.8+ or Next.js 13+ - TypeScript support (recommended) **Technical knowledge:** - Basic React hooks and state management - Understanding of authentication flows ## Quick start ### Step 1: install the SDK ```bash npm i @crossmint/client-sdk-react-ui ``` ### Step 2: add crossmint providers ```tsx "use client"; import { CrossmintProvider, CrossmintAuthProvider, CrossmintWalletProvider } from "@crossmint/client-sdk-react-ui"; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Step 3: create authentication component ```tsx "use client"; import { useAuth } from "@crossmint/client-sdk-react-ui"; export function AuthButton() { const { login, logout, user, jwt } = useAuth(); return (
    {user == null ? ( ) : ( )} {/* Display user information */}

    User ID: {user?.userId}

    Email: {user?.email ?? "None"}

    Phone: {user?.phoneNumber ?? "None"}

    Farcaster: {user?.farcaster?.username ?? "None"}

    Google: {user?.google?.displayName ?? "None"}

    JWT: {jwt}

    ); } ``` ### Environment configuration ```typescript // Use environment-specific API keys const crossmintConfig = { apiKey: process.env.NODE_ENV === 'production' ? process.env.CROSSMINT_PROD_API_KEY : process.env.CROSSMINT_STAGING_API_KEY, environment: process.env.NODE_ENV === 'production' ? 'production' : 'staging' }; ``` ## Production deployment ### 1. Create production account 1. Create a developer account on the [Production Console]. ![Production Console Login](./imgs/staging.png) 2. Complete account verification and KYB process. ### 2. Configure production API keys 1. Create a production client API key. Navigate to **Integrate > API Keys** ![API Keys](./imgs/api_keys.png) 2. Activate required scopes: - `users.create` - `users.read` - `wallets.read` - `wallets.create` ### 3. Update environment variables ```bash # Production CROSSMINT_API_KEY=your_production_client_api_key CROSSMINT_ENVIRONMENT=production # Staging (for testing) CROSSMINT_API_KEY=your_staging_client_api_key CROSSMINT_ENVIRONMENT=staging ``` ### 4. Test authentication flow ```typescript // Test authentication in staging first const testAuth = async () => { const { login, user } = useAuth(); await login(); if (user) { console.log('Authentication successful:', user); // Test wallet creation await createUserWallet(); } }; ``` ## 🔧 Troubleshooting ### Common issues **Authentication fails:** - Verify API key is correct. - Check authentication scopes are activated. - Make sure you're using the right environment (staging vs. production) **Wallet creation fails:** - Verify user is authenticated. - Check wallet creation scopes. - Ensure proper wallet configuration for Flow. ### Get Help - **[Crossmint Authentication Docs](https://docs.crossmint.com/authentication/introduction)** - **[Flow Developer Portal](https://developers.flow.com/)** [Crossmint Console]: https://staging.crossmint.com [Sign In With Farcaster (SIWF) standard]: https://github.com/farcasterxyz/protocol/discussions/110 [Production Console]: https://www.crossmint.com/signin?callbackUrl=/console --- # File: /md/blockchain-development-tutorials/integrations/crossmint/minting-platform.md --- sidebar_position: 4 title: Minting Platform Integration description: Create and distribute tokens at scale on Flow using Crossmint's no-code and API-based minting platform. keywords: - minting - tokens - nft - flow - crossmint - smart contracts - airdrop --- # Minting Platform Integration Guide Deploy secure smart contracts and mint tokens at scale on Flow with Crossmint's comprehensive minting platform. ## Overview Crossmint's minting platform provides no-code tools and powerful APIs to create, mint, update, burn, and airdrop tokens on Flow. > **Key benefits:** > - Deploy secure smart contracts without coding. > - Mint, update, burn, and airdrop tokens at scale. > - Manage metadata and collections. > - Flow EVM and Cadence support. --- ## Prerequisites - Crossmint account with minting activated. - Flow development environment. - Basic knowledge of NFT standards. ## Step 1: Deploy smart contract ### No-code contract deployment 1. Go to Crossmint Console > **Collections** 2. Click **Create Collection** 3. Choose **Flow** blockchain and configure: - Contract type: ERC-721 or Cadence NFT - Collection metadata - Royalty settings - Access controls ### API contract deployment ```typescript // Deploy contract via API const contract = await crossmint.contracts.deploy({ blockchain: "flow", type: "erc-721", name: "My Flow Collection", symbol: "MFC", metadata: { description: "Amazing NFTs on Flow", image: "https://example.com/collection.png" }, royalty: { recipient: "0x...", percentage: 250 // 2.5% } }); ``` ## Step 2: mint NFTs ### Single NFT minting ```typescript const nft = await crossmint.nfts.mint({ collectionId: "your-collection-id", recipient: "user-wallet-address", metadata: { name: "Amazing Flow NFT", description: "Unique digital art", image: "https://example.com/nft.png", attributes: [ { trait_type: "Rarity", value: "Legendary" }, { trait_type: "Network", value: "Flow" } ] } }); ``` ### Batch minting ```typescript const batchMint = await crossmint.nfts.batchMint({ collectionId: "your-collection-id", recipients: [ { address: "0x...", metadata: { name: "NFT #1" } }, { address: "0x...", metadata: { name: "NFT #2" } } ] }); ``` ## Step 3: airdrops ```typescript const airdrop = await crossmint.airdrops.create({ collectionId: "your-collection-id", recipients: ["0x...", "0x...", "0x..."], metadata: { name: "Flow Airdrop NFT", description: "Special airdrop for community" } }); ``` --- # File: /md/blockchain-development-tutorials/integrations/crossmint/payment-checkout.md --- sidebar_position: 3 title: Payment Checkout Integration description: Enable fiat and cross-chain payments for Flow assets with credit cards, Apple Pay, Google Pay, and crypto across 40+ chains. keywords: - payment checkout - fiat payments - credit card - apple pay - google pay - cross-chain - flow - crossmint - nft checkout --- # Payment checkout integration guide Enable seamless fiat and cryptocurrency payments for your Flow assets. Crossmint's checkout solution supports credit cards, Apple Pay, Google Pay, and cross-chain crypto payments, which allows users to buy Flow NFTs and tokens without FLOW tokens. ## Overview Crossmint Checkout supports multiple payment methods and handles complex blockchain interactions behind the scenes, which eliminates payment friction. Users can buy your Flow assets with familiar payment methods. > **Key benefits:** > - **No wallet required** - guest checkout available. > - **Global coverage** - 197 countries supported. > - **No buyer KYC** for most transactions. > - **Cross-chain payments** - Pay with any crypto, receive on Flow. ## What you'll build You'll integrate checkout functionality that activates: - Credit card payments for Flow NFTs and tokens. - Apple Pay and Google Pay support. - Cross-chain crypto payments. - Guest checkout (no wallet required). ## Prerequisites - Crossmint account with checkout activated. - Flow collection created or imported. - Basic understanding of payment flows. - For production: KYB verification completed. ## Step 1: Collection setup ### Create or import collection **Option A: create new collection** 1. Go to [Crossmint Console] > **Collections**. 2. Click **Create Collection**. 3. Choose **Flow** blockchain. 4. Configure collection settings: - Network: Flow Testnet/Mainnet - Contract type: ERC-721 (EVM) or Cadence NFT - Pricing in USD or FLOW - Maximum supply and metadata **Option B: Import current collection** ```javascript // Import existing Flow contract const collection = await crossmint.collections.import({ blockchain: "flow", contractAddress: "0x1234567890abcdef", // Your contract address type: "erc-721", // or "cadence-nft" metadata: { name: "My Flow Collection", symbol: "MFC", description: "Amazing NFTs on Flow" } }); ``` ### Configure payment settings In your collection settings: 1. Go to **Payments > Settings**. 2. Choose fee structure: - **Buyer pays fees**: user pays NFT price + fees. - **Seller pays fees**: user pays exact price, you pay fees. 3. Set accepted payment methods. 4. Configure webhooks for order updates. ## Step 2: Hosted Checkout Integration The fastest way to get started - Crossmint hosts the entire checkout experience. ### Basic hosted checkout ```javascript // src/components/HostedCheckout.jsx import React from 'react'; export function HostedCheckout({ collectionId, nftId, onSuccess }) { const openCheckout = () => { const checkoutUrl = `https://www.crossmint.com/checkout?` + `clientId=${process.env.REACT_APP_CROSSMINT_CLIENT_ID}&` + `collectionId=${collectionId}&` + `templateId=${nftId}&` + `successCallbackURL=${encodeURIComponent(window.location.origin + '/success')}&` + `cancelCallbackURL=${encodeURIComponent(window.location.origin + '/cancel')}`; // Open in new window const popup = window.open( checkoutUrl, 'crossmint-checkout', 'width=500,height=700,scrollbars=yes,resizable=yes' ); // Listen for completion const checkClosed = setInterval(() => { if (popup.closed) { clearInterval(checkClosed); onSuccess?.(); } }, 1000); }; return (
    ); } ``` ### Advanced hosted checkout ```javascript // More control over hosted checkout export function AdvancedHostedCheckout({ collectionId, nftId, customization, onSuccess, onError }) { const openCheckout = () => { const params = new URLSearchParams({ clientId: process.env.REACT_APP_CROSSMINT_CLIENT_ID, collectionId, templateId: nftId, // Customization options theme: customization.theme || 'dark', accentColor: customization.accentColor || '#00D4AA', backgroundColor: customization.backgroundColor || '#1A1A1A', // Callback URLs successCallbackURL: `${window.location.origin}/checkout/success`, cancelCallbackURL: `${window.location.origin}/checkout/cancel`, // Payment options enableApplePay: 'true', enableGooglePay: 'true', enableCrypto: 'true', // User experience showConnectWallet: 'true', collectEmail: 'true' }); window.open( `https://www.crossmint.com/checkout?${params}`, 'crossmint-checkout', 'width=500,height=700,scrollbars=yes,resizable=yes' ); }; return ( ); } ``` --- ## Step 3: embedded checkout integration Embed checkout directly in your application with full UI control. ### Basic embedded checkout ```jsx // src/components/EmbeddedCheckout.jsx import React from 'react'; import { CrossmintPayButton } from '@crossmint/embed-react'; export function EmbeddedCheckout({ collectionId, nftId, recipient }) { return (
    { console.log('Checkout event:', event); switch (event.type) { case 'payment:process.succeeded': console.log('✅ Payment succeeded:', event.payload); break; case 'payment:process.failed': console.log('❌ Payment failed:', event.payload); break; case 'ui:payment-method.selected': console.log('Payment method selected:', event.payload); break; } }} environment="staging" // or "production" />
    ); } ``` ### Custom styled embedded checkout ```jsx // Advanced embedded checkout with custom styling export function CustomEmbeddedCheckout({ collectionId, nftId, pricing, onCheckoutComplete }) { return (

    Complete Your Purchase

    ${pricing.usd} ≈ {pricing.flow} FLOW
    ); function handleCheckoutEvent(event) { switch (event.type) { case 'payment:process.succeeded': onCheckoutComplete?.({ success: true, transactionId: event.payload.transactionId, nftId: event.payload.nftId }); break; case 'payment:process.failed': onCheckoutComplete?.({ success: false, error: event.payload.error }); break; } } } ``` --- ## Step 4: Headless checkout integration For maximum customization, use the headless API to build completely custom checkout flows. ### Order creation service ```typescript // src/services/checkoutService.ts import { CrossmintSDK } from '@crossmint/client-sdk'; const crossmint = new CrossmintSDK({ apiKey: process.env.CROSSMINT_API_KEY!, environment: 'staging' }); export interface CheckoutOrder { id: string; status: string; clientSecret: string; paymentIntent?: any; } export class CheckoutService { // Create fiat payment order async createFiatOrder(params: { collectionId: string; nftId?: string; recipientEmail: string; recipientWallet?: string; quantity?: number; }): Promise { try { const order = await crossmint.orders.create({ payment: { method: "fiat", currency: "usd" }, lineItems: [{ collectionLocator: `crossmint:${params.collectionId}`, ...(params.nftId && { templateId: params.nftId }), quantity: params.quantity || 1 }], recipient: { email: params.recipientEmail, ...(params.recipientWallet && { walletAddress: params.recipientWallet }) }, metadata: { source: 'custom_checkout' } }); return { id: order.id, status: order.status, clientSecret: order.clientSecret, paymentIntent: order.paymentIntent }; } catch (error) { console.error('❌ Order creation failed:', error); throw error; } } // Create crypto payment order async createCryptoOrder(params: { collectionId: string; nftId?: string; recipientWallet: string; paymentToken: string; // 'ETH', 'MATIC', 'SOL', etc. quantity?: number; }): Promise { try { const order = await crossmint.orders.create({ payment: { method: "crypto", currency: params.paymentToken.toLowerCase() }, lineItems: [{ collectionLocator: `crossmint:${params.collectionId}`, ...(params.nftId && { templateId: params.nftId }), quantity: params.quantity || 1 }], recipient: { walletAddress: params.recipientWallet } }); return { id: order.id, status: order.status, clientSecret: order.clientSecret }; } catch (error) { console.error('❌ Crypto order creation failed:', error); throw error; } } // Check order status async getOrderStatus(orderId: string) { try { const order = await crossmint.orders.get(orderId); return order; } catch (error) { console.error('❌ Order status check failed:', error); throw error; } } // Handle order completion async handleOrderComplete(orderId: string) { try { const order = await crossmint.orders.get(orderId); if (order.status === 'succeeded') { // Order completed successfully return { success: true, nft: order.nft, transaction: order.transaction }; } else if (order.status === 'failed') { // Order failed return { success: false, error: order.error }; } // Order still processing return { success: false, processing: true, status: order.status }; } catch (error) { console.error('❌ Order completion check failed:', error); throw error; } } } export const checkoutService = new CheckoutService(); ``` ### Custom checkout component ```tsx // src/components/CustomCheckout.tsx import React, { useState } from 'react'; import { loadStripe } from '@stripe/stripe-js'; import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js'; import { checkoutService } from '../services/checkoutService'; const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY!); interface CheckoutFormProps { collectionId: string; nftId: string; onSuccess: (result: any) => void; onError: (error: any) => void; } function CheckoutForm({ collectionId, nftId, onSuccess, onError }: CheckoutFormProps) { const stripe = useStripe(); const elements = useElements(); const [isProcessing, setIsProcessing] = useState(false); const [paymentMethod, setPaymentMethod] = useState<'fiat' | 'crypto'>('fiat'); const [recipientEmail, setRecipientEmail] = useState(''); const handleFiatPayment = async (e: React.FormEvent) => { e.preventDefault(); if (!stripe || !elements) return; setIsProcessing(true); try { // Create order const order = await checkoutService.createFiatOrder({ collectionId, nftId, recipientEmail }); // Confirm payment with Stripe const { error, paymentIntent } = await stripe.confirmPayment({ elements, clientSecret: order.clientSecret, confirmParams: { return_url: `${window.location.origin}/checkout/complete` } }); if (error) { onError(error); } else if (paymentIntent?.status === 'succeeded') { // Poll for NFT delivery const result = await checkoutService.handleOrderComplete(order.id); onSuccess(result); } } catch (error) { onError(error); } finally { setIsProcessing(false); } }; const handleCryptoPayment = async () => { // Implement crypto payment flow // This would integrate with wallet providers console.log('Crypto payment not implemented in this example'); }; return (
    {paymentMethod === 'fiat' ? (
    setRecipientEmail(e.target.value)} required placeholder="your@email.com" />
    ) : (
    )}
    ); } export function CustomCheckout(props: CheckoutFormProps) { return ( ); } ``` --- ## Step 5: webhook integration Set up webhooks to handle order status updates in real-time. ### Webhook handler ```typescript // src/api/webhooks/crossmint.ts (Next.js API route example) import { NextApiRequest, NextApiResponse } from 'next'; import crypto from 'crypto'; export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } // Verify webhook signature const signature = req.headers['x-crossmint-signature'] as string; const payload = JSON.stringify(req.body); const expectedSignature = crypto .createHmac('sha256', process.env.CROSSMINT_WEBHOOK_SECRET!) .update(payload) .digest('hex'); if (signature !== expectedSignature) { return res.status(401).json({ error: 'Invalid signature' }); } const event = req.body; switch (event.type) { case 'order.succeeded': handleOrderSucceeded(event.data); break; case 'order.failed': handleOrderFailed(event.data); break; case 'order.delivered': handleOrderDelivered(event.data); break; default: console.log('Unhandled webhook event:', event.type); } res.status(200).json({ received: true }); } async function handleOrderSucceeded(orderData: any) { console.log('✅ Order succeeded:', orderData.orderId); // Update your database // Send confirmation email // Trigger any post-purchase flows } async function handleOrderFailed(orderData: any) { console.log('❌ Order failed:', orderData.orderId, orderData.error); // Handle failed order // Notify user // Log for analysis } async function handleOrderDelivered(orderData: any) { console.log('📦 NFT delivered:', orderData.orderId, orderData.nft); // NFT successfully delivered to user // Update user's account // Send delivery confirmation } ``` --- ## Step 6: multi-payment method component Create a comprehensive checkout that supports all payment methods: ```tsx // src/components/UniversalCheckout.tsx import React, { useState } from 'react'; import { EmbeddedCheckout } from './EmbeddedCheckout'; import { CustomCheckout } from './CustomCheckout'; import { HostedCheckout } from './HostedCheckout'; interface UniversalCheckoutProps { collectionId: string; nftId: string; pricing: { usd: number; flow: number; }; onCheckoutComplete: (result: any) => void; } export function UniversalCheckout({ collectionId, nftId, pricing, onCheckoutComplete }: UniversalCheckoutProps) { const [checkoutMode, setCheckoutMode] = useState<'hosted' | 'embedded' | 'custom'>('embedded'); const [showPaymentMethods, setShowPaymentMethods] = useState(false); return (

    🛒 Purchase NFT

    ${pricing.usd}
    ≈ {pricing.flow} FLOW
    💳 🍎 🅿️ 🪙

    Credit Card, Apple Pay, Google Pay, and 40+ cryptocurrencies

    {checkoutMode === 'hosted' && ( )} {checkoutMode === 'embedded' && ( )} {checkoutMode === 'custom' && ( console.error('Checkout error:', error)} /> )}
    🔒 Secure & trusted by Fortune 500
    🌍 Available in 197 countries
    Instant delivery to wallet
    ); } ``` ## Key takeaways - **Multiple Integration Options**: hosted, embedded, or headless - choose what fits your needs. - **Universal Payment Support**: credit cards, mobile payments, and over 40 cryptocurrencies. - **Flow Native**: optimized for both Flow EVM and Cadence ecosystems. - **Global Scale**: support for 197 countries with no buyer KYC. [Crossmint Console]: https://staging.crossmint.com --- # File: /md/blockchain-development-tutorials/integrations/crossmint/smart-wallets.md --- sidebar_position: 4 title: Crossmint Smart Wallets description: Learn how to integrate Crossmint Smart Wallets to create seamless Web3 experiences with email-based authentication on Flow. keywords: - tutorials - guides - flow - smart wallets - crossmint - authentication - wallet infrastructure - web3 - email authentication - gasless transactions --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Crossmint Smart Wallets Integration Guide Traditional blockchain wallets create significant friction for mainstream users. Managing seed phrases, understanding gas fees, and connecting multiple wallets are barriers that prevent widespread Web3 adoption. Crossmint Smart Wallets solves these problems by providing **enterprise-grade wallet infrastructure** that enables Web2-like user experiences without compromising on security or decentralization. With Crossmint Smart Wallets, you can: - **Eliminate wallet complexity** with email and social login authentication. - **Remove onboarding friction** with automatic user wallet creation. - **Support multiple authentication methods** such as email, Google, passkeys, and external wallets. - **Enable gasless transactions** to improve user experience. - **Build on Flow** with full support for both mainnet and testnet environments. - **Scale with confidence** using infrastructure trusted by Fortune 500 companies. This tutorial will guide you through how to integrate Crossmint Smart Wallets into your Flow application. You'll learn how to set up authentication, automatically create wallets, check balances, transfer tokens, and display transaction historyall with a familiar Web2-style developer experience. :::info Crossmint provides flexible wallet solutions across more than 50 blockchains, such as Flow. This tutorial focuses on the **React implementation** for web applications, but Crossmint also supports Node.js, React Native, Swift (iOS), and Kotlin (Android) platforms. ::: ## Objectives After you complete this guide, you'll be able to: - Configure a Crossmint account with proper API keys and permissions. - Implement email and social authentication for automatic wallet creation. - Display wallet information including address, balance, and ownership details. - Execute token transfers on Flow using Crossmint's SDK. - Build an activity feed showing transaction history. - Handle authentication states and error scenarios properly. - Deploy your Crossmint-powered application to production. ## Prerequisites Before you start this tutorial, you should have: - **Development Environment**: Node.js and npm/yarn/pnpm installed. - **React Knowledge**: Familiarity with React hooks and component patterns. - **Next.js or Create-React-App**: A React application ready for integration. - **Basic Blockchain Concepts**: Knowledge of wallet addresses and token transfers (helpful but not required). ## Set up your crossmint account You need to create a Crossmint account and configure API access before you implement wallet functionality. ### Step 1. Create your Crossmint account Sign up on the [Crossmint Console] to establish an account. For development and testing, use the [Staging Console] instead. :::tip Always use the staging environment during development. Staging supports testnet blockchains only, while production supports mainnet deployments. ::: ### Step 2. Create a new project After you log in to the console: 1. Click **Create New Project**. 2. Enter a project name (such as "Flow DApp"). 3. Select your project type (Web Application recommended). 4. Save your project settings. ### Step 3. Generate API keys Navigate to your project dashboard to create a client-side API key: 1. Go to the **API Keys** section 2. Click **Create New API Key** 3. Select **Client API Key** (not server key) 4. Activate the following scopes: - `users.create` - Create new users - `users.read` - Read user information - `wallets.read` - Read wallet data - `wallets.create` - Create new wallets - `wallets:transactions.create` - Create transactions - `wallets:transactions.sign` - Sign transactions - `wallets:balance.read` - Read balance information - `wallets.fund` - Fund wallets (staging and development only) 5. Copy the generated API key to your clipboard :::warning Keep your API keys secure! Never commit them to version control. Use environment variables to store sensitive credentials. ::: ### Step 4. Configure environment variables Create a `.env` or `.env.local` file in your project root: ```bash NEXT_PUBLIC_CROSSMINT_API_KEY=your_api_key_here NEXT_PUBLIC_CHAIN=flow-testnet ``` For production deployments, update to: ```bash NEXT_PUBLIC_CROSSMINT_API_KEY=your_production_api_key NEXT_PUBLIC_CHAIN=flow ``` ## Implement Crossmint Smart Wallets With your Crossmint account configured, you can now integrate wallet functionality into your React application. ### Step 1. Install dependencies Install the Crossmint React SDK: ```bash pnpm add @crossmint/client-sdk-react-ui ``` ```bash bun add @crossmint/client-sdk-react-ui ``` ```bash yarn add @crossmint/client-sdk-react-ui ``` ```bash npm install @crossmint/client-sdk-react-ui ``` ### Step 2. Configure Crossmint providers Crossmint requires three providers to be set up in a specific hierarchy. These providers handle API configuration, authentication, and wallet management. Create a new file `app/providers.tsx`: ```tsx "use client"; import { CrossmintProvider, CrossmintAuthProvider, CrossmintWalletProvider, } from "@crossmint/client-sdk-react-ui"; if (!process.env.NEXT_PUBLIC_CROSSMINT_API_KEY) { throw new Error("NEXT_PUBLIC_CROSSMINT_API_KEY is not set"); } const chain = (process.env.NEXT_PUBLIC_CHAIN ?? "flow-testnet") as any; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Then wrap your app in `app/layout.tsx`: ```tsx import { Providers } from "./providers"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` Update your `src/index.tsx` or `src/index.jsx`: ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { CrossmintProvider, CrossmintAuthProvider, CrossmintWalletProvider, } from "@crossmint/client-sdk-react-ui"; const chain = process.env.REACT_APP_CHAIN ?? "flow-testnet"; const apiKey = process.env.REACT_APP_CROSSMINT_API_KEY; if (!apiKey) { throw new Error("REACT_APP_CROSSMINT_API_KEY is not set"); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render( ); ``` **Provider configuration cptions:** - **CrossmintProvider**: Top-level provider that requires only your API key. - **CrossmintAuthProvider**: Manages authentication with configurable options: - `authModalTitle`: Title displayed in the authentication modal. - `loginMethods`: Array of active authentication methods (`"email"`, `"google"`, `"apple"`, `"twitter"`, `"farcaster"`). - `appearance`: Customize UI colors and style. - **CrossmintWalletProvider**: Handles wallet creation and management: - `createOnLogin.chain`: Target blockchain (such as `"flow"`, `"flow-testnet"`) - `createOnLogin.signer.type`: Authentication method for wallet signing (`"email"`, `"passkey"`). :::info The `createOnLogin` configuration enables **automatic wallet creation**. When a user logs in for the first time, Crossmint automatically provisions a wallet on the specified chain. No additional setup required! ::: ### Step 3. Implement authentication Create login and logout components with Crossmint's `useAuth` hook. **LoginButton.tsx:** ```tsx "use client"; import { useAuth } from "@crossmint/client-sdk-react-ui"; export function LoginButton() { const { login } = useAuth(); return ( ); } ``` **LogoutButton.tsx:** ```tsx "use client"; import { useAuth } from "@crossmint/client-sdk-react-ui"; export function LogoutButton() { const { logout } = useAuth(); return ( ); } ``` **Header.tsx (Conditional rendering):** ```tsx "use client"; import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; import { LoginButton } from "./LoginButton"; import { LogoutButton } from "./LogoutButton"; export function Header() { const { status: authStatus } = useAuth(); const { wallet } = useWallet(); const isLoggedIn = wallet != null && authStatus === "logged-in"; return (

    Flow DApp

    {isLoggedIn ? : }
    ); } ``` ### Step 4. Display wallet information Create a component to show wallet details with the `useWallet` hook. **WalletInfo.tsx:** ```tsx "use client"; import { useState } from "react"; import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; export function WalletInfo() { const { wallet, status } = useWallet(); const { user } = useAuth(); const [copied, setCopied] = useState(false); if (status === "in-progress") { return (
    Loading wallet...
    ); } if (!wallet) { return null; } const formatAddress = (address: string) => { return `${address.slice(0, 6)}...${address.slice(-6)}`; }; const handleCopy = async () => { await navigator.clipboard.writeText(wallet.address); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return (

    Wallet Details

    Address
    {formatAddress(wallet.address)}
    Chain
    {wallet.chain}
    Owner
    {user?.email || wallet.owner}
    ); } ``` ### Step 5. Display wallet balance Fetch and display the wallet's token balance with the `wallet.balances()` method. **WalletBalance.tsx:** ```tsx "use client"; import { useEffect, useState } from "react"; import { Balances, useWallet } from "@crossmint/client-sdk-react-ui"; export function WalletBalance() { const { wallet } = useWallet(); const [balances, setBalances] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function fetchBalances() { if (!wallet) return; try { setIsLoading(true); const balances = await wallet.balances(); setBalances(balances); } catch (error) { console.error("Error fetching wallet balances:", error); } finally { setIsLoading(false); } } fetchBalances(); }, [wallet]); if (isLoading) { return (
    Loading balance...
    ); } const nativeBalance = balances?.nativeToken?.amount ? Number(balances.nativeToken.amount).toFixed(4) : "0.0000"; return (

    Balance

    {balances?.nativeToken?.symbol || "FLOW"}
    {nativeBalance}
    {balances?.tokens && balances.tokens.length > 0 && (
    Tokens
    {balances.tokens.map((token, index) => (
    {token.symbol} {Number(token.amount).toFixed(2)}
    ))}
    )}
    ); } ``` ### Step 6. Implement token transfers Create a component to transfer tokens with the `wallet.send()` method. **TransferTokens.tsx:** ```tsx "use client"; import { useState } from "react"; import { useWallet } from "@crossmint/client-sdk-react-ui"; export function TransferTokens() { const { wallet } = useWallet(); const [recipient, setRecipient] = useState(""); const [amount, setAmount] = useState(""); const [isLoading, setIsLoading] = useState(false); const [explorerLink, setExplorerLink] = useState(null); const [error, setError] = useState(null); async function handleTransfer() { if (!wallet || !recipient || !amount) { setError("Please fill in all fields"); return; } try { setIsLoading(true); setError(null); setExplorerLink(null); const txn = await wallet.send( recipient, "flow", // Token symbol - use native FLOW token amount ); setExplorerLink(txn.explorerLink); // Reset form setRecipient(""); setAmount(""); } catch (err) { console.error("Transfer error:", err); if (err instanceof Error && err.name === "AuthRejectedError") { // User cancelled the transaction - don't show error return; } setError(err instanceof Error ? err.message : "Transfer failed"); } finally { setIsLoading(false); } } return (

    Transfer Tokens

    setAmount(e.target.value)} placeholder="0.00" className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" disabled={isLoading} />
    setRecipient(e.target.value)} placeholder="0x..." className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono text-sm" disabled={isLoading} />
    {error && (
    {error}
    )} {explorerLink && (
    Transaction successful!
    View on Explorer �
    )}
    ); } ``` :::tip The `wallet.send()` method throws an `AuthRejectedError` when users cancel the transaction. Handle this separately to avoid a display of unnecessary error messages. ::: ### Step 7. Build activity feed Display transaction history with the `wallet.experimental_activity()` method with polling for real-time updates. **ActivityFeed.tsx:** ```tsx "use client"; import { useEffect, useState } from "react"; import { type Activity, useWallet } from "@crossmint/client-sdk-react-ui"; export function ActivityFeed() { const { wallet } = useWallet(); const [activity, setActivity] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { if (!wallet) return; const fetchActivity = async () => { try { const activity = await wallet.experimental_activity(); setActivity(activity); setIsLoading(false); } catch (error) { console.error("Failed to fetch activity:", error); setIsLoading(false); } }; // Initial fetch fetchActivity(); // Poll every 8 seconds for updates const interval = setInterval(fetchActivity, 8000); return () => clearInterval(interval); }, [wallet]); const formatAddress = (address: string) => { return `${address.slice(0, 6)}...${address.slice(-6)}`; }; const formatTimestamp = (timestamp: number) => { // Handle both seconds and milliseconds const date = new Date( timestamp < 10000000000 ? timestamp * 1000 : timestamp ); const now = new Date(); const diffInMs = now.getTime() - date.getTime(); if (diffInMs < 0) return "just now"; const diffInMinutes = Math.floor(diffInMs / (1000 * 60)); const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60)); const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); if (diffInMinutes < 1) return "just now"; else if (diffInMinutes < 60) return `${diffInMinutes}m ago`; else if (diffInHours < 24) return `${diffInHours}h ago`; else return `${diffInDays}d ago`; }; if (isLoading) { return (
    Loading activity...
    ); } return (

    Recent Activity

    {activity?.events && activity.events.length > 0 ? (
    {activity.events.map((event, index) => { const isIncoming = event.to_address?.toLowerCase() === wallet?.address.toLowerCase(); return (
    {isIncoming ? "Received" : "Sent"} {formatTimestamp(event.timestamp)}
    {isIncoming ? `From ${formatAddress(event.from_address)}` : `To ${formatAddress(event.to_address)}` }
    {isIncoming ? "+" : "-"}{event.amount}
    {event.token_symbol || "FLOW"}
    ); })}
    ) : (

    No transactions yet

    Your activity will appear here

    )}
    ); } ``` :::warning The `experimental_activity()` method is experimental and may change in future SDK versions. Always handle errors gracefully and provide fallback UI. ::: ### Step 8. Create main dashboard Combine all components into a cohesive dashboard with proper state management. **Dashboard.tsx:** ```tsx "use client"; import { WalletInfo } from "./WalletInfo"; import { WalletBalance } from "./WalletBalance"; import { TransferTokens } from "./TransferTokens"; import { ActivityFeed } from "./ActivityFeed"; export function Dashboard() { return (
    ); } ``` **page.tsx (Main application):** ```tsx "use client"; import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; import { Header } from "@/components/Header"; import { Dashboard } from "@/components/Dashboard"; import { LoginButton } from "@/components/LoginButton"; export default function Home() { const { wallet, status: walletStatus } = useWallet(); const { status: authStatus } = useAuth(); const isLoggedIn = wallet != null && authStatus === "logged-in"; const isLoading = walletStatus === "in-progress" || authStatus === "initializing"; return (
    {isLoading ? (

    Initializing wallet...

    ) : isLoggedIn ? ( ) : (

    Welcome to Flow

    Sign in to access your wallet and start transacting on Flow blockchain

    )}
    ); } ``` --- ## Additional Platform Support While this tutorial focuses on React for web applications, Crossmint provides SDKs for multiple platforms: ### Node.js (Backend) For server-side wallet creation and management, use the Node.js SDK: - [Node.js Quickstart Documentation] ### React Native (Mobile) For iOS and Android mobile applications: - [React Native Quickstart Documentation] ### Swift (iOS Native) For native iOS development: - Contact [Crossmint Sales] for access ### Kotlin (Android Native) For native Android development: - Contact [Crossmint Sales] for access --- ## Conclusion In this tutorial, you successfully integrated Crossmint Smart Wallets to enable seamless blockchain experiences on Flow. You learned how to implement email-based authentication, automatically create wallets for users, display balances, execute token transfers, and show transaction history, all without a requirement that users understand complex blockchain concepts like seed phrases or gas fees. Now that you have completed the tutorial, you should be able to: - Configure Crossmint accounts with proper API keys and permissions - Implement multiple authentication methods including email and social login - Automatically create and manage wallets for users - Display wallet information, balances, and transaction history - Execute token transfers with proper error handling - Build production-ready applications with enterprise-grade wallet infrastructure Crossmint's wallet infrastructure, combined with Flow's high-performance blockchain, provides a powerful foundation for building user-friendly Web3 applications. By eliminating wallet complexity and onboarding friction, you can create experiences that attract mainstream users while maintaining the security and transparency benefits of blockchain technology. ## Next Steps - Explore [Crossmint's NFT Minting Platform]to add NFT functionality - Learn about [Payment Checkout] for credit card and crypto payments - Implement [Passkey Authentication] for enhanced security - Review [Flow Smart Contract Development] to build custom on-chain logic - Join the [Flow Discord] to connect with other developers [Crossmint Console]: https://www.crossmint.com/console [Staging Console]: https://staging.crossmint.com/console [Crossmint Documentation]: https://docs.crossmint.com/ [Crossmint Wallets SDK]: https://github.com/Crossmint/crossmint-sdk [Crossmint Sales]: https://www.crossmint.com/contact/sales [Flow Discord]: https://discord.gg/flow [Crossmint's NFT Minting Platform]: https://docs.crossmint.com/nft-minting/overview [Payment Checkout]: https://docs.crossmint.com/payments/overview [Passkey Authentication]: https://docs.crossmint.com/wallets/signers/passkey [Flow Smart Contract Development]: ../../cadence/ [Node.js Quickstart Documentation]: https://docs.crossmint.com/wallets/quickstarts/nodejs [React Native Quickstart Documentation]: https://docs.crossmint.com/wallets/quickstarts/react-native --- # File: /md/blockchain-development-tutorials/native-vrf.md --- title: Native VRF (Built-in Randomness) Tutorials description: Build on a blockchain with built-in randomness. Flow's native VRF enables verifiable, low-cost randomness for gaming, NFTs, and DeFi. WIth no third-party dependencies. sidebar_position: 8 keywords: - VRF - randomness - Cadence - Solidity - Flow EVM - smart contracts - commit-reveal - secure randomness - Random Beacon - blockchain - gaming - NFTs - DeFi --- # Native VRF (Built-in Randomness) Tutorials Flow is a **blockchain with built-in randomness**, powered by its native **VRF (Verifiable Random Function)** capabilities. Unlike other blockchains that require external oracles, Flow's **Random Beacon** provides cryptographically secure randomness **at the protocol level**. This elimiates extra costs, reduces latency, and improves reliability for decentralized applications. These tutorials cover how to implement secure randomness directly in both **Cadence** and **Solidity** smart contracts on Flow. Whether you build on Flow's native environment or Flow EVM, you can generate unbiased, verifiable random values without third-party dependencies. ## Tutorials ### [Secure Randomness with Commit-Reveal in Cadence] Learn how to implement secure randomness in Cadence with Flow's commit-reveal scheme, which ensures fairness and resistance to manipulation. ### [VRF (Randomness) in Solidity] Learn how to use Flow's **native verifiable randomness** in Consumer Decentralized Finance (DeFi) contracts on Flow EVM, which includes best practices, security considerations, and complete code examples. ## Why Flow for randomness? - Protocol-level randomness: no need for external oracles or APIs. - Lower costs: built-in randomness means no extra transaction fees. - Enhanced security: cryptographically secure and verifiable onchain. - Cross-language support: Wwrks seamlessly in both Cadence and Consumer DeFi. - Speed: Flow is a [fast blockchain] with the design goal of 1,000,000 transactions per second. [Secure Randomness with Commit-Reveal in Cadence]: ./commit-reveal-cadence.md [VRF (Randomness) in Solidity]: ./vrf-in-solidity.md [VRF (Randomness) in Solidity]: ./vrf-in-solidity.md [fast blockchain]: https://flow.com/core-protocol-vision --- # File: /md/blockchain-development-tutorials/native-vrf/commit-reveal-cadence.md --- sidebar_position: 1 title: Secure Randomness with Commit-Reveal in Cadence description: Guide on implementing secure randomness in Cadence using a commit-reveal scheme on Flow keywords: - blockchain - randomness - Cadence - Flow blockchain - commit-reveal - secure randomness - Random Beacon - smart contracts - Coin Toss - decentralization - fairness - cryptography --- # Secure Randomness with Commit-Reveal in Cadence Randomness is a critical component in blockchain applications. It allows fair and unpredictable outcomes for use cases like gaming, lotteries, and cryptographic protocols. The most basic approach that you can use to generate a random number on EVM chains is to use block hashes, which combines the block hash with a user-provided seed and hashes them together. You can use the hash that results as a pseudo-random generator seed. However, this approach has limitations. A validator that influences the random source used to compute transactions can manipulate the block hash. The block proposer can decide what to include into a block and can iterate through different combinations until they find a favorable random source. [Chainlink VRF][chainlink-vrf] is a popular tool that improves on this. It provides another approach you can use to generate provably random values on Ethereum and other blockchains. It relies on a decentralized oracle network to deliver cryptographically secure randomness from off-chain sources. However, this dependence on external oracles introduces several weaknesses, such as cost, latency, and scalability concerns. In contrast, Flow offers a simpler and more integrated approach with its native onchain Randomness Beacon at the protocol level, which eliminates reliance on external oracles and the need to sidestep their associated risks. In addition to instant randomness that is available to any transaction (via `revertibleRandom` function), Flow provides a solution to the problem of a user who reverts a transaction with an unfavorable outcome. Commit-Reveal schemes on Flow also rely on protocol-native secure randomness and they fix the issue of post-selection by trustless users. Commit-Reveal tools on Flow can be used within both Cadence and Consumer Decentralized Finance (DeFi) contracts. This tutorial focuses on Cadence. ## Objectives By the end of this guide, you will be able to: - Deploy a Cadence smart contract on the Flow blockchain. - Implement commit-reveal pattern for randomness to ensure fairness. - Interact with onchain randomness features on Flow. - Build and test the Coin Toss game with the Flow Testnet. ## Prerequisites You'll need the following: - Flow Testnet Account: an account on the Flow Testnet with test FLOW tokens to deploy contracts and execute transactions (for example, via [Flow Faucet][flow-faucet]). - Flow CLI or Playground: the Flow CLI or Flow Playground to deploy and test contracts (install via [Flow Docs][flow-docs]). ## Overview In this guide, we will explore how to use a commit-reveal scheme based on the Flow Random Beacon to achieve secure, non-revertible randomness. This mechanism mitigates post-selection attacks, where participants attempt to reject unfavorable random outcomes after they are revealed. To illustrate this concept, we will build a Coin Toss game on Flow, which demonstrates how smart contracts can leverage a commit-reveal scheme for fair, tamper-resistant results. ![Commit Reveal](./imgs/commit-reveal.png) ### What is the Coin Toss Game? The Coin Toss Game is a decentralized betting game that showcases the commit-reveal pattern. Players place bets and don't know the random outcome, which ensures fairness and resistance to manipulation. The game consists of two distinct phases: 1. Commit Phase - To place a bet, the player sends Flow tokens to the contract. The contract records the commitment to use a future random value from the Flow Random Beacon. The player receives a Receipt, which they will use to reveal the result later. 2. Reveal Phase - When the random value becomes available in the `RandomBeaconHistory` contract, the player submits their Receipt to determine the outcome: - If the result is 0, the player wins and receives double their bet. - If the result is 1, the player loses, and their bet remains in the contract. ### Why use a Commit-Reveal scheme? Similarly to revertible randomness, Commit-Reveal inherits the security of Flow native randomness beacon: - Ensures security - The Flow Random Beacon provides cryptographically unpredictable and non-biased randomness. - Ensure fairness - The Flow Random Beacon uses a Verifiable Random Function (VRF) under the hood which allows any external client or user to verify that randoms were generated fairly. - Reduces reliance on external oracles - The randomness is generated natively onchain, and avoids additional complexity, third party risk and cost. In addition, commit-reveal patterns solve the issue of revertible randoms: - Prevents user manipulation - Players cannot evaluate the outcome and choose to revert the transaction if they do not like the result. - :::info One of the powers of Cadence transactions is that a developer can set post-conditions that must be true, or the transaction will revert. This is very useful for scenarios such as to guarantee a user receives their purchase in a complex and multi-step transaction, but it also means that they can set conditions to reject the transaction. In an instant-win lottery, this would allow users to test large numbers of tickets for a win without purchase price payment. ::: ## Build the Coin Toss contract In this section, we'll walk through how to construct the `CoinToss.cdc` contract, which contains the core logic for the Coin Toss game. To function properly, the contract relies on support contracts and a proper deployment setup. This tutorial will focus specifically on how to write and understand the `CoinToss.cdc` contract, while you can find additional setup details in the [original GitHub repo][github-repo]. ### Step 1: Define the `CoinToss.cdc` contract Let's define our `CoinToss.cdc` and bring the other supporting contracts. ```cadence import "Burner" import "FungibleToken" import "FlowToken" import "RandomConsumer" access(all) contract CoinToss { /// The multiplier used to calculate the winnings of a successful coin toss access(all) let multiplier: UFix64 /// The Vault used by the contract to store funds. access(self) let reserve: @FlowToken.Vault /// The RandomConsumer.Consumer resource used to request & fulfill randomness access(self) let consumer: @RandomConsumer.Consumer /* --- Events --- */ access(all) event CoinFlipped(betAmount: UFix64, commitBlock: UInt64, receiptID: UInt64) access(all) event CoinRevealed(betAmount: UFix64, winningAmount: UFix64, commitBlock: UInt64, receiptID: UInt64) } ``` ### Step 2: Implement the commit phase with `flipCoin` Let's define the first step in our scheme; the commit phase. We do this through a `flipCoin` public function. In this method, the caller commits a bet. The contract takes note of a future block height and bet amount and returns a `Receipt` resource, which the former uses to reveal the coin toss result and determine their winnings. ```cadence access(all) fun flipCoin(bet: @{FungibleToken.Vault}): @Receipt { let request <- self.consumer.requestRandomness() let receipt <- create Receipt( betAmount: bet.balance, request: <-request ) self.reserve.deposit(from: <-bet) emit CoinFlipped(betAmount: receipt.betAmount, commitBlock: receipt.getRequestBlock()!, receiptID: receipt.uuid) return <- receipt } ``` ### Step 3: Implement the reveal phase With `revealCoin` Now we implement the reveal phase with the `revealCoin` function. Here, the caller provides the Receipt they recieve at commitment. The contract then "flips a coin" and `_randomCoin()` provides the Receipt's contained Request. The reveal step is possible only when the protocol random source at the committed block height becomes available. If result is 1, the user loses, but if it's 0, the user doubles their bet. Note that the caller could condition the revealed transaction, but they've already provided their bet amount, so there's no loss for the contract if they do. ```cadence access(all) fun revealCoin(receipt: @Receipt): @{FungibleToken.Vault} { let betAmount = receipt.betAmount let commitBlock = receipt.getRequestBlock()! let receiptID = receipt.uuid let coin = self._randomCoin(request: <-receipt.popRequest()) Burner.burn(<-receipt) // Deposit the reward into a reward vault if the coin toss was won let reward <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) if coin == 0 { let winningsAmount = betAmount * self.multiplier let winnings <- self.reserve.withdraw(amount: winningsAmount) reward.deposit( from: <-winnings ) } emit CoinRevealed(betAmount: betAmount, winningAmount: reward.balance, commitBlock: commitBlock, receiptID: receiptID) return <- reward } ``` The final version of `CoinToss.cdc` will look like [this contract code][coin-toss-contract-code]. ## Test CoinToss on Flow Testnet To make things easy, we've already deployed the `CoinToss.cdx` contract for you at this address: [0xb6c99d7ff216a684][coin-toss-contract]. We'll walk through how to place a bet and reveal the result with [run.dnz][run-dnz], a Flow-friendly tool similar to Ethereum's Remix. ### Place a bet with flipCoin First, you'll submit a bet to the CoinToss contract. To do this, you'll withdraw Flow tokens and store a receipt. Here's how to get started: 1. Open Your Dev Environment: head to [run.dnz][run-dnz]. 2. Enter the Transaction Code: paste the following Cadence code into the editor: ```cadence import FungibleToken from 0x9a0766d93b6608b7 import FlowToken from 0x7e60df042a9c0868 import CoinToss from 0xb6c99d7ff216a684 /// Commits the defined amount of Flow as a bet to the CoinToss contract, saving the returned Receipt to storage /// transaction(betAmount: UFix64) { prepare(signer: auth(BorrowValue, SaveValue) &Account) { // Withdraw my bet amount from my FlowToken vault let flowVault = signer.storage.borrow(from: /storage/flowTokenVault)! let bet <- flowVault.withdraw(amount: betAmount) // Commit my bet and get a receipt let receipt <- CoinToss.flipCoin(bet: <-bet) // Check that I don't already have a receipt stored if signer.storage.type(at: CoinToss.ReceiptStoragePath) != nil { panic("Storage collision at path=".concat(CoinToss.ReceiptStoragePath.toString()).concat(" a Receipt is already stored!")) } // Save that receipt to my storage // Note: production systems would consider handling path collisions signer.storage.save(<-receipt, to: CoinToss.ReceiptStoragePath) } } ``` 3. Set Your Bet: a window will appear that asks for the `betAmount`. Enter a value (such as 1.0 for 1 Flow token) and submit. 4. Execute the Transaction: click "Run," and a WalletConnect window will appear. Choose Blocto, sign in with your email, and click "Approve" to send the transaction to Testnet. ![remix5-sc](./imgs/remix5.png) 5. Track it: You can take the transaction id to [FlowDiver][flow-diver][.io] to have a full view of everything about this `FlipCoin` transaction. ### Reveal the coin toss result Let's reveal the outcome of your coin toss to see if you've won. This step uses the receipt from your bet, so ensure you use the same account that placed the bet. Here's how to do it: 1. Return to your Dev Environment: Open [run.dnz][run-dnz] again. 2. Enter the Reveal Code. Paste the following Cadence transaction into the editor: ```cadence import FlowToken from 0x7e60df042a9c0868 import CoinToss from 0xb6c99d7ff216a684 /// Retrieves the saved Receipt, redeems it to reveal the coin toss result, and deposits the winnings with any luck /// transaction { prepare(signer: auth(BorrowValue, LoadValue) &Account) { // Load my receipt from storage let receipt <- signer.storage.load<@CoinToss.Receipt>(from: CoinToss.ReceiptStoragePath) ?? panic("No Receipt found in storage at path=".concat(CoinToss.ReceiptStoragePath.toString())) // Reveal by redeeming my receipt - fingers crossed! let winnings <- CoinToss.revealCoin(receipt: <-receipt) if winnings.balance > 0.0 { // Deposit winnings into my FlowToken Vault let flowVault = signer.storage.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)! flowVault.deposit(from: <-winnings) } else { destroy winnings } } } ``` After we run this transaction, we reveal the result of the coin flip and it's 1! It means we haven't won anything this time, but we'll try again! You can find the full transaction used for this example, with its result and events, at [FlowDiver.io/tx/][flow-diver-tx]. ## Conclusion The commit-reveal scheme, implemented within the context of the Flow Randomness Beacon, provides a robust solution to generate secure and non-revertible randomness in decentralized applications. When developers leverage this mechanism, they can ensure that their applications are: - Fair: outcomes remain unbiased and unpredictable. - Resistant to post-selection: protects against trustless users who cannot reverse their commitments. The CoinToss game serves as a practical example of these principles in action. Now that you've walked through its implementation, you've seen firsthand how straightforward yet effective this approach can be, as it balances simplicity for developers with robust security for users. As blockchain technology advances, it's essential that you embrace such best practices to create a decentralized ecosystem that upholds fairness and integrity, which empowers developers to innovate with confidence. This tutorial has equipped you with hands-on experience and key skills: - You deployed a Cadence smart contract on the Flow blockchain. - You implemented commit-reveal to ensure fairness. - You interacted with onchain randomness features on Flow. - You built and tested the Coin Toss game with the Flow Testnet. When you harness the built-in randomness capabilities on Flow, you can create engaging, user-centric experiences without grappling with the complexities or limitations of external systems. This knowledge empowers you to create secure, scalable, and fair decentralized applications. [chainlink-vrf]: https://docs.chain.link/vrf [flow-faucet]: https://faucet.flow.com/fund-account [flow-docs]: https://developers.flow.com [flow-diver]: https://testnet.flowdiver.io/ [github-repo]: https://github.com/onflow/random-coin-toss [.io]: https://testnet.flowdiver.io/tx/9c4f5436535d36a82d4ae35467b37fea8971fa0ab2409dd0d5f861f61e463d98 [run-dnz]: https://run.dnz.dev/ [coin-toss-contract]: https://contractbrowser.com/A.b6c99d7ff216a684.CoinToss [coin-toss-contract-code]: https://github.com/onflow/random-coin-toss/blob/main/contracts/CoinToss.cdc [flow-diver-tx]: https://testnet.flowdiver.io/tx/a79fb2f947e7803eefe54e48398f6983db4e0d4d5e217d2ba94f8ebdec132957 --- # File: /md/blockchain-development-tutorials/native-vrf/vrf-in-solidity.md --- sidebar_position: 2 title: VRF (Randomness) in Solidity sidebar_label: VRF (Randomness) in Solidity keywords: - VRF - randomness - Cadence - Solidity - Flow EVM - smart contracts - blockchain - NFTs - DeFi --- # VRF (Randomness) in Solidity Flow provides secure, native onchain randomness that developers can leverage through Cadence Arch, a precompiled contract available on the Flow EVM environment. This guide walks you through how Consumer Decentralized Finance (DeFi) developers can use Cadence Arch to access Flow's verifiable randomness with Consumer DeFi. ### What is Cadence Arch? [Cadence Arch] is a precompiled smart contract that allows DeFi developers on Flow EVM to interact with Flow's randomness and other network features like block height. This contract can be accessed with its specific address, and DeFi developers can make static calls to retrieve random values and other information. ## Prerequisites - Basic DeFi knowledge - Installed Metamask extension - Remix IDE for compilation and deployment - Flow EVM Testnet setup in Metamask ## Network information for Flow EVM See [Network information] for more details. ## Steps to connect Flow EVM testnet to metamask See [Wallets & Configurations] for more details. ## Solidity commit reveal Make sure you review the Solidity version of the [commit reveal] to learn more about Flow EVM's native secure randomness through a simple demonstration. ## Obtaining testnet FLOW You can fund your account with testnet FLOW with the [Flow Faucet]. Enter your Flow-EVM testnet address, and you'll receive testnet FLOW tokens to interact with smart contracts. ## Solidity code example: retrieving random numbers The following is a simple Solidity contract that interacts with the Cadence Arch contract to retrieve a pseudo-random number: ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract CadenceArchCaller { // Address of the Cadence Arch contract address constant public cadenceArch = 0x0000000000000000000000010000000000000001; // Function to fetch a pseudo-random value function revertibleRandom() public view returns (uint64) { // Static call to the Cadence Arch contract's revertibleRandom function (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("revertibleRandom()")); require(ok, "Failed to fetch a random number through Cadence Arch"); uint64 output = abi.decode(data, (uint64)); // Return the random value return output; } } ``` ### Explanation of the contract 1. **Cadence Arch Address**: The `cadenceArch` variable stores the address of the Cadence Arch precompiled contract (`0x0000000000000000000000010000000000000001`), which is constant across Flow EVM. 2. **Revertible Random**: The `revertibleRandom()` function makes a static call to the `revertibleRandom()` function to fetch a pseudo-random number. If the call is successful, it decodes the result as a `uint64` random value. ## Deploy and test the contract ### Compile and deploy the contract 1. Open Remix IDE. 2. Create a new file and paste the Solidity code above. ![Creating file in Remix](./imgs/vrf-2.png) 3. To compile the contract, select the appropriate Consumer DeFi compiler version (0.8.x). ![Compiling in Remix](./imgs/vrf-3.png) 4. Connect Remix to your Metamask wallet (with Flow EVM testnet). To do this, select **Injected Web3** as the environment. ![Connecting to MetaMask](./imgs/vrf-4.png) 5. Deploy the contract. ![Deploying the contract](./imgs/vrf-5.png) ### Call revertibleRandom After deployment, you can interact with the contract to retrieve a random number. Call the `revertibleRandom()` function in the left sidebar on the deployed contract. This fetches a pseudo-random number that Flow's VRF generates. ![Calling revertibleRandom function](./imgs/vrf-6.png) The result will be a `uint64` random number generated on Flow EVM. ## Generate random numbers in a range For use-cases like games and lotteries, it's useful to generate a random number within a specified range. The following example shows how to get a value between a min and max number. ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract RandomInRange { address constant public cadenceArch = 0x0000000000000000000000010000000000000001; // Generate a random number between min and max function getRandomInRange(uint64 min, uint64 max) public view returns (uint64) { // Static call to the Cadence Arch contract's revertibleRandom function (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("revertibleRandom()")); require(ok, "Failed to fetch a random number through Cadence Arch"); uint64 randomNumber = abi.decode(data, (uint64)); // Return the number in the specified range return (randomNumber % (max + 1 - min)) + min; } } ``` :::warning The above code is susceptible to the [modulo bias], particularly if the random number range is not a multiple of your desired range. To avoid this, you can use a more complex algorithm like rejection sampling, an example for which is provided in [this repository]. ::: ## Secure randomness with commit-reveal scheme in Solidity You can use the **`revertibleRandom()`** function directly to generate a pseudo-random number. However, in certain situations, especially with untrusted callers, this function exposes a vulnerability: the ability of a transaction to **revert after seeing the random result**. **The Issue with Using `revertibleRandom()` Directly** - When an untrusted party calls a contract function that uses `revertibleRandom()`, they receive the random number **during the transaction execution**. - **Post-selection** is the caller's ability to abort the transaction if the random outcome is unfavorable. In this case, the user could choose to revert the transaction (for example, if they lose a bet) and attempt to call the function again in hopes of a better outcome. - This can lead to a form of _transaction reversion attack_, where the randomness can be exploited by repeatedly attempting transactions until a favorable result is obtained. ## Further reading For further details on Flow's randomness and secure development practices, check out the [Flow Randomness Documentation]. You can also view an example in both Solidity and Cadence of a [random coin toss implentation] using the VRF. _This documentation was contributed by [Noah Naizir] a community developer._ [Cadence Arch]: https://github.com/onflow/flips/blob/main/protocol/20231116-evm-support.md#cadence-arch [Flow EVM Testnet RPC]: https://testnet.evm.nodes.onflow.org/ [Flow EVM Testnet Explorer]: https://evm-testnet.flowscan.io/ [Flow Faucet]: https://testnet-faucet.onflow.org/fund-account [modulo bias]: https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ [this repository]: https://github.com/onflow/random-coin-toss [Flow Randomness Documentation]: https://developers.flow.com/build/cadence/advanced-concepts/randomness [random coin toss implentation]: https://github.com/onflow/random-coin-toss [Noah Naizir]: https://x.com/noah_overflow [Network information]: ../../build/evm/quickstart.md#network-information [Wallets & Configurations]: ../../blockchain-development-tutorials/evm/setup/integrating-metamask.mdx [commit reveal]: https://github.com/onflow/random-coin-toss/blob/main/solidity/src/CoinToss.sol --- # File: /md/blockchain-development-tutorials/tokens.md --- title: Token Development and Registration description: Complete guides for creating, deploying, and registering tokens on Flow using both Cadence and EVM approaches. sidebar_position: 8 --- # Token Development and Registration Flow supports token development through two primary approaches: native Cadence contracts that leverage Flow's unique resource-oriented programming model, and EVM-compatible contracts that enable easy migration from Ethereum. This section provides comprehensive guides for how to create tokens with both approaches, along with registration processes to ensure ecosystem visibility. ## Cadence token development ### [Creating a Fungible Token] Learn how to build a complete fungible token contract with Cadence's resource-based programming model. This comprehensive guide covers how to implement the Flow Fungible Token standard, manage token vaults for secure storage, create minter resources for controlled token creation, and deploy with proper metadata views. You'll build a production-ready token that integrates seamlessly with Flow's ecosystem, such as wallets, exchanges, and DeFi protocols. ### [Creating an NFT Contract] Master the development of non-fungible token contracts with Flow's powerful Cadence language. This detailed tutorial walks through how to implement the Flow NFT standard, create collection resources for efficient NFT management, build minting mechanisms with access controls, and add comprehensive metadata support. Learn to create NFTs that work perfectly with marketplaces, wallets, and other Flow ecosystem applications. ### [Register Your Assets in Cadence] Complete the registration process for your Cadence-based fungible and non-fungible tokens to ensure visibility across the Flow ecosystem. This guide covers how to submit your tokens to official registries, provide required metadata and documentation, follow community standards for token information, and integrate with major wallets and applications like Flow Wallet and IncrementFi. ## Flow EVM Token Development ### [Register Your ERC20 Token] Register your ERC20 tokens deployed on Flow EVM to appear in wallets and ecosystem applications. This guide details the GitHub Pull Request process to add tokens to the Flow standard token list, provide proper token metadata and logos, meet verification requirements, and ensure compatibility with Flow Wallet, MetaMask, and other ecosystem tools. ## Conclusion Flow's dual approach to token development provides developers with flexibility to choose the best paradigm for their projects. Whether you want to leverage Cadence's innovative resource model for enhanced security and composability, or use familiar EVM patterns for quick deployment, these guides ensure your tokens integrate seamlessly with Flow's thriving ecosystem of wallets, exchanges, and applications. [Creating a Fungible Token]: ./fungible-token-cadence.md [Creating an NFT Contract]: ./nft-cadence.md [Register Your Assets in Cadence]: ./register-cadence-assets.md [Register Your ERC20 Token]: ./register-erc20-token.md --- # File: /md/blockchain-development-tutorials/tokens/fungible-token-cadence.md --- title: Creating a Fungible Token description: Learn how to create and deploy a fungible token on Flow using Cadence. Follow this guide to implement the Flow Fungible Token standard, manage token minting, transfers, and vault management. sidebar_position: 1 keywords: - fungible token - Flow token - token standard - smart contract - Cadence - token minting - token transfers - vault management - Flow CLI - token deployment - FT standard - token balance - Flow development - token contract - blockchain tokens - bridge --- :::info This guide is an in-depth tutorial on launching a Fungible Token contract from scratch. To launch in two minutes with a tool, check out [Toucans] ::: ## What are fungible tokens? Fungible tokens are digital assets that are interchangeable and indistinguishable with other tokens of the same type. This means that each token is identical in specification to every other token in circulation. Think of them like traditional money; every dollar bill has the same value as every other dollar bill. Fungible tokens play a crucial role in web3 ecosystems. They serve as both a means of payment and an incentive for network participation. They can take on various roles, such as currencies, structured financial instruments, shares of index funds, and even voting rights in decentralized autonomous organizations. ## Vaults on Flow On the Flow blockchain and in the Cadence programming language, fungible tokens are stored in structures called resources. Resources are objects in Cadence that store data, but have special restrictions about how they can be stored and transferred, which makes them perfect to represent digital objects with real value. You can learn more about resources in the [Cadence documentation] and [Cadence tutorials]. For fungible tokens specifically, tokens are represented by a resource type called a `Vault`: ```cadence access(all) resource interface Vault { /// Field that tracks the balance of a vault access(all) var balance: UFix64 } ``` Think of a `Vault` as a digital piggy bank. Users who own fungible tokens store vault objects that track their balances directly in their account storage. This is opposed to languages that track user balances in a central ledger smart contract. When you transfer tokens from one vault to another: 1. The transferor's vault creates a temporary vault that contains the transfer amount. 2. The original vault's balance decreases by the transfer amount. 3. The recipient's vault receives the tokens from the temporary vault and adds the temporary vault's balance to the its own balance. 4. The temporary vault is then destroyed. This process ensures secure and accurate token transfers on the Flow blockchain. ## Fungible token standard The [Fungible Token Standard] defines how a fungible token should behave on Flow. Wallets and other platforms need to recognize these tokens, so they adhere to a specific interface, which defines fields like balance, totalSupply, withdraw functionality, and more. This interface ensures that all fungible tokens on Flow have a consistent structure and behavior. Clink the link to the fungible token standard to see the full standard and learn about specific features and requirements. [Learn more about interfaces here]. ## Set up a project To create a fungible token on the Flow blockchain, you'll first need some tools and configurations in place. ### Install Flow CLI The **Flow CLI** (Command Line Interface) provides a suite of tools that allow developers to interact seamlessly with the Flow blockchain. If you haven't installed the Flow CLI yet and have [Homebrew] installed, you can run `brew install flow-cli`. If you don't have Homebrew, follow the [Flow CLI installation guide]. ### Initialize a new project :::info > 💡 Here is a link to the [completed code] if you want to skip ahead or reference as you follow along. ::: After you've installed the Flow CLI, you can set up a new project with the `flow init` command. This command initializes the necessary directory structure and a `flow.json` configuration file (a way to configure your project for contract sources, deployments, accounts, and more): ```bash flow init FooToken ``` Select `Basic Cadence project (no dependencies)`. When you execute the command, it generates the following directory structure: ``` /cadence /contracts /scripts /transactions /tests flow.json ``` Now, navigate into the project directory: ```bash cd FooToken ``` In our configuration file, called `flow.json`, for the network we want to use, we'll state the address the `FungibleToken` contract is deployed to via `aliases` in a new `contracts` section. Since it is a standard contract, it has already been deployed to the emulator, a tool that runs and emulates a local development version of the Flow Blockchain, for us. You can find addresses for other networks, like Testnet and Mainnet, on the [Fungible Token Standard] repo. We'll also need to add the addresses for `ViewResolver`, `MetadataViews`, and `FungibleTokenMetadataViews`, which are other important contracts to use. These contracts are deployed to the Flow emulator by default, so there is not need to copy their code into your repo. The addresses below are the addresses in the emulator that your contract will import them from. ```json "contracts": { "FungibleToken": { "aliases": { "emulator": "0xee82856bf20e2aa6" } }, "FungibleTokenMetadataViews": { "aliases": { "emulator": "0xee82856bf20e2aa6" } }, "ViewResolver": { "aliases": { "emulator": "0xf8d6e0586b0a20c7" } }, "MetadataViews": { "aliases": { "emulator": "0xf8d6e0586b0a20c7" } } } ``` ## Write Our token contract Next let's create a `FooToken` contract at `cadence/contract/FooToken.cdc` with the boilerplate `generate` command from the Flow CLI: ```bash flow generate contract FooToken ``` This will create a new file called `FooToken.cdc` in the `contracts` directory. Let's open it up and add some code. In this contract file, we want to import our `FungibleToken` contract that we've defined in `flow.json`. ```cadence import "FungibleToken" ``` In this same file, let's create our contract which implements the `FungibleToken` contract interface (to do this, it sets it after the `FooToken:`). We'll also include fields for standard storage and public paths for our resource definitions. In our `init` — which runs on the contract's first deployment and is used to set initial values — let's set an initial total supply of 1,000 tokens for this example. ```cadence // ...previous code access(all) contract FooToken: FungibleToken { access(all) var totalSupply: UFix64 access(all) let VaultStoragePath: StoragePath access(all) let VaultPublicPath: PublicPath access(all) let MinterStoragePath: StoragePath init() { self.totalSupply = 1000.0 self.VaultStoragePath = /storage/fooTokenVault self.VaultPublicPath = /public/fooTokenVault self.MinterStoragePath = /storage/fooTokenMinter } } ``` ### Create a vault Inside of this contract, we'll need to create a resource for a `Vault`. The `FungibleToken` standard requires that your vault implements the `FungibleToken.Vault` interface. This interface inherits from [many other interfaces], which enforce different functionality that you can learn about in the standard. ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...totalSupply and path code access(all) resource Vault: FungibleToken.Vault { access(all) var balance: UFix64 init(balance: UFix64) { self.balance = balance } } // ...init code } ``` In order to give an account a vault, we need to create a function that creates a vault of our `FooToken` type and returns it to the account. This function takes a `vaultType: Type` argument that allows the caller to specify which type of `Vault` they want to create. Contracts that implement multiple `Vault` types can use this argument, but since your contract only implements one `Vault` type, it can ignore the argument. You should also add a simpler version of this function with no parameter to your `Vault` implementation. ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...other code access(all) resource Vault: FungibleToken.Vault { // ...other vault code access(all) fun createEmptyVault(): @FooToken.Vault { return <-create Vault(balance: 0.0) } // ...vault init code } // ...other code access(all) fun createEmptyVault(vaultType: Type): @FooToken.Vault { return <- create Vault(balance: 0.0) } // ...FooToken.init() code } ``` Inside our `Vault` resource, we also need a way to withdraw balances. To do that, we need to add a `withdraw()` function that returns a new vault with the transfer amount and decrements the current balance. ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...previous code access(all) resource Vault: FungibleToken.Vault { // ...other vault code access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @FooToken.Vault { self.balance = self.balance - amount return <-create Vault(balance: amount) } // ...vault init code } // ...additional code } ``` As you can see, this function has an `access(FungibleToken.Withdraw)` access modifier. This is an example of entitlements in Cadence. [Entitlements] are a way for developers to restrict access to privileged fields and functions in a composite type like a resource when a reference is created for it. They are what protects third-party access to the privileged functionality in your resource objects. We recommend that you read the [Entitlements] documentation to understand how to use the feature properly. [References]can be freely up-casted and down-casted in Cadence, so it is important for privileged functionality to be protected by an entitlement so that it can only be accessed if it is authorized. In this example, the `withdraw()` function is always accessible to code that controls the full `Vault` object, but if a reference is created for it, the `withdraw()` function can only be called if the reference is authorized by the owner with `FungibleToken.Withdraw`, which is a [standard entitlement] defined by the FungibleToken contract: ```cadence // Example of an authorized entitled reference to a FungibleToken.Vault ``` In addition to withdrawl, the vault also needs a way to deposit. We'll [typecast] to make sure we are dealing with the correct token, update the vault balance, and destroy the vault. Add this code to your resource: ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...previous code access(all) resource Vault: FungibleToken.Vault { // ...other vault code access(all) fun deposit(from: @{FungibleToken.Vault}) { let vault <- from as! @FooToken.Vault self.balance = self.balance + vault.balance destroy vault } // ...vault init } // ...additional code } ``` Many projects rely on events the signal when withdrawals, deposits, or burns happen. Luckily, the `FungibleToken` standard handles the definition and emission of events for projects, so there is no need for you to add any events to your implementation for withdraw, deposit, and burn. Here are the `FungibleToken` event definitions: ```cadence /// The event that is emitted when tokens are withdrawn from a Vault access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64, balanceAfter: UFix64) /// The event that is emitted when tokens are deposited to a Vault access(all) event Deposited(type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64, balanceAfter: UFix64) /// Event that is emitted when the global burn method is called with a non-zero balance access(all) event Burned(type: String, amount: UFix64, fromUUID: UInt64) ``` These events are emitted by the [`Vault` interface] in the `FungibleToken` contract whenever the relevant function is called on any implementation. One important piece to understand about the `Burned` event in particular is that for it to be emitted when a `Vault` is burned, it needs to be burnt via [the `Burner` contract's `burn()` method]. The [`Burner` contract] defines a standard that all projects should use to handle the destruction of any resource. It allows projects to define custom logic that can be executed when a resource is destroyed, like emitting events, or update a field in the contract to show that the resource was destroyed. This will call the resource's `burnCallback()` function, which emits the event. You'll need to also add this function to your token contract now: ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...previous code access(all) resource Vault: FungibleToken.Vault { // ...other vault code /// Called when a fungible token is burned via the `Burner.burn()` method access(contract) fun burnCallback() { if self.balance > 0.0 { FooToken.totalSupply = FooToken.totalSupply - self.balance } self.balance = 0.0 } // ...vault init } // ...additional code } ``` If you ever need to destroy a `Vault` with a non-zero balance, you should destroy it via the `Burner.burn` method so this important function can be called. There are three other utility methods that need to be added to your `Vault` to get various information: ```cadence import "FungibleToken" access(all) contract FooToken: FungibleToken { // ...previous code access(all) resource Vault: FungibleToken.Vault { // ...other vault code /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts access(all) view fun getSupportedVaultTypes(): {Type: Bool} { let supportedTypes: {Type: Bool} = {} supportedTypes[self.getType()] = true return supportedTypes } /// Says if the Vault can receive the provided type in the deposit method access(all) view fun isSupportedVaultType(type: Type): Bool { return self.getSupportedVaultTypes()[type] ?? false } /// Asks if the amount can be withdrawn from this vault access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool { return amount <= self.balance } // ...vault init } // ...additional code } ``` ### Add support for metadata views The Fungible Token standard also enforces that implementations provide functionality to return a set of standard views about the tokens via the [ViewResolver] and [FungibleTokenMetadataViews] definitions. (You will need to add these imports to your contract now.) These provide developers with standard ways to represent metadata about a given token such as supply, token symbols, website links, and standard account paths and types that third-parties can access in a standard way. You can see the [metadata views documentation] for a more thorough guide that uses an NFT contract as an example. For now, you can add this code to your contract to support the important metadata views: ```cadence import "FungibleToken" // Add these imports import "MetadataViews" import "FungibleTokenMetadataViews" access(all) contract FooToken: FungibleToken { // ...other code access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type(), Type(), Type(), Type() ] } access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { switch viewType { case Type(): return FungibleTokenMetadataViews.FTView( ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTDisplay?, ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? ) case Type(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( // Change this to your own SVG image url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" ), mediaType: "image/svg+xml" ) let medias = MetadataViews.Medias([media]) return FungibleTokenMetadataViews.FTDisplay( // Change these to represent your own token name: "Example Foo Token", symbol: "EFT", description: "This fungible token is used as an example to help you develop your next FT #onFlow.", externalURL: MetadataViews.ExternalURL("https://developers.flow.com/build/cadence/guides/fungible-token"), logos: medias, socials: { "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") } ) case Type(): return FungibleTokenMetadataViews.FTVaultData( storagePath: self.VaultStoragePath, receiverPath: self.VaultPublicPath, metadataPath: self.VaultPublicPath, receiverLinkedType: Type<&FooToken.Vault>(), metadataLinkedType: Type<&FooToken.Vault>(), createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} { return <-FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>()) }) ) case Type(): return FungibleTokenMetadataViews.TotalSupply( totalSupply: FooToken.totalSupply ) } return nil } // ...other code access(all) resource Vault: FungibleToken.Vault { // ...other vault code access(all) view fun getViews(): [Type] { return FooToken.getContractViews(resourceType: nil) } access(all) fun resolveView(_ view: Type): AnyStruct? { return FooToken.resolveContractView(resourceType: nil, viewType: view) } // ...other vault code } // ...other FooToken code } ``` ### Create a minter Let's create a minter resource which is used to mint vaults that have tokens in them. We can keep track of tokens we mint with totalSupply. If we want the ability to create new tokens, we'll need a way to mint them. To do that, let's create another resource on the `FooToken` contract. This will have a `mintToken`function which can increase the total supply of the token. ```cadence import "FungibleToken" import "MetadataViews" import "FungibleTokenMetadataViews" access(all) contract FooToken: FungibleToken { // ...additional contract code // Add this event access(all) event TokensMinted(amount: UFix64, type: String) /// Minter /// /// Resource object that token admin accounts can hold to mint new tokens. /// access(all) resource Minter { /// mintTokens /// /// Function that mints new tokens, adds them to the total supply, /// and returns them to the calling context. /// access(all) fun mintTokens(amount: UFix64): @FooToken.Vault { FooToken.totalSupply = FooToken.totalSupply + amount let vault <-create Vault(balance: amount) emit TokensMinted(amount: amount, type: vault.getType().identifier) return <-vault } } // ...additional contract code } ``` We also want to decide which accounts we want to give this ability to. In our example, we'll give it to the account where the contract is deployed. We can set this in the contract init function below the setting of total supply so that when the contract is created, the minter is stored on the same account. ```cadence import "FungibleToken" import "MetadataViews" import "FungibleTokenMetadataViews" access(all) contract FooToken: FungibleToken { // ...additional contract code init() { self.totalSupply = 1000.0 // existed before self.account.save(<- create Minter(), to: self.MinterStoragePath) } } ``` After each of these steps, your `FooToken.cdc` contract file will now look like this: ```cadence import "FungibleToken" import "MetadataViews" import "FungibleTokenMetadataViews" access(all) contract FooToken: FungibleToken { /// The event that is emitted when new tokens are minted access(all) event TokensMinted(amount: UFix64, type: String) /// Total supply of FooTokens in existence access(all) var totalSupply: UFix64 /// Storage and Public Paths access(all) let VaultStoragePath: StoragePath access(all) let VaultPublicPath: PublicPath access(all) let ReceiverPublicPath: PublicPath access(all) let MinterStoragePath: StoragePath access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type(), Type(), Type(), Type() ] } access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { switch viewType { case Type(): return FungibleTokenMetadataViews.FTView( ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTDisplay?, ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? ) case Type(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( // Change this to your own SVG image url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" ), mediaType: "image/svg+xml" ) let medias = MetadataViews.Medias([media]) return FungibleTokenMetadataViews.FTDisplay( // Change these to represent your own token name: "Example Foo Token", symbol: "EFT", description: "This fungible token is used as an example to help you develop your next FT #onFlow.", externalURL: MetadataViews.ExternalURL("https://developers.flow.com/build/cadence/guides/fungible-token"), logos: medias, socials: { "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") } ) case Type(): return FungibleTokenMetadataViews.FTVaultData( storagePath: self.VaultStoragePath, receiverPath: self.VaultPublicPath, metadataPath: self.VaultPublicPath, receiverLinkedType: Type<&FooToken.Vault>(), metadataLinkedType: Type<&FooToken.Vault>(), createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} { return <-FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>()) }) ) case Type(): return FungibleTokenMetadataViews.TotalSupply( totalSupply: FooToken.totalSupply ) } return nil } access(all) resource Vault: FungibleToken.Vault { /// The total balance of this vault access(all) var balance: UFix64 // initialize the balance at resource creation time init(balance: UFix64) { self.balance = balance } /// Called when a fungible token is burned via the `Burner.burn()` method access(contract) fun burnCallback() { if self.balance > 0.0 { FooToken.totalSupply = FooToken.totalSupply - self.balance } self.balance = 0.0 } access(all) view fun getViews(): [Type] { return FooToken.getContractViews(resourceType: nil) } access(all) fun resolveView(_ view: Type): AnyStruct? { return FooToken.resolveContractView(resourceType: nil, viewType: view) } access(all) view fun getSupportedVaultTypes(): {Type: Bool} { let supportedTypes: {Type: Bool} = {} supportedTypes[self.getType()] = true return supportedTypes } access(all) view fun isSupportedVaultType(type: Type): Bool { return self.getSupportedVaultTypes()[type] ?? false } access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool { return amount <= self.balance } access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @FooToken.Vault { self.balance = self.balance - amount return <-create Vault(balance: amount) } access(all) fun deposit(from: @{FungibleToken.Vault}) { let vault <- from as! @FooToken.Vault self.balance = self.balance + vault.balance vault.balance = 0.0 destroy vault } access(all) fun createEmptyVault(): @FooToken.Vault { return <-create Vault(balance: 0.0) } } access(all) resource Minter { /// mintTokens /// /// Function that mints new tokens, adds them to the total supply, /// and returns them to the calling context. /// access(all) fun mintTokens(amount: UFix64): @FooToken.Vault { FooToken.totalSupply = FooToken.totalSupply + amount let vault <-create Vault(balance: amount) emit TokensMinted(amount: amount, type: vault.getType().identifier) return <-vault } } access(all) fun createEmptyVault(vaultType: Type): @FooToken.Vault { return <- create Vault(balance: 0.0) } init() { self.totalSupply = 1000.0 self.VaultStoragePath = /storage/fooTokenVault self.VaultPublicPath = /public/fooTokenVault self.MinterStoragePath = /storage/fooTokenMinter // Create the Vault with the total supply of tokens and save it in storage // let vault <- create Vault(balance: self.totalSupply) emit TokensMinted(amount: vault.balance, type: vault.getType().identifier) self.account.storage.save(<-vault, to: self.VaultStoragePath) // Create a public capability to the stored Vault that exposes // the `deposit` method and getAcceptedTypes method through the `Receiver` interface // and the `balance` method through the `Balance` interface // let fooTokenCap = self.account.capabilities.storage.issue<&FooToken.Vault>(self.VaultStoragePath) self.account.capabilities.publish(fooTokenCap, at: self.VaultPublicPath) let minter <- create Minter() self.account.storage.save(<-minter, to: self.MinterStoragePath) } } ``` ## Deploy the contract To use the contract, we need to deploy it to the network we want to use it on. In our case, we'll deploy it to emulator while we develop it. Back in our `flow.json`, let's add our `FooToken` to the `contracts` after `FungibleToken` with the path of the source code: ```json "FooToken": "cadence/contracts/FooToken.cdc" ``` Let's also add a new `deployments` section to `flow.json` with the network we want to deploy it to, `emulator`, the account we want it deployed to `emulator-account`, and the list of contracts we want deployed in the array. ```json "deployments": { "emulator": { "emulator-account": ["FooToken"] } } ``` Next, via the Flow CLI, we will start the emulator. As mentioned, this will give us a local development environment for the Flow Blockchain. ```bash flow emulator start ``` Open a new terminal and run the following to deploy your project: ```bash flow project deploy ``` Congrats, you've deployed your contract to the Flow Blockchain emulator. To read more about how to deploy your project to other environments, see the [Deploy Project Contracts with CLI] docs. ## Read the token's total supply Let's now check that our total supply was initialized with 1,000 FooTokens. Go ahead and create a script called `get_total_supply.cdc` with the `generate` command. ```bash flow generate script get_total_supply ``` In `cadence/scripts/get_total_supply.cdc` (which was just created), let's add this code which will log the `totalSupply` value from the `FooToken` contract: ```cadence import "FooToken" access(all) fun main(): UFix64 { return FooToken.totalSupply } ``` To run this with the CLI, enter this in your terminal: ```bash flow scripts execute cadence/scripts/get_total_supply.cdc ``` In the terminal where you started the emulator, you will see `Result: 1000.0` To learn more about how to run scripts with Flow CLI, see the [Execute Scripts in Flow CLI] docs. ## Give accounts the ability to receive tokens On Flow, newly-created accounts cannot receive arbitrary assets. They need to be initialized to receive resources. In our case, we want to give accounts tokens and we'll need to create a `Vault` (which acts as a receiver) on each account that we want to have the ability to receive tokens. To do this, we'll need to run a transaction which will create the vault and set it in their storage with the `createEmptyVault()` function we created earlier on the contract. Let's first create the file at `cadence/transactions/setup_ft_account.cdc` with the `generate` command: ```bash flow generate transaction setup_ft_account ``` Then add this code to it. This will call the `createEmptyVault` function, save it in storage, and create a capability for the vault which will later allow us to read from it. To learn more about capabilities, see the [Cadence Capabilities] docs ```cadence import "FungibleToken" import "FooToken" transaction () { prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { // Return early if the account already stores a FooToken Vault if signer.storage.borrow<&FooToken.Vault>(from: FooToken.VaultStoragePath) != nil { return } let vault <- FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>()) // Create a new FooToken Vault and put it in storage signer.storage.save(<-vault, to: FooToken.VaultStoragePath) // Create a public capability to the Vault that exposes the Vault interfaces let vaultCap = signer.capabilities.storage.issue<&FooToken.Vault>( FooToken.VaultStoragePath ) signer.capabilities.publish(vaultCap, at: FooToken.VaultPublicPath) } } ``` There are also examples of [generic transactions] that you can use to setup an account for ANY fungible token with metadata views! Check those out and try to use generic transactions whenever it is possible. Next let's create a new emulator account with the CLI. We'll use this account to create a new vault and mint tokens into it. Run: ```bash flow accounts create ``` Let's call it `test-acct` and select "Emulator" for the network: ```bash test-acct ``` This will have added a new account, called `test-acct` to your `flow.json`. To call our setup account transaction from the CLI, we'll run the following: ```bash flow transactions send ./cadence/transactions/setup_ft_account.cdc --signer test-acct --network emulator ``` To learn more about how to run transactions with Flow CLI, see the [Send a Transaction] docs. ## Read a vault's balance Let's now read the balance of the newly-created account (`test-acct`) to check that it's zero. Create this new script file `cadence/scripts/get_footoken_balance.cdc`: ```bash flow generate script get_footoken_balance ``` Add this code which attempts to borrow the capability from the account requested and logs the vault balance if permitted: ```cadence import "FungibleToken" import "FooToken" import "FungibleTokenMetadataViews" access(all) fun main(address: Address): UFix64 { let vaultData = FooToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? ?? panic("Could not get FTVaultData view for the FooToken contract") return getAccount(address).capabilities.borrow<&{FungibleToken.Balance}>( vaultData.metadataPath )?.balance ?? panic("Could not borrow a reference to the FooToken Vault in account " .concat(address.toString()).concat(" at path ").concat(vaultData.metadataPath.toString()) .concat(". Make sure you are querying an address that has an FooToken Vault set up properly.")) } ``` To run this script with the CLI, enter the following in your terminal. You'll need to replace `123` with the address created by Flow CLI in your `flow.json` for the `test-acct` address. ```bash flow scripts execute cadence/scripts/get_footoken_balance.cdc 123 // change "123" to test-acct address ``` You will see a balance of zero logged. ## Mint more tokens Now that we have an account with a vault, let's mint some tokens into it with the Minter we created on the contract account. To do this, let's create a new transaction file `cadence/transactions/mint_footoken.cdc`: ```bash flow generate transaction mint_footoken ``` Next, let's add the following code to the `mint_footoken.cdc` file. This code will attempt to borrow the minting capability and mint 20 new tokens into the receivers account. ```cadence import "FungibleToken" import "FooToken" transaction(recipient: Address, amount: UFix64) { /// Reference to the Example Token Minter Resource object let tokenMinter: &FooToken.Minter /// Reference to the Fungible Token Receiver of the recipient let tokenReceiver: &{FungibleToken.Receiver} prepare(signer: auth(BorrowValue) &Account) { // Borrow a reference to the admin object self.tokenMinter = signer.storage.borrow<&FooToken.Minter>(from: FooToken.MinterStoragePath) ?? panic("Cannot mint: Signer does not store the FooToken Minter in their account!") self.tokenReceiver = getAccount(recipient).capabilities.borrow<&{FungibleToken.Receiver}>(FooToken.VaultPublicPath) ?? panic("Could not borrow a Receiver reference to the FungibleToken Vault in account " .concat(recipient.toString()).concat(" at path ").concat(FooToken.VaultPublicPath.toString()) .concat(". Make sure you are sending to an address that has ") .concat("a FungibleToken Vault set up properly at the specified path.")) } execute { // Create mint tokens let mintedVault <- self.tokenMinter.mintTokens(amount: amount) // Deposit them to the receiever self.tokenReceiver.deposit(from: <-mintedVault) } } ``` To run this transaction, enter this in your terminal. Replace `123` with the `test-acct` address found in your `flow.json`. This command also states to sign with our `emulator-account` on the Emulator network. ```bash flow transactions send ./cadence/transactions/mint_footoken.cdc 123 20.0 --signer emulator-account --network emulator ``` Let's go ahead and read the vault again. Remember to replace `123` with the correct address. ```bash flow scripts execute cadence/scripts/get_footoken_balance.cdc 123 ``` It will now say 20 tokens are in the vault. ## Transfer tokens between accounts The final functionality we'll add is the ability to transfer tokens from one account to another. To do that, create a new `cadence/transactions/transfer_footoken.cdc` transaction file: ```bash flow generate transaction transfer_footoken ``` Let's add the code which states that the signer of the transaction will withdraw from their vault and put it into the receiver's vault, which will be passed as a transaction argument. ```cadence import "FungibleToken" import "FooToken" transaction(to: Address, amount: UFix64) { // The Vault resource that holds the tokens that are being transferred let sentVault: @{FungibleToken.Vault} prepare(signer: auth(BorrowValue) &Account) { // Get a reference to the signer's stored vault let vaultRef = signer.storage.borrow(from: FooToken.VaultStoragePath) ?? panic("The signer does not store an FooToken.Vault object at the path " .concat(FooToken.VaultStoragePath.toString()) .concat(". The signer must initialize their account with this vault first!")) // Withdraw tokens from the signer's stored vault self.sentVault <- vaultRef.withdraw(amount: amount) } execute { // Get the recipient's public account object let recipient = getAccount(to) // Get a reference to the recipient's Receiver let receiverRef = recipient.capabilities.borrow<&{FungibleToken.Receiver}>(FooToken.VaultPublicPath) ?? panic("Could not borrow a Receiver reference to the FooToken Vault in account " .concat(recipient.toString()).concat(" at path ").concat(FooToken.VaultPublicPath.toString()) .concat(". Make sure you are sending to an address that has ") .concat("a FooToken Vault set up properly at the specified path.")) // Deposit the withdrawn tokens in the recipient's receiver receiverRef.deposit(from: <-self.sentVault) } } ``` To send our tokens, we'll need to create a new account to send them to. Let's make one more account on emulator. Run: ```bash flow accounts create ``` And pick the name: ```bash test-acct-2 ``` Make sure to select Emulator as the network. Don't forget the new account will need a vault added, so let's run the following transaction to add one: ```bash flow transactions send ./cadence/transactions/setup_ft_account.cdc --signer test-acct-2 --network emulator ``` Now, let's send one token from our earlier account to the new account. Remember to replace `123` with account address of `test-acct-2`. ```bash flow transactions send ./cadence/transactions/transfer_footoken.cdc 123 1.0 --signer test-acct --network emulator ``` After that, read the balance of `test-acct-2` (replace the address `123`). ```bash flow scripts execute cadence/scripts/get_footoken_balance.cdc 123 ``` You will now see one token in `test-acct-2` account! The transfer transaction also has a [generic version] that developers are encouraged to use! ## More - View a repo of this [completed code]. - Review an [`ExampleToken`] contract that implements all of the remaining FungibleToken interface. - View the [Flow Token Standard] - Learn about how you can [bridge your FTs to Flow-EVM] and how you can [build your FT project] to be compatible with the Flow VM bridge. [bridge your FTs to Flow-EVM]: ../cross-vm-apps/vm-bridge.md#cross-vm-bridge [`Burner` contract]: ../../build/cadence/core-contracts/14-burner.md [build your FT project]: ../cross-vm-apps/vm-bridge.md#prep-your-assets-for-bridging [Cadence Capabilities]: https://cadence-lang.org/docs/language/capabilities [Cadence documentation]: https://cadence-lang.org/docs/language/resources [Cadence tutorials]: https://cadence-lang.org/docs/tutorial/resources [completed code]: https://github.com/onflow/FooToken [Deploy Project Contracts with CLI]: /docs/build/tools/flow-cli/deployment/deploy-project-contracts.md [Entitlements]: https://cadence-lang.org/docs/language/access-control#entitlements [`ExampleToken`]: https://github.com/onflow/flow-ft/blob/master/contracts/ExampleToken.cdc [Execute Scripts in Flow CLI]: /docs/build/tools/flow-cli/scripts/execute-scripts.md [Flow Token Standard]: https://github.com/onflow/flow-ft [FungibleTokenMetadataViews]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleTokenMetadataViews.cdc [Funglible Token Standard]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc [generic version]: https://github.com/onflow/flow-ft/blob/master/transactions/generic_transfer_with_address.cdc [generic transactions]: https://github.com/onflow/flow-ft/blob/master/transactions/metadata/setup_account_from_address.cdc [Flow CLI installation guide]: ../../build/tools/flow-cli/install.md [Homebrew]: https://brew.sh/ [Learn more about interfaces here]: https://cadence-lang.org/docs/language/interfaces [many other interfaces]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc#L140 [metadata views documentation]: ../../build/cadence/advanced-concepts/metadata-views.md [References]: https://cadence-lang.org/docs/language/references [Send a Transaction]: /docs/build/tools/flow-cli/transactions/send-transactions.md [standard entitlement]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc#L53 [the `Burner` contract's `burn()` method]: https://github.com/onflow/flow-ft/blob/master/contracts/utility/Burner.cdc#L23 [Toucans]: https://toucans.ecdao.org/ [typecast]: https://cadence-lang.org/docs/language/operators/casting-operators [ViewResolver]: https://github.com/onflow/flow-nft/blob/master/contracts/ViewResolver.cdc [`Vault` interface]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc#L198 --- # File: /md/blockchain-development-tutorials/tokens/nft-cadence.md --- title: Creating an NFT Contract description: Learn how to create and deploy a non-fungible token (NFT) contract on Flow using Cadence. Follow this guide to implement the Flow NFT standard, manage collections, and handle token minting and transfers. sidebar_position: 2 keywords: - NFT - non-fungible token - Flow NFT - NFT standard - smart contract - Cadence - token minting - NFT collection - Flow CLI - token deployment - NFT transfer - digital assets - Flow development - NFT contract - blockchain NFTs - bridge --- :::info This guide is an in-depth tutorial on launching NFT contracts from scratch. To launch in two minutes with a tool, check out [Touchstone] ::: ## What are NFTs NFTs, or Non-Fungible Tokens, represent a unique digital asset verified with blockchain technology. Unlike cryptocurrencies such as Bitcoin, which are fungible and can be exchanged on a one-for-one basis, NFTs are distinct and cannot be exchanged on a like-for-like basis. This uniqueness and indivisibility make them ideal to represent rare and valuable items like art, collectibles, tickets and even real estate. Their blockchain-backed nature ensures the authenticity and ownership of these digital assets. ## Set up a project To create an NFT on the Flow blockchain, you'll first need some tools and configurations in place. ### Instal Flow CLI The **Flow CLI** (Command Line Interface) provides a suite of tools that allow developers to interact seamlessly with the Flow blockchain. If you haven't installed the Flow CLI yet and have [Homebrew]installed, you can run `brew install flow-cli`. If you don't have Homebrew, follow the [Flow CLI installation guide]. ### Initialize a New Project > 💡 Note: Here is [a link to the completed code] if you want to skip ahead or reference as you follow along. After you install the Flow CLI, you can set up a new project with the `flow init` command. This command initializes the necessary directory structure and a `flow.json` configuration file (a way to configure your project for contract sources, deployments, accounts, and more): ```bash flow init foobar-nft ``` Select `Basic Cadence project (no dependencies)`. Upon execution, the command will generate the following directory structure: ``` /cadence /contracts /scripts /transactions /tests flow.json ``` Now, navigate into the project directory: ```bash cd foobar-nft ``` To begin, let's create a contract file named `FooBar` for the `FooBar` token, which is the focus of this tutorial. To do this, we can use the boilerplate `generate` command from the Flow CLI: ```bash flow generate contract FooBar ``` This will create a new file at `cadence/contracts/FooBar.cdc` with the following contents: ```cadence access(all) contract FooBar { init() {} } ``` Now, add these contracts to your `flow.json`. These are important contracts that your contract will import that are pre-deployed to the emulator. ```json "contracts": { "NonFungibleToken": { "aliases": { "emulator": "f8d6e0586b0a20c7" } }, "ViewResolver": { "aliases": { "emulator": "0xf8d6e0586b0a20c7" } }, "MetadataViews": { "aliases": { "emulator": "0xf8d6e0586b0a20c7" } } } ``` ## Set up our NFT on the contract ### understand resources On the Flow blockchain, "[Resources]" are a key feature of the Cadence programming language. They represent unique, non-duplicable assets, and ensure that they can only exist in one place at a time. This concept is crucial for representing NFTs on Flow, as it guarantees their uniqueness. To begin, let's define a basic `NFT` resource. This resource requires an `init` method, which is invoked when the resource is instantiated: ```cadence access(all) contract FooBar { access(all) resource NFT { init() {} } init() {} } ``` Every resource in Cadence has a unique identifier assigned to it. We can use it to set an ID for our NFT. Here's how you can do that: ```cadence access(all) contract FooBar { access(all) resource NFT { access(all) let id: UInt64 init() { self.id = self.uuid } } init() {} } ``` To control the creation of NFTs, it's essential to have a mechanism that restricts their minting. This ensures that not just anyone can create an NFT and inflate its supply. To achieve this, you can introduce an `NFTMinter` resource that contains a `createNFT` function: ```cadence access(all) contract FooBar { // ...[previous code]... access(all) resource NFTMinter { access(all) fun createNFT(): @NFT { return <-create NFT() } init() {} } init() {} } ``` In this example, the `NFTMinter` resource is stored on the contract account's storage. This means that only the contract account will have the ability to mint new NFTs. To set this up, add the following line to the contract's `init` function: ```cadence access(all) contract FooBar { // ...[previous code]... init() { self.account.storage.save(<- create NFTMinter(), to: /storage/fooBarNFTMinter) } } ``` ### Set Up an NFT Collection If you store individual NFTs directly in an account's storage, it can cause issues, especially if you want to store multiple NFTs. Instead, you must create a collection that can hold multiple NFTs. You can then store this collection in the account's storage. To start, create a new resource named `Collection`. This resource will act as a container for your NFTs, and stores them in a dictionary indexed by their IDs. ```cadence access(all) contract FooBar { // ...[NFT resource code]... access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init() { self.ownedNFTs <- {} } } // ...[NFTMinter code]... } ``` ## Fit the Flow NFT standard To ensure compatibility and interoperability within the Flow ecosystem, it's crucial that your NFT contract adheres to the Flow [NFT standard]. This standard defines the events, functions, resources, metadata and other elements that a contract should have. When you follow this standard, your NFTs will be compatible with various marketplaces, apps, and other services within the Flow ecosystem. ### Apply the Standard To start, you need to inform the Flow blockchain that your contract will implement the `NonFungibleToken` standard. Since it's a standard, there's no need for deployment. It's already available on the Emulator, Testnet, and Mainnet for the community's benefit. To start, import the token standard into your contract and add the correct interface conformances to FooBar, NFT, and Collection: ```cadence import "NonFungibleToken" access(all) contract FooBar: NonFungibleToken { /// Standard Paths access(all) let CollectionStoragePath: StoragePath access(all) let CollectionPublicPath: PublicPath /// Path where the minter should be stored /// The standard paths for the collection are stored in the collection resource type access(all) let MinterStoragePath: StoragePath // ...contract code access(all) resource NFT: NonFungibleToken.NFT { // ...NFT code } access(all) resource Collection: NonFungibleToken.Collection { // Make sure to update this field! access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} // ...Collection Code } // ...rest of the contract code init() { // Set the named paths self.CollectionStoragePath = /storage/fooBarNFTCollection self.CollectionPublicPath = /public/fooBarNFTCollection self.MinterStoragePath = /storage/fooBarNFTMinter self.account.storage.save(<- create NFTMinter(), to: self.MinterStoragePath) } } ``` As you can see, we also added standard paths for the Collection and Minter. These interface conformances for [NFT] and [Collection] inherit from other interfaces that provide important functionality and restrictions for your NFT and Collection types. To allow accounts to create their own collections, add a function in the main contract that creates a new `Collection` and returns it. This function takes a `nftType: Type` argument that allows the caller to specify which type of `Collection` they want to create. Contracts that implement multiple `NFT` and/or `Collection` types can use this argument, but since your contract only implements one `NFT` and `Collection` type, it can ignore the argument. You'll also want to add a simpler one directly to the `NFT` and `Collection` definitions so users can directly create a collection from a current collection: ```cadence access(all) contract FooBar: NonFungibleToken { // ...other FooBar contract code access(all) resource NFT: NonFungibleToken.NFT { // ...NFT code access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>()) } } access(all) resource Collection: NonFungibleToken.Collection { // ...other Collection code /// createEmptyCollection creates an empty Collection of the same type /// and returns it to the caller /// @return A an empty collection of the same type access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>()) } } // ...other FooBar contract code /// createEmptyCollection creates an empty Collection for the specified NFT type /// and returns it to the caller so that they can own NFTs access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { return <- create Collection() } // ...FooBar minter and init code } ``` To manage the NFTs within a collection, you'll need functions to deposit and withdraw NFTs. Here's how you can add a `deposit` function: ```cadence access(all) resource Collection: NonFungibleToken.Collection { access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} /// deposit takes a NFT and adds it to the collections dictionary /// and adds the ID to the id array access(all) fun deposit(token: @{NonFungibleToken.NFT}) { let token <- token as! @FooBar.NFT let id = token.id // add the new token to the dictionary which removes the old one let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } // ...[following code]... } ``` Similarly, you can add a `withdraw` function to remove an NFT from the collection: ```cadence access(all) resource Collection: NonFungibleToken.Collection { // ...[deposit code]... /// withdraw removes an NFT from the collection and moves it to the caller access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("FooBar.Collection.withdraw: Could not withdraw an NFT with ID " .concat(withdrawID.toString()) .concat(". Check the submitted ID to make sure it is one that this collection owns.")) return <-token } // ...[createEmptyCollection code]... } ``` As you can see, this function has an `access(NonFungibleToken.Withdraw)` access modifier. This is an example of entitlements in Cadence. [Entitlements] are a way for developers to restrict access to privileged fields and functions in a composite type like a resource when a reference is created for it. In this example, the `withdraw()` function is always accessible to code that controls the full `Collection` object, but if a reference is created for it, the `withdraw()` function can only be called if the reference is authorized by the owner with `NonFungibleToken.Withdraw`, which is a [standard entitlement] defined by the `NonFungibleToken` contract: ```cadence // Example of an authorized entitled reference to a NonFungibleToken.Collection ``` It's important to understand entitlements because they protect privileged functionality in your resource objects from third-party access. We recommended that you read the [Entitlements] documentation to understand how to use the feature properly. [References] can be freely up-casted and down-casted in Cadence, so it is important for you to use an entitlement to protect privileged functionality so that it can only be accessed if it is authorized. ### Standard NFT Events Many projects rely on events the signal when withdrawals or deposits happen. Luckily, the `NonFungibleToken` standard handles the definition and emission of events for projects, so there is no need for you to add any events to your implementation for withdraw and deposit. Here are the `FungibleToken` event definitions: ```cadence /// Event that is emitted when a token is withdrawn, /// indicating the type, id, uuid, the owner of the collection that it was withdrawn from, /// and the UUID of the resource it was withdrawn from, usually a collection. /// /// If the collection is not in an account's storage, `from` will be `nil`. /// access(all) event Withdrawn(type: String, id: UInt64, uuid: UInt64, from: Address?, providerUUID: UInt64) /// Event that emitted when a token is deposited to a collection. /// Indicates the type, id, uuid, the owner of the collection that it was deposited to, /// and the UUID of the collection it was deposited to /// /// If the collection is not in an account's storage, `from`, will be `nil`. /// access(all) event Deposited(type: String, id: UInt64, uuid: UInt64, to: Address?, collectionUUID: UInt64) ``` These events are emitted by the [`Collection` interface] in the `NonFungibleToken` contract whenever the relevant function is called on any implementation. There is also a `NonFungibleToken.NFT.ResourceDestroyed` event that is emitted every time an NFT is destroyed: ```cadence /// Event that is emitted automatically every time a resource is destroyed /// The type information is included in the metadata event so it is not needed as an argument access(all) event ResourceDestroyed(id: UInt64 = self.id, uuid: UInt64 = self.uuid) ``` `ResourceDestroyed` events are standard events that can be added to any resource definition to be emitted when the resource is destroyed. Learn more about them [in the Cadence docs]. Additionally, check out the optional [`Burner` contract], which is the standard that all projects should use to handle any resource's destruction. Lastly, there is a [standard `NonFungibleToken.Updated` event] that your contract can emit if the NFT is updated in any way. This is optional though, so no need to include support for it in your implementation. To facilitate querying, you'll also want a function to retrieve important information from the collection, like what types it supports and all the NFT IDs within a collection: ```cadence access(all) resource Collection: NonFungibleToken.Collection { // ...[withdraw code]... /// getIDs returns an array of the IDs that are in the collection access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts access(all) view fun getSupportedNFTTypes(): {Type: Bool} { let supportedTypes: {Type: Bool} = {} supportedTypes[Type<@FooBar.NFT>()] = true return supportedTypes } /// Returns whether or not the given type is accepted by the collection /// A collection that can accept any type should just return true by default access(all) view fun isSupportedNFTType(type: Type): Bool { return type == Type<@FooBar.NFT>() } // ...[createEmptyCollection code]... } ``` ### Supporting NFT metadata The Non-Fungible Token standard also enforces that implementations provide functionality to return a set of standard views about the tokens via the [ViewResolver] and [MetadataViews] definitions. (You will need to add these imports to your contract) These provide developers with standard ways of representing metadata about a given token such as token symbols, images, royalties, editions, website links, and standard account paths and types that third-parties can access in a standard way. You can see the [metadata views documentation] for a more thorough guide that uses a NFT contract as an example. For now, you can add this code to your contract to support the important metadata: ```cadence // Add this import! import "MetadataViews" access(all) contract FooBar: NonFungibleToken { // ...other FooBar contract code access(all) resource NFT: NonFungibleToken.NFT { // ...other NFT code /// Gets a list of views specific to the individual NFT access(all) view fun getViews(): [Type] { return [ Type(), Type(), Type(), Type(), Type() ] } /// Resolves a view for this specific NFT access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return MetadataViews.Display( name: "FooBar Example Token", description: "An Example NFT Contract from the Flow NFT Guide", thumbnail: MetadataViews.HTTPFile( url: "Fill this in with a URL to a thumbnail of the NFT" ) ) case Type(): // There is no max number of NFTs that can be minted from this contract // so the max edition field value is set to nil let editionInfo = MetadataViews.Edition(name: "FooBar Edition", number: self.id, max: nil) let editionList: [MetadataViews.Edition] = [editionInfo] return MetadataViews.Editions( editionList ) case Type(): return MetadataViews.Serial( self.id ) case Type(): return FooBar.resolveContractView(resourceType: Type<@FooBar.NFT>(), viewType: Type()) case Type(): return FooBar.resolveContractView(resourceType: Type<@FooBar.NFT>(), viewType: Type()) } return nil } } access(all) resource Collection: NonFungibleToken.Vault { // ...[getIDs code]... /// Allows a caller to borrow a reference to a specific NFT /// so that they can get the metadata views for the specific NFT access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { return &self.ownedNFTs[id] } // ...[rest of code]... } /// Gets a list of views for all the NFTs defined by this contract access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type(), Type() ] } /// Resolves a view that applies to all the NFTs defined by this contract access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { switch viewType { case Type(): let collectionData = MetadataViews.NFTCollectionData( storagePath: self.CollectionStoragePath, publicPath: self.CollectionPublicPath, publicCollection: Type<&FooBar.Collection>(), publicLinkedType: Type<&FooBar.Collection>(), createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} { return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>()) }) ) return collectionData case Type(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "Add your own SVG+XML link here" ), mediaType: "image/svg+xml" ) return MetadataViews.NFTCollectionDisplay( name: "The FooBar Example Collection", description: "This collection is used as an example to help you develop your next Flow NFT.", externalURL: MetadataViews.ExternalURL("Add your own link here"), squareImage: media, bannerImage: media, socials: { "twitter": MetadataViews.ExternalURL("Add a link to your project's twitter") } ) } return nil } } ``` If you ever plan to make your NFTs more complex, you could add views for `Edition`, `EVMBridgedMetadata`, `Traits`, and `Royalties`. These views make it much easier for third-party sites like marketplaces and NFT information aggregators to clearly display information about your projects on their apps and websites and are critical for every project to include if we want to have a vibrant and interoperable ecosystem. ## Flow VM bridge NFTs Flow provides an EVM environment where projects can deploy their solidity smart contracts as an easier on-ramp to building on Flow. The [Cross-VM Bridge] allows the movement of fungible and non-fungible tokens between Flow-Cadence & Flow-EVM. Learn about how you can [bridge your NFTs to Flow-EVM] and how you can [build your NFT project] to be compatible with the Flow VM bridge. ## Deploy the Contract With your contract ready, it's time to deploy it. First, add the `FooBar` contract to the `flow.json` configuration file: ```bash flow config add contract ``` When prompted, enter the following name and location (press `Enter` to skip alias questions): ``` Enter name: FooBar Enter contract file location: cadence/contracts/FooBar.cdc ``` Next, configure the deployment settings by running the following command: ```bash flow config add deployment ``` Choose the `emulator` for the network and `emulator-account` for the account to deploy to. Then, select the `FooBar` contract (you may need to scroll down). This will update your `flow.json` configuration. After that, you can select `No` when asked to deploy another contract. To start the Flow emulator, run: ```bash flow emulator start ``` :::info you may need to approve a prompt to allow connection the first time ::: In a separate terminal or command prompt, deploy the contract: ```bash flow project deploy ``` You'll then see a message that says `All contracts deployed successfully`. ## Create an NFTCollection To manage multiple NFTs, you'll need an NFT collection. To start, create a transaction file for this purpose (we can use the `generate` command again): ```bash flow generate transaction setup_foobar_collection ``` This creates a transaction file at `cadence/transactions/setup_foobar_collection.cdc`. Transactions, on the other hand, are pieces of Cadence code that can mutate the state of the blockchain. Transactions need to be signed by one or more accounts, and they can have multiple phases, represented by different blocks of code. In this file, import the necessary contracts and define a transaction to create a new collection, and store it in the account's storage. Additionally, the transaction creates a capability that allows others to get a public reference to the collection to read from its methods. This capability ensures secure, restricted access to specific functionalities or information within a resource. ```cadence import "FooBar" import "NonFungibleToken" transaction { prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) { // Return early if the account already has a collection if signer.storage.borrow<&FooBar.Collection>(from: FooBar.CollectionStoragePath) != nil { return } // Create a new empty collection let collection <- FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>()) // save it to the account signer.storage.save(<-collection, to: FooBar.CollectionStoragePath) let collectionCap = signer.capabilities.storage.issue<&FooBar.Collection>(FooBar.CollectionStoragePath) signer.capabilities.publish(collectionCap, at: FooBar.CollectionPublicPath) } } ``` There are also examples of [generic transactions] that you can use to setup an account for ANY non-fungible token using metadata views! You should check those out and try to use generic transactions whenever it is possible. To store this new NFT collection, create a new account: ```bash flow accounts create ``` Name it `test-acct` and select `emulator` as the network. Then, using the Flow CLI, run the transaction: ```bash flow transactions send cadence/transactions/setup_foobar_collection.cdc --signer test-acct --network emulator ``` Congratulations! You've successfully created an NFT collection for the `test-acct`. ## Get an Account's NFTs To retrieve the NFTs associated with an account, you'll need a script. Scripts are read-only operations that allow you to query the blockchain. They don't modify the blockchain's state, and therefore, they don't require compute unit fees or signatures. To start, create a script file with the `generate` command again: ```bash flow generate script get_foobar_ids ``` In this script, import the necessary contracts and define a function that retrieves the NFT IDs associated with a given account: ```cadence import "NonFungibleToken" import "FooBar" access(all) fun main(address: Address): [UInt64] { let account = getAccount(address) let collectionRef = account.capabilities.borrow<&{NonFungibleToken.Collection}>( FooBar.CollectionPublicPath ) ?? panic("The account ".concat(address.toString()).concat(" does not have a NonFungibleToken Collection at ") .concat(FooBar.CollectionPublicPath.toString()) .concat(". The account must initialize their account with this collection first!")) return collectionRef.getIDs() } ``` To check the NFTs associated with the `test-acct`, run the script (note: replace `0x123` with the address for `test-acct` from `flow.json`): ```bash flow scripts execute cadence/scripts/get_foobar_ids.cdc 0x123 ``` Since you haven't added any NFTs to the collection yet, the result will be an empty array. ## Mint and Deposit an NFT to a Collection To mint and deposit an NFT into a collection, create a new transaction file: ```bash flow generate transaction mint_foobar_nft ``` In this file, define a transaction that takes a recipient's address as an argument. This transaction will borrow the minting capability from the contract account, borrow the recipient's collection capability, create a new NFT with the minter, and deposit it into the recipient's collection: ```cadence import "NonFungibleToken" import "FooBar" transaction( recipient: Address ) { /// local variable for storing the minter reference let minter: &FooBar.NFTMinter /// Reference to the receiver's collection let recipientCollectionRef: &{NonFungibleToken.Receiver} prepare(signer: auth(BorrowValue) &Account) { // borrow a reference to the NFTMinter resource in storage self.minter = signer.storage.borrow<&FooBar.NFTMinter>(from: FooBar.MinterStoragePath) ?? panic("The signer does not store a FooBar Collection object at the path " .concat(FooBar.CollectionStoragePath.toString()) .concat("The signer must initialize their account with this collection first!")) // Borrow the recipient's public NFT collection reference self.recipientCollectionRef = getAccount(recipient).capabilities.borrow<&{NonFungibleToken.Receiver}>( FooBar.CollectionPublicPath ) ?? panic("The account ".concat(recipient.toString()).concat(" does not have a NonFungibleToken Receiver at ") .concat(FooBar.CollectionPublicPath.toString()) .concat(". The account must initialize their account with this collection first!")) } execute { // Mint the NFT and deposit it to the recipient's collection let mintedNFT <- self.minter.createNFT() self.recipientCollectionRef.deposit(token: <-mintedNFT) } } ``` To run this transaction, use the Flow CLI. Remember, the contract account (which has the minting resource) should be the one that signs the transaction. Pass the test account's address (from the `flow.json` file) as the recipient argument: :info replace `0x123` with the address for `test-acct` from `flow.json`) ::: ```bash flow transactions send cadence/transactions/mint_foobar_nft.cdc 0x123 --signer emulator-account --network emulator ``` After you execute the transaction, run the earlier script to verify that the NFT was added to the `test-acct`'s collection (remember to replace `0x123`): ```bash flow scripts execute cadence/scripts/get_foobar_ids.cdc 0x123 ``` You will now see a value in the `test-acct`'s collection array! ## Transfer an NFT to Another Account To transfer an NFT to another account, create a new transaction file with `generate`: ```bash flow generate transaction transfer_foobar_nft ``` In this file, define a transaction that takes a recipient's address and the ID of the NFT you want to transfer as arguments. This transaction will borrow the sender's collection, get the recipient's capability, withdraw the NFT from the sender's collection, and deposit it into the recipient's collection: ```cadence import "FooBar" import "NonFungibleToken" transaction(recipient: Address, withdrawID: UInt64) { /// Reference to the withdrawer's collection let withdrawRef: auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection} /// Reference of the collection to deposit the NFT to let receiverRef: &{NonFungibleToken.Receiver} prepare(signer: auth(BorrowValue) &Account) { // borrow a reference to the signer's NFT collection self.withdrawRef = signer.storage.borrow( from: FooBar.CollectionStoragePath ) ?? panic("The signer does not store a FooBar Collection object at the path " .concat(FooBar.CollectionStoragePath.toString()) .concat("The signer must initialize their account with this collection first!")) // get the recipients public account object let recipient = getAccount(recipient) // borrow a public reference to the receivers collection let receiverCap = recipient.capabilities.get<&{NonFungibleToken.Receiver}>(FooBar.CollectionPublicPath) self.receiverRef = receiverCap.borrow() ?? panic("The account ".concat(recipient.toString()).concat(" does not have a NonFungibleToken Receiver at ") .concat(FooBar.CollectionPublicPath.toString()) .concat(". The account must initialize their account with this collection first!")) } execute { let nft <- self.withdrawRef.withdraw(withdrawID: withdrawID) self.receiverRef.deposit(token: <-nft) } } ``` To transfer the NFT, first create a new account: ```bash flow accounts create ``` Name it `test-acct-2` and select `Emulator` as the network. Next, create a collection for this new account: ```bash flow transactions send cadence/transactions/setup_foobar_collection.cdc --signer test-acct-2 --network emulator ``` Now, run the transaction to transfer the NFT from `test-acct` to `test-acct-2` with the addresses from the `flow.json` file (replace `0x124` with `test-acct-2`'s address. Also note that `0` is the `id` of the `NFT` we'll be transferring): ```bash flow transactions send cadence/transactions/transfer_foobar_nft.cdc 0x124 0 --signer test-acct --network emulator ``` To verify the transfer, you can run the earlier script for `test-acct-2` (replace `0x124`): ```bash flow scripts execute cadence/scripts/get_foobar_ids.cdc 0x123 ``` The transfer transaction also has a [generic version] that developers are encouraged to use! Congrats, you did it! You're now ready to launch the next fun NFT project on Flow. ## More - Explore an [example NFT repository] - Dive into the details of the [NFT Standard] - Check out the [`Burner` contract], which is the standard that all projects should use for handling the destruction of any resource. - For a deeper dive into `MetadataViews`, consult the [introduction guide] or [the FLIP that introduced this feature]. - Learn about how you can [bridge your NFTs to Flow-EVM]and how you can [build your FT project] to be compatible with the Flow VM bridge. - Use a no code tool to create NFT projects on Flow with [Touchstone]. [a link to the completed code]: https://github.com/onflow/foobar-nft [bridge your NFTs to Flow-EVM]: ../cross-vm-apps/vm-bridge.md#cross-vm-bridge [`Burner` contract]: ../../build/cadence/core-contracts/14-burner.md [build your NFT project]: ../cross-vm-apps/vm-bridge.md#prep-your-assets-for-bridging [in the Cadence docs]: https://cadence-lang.org/docs/language/resources#destroy-events [`Collection` interface]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc#L202 [Collection]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc#L190 [Cross-VM Bridge]: https://www.github.com/onflow/flow-evm-bridge [Entitlements]: https://cadence-lang.org/docs/language/access-control#entitlements [example NFT repository]: https://github.com/nvdtf/flow-nft-scaffold/blob/main/cadence/contracts/exampleNFT/ExampleNFT.cdc [Flow CLI installation guide]: ../../build/tools/flow-cli/install.md [generic version]: https://github.com/onflow/flow-nft/blob/master/transactions/generic_transfer_with_address.cdc [generic transactions]:https://github.com/onflow/flow-nft/blob/master/transactions/setup_account_from_address.cdc [Homebrew]: https://brew.sh/ [introduction guide]: ../../build/cadence/advanced-concepts/metadata-views.md [metadata views documentation]: ../../build/cadence/advanced-concepts/metadata-views.md [MetadataViews]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc [NFT Standard]: https://github.com/onflow/flow-nft [References]: https://cadence-lang.org/docs/language/references [Resources]: https://cadence-lang.org/docs/language/resources [NFT]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc#L98) [standard entitlement]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc#L58 [standard `NonFungibleToken.Updated` event]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc#L63-L77 [the FLIP that introduced this feature]: https://github.com/onflow/flips/blob/main/application/20210916-nft-metadata.md. [Touchstone]: https://www.touchstone.city/ [ViewResolver]: https://github.com/onflow/flow-nft/blob/master/contracts/ViewResolver.cdc --- # File: /md/blockchain-development-tutorials/tokens/register-cadence-assets.md --- title: Register Your Assets on Flow description: 'Register your Fungible Token or Non-Fungible Token on Flow so it appears in Flow Wallet, IncrementFi, and other ecosystem apps.' sidebar_label: Register Cadence Assets sidebar_position: 3 keywords: - Fungible Token - Non-Fungible Token - Flow - Flow Wallet - IncrementFi - Token List - Register Fungible Token - Register Non-Fungible Token - Flow Official Assets --- # Register Your Assets on Flow To make your fungible token or non-fungible token visible in Flow ecosystem projects like **Flow Wallet** and **IncrementFi**, you need to register it on the Flow Token List. This process will generate JSON files based on the Uniswap TokenList Standard and ensures that wallets, explorers, and onchain apps can recognize and display your token correctly. There are two ways to register your token: **manually** via a web interface or **programmatically** during token deployment. --- ## Manual registration (~1 min) The following works for both fungible and non-fungible tokens on Flow Cadence or Flow EVM. 1. **Go to** [Token List Registration]. 2. **Enter your contract address** in the **"Quick Register"** field and press **Enter**. - Both Fungible and Non-Fungible tokens are supported. - Both EVM and Cadence contracts are supported. 3. **Click "Register"** and sign the transaction. - If your token is **already registered to VM Bridge**, you're done. - Otherwise, the **first transaction** registers the token to **VM Bridge** (costs **1 $FLOW**). - After that, click **"Register"** again and sign the **second transaction** to finalize the process. :::warning ERC-20 tokens registered with this method will use the default logo of Flow Official Assets. If you want to register your ERC-20 token on Flow EVM with your customized logo, follow the [Register ERC-20 Token on Flow EVM] guide. ::: --- ## Programmatic registration For seamless automation, you can integrate token registration into your token deployment workflow. You can use this Cadence transaction to register your Fungible or Non-Fungible token on Flow Cadence or Flow EVM. ### Register Fungible Token or Non-Fungible Token automatically on Flow Cadence Use a standalone Cadence transaction to register your Fungible Token or Non-Fungible Token on Flow Cadence. - Use this **Cadence transaction**: [register-standard-asset.cdc]. - Execute this transaction **right after you deploy your Fungible Token or Non-Fungible Token contract**. Or, you can also pick up some code from the [register-standard-asset.cdc] file to make your own Cadence transaction with the same logic for more seamless integration. Here is an example: ```cadence import "TokenList" import "NFTList" transaction( address: Address, contractName: String, ) { execute { if TokenList.isValidToRegister(address, contractName) { TokenList.ensureFungibleTokenRegistered(address, contractName) } else if NFTList.isValidToRegister(address, contractName) { NFTList.ensureNFTCollectionRegistered(address, contractName) } } } ``` ### Register ERC-20 or ERC-721 tokens automatically on Flow EVM - Use this **Cadence transaction**: [register-evm-asset.cdc] - Execute this transaction **right after you deploy your ERC-20 or ERC-721 contract**. :::warning Similar to manual registration: - If the token **is not bridged** to **VM Bridge**, you will need to **send the transaction twice**. - The **first transaction** deploys a **VM Bridged Cadence contract** for the ERC-20. - The **second transaction** registers it on the Token List. ::: --- ### Next Steps - Verify your token listing in Flow Wallet. For any issues, refer to the [Token List GitHub Repository] or reach out to the [Flow developer community]. [Token List Registration]: https://token-list.fixes.world/ [Register ERC-20 Token on Flow EVM]: ./register-erc20-token.md [register-standard-asset.cdc]: https://github.com/fixes-world/token-list/blob/main/cadence/transactions/register-standard-asset.cdc [register-evm-asset.cdc]: https://github.com/fixes-world/token-list/blob/main/cadence/transactions/register-evm-asset.cdc [Token List GitHub Repository]: https://github.com/fixes-world/token-list [Flow developer community]: https://discord.gg/flow --- # File: /md/blockchain-development-tutorials/tokens/register-erc20-token.md --- title: Register Your ERC20 Token on Flow EVM description: 'Register your ERC20 token on Flow EVM so it appears in Flow Wallet, MetaMask, and other ecosystem apps.' sidebar_label: Register ERC20 Token sidebar_position: 4 keywords: - ERC20 - Fungible Token - Flow EVM - Flow Wallet - MetaMask - Token List - Register ERC20 Token - Flow Official Assets --- # Register Your ERC20 Token on Flow EVM
    ## Overview This section covers how to register your ERC20 token on Flow EVM via a Github Pull Request process so it appears in Flow standard Token List, which Flow Wallet, MetaMask, and other ecosystem apps use. We will use the [Flow Official Assets] repository as the standard token list repository to update the token list for the whole Flow ecosystem. The repository is open to the public and you can submit your PRs to add your token to the list. :::info The logic of the registration is based on the [Register Assets in Cadence] backend process. ::: ## Guides for how to submit your PRs Steps to submit your PRs: 1. **Fork the [Flow Official Assets] repository** - Click `Fork` in the top right corner of the repository. - Create a new fork of the repository in your own Github account. 2. **Create a new branch** - Clone your forked repository to your local development environment by `git clone https://github.com/your-github-username/assets` - Create a new branch for your token by `git checkout -b new-token-branch` 3. **Add or update your token to the list** - For new Tokens: - Create the token folders in the `tokens/registry` directory. - The name of the token folders must be the same as the token's contract address. - For example, `tokens/registry/0x1234567890123456789012345678901234567890` - for Testnet tokens, the folder should be `tokens/registry/testnet:0x1234567890123456789012345678901234567890` - Put the required metadata file in the token folder, include at least one of the following files: - `logo.png`: PNG format token logo (256x256px recommended) - `logo.svg`: SVG format token logo, optimized and viewboxed - You can also add extra optional metadata file: - `mods.json`: Mods JSON file for token metadata, you can adjust the `symbol`, `name`, `description` for the final output in the `token.json` file. - For current Tokens: - Identify the token folder in the `tokens/registry` directory by the token's contract address. - Update the token metadata in the `tokens/registry/${token_address}` directory. 4. **Submit a Pull Request** - Commit your changes and push to your forked repository. - Create a new Pull Request (PR) for your changes in the [Flow Official Assets] repository. - A Github Action will be triggered to verify the onchain status of the token and update the report in the PR's comment. - If there is any issue, you will see some warnings and suggestions in the PR's comment. Check the report and update the token metadata if needed. - You may see a comment from the Github Action that you need to send 1 $FLOW to the registry address for the token registration because there is a VM Bridge onboarding fee. Learn more about the registration process in the [Assets Registry] README.md file of the repository. ## What's next? After you submit your PR, you just need to wait for the Flow team to review your token and merge your PR. After the PR is merged, your token will be registered by the Github Actions in the [Flow Official Assets] repository automatically and Github Actions automatically creates a new PR to update the token list. The Flow team will regularly merge the token list updates PR to the main branch. ## How to verify the token is registered As Github Actions executes the registration and token list generation, you can check the status of the PRs and the token list JSON files in the [Flow Official Assets] repository. Here are the URLs for the token list JSON files: - Mainnet: `https://raw.githubusercontent.com/onflow/assets/refs/heads/main/tokens/outputs/mainnet/token-list.json` - Testnet: `https://raw.githubusercontent.com/onflow/assets/refs/heads/main/tokens/outputs/testnet/token-list.json` You can check the token list JSON files to verify the token is registered in the `token-list.json` file. [Flow Official Assets]: https://github.com/onflow/assets [Register Assets in Cadence]: ./register-cadence-assets.md [Assets Registry]: https://github.com/onflow/assets/tree/main/tokens --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow.md --- title: Use AI To Build On Flow description: Learn how to use AI to build on the Flow Blockchain sidebar_position: 4 keywords: - AI - ChatGPT - Cursor - Cursor Rules - Claude - AgentKit - Flow documentation - Cadence documentation - Flow development - Flow tools - Flow IDE - Flow setup - Flow configuration - Flow AI assistance --- # Use AI To Build On Flow Artificial Intelligence (AI) tools can significantly enhance your Flow development experience with intelligent assistance, code generation, and documentation access. This tutorial series will guide you through how to integrate various AI tools with Flow development to boost your productivity and code quality. ## What You'll Learn In this tutorial series, you'll discover how to: - Configure AI-powered development environments for Flow. - Access Flow documentation directly from AI assistants. - Generate Cadence and Solidity code with AI assistance. - Debug and troubleshoot Flow applications with AI support. - Leverage AI for testing and optimization. - Build AI agents that interact with Flow using AgentKit. # AI tutorials for Flow ## Use Claude Code with Flow Master systematic AI-powered Flow development with Claude Code, a terminal-integrated coding assistant designed for iterative blockchain development. This comprehensive guide teaches you to implement a four-stage development methodology (Idea → Visualization → Planning → Build) while you leverage unlimited context windows, subagent capabilities, and persistent project memory. Learn to configure `CLAUDE.md` files for Flow-specific instructions, integrate MCP servers for blockchain interactions, and implement checkpoint-based workflows that ensure reliable smart contract development from emulator to mainnet deployment. Tutorial: [Claude Code for Flow Development] ## Use Cursor with Flow This guide details how you can set up the Cursor AI code editor with custom Flow knowledge bases, which transforms it into a specialized assistant to build powerful applications on the Flow network. When you provide the AI with direct access to the official Flow documentation, Cadence language references, and best-practice examples, you unlock a new tier of intelligent assistance that goes far beyond simple autocompletion Tutorial: [Use Flow Knowledge Base in Cursor] ## Use ChatGPT with Flow Build your own expert AI assistant and create a custom GPT specifically engineered to master the Flow blockchain and its Cadence smart contract language. This specialized tool will act as your personal pair programmer and provide highly accurate and context-aware answers to your most challenging development questions. By doing this, you don't just use a generic AI, you create a specialist trained on the exact documentation, code patterns, and best practices relevant to your work. Tutorial: [Use Flow Knowledge Base in ChatGPT] ## Flow Data Sources Learn about Flow Data Sources, a meticulously curated library designed to autonomously gather and structure information from the entire Flow ecosystem. This project systematically transforms a wide array of content into clean, AI-ready Markdown files, which establishes a unified source of truth. This collection acts as a foundational knowledge base, perfectly suited to power advanced applications such as custom chatbots and sophisticated Retrieval-Augmented Generation (RAG) systems. Tutorial: [Data Sources] ## Eliza integration Learn about how to use Eliza on Flow, a versatile framework you can use to construct sophisticated AI agents that communicate with users through natural language. This guide walks you through how to configure and launch an AI agent built with Eliza directly onto the Flow blockchain. You'll discover how to engineer intelligent agents that can comprehend and address user prompts, all while you harness the power of Flow's inherently secure and scalable onchain infrastructure. Tutorial: [Eliza on Flow] ## Build AI agents with AgentKit Learn how to build AI agents on Flow with AgentKit, a versatile and modular developer toolkit that is not tied to any single platform. It's engineered to dramatically accelerate the process of building, deploying, and refining AI agents by supplying pre-configured environments and a library of ready-to-use templates. This guide walks you through how to launch your own custom agent on Flow's EVM-compatible testnet, which lets you leverage the powerful combination of the Langchain framework and Anthropic's Claude large language model. Tutorial: [Build AI Agents with AgentKit] ## MCP guides Learn how to construct a custom Flow MCP (Model Context Protocol) server or use a current one to empower your AI tools. These tutorials guide you through how to equip your AI applications with the unique capability to directly interact with the Flow blockchain, which allows them to perform onchain operations and access real-time data. Tutorial: [Flow MCP] ## Cadence rules Learn how to establish and use Cursor Rules to transform your AI assistant into a dedicated Flow development expert. This process embeds your AI with persistent, foundational knowledge of essential topics, such as proper Cadence syntax, official NFT standards, project-specific configurations, and established development methodologies. Tutorial: [Cadence Rules] ## Best practices When you use AI tools with Flow development: - Always verify AI-generated code against Flow documentation. - Use specific prompts that reference Flow concepts and terminology. - Combine AI assistance with your own knowledge of Flow architecture. - Keep your AI tools updated with the latest Flow documentation. - Test AI-generated code thoroughly before you deploy to production. - Consider the security implications of AI agents that interact with your contracts. ## Next steps After you complete these tutorials, you'll be equipped to leverage AI tools effectively in your Flow development workflow. We recommend that you explore our other tutorial series to deepen your knowledge of Flow development: - [Cross-VM Apps][cross-vm-apps] - Build applications that integrate Flow EVM and Cadence. - [Native VRF][native-vrf] - Implement verifiable random functions in your applications. - [Token Launch][token-launch] - Create and launch tokens on Flow. ## Conclusion Flow is the ideal platform for AI-enhanced blockchain development. The combination of Cadence's resource-oriented programming model, comprehensive AI ingestable documentation, and growing AI tooling support creates an unparalleled development experience. With tools like AgentKit, MCP servers, and AI-powered development environments, developers can build consumer applications faster than ever. This is why many believe that Flow is the best Blockchain to build on with AI. [Claude Code for Flow Development]: ./llms/claude-code.md [Use Flow Knowledge Base in Cursor]: ./cursor/index.md [Use Flow Knowledge Base in ChatGPT]: ./llms/chatgpt.md [Data Sources]: ./cursor/flow-data-sources.md [Eliza on Flow]: ./agents/eliza/index.md [Build AI Agents with AgentKit]: ./agents/agentkit-flow-guide.md [cross-vm-apps]: ../cross-vm-apps/introduction.md [native-vrf]: ../native-vrf/index.md [token-launch]: ../tokens/index.md [Flow MCP]: ./mcp/index.md [Cadence Rules]: ./cursor/cadence-rules.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/agents.md --- title: AI Agents description: Learn how to build and deploy intelligent AI agents that can interact with the Flow blockchain, execute smart contracts, and automate complex workflows using modern AI frameworks. sidebar_position: 3 keywords: - AI agents - AgentKit - autonomous agents - Flow blockchain - smart contracts - automation - Langchain - AI workflows - blockchain interactions --- # AI Agents AI agents represent the next evolution in blockchain interaction. They combine artificial intelligence with blockchain capabilities to create autonomous systems that can execute complex tasks, interact with smart contracts, and automate workflows. This section explores how to build intelligent agents that leverage Flow's unique features to create powerful, self-directed blockchain applications. Unlike traditional chatbots or simple AI assistants, blockchain AI agents can autonomously execute transactions, manage digital assets, interact with DeFi protocols, and coordinate complex multi-step operations across the Flow ecosystem. These agents bridge the gap between AI decision-making and blockchain execution, which allows new forms of automated financial services, gaming mechanics, and decentralized applications. ## [Eliza on Flow] Learn how to build conversational AI agents on Flow with [Eliza on Flow], a framework for that you can use to create intelligent agents that interact through natural language. Set up agents with customizable personalities, integrate multiple AI models, and connect to Flow's blockchain infrastructure. ### [Eliza Plugin Development Guide] Extend your Eliza agents with custom plugins for specialized functionality. Create plugin repositories, implement custom actions, and publish to the Eliza Plugin Registry for community sharing. ## [Build Custom AI Agents on Flow with AgentKit] Create autonomous AI agents that interact with Flow's blockchain with AgentKit. Set up agents on Flow's EVM-compatible environment, integrate AI models like Claude and GPT-4, and deploy systems that execute blockchain transactions automatically. ## Key benefits of AI agents on Flow **Autonomous Execution**: Agents can independently execute complex blockchain operations without human intervention, from simple token transfers to sophisticated decentralized finance (DeFi) strategies. **Flow-Native Integration**: Leverage Flow's unique multi-role architecture, built-in randomness, and efficient transaction processing for enhanced agent capabilities. **EVM Compatibility**: Build agents that work seamlessly with both Flow's native Cadence environment and EVM-compatible tools and frameworks. **Intelligent Decision Making**: Combine AI reasoning with real-time blockchain data to make informed decisions about transactions, asset management, and protocol interactions. **Scalable Automation**: Create agents that can manage multiple wallets, coordinate complex workflows, and scale operations across the Flow ecosystem. ## Use cases for AI agents **Conversational Interfaces**: Build Eliza-powered agents that can provide natural language interfaces to Flow applications, which helps users navigate complex DeFi protocols or gaming mechanics through conversation. **Automated Trading**: Build agents that can analyze market conditions, execute trades, manage portfolios across Flow's DeFi ecosystem, and communicate decisions to users. **Gaming Automation**: Create intelligent NPCs or automated players that can interact with Flow-based games, manage in-game assets, and execute complex game mechanics with personality-driven responses. **Portfolio Management**: Develop agents that can automatically rebalance portfolios, compound yields, optimize asset allocation based on market conditions, and provide conversational feedback. **Protocol Automation**: Build agents that can interact with complex DeFi protocols, execute arbitrage opportunities, and manage liquidity positions with intelligent decision-making capabilities. **Asset Management**: Create agents that can automatically mint, transfer, and manage NFTs based on predefined conditions or AI-driven decisions, with customizable personalities and interaction styles. ## Conclusion AI agents combine artificial intelligence with blockchain capabilities to create autonomous systems on Flow. Whether you want to build conversational agents with Eliza or autonomous systems with AgentKit, these tutorials provide the foundation for you to create intelligent blockchain applications that can execute transactions, engage users, and automate complex workflows across the Flow ecosystem. [Eliza on Flow]: ./eliza/index.md [Eliza Plugin Development Guide]: ./eliza/build-plugin.md [Build Custom AI Agents on Flow with AgentKit]: ./agentkit-flow-guide.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/agents/agentkit-flow-guide.md --- title: Build Custom AI Agents on Flow with AgentKit description: Learn how to configure and deploy AI agents on the Flow testnet using AgentKit, Langchain, and the EVM-compatible Flow environment. sidebar_label: Using AgentKit on Flow sidebar_position: 2 --- # Build Custom AI Agents on Flow with AgentKit AgentKit is an ecosystem-agnostic modular developer toolkit that lets you rapidly build, deploy, and iterate on AI agents using pre-configured environments and ready-to-use templates. In this guide, you'll set up your own custom agent that runs on **Flow's EVM-compatible testnet**, powered by **Langchain** and **Anthropic's Claude** LLM. --- ## Quickstart - start from scratch Open your terminal and run: ```bash npm create onchain-agent@latest ``` Follow the interactive setup: 1. Type `y` to proceed, then press **Enter**. 2. Select your framework: **Langchain** 3. Choose your network: **EVM** 4. Set the custom Chain ID: - `545` for **Flow Testnet** - `747` for **Flow Mainnet** 5. JSON-RPC endpoint: ```txt https://testnet.evm.nodes.onflow.org ``` --- ## Project setup When your scaffold is ready: ```bash cd onchain-agent npm install ``` Now open the project in your preferred integrated development environment (IDE) (for example, [Cursor]. ### Environment Configuration 1. Create a `.env.local` file (or edit the one that you generated). 2. Add your API keys (we'll use **Anthropic** here). > You can also use OpenAI, DeepSeek, or any other supported LLM. ### Get Your Anthropic API Key - Head to [Anthropic Console]. - Create an account and **purchase credits**. - Click **Create Key**, name it, and copy the API key. - Add this to your `.env.local`: ```env ANTHROPIC_API_KEY=your_api_key_here ``` ### Wallet setup with MetaMask 1. Add [Flow Testnet] to MetaMask. 2. Use the [Faucet] to fund your wallet. 3. Get your private key: - Click the `...` menu in MetaMask > **Account Details**. - Enter your password, copy the private key. 4. Add it to `.env.local`: ```env PRIVATE_KEY=your_private_key_here ``` Your `.env.local` should look something like this: ```env PRIVATE_KEY=... ANTHROPIC_API_KEY=... ``` Now run: ```bash mv .env.local .env npm run dev ``` Visit your local server: ``` http://localhost:3000 ``` --- ## Configure Your LLM If your agent doesn't respond yet, no worries! You still need to configure your **LLM and client libraries**. ### Choose a model Langchain supports many LLMs ([full list here]). For this example, we'll use **Anthropic's `claude-3-5-haiku-20241022`**, a lightweight and affordable model. Alternatively, [DeepSeek] is highly recommended for budget-friendly usage. ### Update `create-agent.ts` Change the default model from OpenAI: ```ts const llm = new ChatOpenAI({ model: 'gpt-4o-mini' }); ``` To Anthropic: ```ts import { ChatAnthropic } from '@langchain/anthropic'; const llm = new ChatAnthropic({ model: 'claude-3-5-haiku-20241022' }); ``` Install the package: ```bash npm install @langchain/anthropic ``` --- ## Configure Flow and Viem wallet ### Update the Faucet provider logic Change this: ```ts const canUseFaucet = walletProvider.getNetwork().networkId == 'base-sepolia'; ``` To: ```ts const canUseFaucet = walletProvider.getNetwork().networkId == 'flow-testnet'; ``` ### Add Flow context message to Agent This gives your agent context about the Flow testnet: ```ts const flowContextMessage = canUseFaucet ? ` You are now operating on the Flow blockchain testnet using a Viem wallet. Flow is a fast, decentralized, and developer-friendly blockchain designed for NFTs, games, and apps. Key facts about Flow: - Flow uses a proof-of-stake consensus mechanism - The native token is FLOW - Flow has a unique multi-role architecture for high throughput - The testnet is EVM-compatible (works with MetaMask + Viem) - RPC URL: https://testnet.evm.nodes.onflow.org - Chain ID: 545 Your wallet address is \${await walletProvider.getAddress()}. ` : ''; ``` Then inject it into the agent message modifier: ```ts agent = createReactAgent({ llm, tools, checkpointSaver: memory, messageModifier: ` You are a helpful agent interacting with the Flow blockchain testnet using a Viem wallet. Flow testnet supports EVM, so you can use Ethereum-compatible tools. \${flowContextMessage} Before your first action, check the wallet details. If you see a 5XX error, ask the user to try again later. If a task is unsupported, let the user know and point them to CDP SDK + AgentKit at: https://docs.cdp.coinbase.com or https://developers.flow.com. Be concise, helpful, and avoid repeating tool descriptions unless asked. `, }); ``` --- ## You're done! You now have a working AI agent connected to Flow testnet with AgentKit! You can send faucet tokens to your wallet and start to test smart contract interactions or onchain workflows. --- ## Starter Project Want to skip the setup? > [Fork the Flow AgentKit Starter](https://github.com/Aliserag/flow-agentkit-starter) This starter includes all of the necessary configurations to start building immediately on Flow. --- ## Add AgentKit to a current project Already have a project and want to add AgentKit? Follow these steps to integrate it into your codebase: ### Install the package Run this command in your project's root directory: ```bash npm install onchain-agent@latest ``` This will: - Download and install the latest version of the `onchain-agent` package. - Add it to the dependencies section of your `package.json`. - Update your `node_modules` folder accordingly. ### Configure environment 1. Create or update your `.env` file with the necessary API keys: ```env PRIVATE_KEY=your_wallet_private_key ANTHROPIC_API_KEY=your_anthropic_api_key # Or other LLM API keys ``` 2. Configure your RPC endpoints for Flow: ```env FLOW_TESTNET_RPC_URL=https://testnet.evm.nodes.onflow.org FLOW_MAINNET_RPC_URL=https://mainnet.evm.nodes.onflow.org ``` ### Integrate AgentKit in your code Import and configure AgentKit in your application: ```ts // Import AgentKit components import { createReactAgent, ChatAnthropic } from 'onchain-agent'; import { createWalletClient, http, createPublicClient } from 'viem'; // Set up your Flow wallet provider const walletClient = createWalletClient({ transport: http('https://testnet.evm.nodes.onflow.org'), chain: { id: 545, // Flow Testnet name: 'Flow Testnet', }, account: yourPrivateKey, }); // Configure the LLM const llm = new ChatAnthropic({ model: 'claude-3-5-haiku-20241022', }); // Create your agent const agent = createReactAgent({ llm, tools: yourSelectedTools, // Additional configuration }); // Use the agent in your application // ... ``` ### Add Specialized tools (optional) To add specialized blockchain tools to your agent: ```ts import { viem, ViemToolConfig } from 'onchain-agent'; // Configure Viem tools for Flow const viemTools = viem.createTools({ chain: { id: 545, name: 'Flow Testnet', }, transport: http('https://testnet.evm.nodes.onflow.org'), } as ViemToolConfig); // Add these tools to your agent const agent = createReactAgent({ llm, tools: [ ...viemTools, // Other tools ], }); ``` --- ## Resources - [AgentKit Docs] - [Flow EVM Guide] - [Langchain LLM Integrations] - [Anthropic Model Comparison] --- Happy hacking on Flow! [Cursor]: ../cursor/index.md [Anthropic Console]: https://console.anthropic.com/dashboard [Flow Testnet]: https://developers.flow.com/evm/using [Faucet]: https://faucet.flow.com/fund-account [full list here]: https://python.langchain.com/docs/integrations/llms/ [DeepSeek]: https://deepseek.com/ [Fork the Flow AgentKit Starter]: https://github.com/Aliserag/flow-agentkit-starter [AgentKit Docs]: https://docs.cdp.coinbase.com/agent-kit/welcome [Flow EVM Guide]: https://developers.flow.com/evm/using [Langchain LLM Integrations]: https://python.langchain.com/docs/integrations/llms/ [Anthropic Model Comparison]: https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison-table --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/agents/eliza.md --- title: Eliza on Flow description: Learn how to build AI Agent on Flow with Eliza sidebar_position: 1 keywords: - AI - AI Agent - Eliza - Eliza on Flow - Flow Development - Quickstart --- # Quickstart Guide to build AI Agent on Flow with Eliza Eliza is a powerful framework you can use to build AI agents that interact with users through natural language. This tutorial will guide you through how to set up and deploy an AI agent on the Flow blockchain with Eliza. You'll learn how to create intelligent agents that can understand and respond to user queries, and leverage Flow's secure and scalable infrastructure. ## Learning objectives After you complete this tutorial, you will be able to: - Set up the Eliza development environment. - Configure and deploy an AI agent on Flow. - Create and customize character configurations. - Integrate different AI models with your agent. - Interact with your AI agent through a web interface. - Add and develop custom plugins for extended functionality. ## Prerequisites Before you get started started with Eliza, make sure you have: - [Node.js 23+] (we recommend that you use [nvm]) - [pnpm 9+] - Git for version control - A code editor (we recommend [VS Code], [Cursor] or [VSCodium]) - [Flow-cli] for Flow blockchain interaction. > **Note for Windows Uuers:** [WSL 2] is required. ## Installation ElizaOnFlow is a Flow-dedicated Eliza wrapper, so: - The plugins from this repository are also compatible with the origin [Eliza]. - You can also use any plugins from original Eliza in this repository. Clone the repository ```bash # The ElizaOnFlow is a wrapper with origin Eliza as submodule git clone --recurse-submodules https://github.com/onflow/elizaOnFlow.git # Enter directory cd elizaOnFlow # Please checkout the main branch which is using the latest release of origin Eliza git checkout main ``` Or, If you want to use the origin Eliza, run: ```bash # Eliza's characters folder is a submodule git clone --recurse-submodules https://github.com/elizaOs/eliza.git # Enter directory cd eliza # Checkout the latest release git checkout $(git describe --tags --abbrev=0) ``` If you already cloned without submodules, run: ```bash # Fetch submodules git submodule update --init --recursive ``` Install dependencies ```bash pnpm install --no-frozen-lockfile ``` :::warning Only use the `--no-frozen-lockfile` option when you initially instantiate the repo or bump the version of a package or add a new package to your `package.json` file. This practice helps maintain consistency in your project's dependencies and prevents unintended changes to the lockfile. ::: If you use ElizaOnFlow, you need to install Flow Cadence contracts dependencies to ensure that the Cadence extension correctly lints `*.cdc`. Install Flow Cadence contracts dependencies: ```bash flow deps install ``` Build all packages: ```bash pnpm build ``` ## Configure environment Copy `.env.example` to `.env` and fill in the appropriate values. ```bash cp .env.example .env ``` :::danger In normal development, it's a best practice to use a `.env` to protect API keys and other sensitive information. When you work with crypto, it's **critical** to always use them, even in test projects or tutorials. If you expose a wallet key, you might lose everything in that wallet immediately, or someone might watch it for years and rob you the day you put something valuable there. ::: Edit `.env` and add your values. Do **NOT** add this file to version control. ### Choose Your model Eliza supports multiple AI models and you set which model to use inside the character JSON file. But remember, after you choose a model, you need to set up the relevant configuration. Check the full list of supported LLMs in origin Eliza: [Models.ts] Suggested models: - Use API to access LLM providers: - OpenAI: set modelProvider as `openai`, and set `OPENAI_API_KEY` in `.env`. - Deepseek: set modelProvider as `deepseek`, and set `DEEPSEEK_API_KEY` in `.env`. - Grok: set modelProvider as `grok`, and set `GROK_API_KEY` in `.env`. - Use local inference - Ollama: set modelProvider as `ollama`, and set `OLLAMA_MODEL` in `.env` to the model name you use in ollama. > To choose a model, you need to set in charactor configuration. For example: OPENAI, set `modelProvider: "openai"` in charactor JSON file or `modelProvider: ModelProviderName.OPENAI` in `charactor.ts` ### Setup Agent's Flow account Create a new Flow account for the Agent. Learn more: [doc] ```bash flow accounts create ``` > If you use Testnet, you can get free tokens from [Flow Faucet] Set the Flow blockchain configuration in `.env` with a newly-generated Flow account. ```bash FLOW_ADDRESS= FLOW_PRIVATE_KEY= FLOW_NETWORK= # Default: mainnet FLOW_ENDPOINT_URL= # Default: ``` For testnet, check Flow's [Networks] for more information. ## Create your first agent ### Create a character file View the `deps/eliza/characters/` directory for a number of character files to try out. Additionally, you can edit `charactor.ts` to override Eliza's `defaultCharacter` file, which is the default character file used if no character json files are provided. Copy one of the example character files and make it your own: ```bash cp characters/scooby.character.json characters/sample.character.json ``` 📝 [Character Documentation] ### **Start the Agent** Tell it which character you want to run: ```bash pnpm start --character="characters/sample.character.json" ``` Or, you can use `pnpm start:debug` for more debugging logs: ```bash pnpm start:debug --character="characters/sample.character.json" ``` You can load multiple characters with a comma-separated list: ```bash pnpm start --characters="characters/sample.character.json, characters/scooby.character.json" ``` ### Add and develop plugins Run `npx elizaos plugins list` to get a list of available plugins or visit [Eliza Plugins Registry] Run `npx elizaos plugins add @elizaos-plugins/plugin-NAME` to install the plugin into your instance To create a new plugin **for your own business**, refer to the [plugin development guide]. #### Additional requirements You may need to install Sharp. If you see an error when you start it up, install it with the following command: ```bash pnpm install --include=optional sharp ``` ### **Interact with the agent** Now you're ready to start a conversation with your agent. Open a new terminal window and run the client's http server. ```bash pnpm start:client ``` After the client is running, you'll see a message like this: ```bash ➜ Local: http://localhost:5173/ ``` Click the link or open your browser to `http://localhost:5173/`. You'll see the chat interface connect to the system, and you can now interact with your character. ## Common issues and solutions Check the orgin Eliza's [Common Issues & Solutions] ## Conclusion In this tutorial, you've learned how to build and deploy an AI agent on the Flow blockchain using Eliza. You've gained hands-on experience with setting up the development environment, configuring agents, creating character configurations, integrating AI models, and developing custom plugins. The Eliza framework provides a powerful way to create intelligent agents that can understand and respond to user queries while leveraging Flow's secure and scalable infrastructure. Now taht you've completed this tutorial, you now have the foundation to build more sophisticated AI agents and create unique user experiences through character customization and plugin development. [Node.js 23+]: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm [nvm]: https://github.com/nvm-sh/nvm [pnpm 9+]: https://pnpm.io/installation [VS Code]: https://code.visualstudio.com/ [Cursor]: https://cursor.com/ [VSCodium]: https://vscodium.com [Flow-cli]: https://developers.flow.com/tools/flow-cli [WSL 2]: https://learn.microsoft.com/en-us/windows/wsl/install-manual [Eliza]: https://github.com/elizaOs/eliza [Models.ts]: https://github.com/elizaOS/eliza/blob/main/packages/core/src/models.ts [doc]: https://developers.flow.com/tools/flow-cli/accounts/create-accounts [Flow Faucet]: https://faucet.flow.com/ [Networks]: https://developers.flow.com/protocol/flow-networks [Character Documentation]: https://elizaos.github.io/eliza/docs/core/characterfile/ [Eliza Plugins Registry]: https://elizaos.github.io/registry [plugin development guide]: build-plugin.md [Common Issues & Solutions]: https://elizaos.github.io/eliza/docs/quickstart/#common-issues--solutions --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/agents/eliza/build-plugin.md --- title: Eliza Plugin Guide description: Learn how to build Eliza plugins for your AI Agent on Flow sidebar_position: 1 keywords: - AI - AI Agent - Eliza - Eliza on Flow - Plugin - Flow Development - Quickstart --- # Eliza Plugin Development Guide ## Overview Plugins are a powerful way to extend the functionality of your Eliza AI agents. This guide will walk you through the process of how to create custom plugins that can enhance your agent's capabilities, from simple utilities to complex integrations with external services. You'll learn how to leverage the plugin system to create modular and reusable components for your AI agents. ## Learning objectives After you complete this tutorial, you will be able to: - Create a new plugin repository from the template. - Understand the plugin development workflow. - Implement custom actions and services. - Integrate plugins with your Eliza agent. - Register and publish plugins to the Eliza Plugin Registry. - Use dependency injection for better plugin architecture. ## Prerequisites Before you get started with Eliza, make sure you have: - [Node.js 23+] (we recommend that you use [nvm]) - [pnpm 9+] - Git for version control - A code editor (we recommend [VS Code], [Cursor] or [VSCodium]) - [Flow-cli] for Flow blockchain interaction. > **Note for Windows users:** [WSL 2] is required. ## Quickstart Follow the [Quickstart Guide] to set up your development environment. ## Plugin development ### Create a plugin repository from Template Visit [Eliza Plugin Template] and click "Use this template" to create a new repository. Or, you can create a new empty repository and copy the files from some examples at the [Eliza Plugins] organization. :::note Flow's Eliza plugin template uses Dependency Injection(`@elizaos-plugins/plugin-di`). You can learn more about the Dependency Injection in the [plugin's README.md]. It allows you can use `Class` instead of `Object` for your `Actions`, `Providers`, `Services`, and so on. **If you don't want to use it, you can follow the other examples in Eliza Plugins organiazation.** ::: ### Add the plugin repository to your Eliza project Let's say you created a repository named `username/plugin-foo`. Use submodules to add the plugin repository to your Eliza project. ```bash git submodule add https://github.com/username/plugin-foo.git packages/plugin-foo ``` Change the package's name in the plugin's `package.json` to `@elizaos-plugins/plugin-foo`. ```json { "name": "@elizaos-plugins/plugin-foo", } ``` Add the plugin to agent's `package.json` ```bash pnpm add @elizaos-plugins/plugin-foo@'workspace:*' --filter ./agent ``` Check the `agent/package.json` to make sure the plugin is added. You'll see something like this: ```json { "dependencies": { "@elizaos-plugins/plugin-foo": "workspace:*" } } ``` ### Build the plugin Build the plugin with the following command: ```bash pnpm build --filter ./packages/plugin-foo # Or build all packages pnpm build ``` ### Add the plugin to the `character.json` you want to use Let's say you want to add the plugin to the `sample` character, which is `characters/sample.character.json`. ```json { "name": "Sample", "plugins": [ "@elizaos-plugins/plugin-foo" ] } ``` :::warning If you use Dependency Injection(`@elizaos-plugins/plugin-di`) in your plugin, remember to add it to the `postProcessors` field. The **`clients` field is deprecated** in the latest version of Eliza, so if you want to add clients, you also need to use `plugins` field. ::: ```json { "name": "Sample", "plugins": [ "@elizaos-plugins/plugin-foo", "@elizaos-plugins/client-discord" ], "postProcessors": [ "@elizaos-plugins/plugin-di" ] } ``` ### Run the Eliza agent with your plugin Run the Eliza agent to test the plugin. ```bash pnpm start --character="characters/sample.character.json" # Or with more debug logs pnpm start:debug --character="characters/sample.character.json" ``` ### Interact with the agent Now, you're ready to start a conversation with your agent. Open a new terminal window and run the client's http server. ```bash pnpm start:client ``` ## Plugin registration You need to register your plugin in the [Eliza Plugin Registry] to make it available for other users. Follow the guide there, modify the [index.json] file, and submit a pull request (PR) to the registry repository. ## Conclusion In this tutorial, you've learned how to develop custom plugins for Eliza. You've gained experience with creating plugin repositories, implementing custom actions and services, integrating plugins with agents, and using dependency injection for better architecture. Eliza's plugin system provides a powerful way to extend the functionality of your AI agents. With the knowledge gained from this tutorial, you can now develop more sophisticated plugins, create reusable components, and share your work through the plugin registry. [Node.js 23+]: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm [nvm]: https://github.com/nvm-sh/nvm [pnpm 9+]: https://pnpm.io/installation [VS Code]: https://code.visualstudio.com/ [Cursor]: https://cursor.com/ [VSCodium]: https://vscodium.com [Flow-cli]: https://developers.flow.com/tools/flow-cli [WSL 2]: https://learn.microsoft.com/en-us/windows/wsl/install-manual [Quickstart Guide]: ./index.md [Eliza Plugin Template]: https://github.com/onflow/eliza-plugin-template [Eliza Plugins]: https://github.com/elizaos-plugins [plugin's README.md]: https://github.com/fixes-world/plugin-di [Eliza Plugin Registry]: https://github.com/elizaos-plugins/registry [index.json]: https://github.com/elizaos-plugins/registry/blob/main/index.json --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/cursor.md --- title: Use Flow Knowledge Base in Cursor sidebar_label: Use Cursor AI sidebar_position: 2 keywords: - Cursor - AI - Flow documentation - Cadence documentation - Flow development - Flow tools - Flow IDE - Flow setup - Flow configuration - Flow AI assistance --- # Use Flow knowledge base in Cursor [Cursor] is an AI code editor that makes it easy to write code while you build Flow apps. This section provides comprehensive guidance on how to set up and use Cursor with Flow's extensive documentation ecosystem to enhance your development experience. To get the most out of Cursor for Flow development, you'll need to understand three key components: 1. **Flow Data Sources** - The comprehensive knowledge base that powers AI assistance. 2. **Documentation Indexing** - How to make Flow documentation available within Cursor. 3. **Cadence Rules** - Persistent AI context that provides specialized Flow development guidance. ## [Flow Data Sources] Learn about Flow's comprehensive, auto-generated documentation repository that serves as the foundation for AI-assisted development. This resource contains the most current information about Flow, Cadence, and the broader ecosystem, specifically formatted for optimal AI integration. The Flow Data Sources guide covers: - What Flow Data Sources contains and how it's organized. - Different file formats available for various use cases. - Integration strategies with AI tools. - Best practices to leverage this knowledge base. ## [Indexing Flow Documentation in Cursor] Follow the step-by-step process for how to set up Flow documentation within Cursor's AI system. This detailed guide walks you through how to index multiple documentation sources to create a comprehensive Flow development environment. The documentation covers how to: - Add Flow developer documentation to Cursor. - Integrate Cadence language documentation. - Include Flow Data Sources for comprehensive coverage. - Troubleshoot common indexing issues. - Apply best pratices for indexed documentation. ## [Cadence Rules] Learn how to use Cursor Rules to enhance AI assistance for Cadence and Flow development with persistent context and automated workflows. This comprehensive guide demonstrates how to create specialized rules that transform your AI assistant into a Flow development expert with continuous knowledge of syntax patterns, NFT standards, and project workflows. The Cadence Rules guide covers: - Cursor Rule basics and how they provide persistent AI context. - Five specialized rules for Flow development: NFT standards, syntax patterns, development workflows, project configuration, and user preferences. - How to create custom rules for your specific Flow development needs. - Best practices for rule organization and team collaboration. - Integration with current Flow development tools and documentation ## Get started 1. **Start with Flow Data Sources**: Understand what documentation is available and how it can enhance your development workflow. 2. **Follow the Indexing Guide**: Set up your Cursor environment with comprehensive Flow documentation. 3. **Configure Cadence Rules**: Implement persistent AI context for consistent Flow development assistance. 4. **Practice with Examples**: Use the indexed documentation and rules to build Flow applications with enhanced AI assistance. ## Best practices for cursor + flow - **Use Specific References**: Target `@Flow`, `@Cadence`, or `@Flow Data Sources` based on your needs. - **Leverage Cursor Rules**: Apply `@cadence-nft-standards`, `@cadence-syntax-patterns`, or other specialized rules for consistent guidance. - **Combine Sources**: Leverage multiple documentation sources and rules for comprehensive assistance. - **Verify AI Output**: Cross-reference generated code with official documentation. - **Stay Updated**: Refresh your documentation indexes and rules periodically for current information. This integrated approach combines comprehensive documentation indexing with persistent AI context through Cursor Rules. This provides you with consistent, accurate, and specialized assistance throughout your Flow development process. [Cursor]: https://www.cursor.com/ [Flow Data Sources]: ./flow-data-sources.md [Indexing Flow Documentation in Cursor]: ./indexing-docs.md [Cadence Rules]: ./cadence-rules.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/cursor/cadence-rules.md --- title: Cadence Rules description: Learn how to use Cursor Rules to enhance AI assistance for Cadence and Flow development with persistent context and automated workflows. sidebar_position: 3 label: Cadence Rules sidebar_label: Cadence Rules keywords: - Cursor Rules - AI - Cursor - Cadence - AI_Flow - Cadence Rules --- # Cadence Rules When you build with AI, it's hard to make the agent consistently understand what standards it should use when it builds or generates responses. To migitate this issue, Cursor Rules sets up global rules, project wide rules or documents specific rules that it inserts in the agent's context before it reads the prompt. With Cursor Rules, you can create an assistant that can consistently understand the intended development process, desired formatted responses, and avoid common mistakes. Consider it your tool to make guard rails for agents that can reduce hallucination and incorrect development flows. In this guide, you'll learn how to configure and use Cursor Rules that transform your AI assistant into a Flow development expert with persistent knowledge of Cadence syntax patterns, NFT standards, project configuration, and development workflows.
    ## Learning objectives After you complete this guide, you'll be able to: - Configure and use Cursor Rules to enhance AI assistance for Flow blockchain development. - Apply specialized Cadence syntax patterns and NFT development standards through persistent AI context. - Use workflow-based rules to guide project setup, deployment, and debugging processes across the Flow development lifecycle. - Create your own Cadence Rules in order to supercharge your development on Flow. ## What are Cursor rules? Cursor rules shape AI behavior with persistent, reusable instructions that guide how Cursor's Agent and Inline Edit assist you. Rules act as continuous context - they embed your preferences, code standards, and workflows directly into the AI's decision-making process. Since AI models start fresh with each interaction, rules automatically inject your preferences and context into every conversation, which bridfges this gap. When active, rules are included at the beginning of the AI's context and provide consistent guidance for code generation, edit suggestions, and workflow assistance. Cursor offers two rule types: - **Project Rules**: Live in `.cursor/rules`, version-controlled with your code, and apply to specific projects. - **User Rules**: Global preferences in Cursor Settings that apply across all your projects. ### Rule anatomy Each rule file is written in MDC (`.mdc`), a format supporting metadata and content. Control how rules are applied from the type dropdown which changes properties `description`, `globs`, `alwaysApply`. | Rule Type | Description | | ----------------------- | --------------------------------------------------------------------------------- | | Always Apply | Always included in model context. | | Apply to Specific Files | Included when files matching a glob pattern are referenced. | | Apply Intellegently | Available to AI, which decides whether to include it. Must provide a description. | | Apply Manually | Only included when explicitly mentioned using `@ruleName`. | ```mdc --- description: Cadence NFT development standards and best practices globs: ["**/*.cdc", "**/contracts/**", "**/cadence/**"] alwaysApply: false --- - Always implement NonFungibleToken interface for NFT contracts. - Use MetadataViews for marketplace compatibility. - Follow proper resource handling with @ and & symbols. - Include required standard functions and path conventions. @nft-template.cdc ``` Referenced files like `@nft-template.cdc` are included as additional context when the rule triggers. ## User rules User rules are global preferences defined in Cursor Settings → Rules that apply across all projects. They’re plain text and perfect for setting preferred communication style or code conventions: ```md Please reply in a concise style. Avoid unnecessary repetition or filler language. ``` ## Project rules Project rules are stored as individual files in `.cursor/rules` and get version-controlled alongside your code. Each rule can target specific file types with glob patterns, be manually invoked when needed, or automatically activate based on context. You can also create nested rule directories - any folder can have its own `.cursor/rules` directory with rules that apply specifically to that area of your project. Project rules help: - Capture specialized knowledge about your codebase and domain. - Establish consistent workflows and development patterns. - Enforce coding standards and architectural decisions across your team. ### Nested rules To organize rules, place them in `.cursor/rules` directories throughout your project. Nested rules automatically attach when files in their directory are referenced. ```mdc project/ .cursor/rules/ # Project-wide rules Cadence/ .cursor/rules/ # Cadence-specific rules frontend/ .cursor/rules/ # Frontend-specific rules ``` ## Create rules Create rules with the Cursor Rule command or go to Cursor Settings > Rules. This creates a new rule file in `.cursor/rules`. From settings you can see all rules and their status. Click the cog icon on the upper right section of the window. Then, click "Rules and Memories" on the left side bar. Finally click "+ Add Rule" in the User Rules or Project Rules section (this depends on your objective). ![Creating a Cursor Rule](./imgs/cursor_rules1.png) ### Generate rules Generate rules directly in conversations with the `/Generate Cursor Rules` command. This is useful when you’ve made decisions about agent behavior and want to reuse them. ![Generate Cursor Rules](./imgs/generate_cursor_rules.png) ## Best practices Good rules are focused, actionable, and scoped. - Keep rules under 500 lines. - Split large rules into multiple, composable rules. - Provide concrete examples or referenced files. - Avoid vague guidance. Write rules like clear internal docs. - Reuse rules when repeating prompts in chat. ## Cadence rules Here are a couple of Cursor Rules made by [claucondor] for Flow development and NFTs. ### Cadence NFT standards The [cadence-nft-standards.mdc] rule provides comprehensive guidelines for NFT development with Cadence on Flow blockchain. It ensures proper implementation of: - **Core Interface Conformance**: `NonFungibleToken`, `NFT`, and `Collection` interfaces. - **MetadataViews Integration**: Marketplace-compatible metadata standards. - **Modular Architecture**: Patterns for complex NFTs with traits, evolution, and breeding. - **Security Best Practices**: Proper resource handling and capability management. - **Event Standards**: Consistent event emission for off-chain indexing. **Perfect for:** - New NFT projects on Flow. - NFT marketplace integration. - Complex NFT systems (traits, evolution, breeding). - Code reviews and standards compliance. - Learning Flow NFT development. **Applies to:** `.cdc` files, NFT transactions, collection setup, metadata implementation. #### Rule configuration ```mdc --- description: Comprehensive standards and best practices for developing Non-Fungible Tokens (NFTs) with Cadence. Ensures proper implementation of NonFungibleToken interfaces, MetadataViews integration for marketplace compatibility, secure resource handling patterns, and advanced modular architectures for complex NFTs with traits, evolution, and breeding mechanics. Includes required standard functions, path conventions, event emission patterns, and security best practices for capability management. globs: ["**/*.cdc", "**/contracts/**", "**/cadence/**"] alwaysApply: false --- ``` Recommended Apply config: `Apply Intelligently` #### Manual reference ``` @cadence-nft-standards help me implement a new NFT contract ``` #### Key benefits - **Ecosystem Compatibility**: Guaranteed marketplace and wallet integration. - **Security**: Proper resource handling and capability management. - **Maintainability**: Modular architecture for complex systems. - **Performance**: Gas-efficient implementations with lazy initialization. - **Standards Compliance**: Follows official Flow NFT patterns. #### Usage examples - `"Create an NFT contract for collectible cards"` → Guides complete interface implementation. - `"Make my NFT marketplace compatible"` → Provides MetadataViews patterns. - `"Build evolving NFTs with traits"` → Suggests modular architecture patterns. - `"Review my NFT contract"` → Validates against all documented standards. ### Cadence syntax patterns #### What this rule does The [cadence-syntax-patterns.mdc] rule provides comprehensive syntax guidance and error prevention for Cadence development. It covers essential language patterns such as: - **Resource Type Syntax**: Proper use of `@` and `&` symbols for resources and references. - **Interface Restrictions**: Correct `{}` syntax for interface constraints. - **Transaction Authorization**: Granular `auth` capabilities and permission patterns. - **Contract Member Access**: Accessing deployed contract functions and constants. - **Type Conversion & Arithmetic**: Explicit type handling and numeric operations. - **Access Control**: Field visibility, entitlements, and `view` function purity. - **Language Constructs**: Optional binding, loops, string manipulation, switch cases. - **Debugging Strategies**: Systematic error resolution and prevention techniques. **Perfect for:** - Writing any Cadence contracts or transactions. - Debugging compilation errors with resources (`@`) or references (`&`). - Fixing authorization issues in transactions. - Learning Cadence syntax and best practices. - Preventing common type system errors. - Understanding Flow blockchain development patterns. **Applies to:** `.cdc` files, transaction scripts, contract development, Flow CLI usage, error debugging. #### How to use this rule #### Auto attached configuration ```mdc --- description: Provides a comprehensive guide to syntax patterns, best practices, and common pitfalls in the Cadence language. Covers resource type syntax (@,&), interface restrictions ({}), transaction authorization patterns, contract member access, type conversion, access modifiers, view function purity, and debugging approaches. Includes language-specific patterns for optional binding, variable initialization, string manipulation, looping constructs, and idempotent transaction design. globs: ["**/*.cdc", "**/cadence/**", "**/transactions/**", "**/scripts/**"] alwaysApply: false --- ``` Recommended Apply config: `Apply Intelligently` #### Manual reference ``` @cadence-syntax-patterns help me fix this authorization error ``` #### Key Benefits - **Error Prevention**: Avoids common syntax mistakes before they happen. - **Faster Debugging**: Systematic approaches to resolve compilation errors. - **Best Practices**: Language-specific patterns that follow Cadence conventions. - **Authorization Mastery**: Proper transaction permission handling. - **Type Safety**: Correct resource handling and type conversions. - **Performance**: Optimized patterns for compute unit (gas) efficiency. #### Usage examples - `"Fix this resource handling error"` → Provides `@` and `&` syntax corrections - `"My transaction authorization is failing"` → Suggests proper `auth` capabilities - `"How do I loop in Cadence?"` → Shows `while` loop patterns (no range loops) - `"String manipulation not working"` → Provides `String.split()` and `String.contains()` patterns - `"Interface restriction syntax error"` → Corrects to `{}` interface syntax - `"Access control for resource fields"` → Guides `access(self)` vs `access(all)` patterns #### Key syntax patterns covered - **Resources**: `@{NonFungibleToken.NFT}` not `@NonFungibleToken.NFT` - **Authorization**: `auth(Storage) &Account` or granular capabilities. - **Optional Binding**: `if let` syntax (no `guard let`) - **Variable Initialization**: All `var` declarations must have initial values - **Type Conversion**: Explicit conversion required for arithmetic - **View Functions**: Cannot call non-`view` functions - **String Operations**: Use `.split()` and `.contains()` (no `.indexOf()`) - **Loops**: `while` loops only (no range-based `for` loops) ### Flow development workflow #### What this rule does The [flow-development-workflow.mdc] rule provides comprehensive workflow methodology for Flow blockchain development that covers the complete development lifecycle. It includes: - **Documentation-First Approach**: Always reference official Flow documentation and standard examples. - **Development Sequence**: Emulator → Testnet → Mainnet progression with proper validation. - **Transaction Authorization**: Granular `auth` capabilities and permission management. - **Deployment Verification**: Post-deployment validation protocols and testing strategies. - **FCL Integration**: Frontend configuration, network management, and user experience patterns. - **Error Resolution**: Systematic debugging approaches and common error prevention. - **Optimization Techniques**: Computation limit handling and compute unit (gas) efficiency strategies. - **Testnet Validation**: Comprehensive validation protocols before mainnet deployment. **Perfect for:** - Starting new Flow projects or need setup guidance. - Moving between development stages (emulator → testnet → mainnet). - Debugging deployment or transaction authorization issues. - Integrating frontend applications with FCL. - Handling computation limits and compute unit (gas) optimization. - Learning Flow development best practices and official patterns. - Comprehensive project validation strategies. **Applies to:** Complete Flow development lifecycle, project setup, deployment, FCL integration, debugging. #### Manual Reference ``` @flow-development-workflow help me deploy to testnet properly ``` #### Auto attached configuration ```mdc --- description: Comprehensive workflow and best practices guide for Flow blockchain development covering the complete development lifecycle from setup through mainnet deployment. Includes documentation-first debugging methodology, transaction authorization patterns, FCL integration best practices, deployment verification protocols, optimization techniques for computation limits, error resolution strategies, and testnet validation procedures. Emphasizes official Flow documentation usage and iterative development approach for both Cadence contracts and frontend integration. globs: ["flow.json", "**/config.js", "**/cadence/**", "**.md"] alwaysApply: false --- ``` Recommended Apply config: `Apply Intelligently` #### Key benefits - **Systematic Approach**: Step-by-step methodology prevents common mistakes. - **Official Patterns**: Emphasizes Flow documentation and standard examples. - **Error Prevention**: Proactive strategies for avoiding deployment and integration issues. - **Full-Stack Awareness**: Covers both Cadence backend and FCL frontend integration. - **Optimization Focus**: Gas efficiency and computation limit management. - **Validation Protocols**: Comprehensive testing before production deployment. #### Usage examples - `"How do I set up a new Flow project?"` → Guides project setup and configuration. - `"My transaction authorization is failing"` → Provides auth capability debugging. - `"Deploy my contract to testnet"` → Shows deployment sequence and verification. - `"FCL integration not working"` → Suggests configuration and network troubleshooting. - `"Computation limit exceeded"` → Recommends optimization strategies. - `"Prepare for mainnet deployment"` → Provides comprehensive validation checklist. #### Key workflow areas covered - **Project Setup**: `flow.json` configuration, FCL setup, environment management. - **Authorization**: `auth(Storage)`, `auth(BorrowValue, SaveValue)`, granular capabilities. - **Development Sequence**: Emulator testing → Frontend integration → Testnet → Validation. - **Error Resolution**: Syntax errors, deployment errors, FCL errors, computation limits. - **FCL Best Practices**: Network configuration, contract address management, user authentication. - **Optimization**: Accumulative processing, loop optimization, compute unit (gas) efficiency. - **Deployment**: Verification protocols, update strategies, multi-network consistency. - **Documentation Usage**: When and how to reference official Flow resources. #### Development philosophy emphasized - **Documentation-Driven**: Reference official sources before creating custom solutions. - **Iterative Approach**: Fix issues one at a time, test frequently at each stage. - **Standard Compliance**: Prefer established Flow patterns over custom implementations. - **Full-Stack Awareness**: Consider entire stack from contracts to frontend UI. - **Error-Driven Learning**: Use errors as opportunities to refine understanding. ### Flow project configuration #### What This Rule Does The [flow-project-config.mdc] rule provides comprehensive guidance for Flow project configuration and `flow.json` management. It covers essential configuration patterns including: - **`flow.json` Structure**: Accounts, contracts, deployments, and networks configuration. - **Account Management**: Named accounts, addresses, private keys, and signer setup. - **Contract Registration**: Source paths, network-specific aliases, and pre-deployment setup. - **Deployment Configuration**: Network-specific deployments and contract mappings. - **FCL Integration**: Synchronizing backend deployment with frontend address configuration. - **Network Management**: Multi-network consistency across emulator, testnet, and mainnet. - **Error Prevention**: Common configuration mistakes and troubleshooting strategies. - **Address Mapping**: Contract address management and import resolution. **Perfect for:** - Setting up new Flow projects or configuring `flow.json`. - Debugging deployment or contract resolution issues. - Managing FCL integration and address mapping problems. - Working across different networks (emulator/testnet/mainnet). - Troubleshooting CLI errors related to signers or contract imports. - Account management and private key handling. - Contract aliasing and deployment configurations. **Applies to:** `flow.json`, FCL config files, deployment scripts, network switching, contract imports. #### Auto attached configuration ```mdc --- description: Comprehensive guide to Flow project configuration and flow.json management covering account setup, contract registration, deployment configuration, network-specific aliases, and FCL integration. Includes best practices for multi-network consistency, common configuration errors prevention, contract address management, and synchronization between backend deployment and frontend FCL configuration across emulator, testnet, and mainnet environments. globs: ["flow.json", "**/config.js", "**/.env*", "**/package.json"] alwaysApply: false --- ``` Recommended Apply config: `Apply Intelligently` #### Manual reference ``` @flow-project-config help me configure my flow.json for testnet deployment ``` #### Key benefits - **Configuration Accuracy**: Prevents common setup and deployment errors. - **Multi-Network Support**: Seamless switching between emulator, testnet, and mainnet. - **FCL Synchronization**: Ensures frontend and backend configurations stay aligned. - **Error Prevention**: Addresses typos, address mismatches, and path issues proactively. - **Deployment Success**: Proper account setup and contract registration workflows. - **Address Management**: Centralized contract address handling across environments. #### Usage examples - `"Configure flow.json for new project"` → Provides complete structure and setup guidance. - `"Failed to resolve contract import"` → Suggests address mapping and alias fixes. - `"Wrong signer/network CLI error"` → Guides account and network configuration. - `"FCL can't find my contract"` → Shows frontend address configuration patterns. - `"Deploy contract to testnet"` → Provides deployment configuration and verification. - `"Switch from emulator to testnet"` → Guides network transition and address updates. #### Key configuration areas covered - **Account Setup**: Address accuracy, key file paths, network-specific accounts. - **Contract Registration**: Source paths, aliases, pre-deployment requirements. - **Deployment Management**: Signer assignment, contract lists, network specificity. - **Standard Contract Addresses**: Official addresses for NonFungibleToken, MetadataViews, FungibleToken. - **FCL Configuration**: Network settings, contract address mapping, environment variables. - **Error Prevention**: Typo checking, address consistency, permission handling. - **Multi-Network Patterns**: Network-specific aliases and deployment strategies. - **CLI Usage**: Correct `--signer`, `--network`, and `--update` flag usage. #### Common issues addressed - **"failed to get contract"**: Missing or incorrect aliases in `flow.json`. - **"failed to resolve import"**: Address mapping issues in FCL configuration. - **"Permission Denied"**: Key file accessibility and path problems. - **"transaction failed to decode"**: Network mismatch or FCL configuration errors. - **Signer/Network Mismatches**: Using emulator accounts on testnet and vice-versa. - **Address Inconsistencies**: Misaligned addresses between `flow.json` and FCL config. #### Best practices emphasized - **Pre-deployment Registration**: Always add contracts to `flow.json` before you deploy it. - **Address Consistency**: Ensure addresses match across `flow.json`, FCL config, and imports. - **Environment Variables**: Use `.env` files for network-specific contract addresses. - **Network Separation**: Maintain distinct configurations for each network environment. - **Key Security**: Proper `.gitignore` setup for private key files. ### User preferences #### What this rule does The [user-preferences.mdc] rule personalizes AI assistance behavior for Flow blockchain development. It defines preferred communication style and development methodology including: - **Communication Style**: Concise, actionable solutions with proactive error prevention explanations. - **Development Philosophy**: Documentation-driven approach with official Flow patterns preference. - **Problem-Solving Methodology**: Root cause analysis, iterative fixes, and pattern recognition. - **Code Quality Standards**: Standards compliance, consistency, and completeness requirements. - **Workflow Preferences**: Thorough setup, emulator-first testing, and systematic debugging. - **Error Resolution Style**: Reference-based solutions and prevention-focused learning. - **Full-Stack Awareness**: Solutions that consider entire stack from Cadence to frontend UI. - **User Experience Focus**: Clear blockchain interaction feedback and user-friendly interfaces. **Perfect for:** - Ensuring consistent AI behavior across all Flow development projects. - Matching AI assistance style to your preferred working methodology. - Getting responses formatted in your preferred communication style. - Maintaining documentation-driven and standards-compliant approaches. - Ensuring full-stack consideration in all solutions provided. - Receiving proactive error prevention guidance with solutions. **Applies to:** All AI assistance, communication style, problem-solving approach, solution methodology #### Synergy with technical rules This rule works as the **behavioral foundation** for your technical Flow rules: - **Technical Rules** provide **WHAT** information to apply. - **User Preferences** defines **HOW** to deliver that information. - **Result**: Consistent, personalized assistance across all Flow development scenarios. #### Always applied configuration ```mdc --- description: Defines personalized development preferences and communication style for Flow blockchain development, whcih includes concise response formatting, documentation-driven problem solving, iterative workflow methodology, full-stack awareness, and systematic error resolution. Guides AI behavior to match user's preferred development philosophy with emphasis on official Flow patterns, practical solutions, and proactive error prevention across the entire development stack. alwaysApply: true --- ``` Recommended Apply config: `Always Apply` #### Key benefits - **Consistent Communication**: AI responses match your preferred style and level of detail. - **Methodology Alignment**: Solutions follow your preferred development philosophy. - **Error Prevention Focus**: Proactive guidance on avoiding similar issues in the future. - **Efficiency**: Concise, but complete responses without unnecessary verbosity. - **Standards Compliance**: Emphasis on official Flow patterns and best practices. - **Full-Stack Perspective**: Solutions consider entire development stack implications. #### How this rule affects AI responses - **Response Style**: `"Configure flow.json for testnet"` → Provides direct steps + brief prevention tips. - **Problem-Solving**: `"Authorization error"` → Root cause analysis + pattern to prevent recurrence. - **Code Solutions**: Always includes reference to official documentation when available. - **Error Debugging**: Systematic, one-issue-at-a-time approach with testing checkpoints. - **Architecture Decisions**: Prefer established Flow patterns over custom implementations. - **Learning Approach**: Uses errors as learning opportunities with rule and pattern references. #### Behavioral patterns defined - **Conciseness**: Clear, actionable solutions without excessive explanation. - **Practical Focus**: Working code examples prioritized over theoretical explanations. - **Documentation-Driven**: Official Flow, Cadence, FCL docs referenced first. - **Iterative Methodology**: Fix one issue at a time, test frequently. - **Standard Compliance**: Established patterns preferred over custom solutions. - **Root Cause Analysis**: Address underlying issues, not just symptoms. - **Prevention-Oriented**: Include brief explanations of why errors occurred. - **Full-Stack Consideration**: Account for contracts, transactions, FCL, and UI implications. #### Configuration recommendation **Best Setup**: Configure as **"Always"** rule or add to **User Rules** in Cursor Settings for global application across all your Flow development work. This ensures consistent AI behavior regardless of which specific project or technical rule is active. ## Conclusion In this guide, you explored how to configure and use Cursor Rules to enhance AI assistance for Flow blockchain development. You learned about the different types of rules available, from always-applied user preferences to context-specific project rules that activate based on file types and project structure. You discovered five specialized Cursor Rules designed specifically for Cadence and Flow development: NFT development standards for marketplace-compatible contracts, syntax patterns for error prevention and debugging, comprehensive development workflows for the complete project lifecycle, project configuration management for seamless multi-network deployment, and personalized user preferences that ensure consistent AI behavior across all your Flow projects. Now that you have completed this guide, you should be able to: - Configure and use Cursor Rules to enhance AI assistance for Flow blockchain development - Apply specialized Cadence syntax patterns and NFT development standards through persistent AI context - Utilize workflow-based rules to guide project setup, deployment, and debugging processes across the Flow development lifecycle [claucondor]: https://gist.github.com/claucondor/453cb30c56597b53071bb5bbf18d2c9c [cadence-rules]: https://github.com/onflow/cadence-rules [cadence-nft-standards.mdc]: https://github.com/onflow/cadence-rules/blob/main/cadence-nft-standards.mdc [cadence-syntax-patterns.mdc]: https://github.com/onflow/cadence-rules/blob/main/cadence-syntax-patterns.mdc [flow-development-workflow.mdc]: https://github.com/onflow/cadence-rules/blob/main/flow-development-workflow.mdc [flow-project-config.mdc]: https://github.com/onflow/cadence-rules/blob/main/flow-project-config.mdc [user-preferences.mdc]: https://github.com/onflow/cadence-rules/blob/main/user-preferences.mdc --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/cursor/flow-data-sources.md --- title: Flow Data Sources sidebar_label: Flow Data Sources sidebar_position: 1 keywords: - Flow Data Sources - AI knowledge base - Flow documentation - Cadence documentation - Flow development - Flow tools - Flow AI assistance - RAG - Retrieval-Augmented Generation - comprehensive knowledge base - Flow ecosystem --- # Flow Data Sources Flow Data Sources is a comprehensive repository that automatically aggregates and formats Flow ecosystem content into Markdown files optimized for AI ingestion. This resource serves as a centralized knowledge base for AI tools, chatbots, and RAG (Retrieval-Augmented Generation) pipelines. It contains the most current documentation, examples, and best practices for Flow blockchain development.
    ## Overview The repository contains Python scripts that: - Crawl Flow-related documentation sites, GitHub repositories, and discussions. - Convert HTML content to Markdown format. - Extract code examples from GitHub repositories. - Capture community discussions and Q&A content. - Merge all content into consolidated files for easy consumption. Flow Data Sources automatically pulls content from: - Official Flow documentation - Cadence language documentation - Flow CLI guides - FCL (Flow Client Library) documentation - Smart contract examples and tutorials - Best practices and development patterns - Community discussions and Q&A content ## Key features - **Daily Updates**: Content is automatically refreshed to ensure the latest information. - **Structured Format**: All content is converted to Markdown for consistent processing. - **Comprehensive Coverage**: Includes official documentation, code examples, and community discussions. - **Optimized for AI**: Designed specifically for AI tools, chatbots, and RAG pipelines. ## Available files The repository provides several merged documentation files optimized for different use cases: **Output options:** - [All Merged Content]: Complete content - [Essentials Only]: Streamlined version that only include official documentation and sample codes - [Cadence Only]: Streamlined version that only includex Cadence related documentation and sample codes ### All merged documentation - **File**: `all_merged.md` - **Content**: Complete comprehensive documentation that covers all aspects of Flow development. - **Use Case**: Most comprehensive knowledge base for AI tools and complex development questions. - **Size**: Very large file - may require powerful systems to process. - **Documentation**: [All Merged Content] ### Essentials merged documentation - **File**: `essentials_merged.md` - **Content**: Core Flow and Cadence development essentials. - **Use Case**: Lighter alternative for systems with resource constraints. - **Size**: Smaller, more focused content for essential development needs. - **Documentation**: [Essentials Only] ### Cadence only documentation - **File**: `cadence_docs_merged.md` - **Content**: Streamlined version that only includes Cadence related documentation and sample codes. - **Use Case**: Focused on Cadence language development and smart contracts. - **Size**: Cadence-specific content for specialized development needs. - **Documentation**: [Cadence Only] ## How to use You can integrate Flow Data Sources with: - **ChatGPT Plugins**: Enhance Q&A capabilities with Flow-specific knowledge. - **Custom Chatbots**: Power Discord and Telegram bots with accurate Flow information. - **RAG Systems**: Index content in vector databases for semantic search. - **Development Tools**: Provide context-aware assistance in IDEs like Cursor. ## Integration with AI tools Flow Data Sources is specifically designed to work seamlessly with various AI development tools: ### [Cursor Integration] To add Flow Data Sources to your Cursor documentation, reference the GitHub URL directly. This provides your AI assistant with up-to-date Flow knowledge. ### [ChatGPT Custom GPTs] Upload the merged documentation files to create specialized Flow development assistants that can answer complex questions about Cadence, Flow CLI, and ecosystem tools. ### [Claude Code Integration] Reference Flow Data Sources in your CLAUDE.md files to ensure persistent, comprehensive Flow knowledge across all development sessions. ## Key Benefits **Always Current**: Automatically updated to reflect the latest Flow ecosystem changes and documentation updates. **Comprehensive Coverage**: Includes documentation from all major Flow development tools and resources in one place. **AI-Optimized Format**: Structured specifically for optimal AI processing and accurate response generation. **Multiple Formats**: Different file sizes to accommodate various system requirements and use cases. **Community Driven**: Benefits from contributions across the entire Flow developer ecosystem. ## Best Practices **Choose the Right File**: Use `all_merged.md` for comprehensive coverage or `essentials_merged.md` for lighter integration. **Regular Updates**: Since the files are continuously updated, refresh your AI tool's knowledge base periodically. **Combine with Live Docs**: Use Flow Data Sources alongside live documentation links for the most complete development assistance. **Verify Critical Information**: While highly accurate, always verify critical implementation details against official sources. ## Accessing the Content The merged documentation files are available at: - [All Merged Content] - [Essentials Only] - [Cadence Only] For integration with AI tools like Cursor or ChatGPT, use the appropriate URL as described in the respective tutorials. ## Get Started 1. **Identify Your Use Case**: Determine whether you need comprehensive or essential documentation coverage 2. **Choose Your AI Tool**: Select the AI platform you want to integrate with Flow Data Sources 3. **Follow Integration Guides**: Use the specific tutorial for your chosen AI tool (ChatGPT, Gemini, Cursor, Claude Code, and so on.) 4. **Test and Validate**: Ask Flow-specific development questions to verify that the integration works. The Flow Data Sources repository represents a powerful resource to enhance AI-assisted Flow development, and provides comprehensive and current knowledge that adapts to the rapidly evolving Flow ecosystem. [Flow Data Sources Repository]: https://github.com/onflow/Flow-Data-Sources [All Merged Content]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md [Essentials Only]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/essentials_merged.md [Cadence Only]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/cadence_docs_merged.md [Cursor Integration]: ./indexing-docs.md [ChatGPT Custom GPTs]: ../llms/chatgpt.md [Claude Code Integration]: ../llms/claude-code.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/cursor/indexing-docs.md --- title: Indexing Flow Documentation in Cursor sidebar_label: Indexing Documentation sidebar_position: 2 keywords: - Cursor - AI - documentation indexing - Flow documentation - Cadence documentation - Flow development - AI assistance - Cursor setup --- # Indexing Flow Documentation in Cursor [Cursor] is an AI code editor that makes it easy to write code while building Flow apps. To get the most accurate and helpful responses when developing Flow applications, you need to index the relevant Flow documentation within Cursor. This guide walks you through how to set up comprehensive Flow knowledge in your Cursor environment. ## Prerequisites - [Cursor] installed on your system. - Active internet connection for documentation indexing. - Cursor Pro subscription (recommended for full documentation access). ## Documentation sources For optimal Flow development assistance, you'll want to index three key documentation sources: 1. **Flow Developer Documentation** - Official Flow blockchain and tooling documentation. 2. **Cadence Language Documentation** - Complete Cadence programming language reference. 3. **Flow Data Sources** - Comprehensive, auto-generated knowledge base with current practices. ## Installation steps ### Step 1: Access documentation settings 1. Open Cursor and navigate to **Settings** (or press `Cmd/Ctrl + ,`). 2. Go to **Features > Docs**. 3. Click **"+ Add new doc"** to add documentation sources. ![Cursor Settings](./imgs/use-cursor-1.png) ### Step 2: Add Flow developer documentation 1. In the URL field, enter: `https://developers.flow.com/tools` - **Note**: Use the `/tools` endpoint as it properly indexes all Flow documentation. - Cursor will automatically detect and crawl the entire Flow documentation site. 2. Set the name as **"Flow"**. 3. Click **"Confirm"** to add the documentation. 4. Wait for the indexing process to complete. ![Cursor Settings](./imgs/use-cursor-2.png) ### Step 3: Add Cadence language documentation 1. Click **"+ Add new doc"** again. 2. Enter the Cadence documentation URL: `https://cadence-lang.org/docs/` 3. Set the name as **"Cadence"**. 4. Click **"Confirm"** to add the documentation. 5. Allow time for indexing to complete. ### Step 4: Add Flow data sources 1. Click **"+ Add new doc"** again. 2. Enter the Flow Data Sources URL: `https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md` 3. Set the name as **"Flow Data Sources"**. 4. Click **"Confirm"** to add the documentation. :::caution Resource Requirements The Flow Data Sources file is very large and comprehensive. For older development machines or those with limited resources, we recommend you use the [essentials merged file] instead: `https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/essentials_merged.md` ::: ### Step 5: Verify indexing 1. Monitor the indexing progress in the **Docs** section of Cursor settings. 2. Wait for all three documentation sources to show as "Indexed" or "Ready." 3. Indexing time varies, and depends on your internet connection and system performance. ## Use indexed documentation After indexing finishes, you can reference the documentation in your Cursor prompts: ### Reference syntax - `@Flow` - Reference Flow developer documentation. - `@Cadence` - Reference Cadence language documentation. - `@Flow Data Sources` - Reference the comprehensive Flow knowledge base. ![Cursor Settings](./imgs/use-cursor-3.png) ### Example usage ``` @Flow How do I deploy a contract to Flow Testnet? @Cadence What's the syntax for how to create a resource in Cadence? @Flow Data Sources How do I implement a marketplace for NFTs with royalties? ``` ## Best practices ### When to use each source - **@Flow**: Use for Flow-specific concepts, tools, CLI commands, network information, and ecosystem questions. - **@Cadence**: Use for Cadence programming language syntax, features, patterns, and code examples. - **@Flow Data Sources**: Use for complex questions, advanced patterns, comprehensive tutorials, or when other sources don't provide satisfactory results. ### Prompt optimization - **Be Specific**: Detailed prompts yield more accurate and relevant responses. - **Combine Sources**: Use multiple references for cross-VM applications (`@Flow` and `@Cadence`). - **Context Matters**: Include relevant project context in your prompts. - **Verify Results**: Use documentation to validate AI-generated code and ensure best practices. ### Example combined usage ``` Using @Flow and @Cadence, help me create a transaction that deploys an NFT contract and mints the first token, then show me how to call this from a React app using FCL. ``` ## Troubleshooting ### Common issues and solutions **Documentation Not Indexed**: - Verify all URLs are correct and accessible. - Check your internet connection stability. - Try to re-add the documentation source. **Outdated Information**: - Remove and re-add sources to refresh documentation. - Clear Cursor's cache if available in settings. - Update to the latest version of Cursor. **Slow or Failed Indexing**: - Verify stable internet connection. - Try to index during off-peak hours. - For Flow Data Sources, switch to the essentials merged file if needed. **Inaccurate AI Responses**: - Verify the documentation sources are properly indexed. - Try more specific prompts with clear context. - Cross-reference responses with official documentation. ### Get Help If you continue to experience issues: 1. Check the [Cursor documentation] for additional troubleshooting steps. 2. Verify that all documentation URLs are accessible in your browser. 3. Contact Cursor support through their official channels. 4. Consider an alternative documentation sources if specific URLs are problematic. ## Maintain your setup ### Regular maintenance - **Refresh Periodically**: Re-index documentation monthly to ensure current information. - **Monitor Updates**: Stay aware of major Flow or Cadence documentation updates. - **Clean Up**: Remove unused documentation sources to improve performance. ### Team collaboration For development teams: - Share the same documentation configuration across team members. - Document your specific setup in your project README. - Consider creating team-specific documentation sources for internal patterns and practices. If you follow this setup guide, you'll have comprehensive Flow and Cadence documentation available directly within Cursor, which allows more accurate AI assistance and faster development workflows. [Cursor]: https://www.cursor.com/ [Cursor documentation]: https://docs.cursor.com/ [essentials merged file]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/essentials_merged.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/llms.md --- title: Large Language Models (LLMs) description: Learn how to integrate various AI assistants and large language models with Flow development to enhance productivity, code quality, and development workflows. sidebar_position: 1 keywords: - LLMs - ChatGPT - Gemini AI - Claude Code - AI development - Flow documentation - Cadence development - AI assistance - Flow blockchain - Smart contracts --- # Large Language Models (LLMs) Large Language Models (LLMs) have revolutionized software development by providing intelligent assistance, code generation, and comprehensive knowledge access. You will learn how to enhance your Flow development experience with these LLMs. Each tutorial demonstrates how to configure specialized AI assistants that understand Flow's unique features, Cadence smart contract development, and the broader Flow ecosystem. ## [Use Flow Knowledge Base in ChatGPT] Learn how to create a Custom GPT with ChatGPT that can reference Flow's comprehensive documentation to answer development questions. This guide walks you through how to set up FlowGPT with OpenAI's platform, configure it with Flow Data Sources, and optimize it for Cadence smart contract development. The tutorial covers the complete GPT creation process, from initial setup through testing and deployment, which allows you to have an AI assistant that understands Flow's architecture, tools, and best practices. ## [Use Flow Knowledge Base in Gemini AI] Discover how to build a Custom GEM using Google's Gemini AI platform that specializes in Flow blockchain development. This tutorial demonstrates how to configure Gemini AI with Flow's documentation as its primary knowledge source, which creates an assistant that can provide practical guidance on Cadence programming, Flow CLI usage, and ecosystem integration. Learn how to set up FlowGem with proper instructions, knowledge base integration, and testing protocols to ensure accurate and helpful responses for your development workflow. ## [Claude Code for Flow Development] Master the comprehensive development approach with Claude Code (Claude) for Flow blockchain applications. This extensive guide covers the four-stage development methodology (Idea → Visualization → Planning → Build), advanced features like subagents and auto-verification, and systematic deployment strategies. Learn how to configure persistent project context through `CLAUDE.md` files, implement checkpoint-based development workflows, and leverage Claude's unlimited context windows for complex Cadence smart contract projects and cross-VM applications. --- ## Conclusion These tutorials provide a comprehensive foundation for how to integrate AI assistance into your Flow development workflow. When you leverage ChatGPT's Custom GPTs, Gemini AI's Custom GEMs, and CC's advanced development capabilities, you can significantly enhance your productivity and build on Flow. Each platform offers unique strengths that complement different aspects of blockchain development, from quick documentation queries to comprehensive project management and systematic code generation. [Use Flow Knowledge Base in ChatGPT]: ./chatgpt.md [Use Flow Knowledge Base in Gemini AI]: ./gemini.md [Claude Code for Flow Development]: ./claude-code.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/llms/chatgpt.md --- title: Use Flow Knowledge Base in ChatGPT sidebar_label: Use ChatGPT sidebar_position: 1 keywords: - ChatGPT - AI - OpenAI - Flow documentation - Cadence documentation - Flow development - Flow tools - Flow IDE - Flow setup - Flow configuration - Flow AI assistance --- # Use Flow Knowledge Base in ChatGPT [ChatGPT] is an AI assistant developed by [OpenAI] that can help with tasks such as writing, coding, and answering questions. It adapts to context and user input to provide relevant, conversational responses. You can integrate ChatGPT into developer tools or workflows to assist with documentation, debugging, and productivity. This guide walks you through how to create a **Custom GPT** with ChatGPT that can reference the [Flow Data Sources] file to answer questions.
    :::warning You'll need a [ChatGPT Plus subscription] to use the **Custom GPT** feature. ::: ## 📍 Step 1: Open the "Explore GPTs" section 1. Log in to [ChatGPT]. 2. In the sidebar on the left, click **Explore GPTs**. ![explore gpts](./imgs/explore-gpts.png) --- ## 📍 Step 2: Click "Create a GPT" 1. In the **Explore GPTs** screen, click **"Create"** in the top-right corner. ![create](./imgs/create.png) --- ## 📍 Step 3: Walk through the GPT builder ChatGPT will now guide you through a conversation to set up your custom GPT. First, drag and drop the [Flow Data Sources All Merged] file into the prompt. ### Suggested prompt ```text I want to make a GPT called FlowGPT that uses the linked file as it's primary source. This file changes, so it should reference the live file at least once a day: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md ``` --- ## 📍 Step 4: Configure the GPT's name and instructions ChatGPT may ask you to customize or verify: - **Name and description** of your GPT. - **Instructions**: Tell it how to behave and what to prioritize (for example, always reference the uploaded document). - **Capabilities**: Allow file browsing, code interpreter, or DALL·E if needed. We've found it helpful to suggest: ```text Please imagine you are a fast and smart junior developer who is eager to help and has memorized all the information in the linked file ``` Please let us know if you find any other useful customization prompts! --- ## 📍 Step 5: Test your GPT After the GPT is built, a preview chat window will appear. To test it, ask a few questions based on your uploaded document. --- ## 📍 Step 6: Save and publish (optional) When you're ready: - Click **"Update & Save"** to finalize - You can choose to keep it **private** or make it **public** --- ## ✅ That's it! You've now created a custom GPT that references your uploaded file as a primary source. You can update the file or instructions later if needed. [ChatGPT]: https://chatgpt.com/ [OpenAI]: https://openai.com/ [ChatGPT Plus subscription]: https://chat.openai.com [Flow Data Sources]: ../cursor/flow-data-sources.md [Flow Data Sources All Merged]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/llms/claude-code.md --- title: Claude Code for Flow Development description: Learn how to leverage Claude Code for efficient Cadence smart contract development and Flow blockchain application building with AI-powered workflows, automated testing, and systematic deployment strategies. sidebar_position: 3 label: Claude Code Flow Guide sidebar_label: Claude Code Flow Guide keywords: - Claude Code - AI Development - Flow Blockchain - Flow - Cadence - Smart Contracts - AI-Powered Development - Workflow Automation - Anthropic --- # Claude Code for Flow Development
    [Claude Code] (Claude) provides an AI-powered coding assistant specifically designed for iterative, systematic development, which transforms the development experience. Unlike general-purpose AI tools, Claude breaks down tasks into manageable, incremental steps while it maintains context across your entire development lifecycle. What makes Claude exceptionally powerful is it can maintain unlimited context windows, which allows it to understand entire codebases without the compression limitations that plague other AI coding tools. This comprehensive knowledge allows Claude to deploy multiple subagent instances that work in parallel on complex tasks, iterate continuously until optimal solutions are achieved, and maintain persistent memory of your project's architecture and coding standards across all development sessions. ## Learning objectives After you complete this guide, you'll be able to: - Set up and configure Claude for optimal Flow blockchain development workflows. - Implement the four-stage development methodology (Idea → Visualization → Planning → Build) for Cadence projects. - Configure persistent project context with `CLAUDE.md` files with Flow-specific instructions and MCP tools. - Apply iterative development practices with git-based checkpoint systems for safe blockchain development. - Use advanced Claude features such subagents, auto-verification, and specialized debugging workflows. - Integrate Claude with Flow CLI, FCL, and other Flow development tools for comprehensive project management. - Create and manage team-wide development standards through shared `CLAUDE.md` configurations. ## Prerequisites Before you proceed with this guide, you should have: ### Technical requirements - [Claude Code Subscription]: $200/month plan recommended for comprehensive Flow development features. - [Flow CLI]: Installed and configured for emulator, testnet, and mainnet interactions. - [Git]: Version control system for checkpoint-based development workflow. - [Node.js]: For Claude, FCL integration, and frontend development components. ## Claude setup and configuration ### What is Claude? Claude is an AI-powered code assitant that integrated directly into your terminal. This allows you to use it in any integrated development environment (IDE) or simply from your terminal. The power of Claude comes from its ability to explain complex and large codebases, manage Git workflows, and iterate for long periods of time to accomplish a task. Most IDEs like Cursor rely on their ability to compress the context window that is fed to agents so that their business model justifies a $20 charge while they use expensive LLM models. This naturally decreases the ability of the agents to have a comprehensive understanding of the working codebase when they manage with large or complex codebases. This is why Claude can be so powerful, because it can include entire codebases in its context, deploy other instances of Claude to work on multiple actions in parallel, and iterate on its results in order to achieve better results. ### Installation and subscription Claude requires a [subscription] to access its full development capabilities. There are three subscription levels: Pro, Max 5x, and Max 20x. The Pro plan is very limited, so expect it to only be sufficient for testing and experimentation. The $200/month Max 20x plan is recommended for developers with a lot of projects or if you need to build something quickly where time is crucial. This plan grants you access to: - Unlimited context windows for complex smart contract projects. - Advanced subagent capabilities for parallel development tasks. - Persistent memory across development sessions. - Integration with MCP (Model Context Protocol) servers. - Team collaboration features through shared configurations. You can also use the API pricing, but we don't recommend it, since any meaningful implementation of Claude most likely requires more than $100 in API credits. ### Initial configuration To install Claude, run the following command: ```bash npm install -g @anthropic-ai/claude-code ``` After the installation process completes, navigate to your project and start Claude: ```bash cd your-awesome-project claude ``` This automatically installs the extension. Run the `/ide` command in the Claude terminal to make sure your IDE is connected to Claude. With the extension installed, click on the orange Anthropic logo on the upper right hand of the screen in order to launch Claude in a separate window. ![Claude Code Extension](./imgs/CC_logo.png) ### CLAUDE.md files `CLAUDE.md` files are configuration files that contain project-specific instructions and context for Claude. They allow you to define development standards, frequently used commands, and project architecture that the AI remembers across all coding sessions. They are similar to Cursor Rules, but they differ in that `CLAUDE.md` only specifies the configuration of Claude. If you know what type of information to place in your `CLAUDE.md` file, create your primary `CLAUDE.md` file in the project root. Use the `/init` command in Claude to generate the initial structure, then customize for Flow development: Create your Flow project with the standard directory structure: ``` flow-project/ ├── .claude/ │ └── CLAUDE.md # Project-wide AI instructions ├── cadence/ │ ├── .claude/ │ │ └── CLAUDE.md # Cadence-specific instructions │ ├── contracts/ │ ├── transactions/ │ └── scripts/ ├── frontend/ │ ├── .claude/ │ │ └── CLAUDE.md # Frontend-specific instructions │ └── src/ ├── flow.json # Flow project configuration └── package.json ``` #### 3. Root CLAUDE.md configuration Place `CLAUDE.md` in the root file sets the instructions you want Claude to do frequently, such as: - Bash commands you want to run frequently. - Files it should really know about when it makes changes or big architectural decisions. - MCP servers. This file is great to share across your team so you set it once and everyone has the same extended functionality. **Team Configuration Setup**: ```md # Flow Project AI assistant configuration ## Project overview This is a Flow blockchain application with Cadence smart contracts and FCL frontend integration. ## Team-wide development standards - MCP servers standardized across development environments. - Git workflow and commit message standards enforced. - Follow official Flow documentation patterns. - Use incremental, checkpoint-based development. - Test on emulator before testnet deployment. - Implement proper resource handling with @ and & syntax - Follow MetadataViews standards for NFT projects. ## Frequently used commands - `flow emulator start` - Start local development environment. - `flow project deploy --network emulator` - Deploy contracts locally. - `flow transactions send ./cadence/transactions/example.cdc --network emulator` - Execute transactions locally. - `npm run dev` - Start frontend development server. ## Key files to reference - `flow.json` - Project configuration and contract deployments. - `cadence/contracts/` - Smart contract implementations. - `frontend/src/config.js` - FCL configuration and contract addresses. ## MCP servers - Use flow-mcp to read blockchain data, manage accounts, check balances, and interact with native contracts. - Use flow-defi-mcp to check token prices, swap tokens on decentralized exchanges, and interact with ERC20 tokens. ## Architecture notes Document your specific project architecture, contract relationships, and deployment strategies ``` #### 3. Nested CLAUDE.md files To maintain a more granular control of the capabilities of Claude when you work with different areas of your repo, you can create specialized instructions for different project areas. To do this, place a nested `CLAUDE.md` file in subdirectories in your repo(cadence, frontend, backend, and so on). Claude will automatically read these files when working on these subdirectories. Here is an example: **cadence/.claude/CLAUDE.md:** ```md # Cadence development instructions ## Syntax requirements - Always use proper resource syntax: @{NonFungibleToken.NFT} - Implement required interfaces: NonFungibleToken, MetadataViews. - Use view functions for read-only operations. - Follow auth capability patterns for transactions. ## Testing protocol - Write unit tests for all contract functions. - Test resource creation and destruction. - Verify proper event emission. - Validate access controls and permissions. - Test for breaking changes and edge cases. ## Standard patterns Reference the Flow documentation for: - Contract deployment and initialization. - Resource collection patterns. - Proper error handling and panics. - Gas optimization techniques. ``` **frontend/.claude/CLAUDE.md:** ```markdown # Frontend FCL integration instructions ## Configuration management - Keep contract addresses in environment variables. - Use proper network switching logic. - Implement user authentication flows. - Handle transaction status updates. ## Best practices - Show loading states for blockchain interactions. - Provide clear error messages for failed transactions. - Cache contract data when appropriate. ``` #### Local Claude.md You can also create a `CLAUDE.local.md` file that is used just for you and not shared with your team. ## Workflow strategies Claude excels when it follows a structured development approach. We recommend you implement this four-stage methodology: ### Stage 1: Idea development **Objective**: Bounce ideas with Claude to better understand of what you can build and why it would work. **Process**: 1. Click `Shift` + `Tab` to cycle through the different response forms that Claude offers until you reach the Plan Mode. ![Plan Mode](./imgs/plan_mode.png) 2. Describe your Flow project concept to Claude. 3. Ask for requirement analysis and technical feasibility assessment. **Example conversation**: ``` User: "I want to create a collectible card game on Flow where players can battle and evolve their cards" Claude Response: [Analyzes requirements, suggests NFT architecture, identifies game mechanics, proposes contract structure] ``` **Outputs**: - Detailed project requirements document. - Technical architecture overview. - Flow-specific implementation considerations. - Resource and timeline estimates. ### Stage 2: Visualization **Objective**: Create visual representations and demos to validate project concepts before development. You can use Claude with this process, but it is best to combine LLM models like Gemini 2.5 in order to create the visual representations. **Tools and techniques**: - **Mermaid Diagrams**: Generate contract relationship diagrams, user flow charts, and system architecture visuals. - **UI Mockups**: Create interface mockups for frontend applications. - **Contract Schemas**: Visualize data structures and resource relationships. - **Transaction Flow Maps**: Diagram user interactions and blockchain state changes. **Example workflow**: ``` User: "Create a diagram showing how card evolution works in my game" [Claude generates Mermaid diagram showing contract interactions, resource transformations, and event emissions] ``` **Claude code commands**: - Use image upload to share visual concepts. - Request Mermaid diagrams for complex systems. - Ask for UI mockups based on Flow wallet integration patterns. ### Stage 3: Planning with Claude code **Objective**: Break down the project into manageable, incremental development tasks with Claude's planning capabilities. **Planning process**: 1. **Turn On Planning Mode**: Explicitly tell Claude you're in planning phase or cycle through the different modes (with `Shift` + `Tab`) until you reach Plan Mode. 2. **Confirm Knowledge Comprehension**: Ask Claude to read important files first and then confirm the knowledge with you. Immediately ask it questions about the codebase to verify its knowledge. 3. **Plan Generation**: Tell Claude to generate an action plan that is incremental and has specific goals and checkpoints (this file can be called `T0-DO.md` and placed in a `/tasks` folder that contains relevant information about the completion of specific tasks or steps). 4. **Task Breakdown**: Request comprehensive task decomposition for incremental changes. 5. **Dependency Mapping**: Identify task dependencies and critical path. 6. **Checkpoint Strategy**: Define git commit points and testing milestones. **Example planning session**: ``` User: "Set up a roadmap and plan of the to-do's that need to be completed for my card game NFT contracts" Claude Response: Task 1: Set up basic NFT contract structure - Implement NonFungibleToken interface - Add MetadataViews compatibility - Create basic card resource - Write deployment transaction - Checkpoint: Basic NFT functionality Task 2: Add game mechanics - Implement battle system - Add evolution functionality - Create game state management - Write game transaction scripts - Checkpoint: Core game mechanics [Continues with detailed breakdown] ``` **Plan outputs**: - Generate a detailed to-do list that is thoroughly divided between goals, objectives, checkpoints and tasks that need to be achieved in an incremental manner. - Dependency graph showing task relationships. - Test strategies for each development phase. - Deployment sequence and validation protocols. A downside of Claude is that it doesn't have a checkpoint control like the agent chat does in Cursor. If you make frequent git commits and work on separate branches, it can help mitigate this. Never attempt to give Claude a big task as it most likely doesn't have enough knowledge about the task at hand to complete it successfully. ### Stage 4: Build execution **Objective**: Implement planned tasks systematically with Claude's development capabilities. **Build process**: 1. **Task Assignment**: Work on one incremental task at a time. 2. **Implementation**: Use Claude to generate code, debug issues, and optimize solutions. 3. **Reporting**: After it completes a task, Claude generates a report of what it did and why it did it in a `.md` file in the `/tasks` folder so that you can have better understand the changes made. 3. **Validation**: Test each component thoroughly before you proceed. 4. **Documentation**: Generate inline documentation and update project docs. 5. **Checkpoint**: Commit working code with descriptive messages. 6. **Updating**: Ask Claude to update the `TO-DO.md` with the completed steps and changes after the commit is approved. **Development workflow**: ``` User: "Implement Task 1: Basic NFT contract structure" [Claude generates contract code, deployment scripts, and tests] User: "Test this implementation" [Claude provides testing commands and validation scripts] User: "Commit this checkpoint" [Claude suggests commit message and validates completion] ``` ## Advanced Claude features ### Subagent utilization For complex Flow projects, leverage Claude's subagent capabilities to handle parallel development tasks: **When to use subagents**: - To develop multiple contracts simultaneously. - Frontend and backend development in parallel. - To test different implementation approaches. - Documentation generation while coding. - To deal with a big task so that Claude can deploy subagents to break down the task into smaller components that are running in parallel. **Example subagent usage**: ``` User: "Create subagents to develop the NFT contract and the marketplace contract in parallel" [Claude spawns separate conversation threads for each contract, which maintains coordination between them.] ``` ### Auto-verification and iteration Configure Claude to automatically verify its work and iterate for improvements: **Verification patterns**: - **Compilation Checks**: Automatically test Cadence syntax after code generation. - **Test Execution**: Run unit tests and integration tests after implementation. - **Deployment Validation**: Verify contract deployment on emulator before you suggest testnet deployment. ### Memory and context management **Use the # memory mode**: Press `#` to enter memory mode and specify important information for Claude to remember: ``` # Remember that this project uses a modular NFT architecture with separate traits contracts. # Remember that we need to use a DS Proxy system for contract upgrades. ``` **Context optimization**: - Use `Ctrl+R` for verbose output when you debug complex issues. - Compact conversations at natural breakpoints (around 20% context usage). - Constantly refactor `CLAUDE.md` to take into accounts changes made throughout the development process. - Maintain focused conversations for specific development tasks. ## Development workflows and best practices Give Claude some sort of tool it can use for feedback (MCP or tool) to check its work and it will iterate by itself to get better results. Claude can iterate for hours if needed, but it needs to be able to analyze its work. These alternative workflows can be very useful as well, but they depend on your ability to close the feedback loop so that Claude can analyze and comprehend the results of its code generation: ### Test-driven development with Claude **Workflow**: Write Tests → Commit → Code → Iterate → Commit ``` User: "Write tests for card evolution functionality first" [Claude generates comprehensive test suite] User: "Now implement the evolution logic to pass these tests" [Claude implements feature with test-driven approach] ``` ### Screenshot-driven development **Workflow**: Write Code → Screenshot Result → Iterate Particularly useful for frontend development: ``` User: "Implement this card display component" [Claude generates React component] User: [Uploads screenshot of result] Claude: "I see the card layout needs improvement. Let me adjust the CSS..." ``` ### Checkpoint-based development **Best practices**: - Commit after each completed task. - Use descriptive commit messages that Claude generates. - Create branches for experimental features. - Tag stable releases for easy rollback. **Example checkpoint strategy**: ``` git commit -m "feat: implement basic NFT contract with MetadataViews - Add NonFungibleToken interface implementation - Include required MetadataViews for marketplace compatibility - Create basic card resource with metadata - Add deployment transaction and initialization script - All tests passing on emulator Checkpoint: Basic NFT functionality complete" ``` ### Error resolution and debugging **Systematic debugging approach**: 1. **Error Analysis**: Provide Claude with complete error messages and context. 2. **Root Cause Investigation**: Let Claude analyze potential causes. 3. **Solution Implementation**: Apply suggested fixes incrementally. 4. **Verification**: Test fixes thoroughly before you proceed. 5. **Documentation**: Update project documentation with lessons learned. **Example debugging session**: ``` User: "Getting authorization error in my transaction" Claude: "Let me analyze the auth capability requirements. I see the issue is with the granular auth pattern. Here's the fix..." [Provides corrected transaction with proper auth syntax] ``` ### Multi-network deployment **Deployment workflow with Claude**: 1. **Emulator Testing**: Comprehensive local testing and validation. 2. **Configuration Update**: Update flow.json and FCL config for testnet. 3. **Testnet Deployment**: Deploy and validate on testnet. 4. **Frontend Integration**: Update frontend configuration and test user flows. 5. **Mainnet Preparation**: Final validation and deployment to mainnet. ### MCP server share You can set up [MCPs] for Claude to use as tools. These can also be set up in the `CLAUDE.md` file so that every team member consistently uses the same MCPs. Share the `/Claude/mcp.json` files so that the team can use the same MCP servers. **Team MCP configuration**: To grant Claude Code [access to use an MCP server], run the following commands: ```bash # Adding a MCP server claude mcp add [args...] # Adding a local server claude mcp add my-server -e API_KEY=123 -- /path/to/server arg1 arg2 ``` You can also try these [MCPs for Flow development]: ```bash # Shared MCP servers for team consistency claude mcp add flow_mcp claude mcp add flow-defi-mcp ``` ### Version control for AI configuration **Best practices**: - Include `CLAUDE.md` files in version control. - Document MCP server configurations in README. - Share `CLAUDE.local.md` patterns (and don't commit personal configs). - Maintain team coding standards through shared AI instructions. ## Key bindings and shortcuts ### Essential Claude shortcuts | Shortcut | Function | Flow Development Usage | |-------------|-------------------|------------------------| | `#` | Memory mode | Store project architecture decisions | | `Shift+Tab` | Auto-accept edits | Quickly accept generated Cadence code | | `!` | Bash mode | Execute Flow CLI commands directly | | `@` | Add file/folder | Reference contracts, transactions, configs | | `Esc` | Cancel operation | Stop incorrect generation and execution | | `Ctrl+R` | Verbose output | Detailed debugging for complex issues | ### Flow-specific usage patterns **Memory mode examples**: ``` # This project follows the composite NFT pattern with separate trait contracts. # Gas optimization is critical - avoid loops in public functions. # All contracts must support MetadataViews for marketplace compatibility. ``` **File reference patterns**: ``` @flow.json - Project configuration @cadence/contracts/MyNFT.cdc - Main NFT contract ``` ## Troubleshooting and optimization ### Common issues and solutions **Context window management**: - Compact conversations at natural breakpoints or manually when around 20% of context usage remains. - Use focused sub-conversations for specific tasks. - Reference key files rather than copying entire contents. **Performance optimization**: - Use the $200/month plan for complex Flow projects. - Turn on auto-compact to prevent context overflow. - Break large tasks into smaller, focused conversations. - Hit `Esc` often if you see the agent on the wrong path and ask it to undo its recent action. **Integration problems**: - Verify MCP server configurations. - Check Flow CLI integration and permissions. - Validate `CLAUDE.md` file syntax and structure. ### Best practices for Flow development **Project management**: - Maintain clear separation between contracts, transactions, and frontend code. - Use nested `CLAUDE.md` files for different development areas. - Keep project documentation synchronized with implementation. **Code quality**: - Always compile Cadence code before deployment. - Use Claude for security review suggestions. - Implement comprehensive testing at each development stage. **Deployment management**: - Test thoroughly on emulator before testnet deployment. - Validate FCL configuration changes across networks. - Use systematic deployment checklists that Claude generates. ## Conclusion In this guide, you explored how to leverage Claude for efficient Flow blockchain and Cadence development. You learned to implement a systematic four-stage development methodology that transforms ideas into production-ready applications through AI-assisted visualization, planning, and execution. You discovered how to configure persistent project context through `CLAUDE.md` files, allowing your AI assistant to maintain comprehensive understanding of Flow-specific patterns, project architecture, and team standards across all development sessions. The integration of specialized tools like Flow CLI, FCL configuration management, and MCP servers creates a comprehensive development environment optimized for blockchain application building. The systematic approaches covered - from test-driven development and checkpoint-based workflows to subagent utilization and auto-verification - provide a foundation for building complex Flow applications with confidence and efficiency. The emphasis on incremental development, comprehensive testing, and systematic deployment ensures your projects meet the reliability requirements essential for blockchain applications. Now that you have completed this guide, you should be able to: - Set up and configure Claude for optimal Flow blockchain development workflows with persistent context and specialized tooling. - Implement the four-stage development methodology (Idea → Visualization → Planning → Build) for systematic Cadence project development. - Apply advanced Claude features including subagents, auto-verification, and team collaboration patterns for complex Flow applications. - Integrate Claude seamlessly with Flow CLI, FCL, and other Flow development tools for comprehensive project management across emulator, testnet, and mainnet environments. The combination of AI-powered development assistance with Flow's comprehensive toolchain creates an unprecedented opportunity for building sophisticated blockchain applications efficiently and reliably. As you continue developing on Flow, these systematic approaches will help you maintain high code quality while aClaudeelerating your development velocity. [Claude Code]: https://docs.anthropic.com/en/docs/claude-code/overview [Claude Code Subscription]: https://claude.ai/upgrade [Flow CLI]: https://developers.flow.com/tools/flow-cli [Git]: https://git-scm.com/downloads [Node.js]: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm [subscription]: https://claude.ai/upgrade [MCPs]: https://docs.anthropic.com/en/docs/claude-code/mcp [access to use an MCP server]: https://docs.anthropic.com/en/docs/claude-code/mcp [MCPs for Flow development]: https://github.com/Outblock/flow-mcp-monorepo --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/llms/gemini.md --- title: Use Flow Knowledge Base in Gemini AI sidebar_label: Use Gemini AI sidebar_position: 2 keywords: - Gemini AI - AI - Google - Flow documentation - Cadence documentation - Flow development - Flow tools - Flow IDE - Flow setup - Flow configuration - Flow AI assistance --- # Use Flow Knowledge Base in Gemini AI [Gemini AI] is Google's AI assistant that can help with tasks such as writing, coding, and answering questions. It adapts to context and user input to provide relevant, conversational responses. You can integrate Gemini AI into developer workflows to assist with documentation, debugging, and productivity. This guide walks you through how to create a **Custom GEM** with Gemini AI that can reference the [Flow Data Sources] file to answer questions. :::warning You'll need a [Gemini Advanced subscription] to use the **Custom GEM** feature. ::: ## Step 1: Access Gem Manager 1. Log in to [Gemini AI]. 2. In the sidebar on the left, click **Explore Gems**. --- ## Step 2: Create a new Gem 1. In the **Gem Manager** screen, click **"New Gem"**. --- ## Step 3: Configure your Gem Gemini AI will now guide you through how to set up up your custom Gem. Configure the name, description and instructions for your Gem to follow. ### Suggested configuration **Name**: FlowGem **Description**: An AI assistant specialized in Flow blockchain development, Cadence smart contracts, and Flow ecosystem tools. **Instructions**: ```text You are FlowGem, a specialized AI assistant for Flow blockchain development. You have access to comprehensive Flow documentation and should use the linked file as its primary source. This file changes, so it should reference the live file at least once a day: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md Key behaviors: - Always reference the uploaded Flow documentation when answering questions. - Provide practical, actionable advice for Flow developers. - Include relevant code examples when applicable. - Stay up-to-date with the latest Flow ecosystem developments. - Be eager to help and imagine you are a knowledgeable Flow developer. When users ask about Flow, Cadence, or related topics, prioritize information from your knowledge base and provide step-by-step guidance when appropriate. ``` To further customize your personalized agent, you can provide more files and determine the actions it can do. --- ## Step 4: Upload knowledge base 1. In the **Knowledge** section, upload the [Flow Data Sources All Merged] file. 2. Configure the Gem to reference this file as its primary knowledge source. --- ## Step 5: Test your Gem After you configure the Gem, ask it Flow-related questions to test it: - "How do I deploy a smart contract to Flow Testnet?" - "What's the syntax for Cadence resources?" - "How do I set up Flow CLI?" --- ## Step 6: Save and deploy When you're satisfied with the performance, click **"Create Gem"** to finalize. - Your Gem will be available in your Gem Manager. - You can share it with your team or keep it private. --- ## Conclusion You've now created a custom Gem that uses Flow's comprehensive documentation as its knowledge base. Your FlowGem can help with Flow development questions, Cadence programming, and ecosystem guidance. [Gemini AI]: https://gemini.google.com/ [Gemini Advanced subscription]: https://gemini.google.com/advanced [Flow Data Sources]: ../cursor/flow-data-sources.md [Flow Data Sources All Merged]: https://github.com/onflow/Flow-Data-Sources/blob/main/merged_docs/all_merged.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/mcp.md --- title: Flow MCP sidebar_label: Flow MCP sidebar_position: 4 keywords: - AI - MCP - MCP server - Flow MCP - Model Context Protocol - AI Tools - Flow AI assistance --- # Flow MCP (Model Context Protocol) The Model Context Protocol (MCP) is an open standard that allows AI applications to interact seamlessly with external tools, systems, and data sources. Flow MCP extends this protocol to provide AI tools with direct access to Flow blockchain data, smart contracts, and onchain operations. This integration allows developers to enhance their AI-powered development workflows with real-time blockchain information and automated Flow interactions. Flow MCP transforms how developers work with the Flow blockchain. It brings blockchain capabilities directly into AI-powered code editors and development tools, which eliminates the need to switch between different interfaces and allows more efficient, context-aware development experiences. ## [Use Flow MCP in Cursor] Learn how to integrate the Flow MCP server with Cursor to turn on AI-driven blockchain queries directly within your code editor. This tutorial guides you through how to set up Flow MCP in Cursor, which allows the AI to fetch onchain data such as account balances, contract information, and blockchain state without the need to leave your development environment. By the end of this tutorial, you'll be able to ask Cursor's AI to perform Flow blockchain operations, speed up development workflows, and access live blockchain data for enhanced debugging and prototyping. ## [Contribute to Flow MCP] Discover how to create custom Action Tools that add new blockchain interaction capabilities and extend the Flow MCP server. This comprehensive guide walks you through the development process, from how to set up the development environment to how to submit pull requests for new features. Learn to create new tools with proper schemas, handlers, and tests, while you follow Flow MCP's contribution guidelines. This tutorial empowers developers to expand the Flow MCP ecosystem with specialized blockchain tools that benefit the entire Flow developer community. ## Conclusion Flow MCP bridges the gap between AI development tools and blockchain functionality, which allows developers to access Flow's comprehensive blockchain features directly through AI-powered interfaces. Whether you use MCP tools in Cursor or want to contribute new capabilities to the Flow MCP server, these tutorials provide the foundation for you to integrate blockchain operations into your AI-enhanced development workflow. [Use Flow MCP in Cursor]: ./use-mcp-in-cursor.md [Contribute to Flow MCP]: ./contribute-to-mcp.md --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/mcp/contribute-to-mcp.md --- title: Contribute to Flow MCP sidebar_label: Contribute to Flow MCP sidebar_position: 2 keywords: - AI - MCP - MCP server - Flow MCP - Model Context Protocol - Flow AI assistance - Build MCP server --- # Contribute to Flow MCP This tutorial will guide you through how to contribute to the Flow MCP server. The [Model Context Protocol (MCP)] is an open standard developed by Anthropic that allows AI applications to interact seamlessly with external tools, systems, and data sources. ## Learning objectives After you complete this tutorial, you should be able to: - Set up and build the Flow MCP server development environment. - Create and register a new Action Tool, including schema, handler, and tests. - Test and validate the functionality of a new Action Tool within the MCP system. - Submit a complete pull request that follows Flow MCP contribution guidelines. ## Prerequisites - [Bun] - the JavaScript runtime. - [Flow MCP server] - the Flow MCP server repository. ## Installation 1. Fork the [Flow MCP server] repository. 2. Clone the repository: ```bash git clone https://github.com/your-username/flow-mcp.git ``` 3. Install the dependencies: ```bash bun install ``` 4. Build the project: ```bash bun build ``` ## Create new action tool for Flow MCP 1. Create a new folder in the `src/tools` directory: ```bash mkdir src/tools/your-tool-name ``` 2. Create and implement the `index.ts`, `schema.ts`, and `your-tool.test.ts` files, which is the entry point, schema, and test file for the new tool respectively. The `export` of `index.ts` file should be a `ToolRegistration` object, which is the registration of the new tool. ```ts type ToolRegistration = { name: string; description: string; inputSchema: z.ZodSchema; handler: (args: T) => CallToolResult | Promise; }; ``` If you want to add new Cadence files for your new tool, you can add them in the `src/cadence` directory. The `bun` will compile the Cadence files into `String`, so the dedicated Cadence files will help the project to be more organized. And we recommended that you add a test for your new tool in the `src/tools/your-tool-name/your-tool.test.ts` file. 3. Add a prompt export in the `src/prompts` directory which is used to confirm that MCP clients can understand the new tool. You can refer to the current tools for examples. 4. Add your new tool to the `src/tools/index.ts` file. ```ts export const createTools = (): ToolRegistration[] => { return [ // ... other tools yourTool, ]; }; ``` 5. Run the test to confirm your new tool works as expected: ```bash bun test ``` 6. Commit and push your changes to your forked repository, and create a pull request. We will review your pull request and merge it if it's ready. [Flow MCP server]: https://github.com/outblock/flow-mcp [Bun]: https://bun.sh/ [Model Context Protocol (MCP)]: https://modelcontextprotocol.io/introduction --- # File: /md/blockchain-development-tutorials/use-AI-to-build-on-flow/mcp/use-mcp-in-cursor.md --- title: Use Flow MCP in Cursor sidebar_label: Use Flow MCP in Cursor sidebar_position: 1 keywords: - AI - Cursor - MCP - MCP server - Model Context Protocol - Flow MCP - AI Tools - Flow AI assistance --- # Use Flow MCP in Cursor
    When you add Flow MCP to Cursor, it gives you powerful AI-driven tools directly inside your code editor. It allows Cursor's AI to understand, query, and interact with Flow blockchain data and smart contracts through a standard protocol called the Model Context Protocol (MCP). Specifically, it lets you: - Ask the AI in Cursor to fetch onchain data such as account balances, account information, or contract source code without the need to leave your editor. - Speed up development when you have AI perform blockchain queries that would normally require manual steps. - Improve context for AI assistance if you let Cursor to pull real blockchain data when needed. - Automate routine Flow tasks with tools exposed by the MCP server. - Prototype and debug faster with direct access to live blockchain information. This tutorial will guide you through setting up and using Flow MCP in [Cursor] to enhance your Flow blockchain development experience with AI assistance. ## Learning objectives After you complete this tutorial, you should be able to: - Configure Cursor to connect with the Flow MCP server with the MCP protocol. - Install and launch the Flow MCP server locally through Cursor. - Identify when Flow MCP tools successfully load and are ready inside Cursor. - Use Flow MCP tools to retrieve blockchain data such as account balances, account details, and contract source code. - Troubleshoot common setup and connectivity issues between Cursor and Flow MCP. ## Prerequisites - [Cursor] - the AI code editor. - [Flow MCP GitHub Repository] - the Flow MCP server repository. ## Installation 1. Open Cursor Settings and go to the "MCP" tab. ![Cursor Settings](./imgs/mcp-settings-in-curosr.png) 2. Configure the MCP configuration file in Cursor: The MCP configuration file resides at this location based on your operating system: - macOS: `~/Library/Application Support/Claude/mcp.json` - Windows: `%APPDATA%/Claude/mcp.json` - Linux: `~/.config/Claude/mcp.json` Add the following configuration: ```json { "mcpServers": { "flow-mcp": { "command": "npx", "args": ["-y", "@outblock/flow-mcp"] } } } ``` 3. Restart Cursor to load the new MCP configuration. You need to wait for the MCP server to start. After it's ready, a green spot appears in the left side of `flow-mcp` server name label, and all tools for Flow MCP display. ![Flow MCP server ready](./imgs/flow-mcp-enabled.png) ## How to use Flow MCP in Cursor ### Check Flow balance ![Sample Image 1](./imgs/sample-1.png) ### View account information ![Sample Image 2](./imgs/sample-2.png) ### Get contract source code ![Sample Image 3](./imgs/sample-3.png) ## Troubleshoot If you encounter any issues: 1. Ensure the MCP server is properly installed. 2. Verify the configuration file is in the correct location. 3. Check that the paths in the configuration are correct. 4. Try to restart Cursor. 5. Check the console for any error messages. ## Additional resources - [Flow MCP GitHub Repository] - [Cursor Documentation] - [Flow Documentation] ## Support For issues or questions: - Open an issue on the [Flow MCP GitHub Repository]. - Join the [Flow Discord] community. [Cursor]: https://www.cursor.com/ [Flow MCP GitHub Repository]: https://github.com/outblock/flow-mcp [Cursor Documentation]: https://cursor.sh/docs [Flow Documentation]: https://developers.flow.com/ [Flow Discord]: https://discord.gg/flow --- # File: /md/build/cadence/advanced-concepts/account-abstraction.md --- title: Build Faster with Flow’s Native Account Abstraction description: Flow is a fast blockchain with account abstraction built in, enabling developers to deliver smooth onboarding, secure transactions, and better Web3 UX. keywords: - account abstraction - multi-sig - sponsored transactions - bundled transactions - account recovery - multi-factor authentication sidebar_position: 2 --- # Blockchain Account Abstraction Flow is a fast blockchain with account abstraction, designed to make Web3 as seamless as Web2. It provides native support for key use cases that Account Abstraction , which empowers developers to deliver mainstream-ready user experiences. With Cadence, Flow was designed with these use cases in mind through the separation of the contract and transaction layers. This guide demonstrates how Flow supports key use cases that Account Abstraction makes possible. ## Multi-sig transactions on a Fast Blockchain with Account Abstraction Since accounts are smart contracts, they can be defined in order to require multiple signatures in order to execute a transaction, which unlocks a range of new users that improve the user experience for Web3 apps. | Account Abstraction | Flow | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The move from from Externally-Owned Accounts (EOAs) to smart contract accounts allows developers to build in logic to require multiple signatures to execute transactions. | Flow has native support for multi-sig transactions since all accounts are defined as smart contracts. Flow provides [support for multiple keys] to be added to an account and weights can be applied to denote relative priority. | ## Sponsored transactions for mainstream-ready Web3 apps The requirement that users pay fees to execute transactions in order to use Web3 apps can be a hurdle for newcomers as they begin to explore these experiences. In order to remove this significant point of friction that requires newcomers to acquire crypto before they can get started with an app, developers can subsidize these costs on behalf of users. | Account Abstraction | Flow | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The ERC-4337 standard introduces the concept of [paymasters], which can allow a developer to pay the fees for a transaction for their users. | Flow has built-in support for [3 different roles] for transactions which provides native support for sponsored transactions. | ## Bundled transactions for faster user experience To deliver a more streamlined user experience that reduces the amount of interruptions in the form of transaction approvals developers can bundle multiple transactions together into a single transaction that executes the set of operations with one signature. | Account Abstraction | Flow | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The ERC-4337 standard outlines support for bundled transactions through a new mempool that holds user operations from smart wallets. Bundlers package sets of these user operations into a single transaction on the blockchain and return the result back to each wallet. | Since Cadence has an explicit separation of contracts and transactions, Flow has protocol-level support to bundle transactions across multiple contracts into a single transaction. | ## Account recovery Account Abstraction allows developers to build more robust account management features for users, which addresses the major pain point where users lose access to assets forever if they lose the keys to their account. Apps can let recover access to their accounts and enclosed assets through social recovery or pre-approved accounts. | Account Abstraction | Flow | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Smart contract accounts can be defined to include account recovery logic that allows users to define custom recovery methods that can rely on specific accounts or other validated sources. | Since all accounts are smart contracts, Flow has native support for account recovery and cycling of keys to help users regain access to accounts in a secure manner. | ## Multi-factor authentication Multi-factor authentication is a broadly accepted concept in Web2 apps for secure access to accounts and Account Abstraction allows developers to deliver the same benefits to Web3 users. | Account Abstraction | Flow | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Smart contract accounts can require a secondary factor to confirm transactions which can be delivered in the form of familiar confirmation channels such as email or SMS. | Since all accounts are smart contracts, Flow has native support for multi-factor authentication as developers can implement these security mechanisms for their users. | ## Seamless experience Account Abstraction brings the potential for dramatic improvements to the user experience of Web3 apps. Developers can introduce conditions under which a user can grant a smart contract account to pre-approve transactions under certain conditions, which reduces interruptions for the user to explicitly sign each transaction. These improvements are especially notable on mobile, where the previous jarring experience required users to switch between apps in approve transactions. | Account Abstraction | Flow | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Developers can build new features that streamline the user experience of Web3 apps, such as 'session keys' that pre-approve transactions for a period of time or set custom limits on transaction volume or network fees. | Since all accounts are smart contracts, Flow has support for these new controls that allow apps to sign pre-approved transactions based on user controls and preferences. | ## Conclusion Flow delivers more than just developer convenience, it’s a **high-performance blockchain with account abstraction** built directly into its core protocol. Flow combines speed, scalability, and advanced features like multi-sig, sponsored transactions, bundled operations, account recovery, and multi-factor authentication. This empowers developers to create secure, seamless, and mainstream-ready Web3 experiences but not sacrifice performance. [support for multiple keys]: ../basics/accounts.md#account-keys [paymasters]: https://eips.ethereum.org/EIPS/eip-4337#extension-paymasters [3 different roles]: ../basics/transactions.md#signer-roles --- # File: /md/build/cadence/advanced-concepts/computation-profiling.md --- title: Cadence Computation Profiling description: This guide provides comprehensive instructions for using the computation profiling and reporting features in the Flow Emulator. These tools help Cadence developers analyze and optimize their smart contracts by understanding computational costs and identifying performance bottlenecks. keywords: - computation profiling - Flow Emulator - smart contract optimization - performance analysis - computational costs - Cadence - profiling - performance bottlenecks sidebar_position: 1 --- # Cadence Computation Profiling This guide provides comprehensive instructions for using the computation profiling and reporting features in the Flow Emulator. These tools help Cadence developers analyze and optimize their smart contracts by understanding computational costs and identifying performance bottlenecks. ## Overview When developing smart contracts on Flow, understanding computational costs is essential for: - **Performance Optimization**: Identify slow operations and optimize your code - **Cost Awareness**: Understand how much computation your transactions and scripts consume - **Bottleneck Identification**: Pinpoint exactly where your code spends the most resources The Flow CLI provides three complementary approaches for profiling computation: | Approach | Output | Best For | |----------|--------|----------| | **Transaction Profiling** (`flow transactions profile`) | pprof profile for sealed transactions | Analyzing production transactions on mainnet/testnet | | **Emulator Computation Reporting** (`flow emulator --computation-reporting`) | JSON report with detailed intensities | Quick numerical analysis, CI/CD integration, automated testing | | **Emulator Computation Profiling** (`flow emulator --computation-profiling`) | pprof profile for development | Visual analysis during development, flame graphs, call stack exploration | :::note Before getting started, make sure you have the [Flow CLI installed](../../tools/flow-cli/install.md). ::: ## Transaction Profiling For analyzing sealed transactions on any Flow network (mainnet, testnet, or emulator), use the `flow transactions profile` command. This is particularly useful for: - **Production Analysis**: Profile real transactions on mainnet to understand actual performance - **Debugging High Costs**: Investigate why a specific transaction used more computation than expected - **Post-Deployment Optimization**: Analyze live transactions to identify optimization opportunities ### Basic Usage ```bash # Profile a mainnet transaction flow transactions profile 07a8...b433 --network mainnet # Profile with custom output location flow transactions profile 0xabc123 --network testnet --output my-profile.pb.gz ``` ### Analyzing Transaction Profiles The command generates a pprof profile that can be analyzed with standard tools: ```bash # Interactive web interface go tool pprof -http=:8080 profile-07a8b433.pb.gz # Command-line analysis go tool pprof -top profile-07a8b433.pb.gz ``` 📖 **[Learn more about transaction profiling](../../tools/flow-cli/transactions/profile-transactions.md)** :::info Transaction profiling works with sealed transactions on any network, while emulator profiling (described below) is designed for development and provides aggregated profiles across multiple executions. ::: ## Computation Reporting Computation reporting provides a JSON-based view of computational costs for all executed transactions and scripts. ### Enabling Computation Reporting Start the emulator with the `--computation-reporting` flag: ```bash flow emulator --computation-reporting ``` :::info For more accurate computation numbers that reflect real network conditions, consider using [emulator fork testing](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md). Forking allows you to profile against actual Mainnet or Testnet state without requiring a full emulator environment setup. ::: ### Viewing Computation Reports Once enabled, access the computation report at: ``` http://localhost:8080/emulator/computationReport ``` The report returns a JSON object with the following structure: ```json { "scripts": { "": { "path": "scripts/myScript.cdc", "computation": 1250, "intensities": { "Statement": 45, "FunctionInvocation": 12, "GetValue": 8 }, "memory": 2048, "source": "access(all) fun main(): Int { ... }", "arguments": ["0x1"] } }, "transactions": { "": { "path": "transactions/myTransaction.cdc", "computation": 3500, "intensities": { "Statement": 120, "EmitEvent": 5, "SetValue": 15 }, "memory": 8192, "source": "transaction { ... }", "arguments": ["100.0"] } } } ``` #### Report Fields | Field | Description | |-------|-------------| | `path` | Source file path (set via `#sourceFile` pragma) | | `computation` | Total computation units used | | `intensities` | Count of each operation type performed | | `memory` | Estimated memory usage | | `source` | Original Cadence source code | | `arguments` | Arguments passed to the transaction/script | ### Understanding Computation Intensities The `intensities` map shows how many times each operation type was performed. The keys are human-readable names like `Statement`, `Loop`, `FunctionInvocation`, `GetValue`, `SetValue`, `EmitEvent`, etc. The total `computation` value is calculated by multiplying each intensity by its corresponding weight (defined by the network) and summing the results. When optimizing, look for operations with high counts - reducing these will lower your total computation cost. ## Computation Profiling (pprof) Computation profiling generates pprof-compatible profiles that can be visualized as flame graphs, providing a powerful way to understand your code's execution patterns. ### Installing pprof To visualize computation profiles, you'll need the [pprof tool](https://github.com/google/pprof). See the [pprof installation guide](https://github.com/google/pprof#building-pprof) for instructions. ### Enabling Computation Profiling Start the emulator with the `--computation-profiling` flag: ```bash flow emulator --computation-profiling ``` > **Note**: You can enable both `--computation-reporting` and `--computation-profiling` simultaneously if you need both types of analysis. ### Downloading the Profile After executing transactions and scripts, download the profile from: ``` http://localhost:8080/emulator/computationProfile ``` This downloads a `profile.pprof` file containing the aggregated computation profile. Using curl: ```bash curl -o profile.pprof http://localhost:8080/emulator/computationProfile ``` ### Viewing Profiles with pprof Open the profile in an interactive web interface: ```bash pprof -http=:8081 profile.pprof ``` Then navigate to `http://localhost:8081` in your browser. #### Available Views The pprof web interface provides several visualization options: | View | Description | |------|-------------| | **Flame Graph** | Visual representation of call stacks with computation costs | | **Graph** | Directed graph showing call relationships | | **Top** | List of functions sorted by computation usage | | **Source** | Source code annotated with computation costs | | **Peek** | Callers and callees of selected functions | ### Viewing Source Code in pprof To see Cadence source code annotated with computation costs: 1. **Download all deployed contracts**: ```bash curl -o contracts.zip http://localhost:8080/emulator/allContracts ``` 2. **Extract the ZIP file into a `contracts` folder**: ```bash mkdir -p contracts unzip contracts.zip -d contracts ``` 3. **Run pprof with the source path**: ```bash pprof -source_path=contracts -http=:8081 profile.pprof ``` Now when you view the "Source" tab in pprof, you'll see your Cadence code with line-by-line computation annotations. ### Resetting Computation Profiles To clear the accumulated profile data (useful between test runs): ```bash curl -X PUT http://localhost:8080/emulator/computationProfile/reset ``` ## Using Source File Pragmas The `#sourceFile` pragma improves computation report readability by associating your code with meaningful file paths. Without it, reports show generic identifiers. > **Note**: The `#sourceFile` pragma currently only affects **Computation Reporting** (JSON reports). It does not change filenames in **Computation Profiling** (pprof profiles). ### Usage Add the pragma at the beginning of your transaction or script: ```cadence #sourceFile("transactions/transfer_tokens.cdc") transaction(amount: UFix64, recipient: Address) { prepare(signer: auth(Storage) &Account) { // Transfer logic } } ``` For scripts: ```cadence #sourceFile("scripts/get_balance.cdc") access(all) fun main(address: Address): UFix64 { return getAccount(address).balance } ``` ### Benefits - Computation reports show file paths instead of generic IDs - Easier to correlate computation costs with source files - Useful for tracking costs across multiple files in a project ## Practical Examples ### Profiling a Simple Transaction Let's profile a simple NFT minting transaction. **1. Start the emulator with profiling enabled:** ```bash flow emulator --computation-profiling --computation-reporting ``` **2. Create a transaction file (`transactions/mint_nft.cdc`):** ```cadence #sourceFile("transactions/mint_nft.cdc") import NonFungibleToken from 0xf8d6e0586b0a20c7 import ExampleNFT from 0xf8d6e0586b0a20c7 transaction { prepare(signer: auth(Storage) &Account) { let collection = signer.storage.borrow<&ExampleNFT.Collection>( from: ExampleNFT.CollectionStoragePath ) ?? panic("Could not borrow collection") collection.deposit(token: <- ExampleNFT.mintNFT()) } } ``` **3. Execute the transaction:** ```bash flow transactions send transactions/mint_nft.cdc ``` **4. View the computation report:** ```bash curl http://localhost:8080/emulator/computationReport | jq ``` **5. Analyze with pprof:** ```bash curl -o profile.pprof http://localhost:8080/emulator/computationProfile pprof -http=:8081 profile.pprof ``` ### Identifying Performance Bottlenecks Consider a script that iterates over a large collection: ```cadence #sourceFile("scripts/find_expensive.cdc") access(all) fun main(address: Address): [UInt64] { let account = getAccount(address) let collection = account.capabilities.borrow<&{NonFungibleToken.Collection}>( /public/NFTCollection ) ?? panic("Could not borrow collection") let ids = collection.getIDs() var result: [UInt64] = [] // Potentially expensive loop for id in ids { let nft = collection.borrowNFT(id) if nft != nil { result.append(id) } } return result } ``` After profiling, you might see high values for: - `Loop`: Many iterations - `FunctionInvocation`: Repeated `borrowNFT` calls - `GetValue`: Multiple storage reads **Optimization strategies:** - Use pagination to limit iterations per call - Cache results when possible - Consider restructuring data for more efficient access ### Comparing Computation Costs You can compare two implementation approaches by downloading and comparing profiles: **1. Reset the profile:** ```bash curl -X PUT http://localhost:8080/emulator/computationProfile/reset ``` **2. Run implementation A and save the profile:** ```bash flow transactions send approach_a.cdc curl -o profile_a.pprof http://localhost:8080/emulator/computationProfile ``` **3. Reset and test implementation B:** ```bash curl -X PUT http://localhost:8080/emulator/computationProfile/reset flow transactions send approach_b.cdc curl -o profile_b.pprof http://localhost:8080/emulator/computationProfile ``` **4. Compare using pprof:** ```bash # View profile A pprof -top profile_a.pprof # View profile B pprof -top profile_b.pprof ``` The `-top` view shows total computation, making it easy to compare the two approaches. ## API Reference | Endpoint | Method | Description | |----------|--------|-------------| | `/emulator/computationReport` | GET | View computation report (JSON) | | `/emulator/computationProfile` | GET | Download pprof profile | | `/emulator/computationProfile/reset` | PUT | Reset computation profile | | `/emulator/allContracts` | GET | Download all deployed contracts (ZIP) | ### Example API Calls ```bash # Get computation report curl http://localhost:8080/emulator/computationReport # Download pprof profile curl -o profile.pprof http://localhost:8080/emulator/computationProfile # Reset computation profile curl -X PUT http://localhost:8080/emulator/computationProfile/reset # Download all contracts curl -o contracts.zip http://localhost:8080/emulator/allContracts ``` ## Troubleshooting ### Profile endpoint returns 404 **Problem**: Accessing `/emulator/computationProfile` returns a 404 error. **Solution**: Make sure you started the emulator with `--computation-profiling`: ```bash flow emulator --computation-profiling ``` ### Empty profile **Problem**: The downloaded profile is empty or has no useful data. **Solution**: Make sure you've executed at least one transaction or script after starting the emulator. The profile only contains data for executed code. ### Source code not showing in pprof **Problem**: The pprof source view doesn't display your Cadence code. **Solution**: 1. Download the contracts ZIP: `curl -o contracts.zip http://localhost:8080/emulator/allContracts` 2. Extract to a `contracts` folder in your working directory 3. Run pprof with the source path: `pprof -source_path=contracts -http=:8081 profile.pprof` ### High memory usage **Problem**: The emulator uses increasing memory over time. **Solution**: Periodically reset computation profiles to free accumulated data: ```bash curl -X PUT http://localhost:8080/emulator/computationProfile/reset ``` ### Computation reports not showing file paths **Problem**: The `path` field in computation reports is empty. **Solution**: Add the `#sourceFile` pragma to your transactions and scripts: ```cadence #sourceFile("path/to/your/file.cdc") ``` ## Related Features ### Code Coverage Reporting The emulator also supports Cadence code coverage reporting, which complements computation profiling: ```bash flow emulator --coverage-reporting ``` View coverage at: `http://localhost:8080/emulator/codeCoverage` Learn more in the [Flow Emulator documentation](../../tools/emulator/index.md). ### Debugger For step-through debugging of Cadence code, use the `#debug()` pragma: ```cadence #debug() transaction { prepare(signer: &Account) { // Execution pauses here for debugging } } ``` This works with VSCode and Flow CLI debugging tools. --- # File: /md/build/cadence/advanced-concepts/flix.md --- title: FLIX (Flow Interaction Templates) description: Learn about Flow Interaction Templates (FLIX), a standard for creating, auditing, and verifying Flow scripts and transactions with improved security and metadata. keywords: - FLIX - Flow Interaction Templates - templates - transactions - scripts - smart contracts - FCL - interaction templates - template service sidebar_position: 10 --- # Flow Interaction Templates Flow Interaction Templates (FLIX) is a standard for how contract developers, wallets, users, auditors, and applications can create, audit, and verify the intent, security, and metadata of Flow scripts and transactions, with the goal to improve the understandability and security of transaction authorizations and promote patterns for change resilient composability of applications on Flow. Interaction Templates provide a way to use and reuse current scripts and transactions, as well as to provide more metadata such as a human-readable title and description of what the transaction or script will do, which the developer can use, as well as the application user. With FLIX transactions and scripts, developers don't have to write their own for common operations! Read more about the design and purpose of FLIX in the [FLIP]. ## Use FLIX Flow makes FLIX available through an API available at flix.flow.com. You can query a FLIX API to get an Interaction Template. An example query looks like [this]. You can read more about how to query a FLIX API in the documentation available [here]. :::info The FLIX working group is currently working on a protocol to publish FLIX templates onchain. ::: ### Example How to integrate FLIX across different developer teams? For this example there are two GitHub repositories. - (smart contracts) [https://github.com/onflow/hello-world-flix] - (web development) [https://github.com/onflow/hello-world-web] The Smart contract developer creates FLIX templates and makes them available in GitHub, these can be versioned. Example is `v0.1.0` release, the templates are available for a specific version. In this example the templates are located at: - https://github.com/onflow/hello-world-flix/blob/v0.1.0/cadence/templates/ReadHelloWorld.template.json - https://github.com/onflow/hello-world-flix/blob/v0.1.0/cadence/templates/UpdateHelloWorld.template.json Developers can use FLIX templates from the smart contract github to interact with their smart contracts. They simply need the FLIX template URLs to create binding files (TypeScript or JavaScript). One major benefit is the web developers don't need to learn Cadence or copy Cadence to their repository in order to integrate with current smart contracts. TypeScript code generated from templates: - https://github.com/onflow/hello-world-web/blob/main/app/cadence/readHelloWorld.ts - https://github.com/onflow/hello-world-web/blob/main/app/cadence/updateHelloWorld.ts :::warning manually added "@ts-ignore" in generated file because of linting error. 'template' property is typed as "object" when it should also allow strings (url to flix template file). There is current a dev effort that will fix this linting issue. ::: See the `hello-world-web` [README]for more information on how to generate and execute FLIX templates here. [flow-cli flix] ### Clients There are currently two clients that have integrated with FLIX that you can use: **Go client** [https://github.com/onflow/flixkit-go] **FCL client you** read how to get started [tools/clients/fcl-js/interaction-templates] ## (Advanced) Run a FLIX API Flow provides an implementation of the Flow interaction template service as an open-source project. If you wish to run your own API, you can find the repository at [https://github.com/onflow/flow-interaction-template-service]. [FLIP]: https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md [this]: https://flix.flow.com/v1/templates?name=transfer-flow [here]: https://github.com/onflow/flow-interaction-template-service [https://github.com/onflow/hello-world-flix]: https://github.com/onflow/hello-world-flix) [https://github.com/onflow/hello-world-web]: https://github.com/onflow/hello-world-web) [README]: https://github.com/onflow/hello-world-web/tree/main [flow-cli flix]: ../../../build/tools/flow-cli/flix.md [https://github.com/onflow/flixkit-go]: https://github.com/onflow/flixkit-go [tools/clients/fcl-js/interaction-templates]: ../../../build/tools/clients/fcl-js/interaction-templates.mdx [https://github.com/onflow/flow-interaction-template-service]: https://github.com/onflow/flow-interaction-template-service --- # File: /md/build/cadence/advanced-concepts/flow-cron.md --- title: Cron-Based Recurring Transactions sidebar_position: 9 description: Learn how to schedule recurring transactions on Flow using the FlowCron smart contract. --- # Cron-Based Recurring Transactions Sometimes you need blockchain logic to run automatically on a schedule: distributing rewards every day, checking conditions every hour, or processing batches every week. Instead of manually triggering these transactions, you can automate them. **Cron** is a time-based scheduling system originally from Unix. It lets you define "run this at 9am every Monday" using a simple pattern called a cron expression. The **FlowCron** smart contract brings this same concept onchain, so you can schedule recurring transactions that run automatically without any external triggers. For example, with the cron expression `0 0 * * *` (daily at midnight), your transaction executes every day at midnight UTC—indefinitely—until you stop it. :::info `FlowCron` builds on Flow's Scheduled Transactions. If you haven't worked with scheduled transactions before, check out the [Scheduled Transactions documentation](scheduled-transactions.md) first. ::: ## How It Works `FlowCron` provides a `CronHandler` resource that wraps your existing [TransactionHandler]. You give it a cron expression (like `*/5 * * * *` for every 5 minutes) and your handler, and `FlowCron` takes care of the rest. Once started, your schedule runs indefinitely without any further action from you. ### Why Two Transactions? A key challenge with recurring schedules is fault tolerance: what happens if your code has a bug? You don't want one failed execution to break the entire schedule. `FlowCron` solves this by running two separate transactions each time your cron triggers: - **Executor**: Runs your code. If your logic fails, only this transaction reverts. - **Keeper**: Schedules the next cycle. Runs independently, so even if your code throws an error, the schedule continues. **The benefit**: Your recurring schedule won't break if your `TransactionHandler` execution fails. The keeper always ensures the next execution is scheduled, regardless of whether the current one succeeded or failed. ``` Timeline ─────────────────────────────────────────────────────────> T1 T2 T3 │ │ │ ├── Executor ──────────►├── Executor ──────────►├── Executor │ (runs user code) │ (runs user code) │ (runs user code) │ │ │ └── Keeper ────────────►└── Keeper ────────────►└── Keeper (schedules T2) (schedules T3) (schedules T4) (+1s offset) (+1s offset) (+1s offset) ``` ## Cron Expressions A cron expression is just five numbers (or wildcards) that define when something should run. `FlowCron` uses the standard 5-field cron format: ``` ┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-6, Sunday=0) │ │ │ │ │ * * * * * ``` **Operators:** `*` (any), `,` (list), `-` (range), `/` (step) | Pattern | When it runs | | --- | --- | | `* * * * *` | Every minute | | `*/5 * * * *` | Every 5 minutes | | `0 * * * *` | Top of every hour | | `0 0 * * *` | Daily at midnight | | `0 0 * * 0` | Weekly on Sunday | | `0 9-17 * * 1-5` | Hourly, 9am-5pm weekdays | :::note When you specify both day-of-month and day-of-week (not `*`), the job runs if **either** matches. So `0 0 15 * 0` fires on the 15th OR on Sundays. ::: ## Setup Setting up a cron job involves four steps: 1. **Create a handler**: Write the code you want to run on each tick 2. **Wrap it with FlowCron**: Connect your handler to a cron schedule 3. **Start the schedule**: Kick off the first execution 4. **Monitor**: Check that everything is running All transactions and scripts referenced below are available in the [FlowCron GitHub repository]. ### Prerequisites Before you start, make sure you have: - **[Flow CLI]** installed: This is the command-line tool you'll use to deploy contracts, send transactions, and run scripts. If you don't have it yet, follow the [installation guide]. - **FLOW tokens** for transaction fees: Every transaction costs a small amount of FLOW. Get free testnet FLOW from the [Faucet]. - A **Flow account**: The CLI will help you create one if you don't have one yet. ### 1. Create Your Handler First, you need to write the code that will run on each scheduled tick. In Cadence, this is called a `TransactionHandler`. A `TransactionHandler` is a resource that implements the `FlowTransactionScheduler.TransactionHandler` interface. The key part is the `executeTransaction` function. This is where you put whatever logic you want to run on schedule: updating state, distributing tokens, checking conditions, etc. For more details on how handlers work, see the [Scheduled Transactions documentation](scheduled-transactions.md#create-a-scheduled-transaction). Here's a simple example contract: ```cadence import "FlowTransactionScheduler" access(all) contract MyRecurringTask { access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { // Your logic here log("Cron fired at ".concat(getCurrentBlock().timestamp.toString())) } access(all) view fun getViews(): [Type] { return [Type(), Type()] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return /storage/MyRecurringTaskHandler case Type(): return /public/MyRecurringTaskHandler default: return nil } } } access(all) fun createHandler(): @Handler { return <- create Handler() } } ``` This example handler simply logs the timestamp when executed. Replace the `log` statement with your own logic. #### Deploy Your Contract Use the Flow CLI to deploy your `TransactionHandler` contract: ```bash flow project deploy --network=testnet ``` This command reads your `flow.json` configuration and deploys all configured contracts. If you're new to deploying, see the [deployment guide] for a complete walkthrough. #### Create and Store a Handler Instance After deploying, you need to create an instance of your handler and save it to your account's **storage**. The **storage** is an area in your account where you can save resources (like your handler) that persist between transactions. See [CounterTransactionHandler.cdc] for a complete working example that includes the storage setup. ### 2. Wrap It with `FlowCron` Now you need to wrap your handler with a `CronHandler`. This connects your handler to a cron schedule. The following transaction creates a new `CronHandler` resource that holds your cron expression and a reference to your handler: ```cadence transaction( cronExpression: String, wrappedHandlerStoragePath: StoragePath, cronHandlerStoragePath: StoragePath ) { prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, SaveValue) &Account) { // Issue capability for wrapped handler let wrappedHandlerCap = acct.capabilities.storage.issue< auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler} >(wrappedHandlerStoragePath) // Create and save the CronHandler let cronHandler <- FlowCron.createCronHandler( cronExpression: cronExpression, wrappedHandlerCap: wrappedHandlerCap, feeProviderCap: feeProviderCap, schedulerManagerCap: schedulerManagerCap ) acct.storage.save(<-cronHandler, to: cronHandlerStoragePath) } } ``` See [CreateCronHandler.cdc] for the full transaction. **Send this transaction using the Flow CLI:** ```bash flow transactions send CreateCronHandler.cdc \ "*/5 * * * *" \ /storage/MyRecurringTaskHandler \ /storage/MyCronHandler \ --network=testnet ``` The arguments are: your cron expression, the storage path where your handler lives, and the path where the new `CronHandler` will be stored. ### 3. Start the Schedule This transaction schedules the first executor and keeper, which kicks off the self-perpetuating loop. After this, your cron job runs automatically: ```cadence transaction( cronHandlerStoragePath: StoragePath, wrappedData: AnyStruct?, executorPriority: UInt8, executorExecutionEffort: UInt64, keeperExecutionEffort: UInt64 ) { prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, SaveValue) &Account) { // Calculate next cron tick time let cronHandler = signer.storage.borrow<&FlowCron.CronHandler>(from: cronHandlerStoragePath) ?? panic("CronHandler not found") let executorTime = FlowCronUtils.nextTick(spec: cronHandler.getCronSpec(), afterUnix: currentTime) } execute { // Schedule executor (runs your code) self.manager.schedule( handlerCap: self.cronHandlerCap, data: self.executorContext, timestamp: UFix64(self.executorTime), ... ) // Schedule keeper (schedules next cycle) self.manager.schedule( handlerCap: self.cronHandlerCap, data: self.keeperContext, timestamp: UFix64(self.keeperTime), ... ) } } ``` See [ScheduleCronHandler.cdc] for the full transaction. **Send this transaction using the Flow CLI:** ```bash flow transactions send ScheduleCronHandler.cdc \ /storage/MyCronHandler \ nil \ 2 \ 500 \ 2500 \ --network=testnet ``` **Parameters:** | Parameter | Description | | --- | --- | | `cronHandlerStoragePath` | Path to your CronHandler | | `wrappedData` | Optional data passed to handler (`nil` or your data) | | `executorPriority` | 0 (High), 1 (Medium), or 2 (Low) | | `executorExecutionEffort` | Computation units for your code (start with `500`) | | `keeperExecutionEffort` | Computation units for keeper (use `2500`) | :::warning[Fees] Starting a cron job requires prepaying fees for the scheduled transactions. FLOW will be deducted from your account to cover the executor and keeper fees. Make sure you have enough FLOW before running this transaction. ::: Once this transaction succeeds, **your cron job is live**. The first execution will happen at the next cron tick, and the schedule will continue automatically from there. You don't need to do anything else, unless you want to monitor it or stop it. ### 4. Check Status Use Cadence **scripts** to check your cron job's status. Scripts are read-only queries that inspect blockchain state without submitting a transaction—they're free to run and don't modify anything. Learn more in the [scripts documentation]. #### Query Cron Info The [GetCronInfo.cdc] script returns metadata about your cron handler: ```cadence access(all) fun main(handlerAddress: Address, handlerStoragePath: StoragePath): FlowCron.CronInfo? { let account = getAuthAccount(handlerAddress) if let handler = account.storage.borrow<&FlowCron.CronHandler>(from: handlerStoragePath) { return handler.resolveView(Type()) as? FlowCron.CronInfo } return nil } ``` **Run this script using the Flow CLI:** ```bash flow scripts execute GetCronInfo.cdc 0xYourAddress /storage/MyCronHandler --network=testnet ``` If your cron job is running, you'll see output showing the cron expression, next scheduled execution time, and handler status. #### Calculate Next Execution Time The [GetNextExecutionTime.cdc] script calculates when your cron expression will next trigger: ```cadence access(all) fun main(cronExpression: String, afterUnix: UInt64?): UFix64? { let cronSpec = FlowCronUtils.parse(expression: cronExpression) if cronSpec == nil { return nil } let nextTime = FlowCronUtils.nextTick( spec: cronSpec!, afterUnix: afterUnix ?? UInt64(getCurrentBlock().timestamp) ) return nextTime != nil ? UFix64(nextTime!) : nil } ``` **Run this script using the Flow CLI:** ```bash flow scripts execute GetNextExecutionTime.cdc "*/5 * * * *" nil --network=testnet ``` #### Additional Debugging Scripts - [GetCronScheduleStatus.cdc] — Returns executor/keeper transaction IDs, timestamps, and current status - [GetParsedCronExpression.cdc] — Validates and parses a cron expression into a `CronSpec` ## Stopping a Cron Job You might want to stop a cron job for several reasons: - **Debugging**: Something isn't working and you need to investigate - **Updating**: You want to change the schedule or handler logic - **Cost**: You no longer need the recurring execution - **Temporary pause**: You want to stop temporarily and restart later To stop a running cron job, you need to cancel both the pending executor and keeper transactions. This transaction retrieves the scheduled transaction IDs from your `CronHandler` and cancels them: ```cadence transaction(cronHandlerStoragePath: StoragePath) { prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, SaveValue) &Account) { let cronHandler = signer.storage.borrow<&FlowCron.CronHandler>(from: cronHandlerStoragePath) ?? panic("CronHandler not found") self.executorID = cronHandler.getNextScheduledExecutorID() self.keeperID = cronHandler.getNextScheduledKeeperID() } execute { // Cancel executor and keeper, receive fee refunds if let id = self.executorID { let refund <- self.manager.cancel(id: id) self.feeReceiver.deposit(from: <-refund) } if let id = self.keeperID { let refund <- self.manager.cancel(id: id) self.feeReceiver.deposit(from: <-refund) } } } ``` See [CancelCronSchedule.cdc] for the full transaction. **Send this transaction using the Flow CLI:** ```bash flow transactions send CancelCronSchedule.cdc /storage/MyCronHandler --network=testnet ``` Cancelling refunds 50% of the prepaid fees back to your account. ## Contract Addresses `FlowCron` is deployed on both Testnet and Mainnet: | Contract | Testnet | Mainnet | | --- | --- | --- | | `FlowCron` | `0x5cbfdec870ee216d` | `0x6dec6e64a13b881e` | | `FlowCronUtils` | `0x5cbfdec870ee216d` | `0x6dec6e64a13b881e` | ## Resources - [FlowCron GitHub repository] - [Scheduled Transactions Documentation] [FlowCron GitHub repository]: https://github.com/onflow/flow-cron [Scheduled Transactions Documentation]: scheduled-transactions.md [TransactionHandler]: scheduled-transactions.md#create-a-scheduled-transaction [Flow CLI]: ../../tools/flow-cli/index.md [installation guide]: ../../tools/flow-cli/install.md [Faucet]: https://faucet.flow.com/ [deployment guide]: ../smart-contracts/deploying.md [scripts documentation]: ../basics/scripts.md [CreateCronHandler.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/transactions/CreateCronHandler.cdc [ScheduleCronHandler.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/transactions/ScheduleCronHandler.cdc [CancelCronSchedule.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/transactions/CancelCronSchedule.cdc [GetCronInfo.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/scripts/GetCronInfo.cdc [GetNextExecutionTime.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/scripts/GetNextExecutionTime.cdc [GetCronScheduleStatus.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/scripts/GetCronScheduleStatus.cdc [GetParsedCronExpression.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/scripts/GetParsedCronExpression.cdc [CounterTransactionHandler.cdc]: https://github.com/onflow/flow-cron/blob/main/cadence/tests/mocks/contracts/CounterTransactionHandler.cdc --- # File: /md/build/cadence/advanced-concepts/metadata-views.md --- title: Metadata Views description: Learn about Flow's standardized way to represent and manage NFT metadata through MetadataViews, enabling consistent metadata interpretation across different platforms and marketplaces. keywords: - NFT metadata - MetadataViews - NFT standards - metadata views - Flow NFT - ViewResolver - NFT traits - NFT royalties - NFT editions - contract metadata - NFT display - metadata implementation sidebar_label: NFT Metadata Views --- # Metadata views on Flow `MetadataViews` on Flow offer a standardized way to represent onchain metadata across different resources. This standard is primarily used for NFTs, but you can use it for any resource that wants a flexible standard for metadata, such as [scheduled transactions] Through integration of the metadata views standard, developers can ensure that different platforms and marketplaces can interpret the metadata of their resources in a unified manner. This means that when users visit different websites, wallets, and marketplaces, the metadata will be presented in a consistent manner, which ensures a uniform experience across various platforms. :::info It is important to understand this document so you can make meaningful decisions about how to manage your project's metadata as support for metadata views does not happen by default. Each project has unique metadata and therefore will have to define how they expose it in unique ways. ::: :::info This document primarily uses NFTs as examples for how metadata views can be used, but metadata views can be used for any kind of project or resource that wants a standard way to represent metadata. ::: A view is a standard Cadence struct that represents a specific type of metadata, such as a [Royalty specification]: ```cadence access(all) struct Royalty { /// Where royalties should be paid to access(all) let receiver: Capability<&{FungibleToken.Receiver}> /// The cut of the sale that should be taken for royalties. access(all) let cut: UFix64 /// Optional description of the royalty access(all) let description: String } ``` or a [rarity description]: ```cadence access(all) struct Rarity { /// The score of the rarity as a number access(all) let score: UFix64? /// The maximum value of score access(all) let max: UFix64? /// The description of the rarity as a string. access(all) let description: String? } ``` This guide acts as a specification for the correct ways to use each metadata view. Many of the standard metadata views do not have built-in requirements for how they are meant to be used, so it is important for developers to understand the content of this document so third party apps can integrate with their smart contracts as easily and effectively as possible. > If you'd like to follow along while we discuss the concepts below, see the [ExampleNFT contract]. Additionally, here is the source code for the [`ViewResolver` contract] and the [`MetadataViews` contract]. Flowty has also provided [a useful guide] for how to manage metadata views properly in order to be compatible with their marketplace. This guide is very useful because all of their advice is generally good advice for any NFT contract, regardless of what marketplace it uses. ## Two levels of metadata: an overview Metadata in Cadence is structured at two distinct levels: 1. **Contract-Level Metadata**: This provides an overarching description of the entire collection/project. Any metadata about individual resources is not included here. 2. **Resource-Level Metadata**: This metadata relates to individual resources, often NFTs. It provides context, describes rarity, and highlights other distinctive attributes that distinguish one object from another within the same contract or collection. While these distinct levels describe different aspects of a project, they both use the same view system to represent the metadata and the same basic function calls to query the information, just from different places. ## Understand `ViewResolver` and `MetadataViews.Resolver` When you consider Flow and how it handles metadata for resources, it is crucial to understand two essential interfaces: `ViewResolver` and `MetadataViews.Resolver`. [Interfaces] serve as blueprints for types that specify the required fields and methods that your contract or [composite type] must adhere to be considered a subtype of that interface. This guarantees that any contract that asserts adherence to these interfaces will possess a consistent set of functionalities that other applications or contracts can rely on. 1. **`ViewResolver` for Contract-Level Metadata**: - This interface ensures that **contracts**, particularly those encapsulating NFT collections, conform to the Metadata Views standard. - Through the adoption of this interface, contracts can provide dynamic metadata that represents the entirety of the project. 2. **`MetadataViews.Resolver` (`ViewResolver.Resolver` in Cadence 1.0) for NFT-Level Metadata**: - Used within **individual resources**, this interface ensures each resource adheres to the Metadata standard format. - It focuses on the distinct attributes of an individual resource, such as its unique ID, name, description, and other defining characteristics. ### Core functions Both the `ViewResolver` and `MetadataViews.Resolver` use these core functions: ### The `getViews` function This function provides a list of supported metadata view types, which you can apply either by the contract (in the case of `ViewResolver`) or by an individual resource (in the case of `MetadataViews.Resolver`). ```cadence access(all) fun getViews(): [Type] { return [ Type(), Type(), ... ] } ``` ### The `resolveView` function Whether used at the contract or resource level, this function's role is to deliver the actual metadata associated with a given view type. The caller provides the type of the view they want to query as the only argument, and the view is returned if it exists, and `nil` is returned if it doesn't. ```cadence access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): ... ... } return nil } ``` As you can see, the return values of `getViews()` can be used as arguments for `resolveView()` if you want to just iterate through all the views that a resource implements. ## Resource-level metadata implementation Resource-level metadata addresses the unique attributes of individual tokens within a collection. It provides structured information for each resource, such as its identifier, descriptive elements, royalties, and other associated metadata. When you incorporate this level of detail, it assures consistency and standardization among individual resources, which makes them interoperable and recognizable across various platforms and marketplaces. ### Core properties In the code below, an NFT has properties such as its unique ID, name, description, and others. When we add the `NonFungibleToken.NFT` and by extension, the `MetadataViews.Resolver` to our NFT resource, we indicate that these variables will adhere to the specifications outlined in the MetadataViews contract for each of these properties. This facilitates interoperability within the Flow ecosystem and assures that the metadata of our NFT can be consistently accessed and understood by various platforms and services that interact with NFTs. ```cadence access(all) resource NFT: NonFungibleToken.NFT { access(all) let id: UInt64 access(all) let name: String access(all) let description: String access(all) let thumbnail: String access(self) let royalties: [MetadataViews.Royalty] access(self) let metadata: {String: AnyStruct} ... } ``` To make this possible though, it is **vital** that projects all use the standard metadata views in the same way, so third-party applications can consume them in standard ways. For example, many metadata views have `String`-typed fields. It is difficult to enforce that these fields are formatted in the correct way, so it is important for projects to be dilligent about how they use them. Take `Traits` for example, a commonly misused metadata view: ```cadence access(all) struct Trait { // The name of the trait. Like Background, Eyes, Hair, etc. access(all) let name: String ... ... } ``` The name of the trait should be formatted in a way so that it is easy to display on a user-facing website. Many projects will use something like CamelCase for the value, so it looks like "HairColor", which is not pretty on a website. The correct format for this example would be "Hair Color". This is just one of many common view uses that projects need to be aware of to maximize the chance of success for their project. ## Metadata views for NFTs `MetadataViews` types define how the NFT presents its data. When invoked, the system knows precisely which view to return, which ensures that the relevant information is presented consistently across various platforms. In this section of the document, we will explore each metadata view and describe how projects should properly use them. ### Display This view provides the bare minimum information about the NFT suitable for list or display purposes. When the `Display` type is invoked, it dynamically assembles the visual and descriptive information that is typically needed to showcase the NFT in marketplaces or collections. ```cadence case Type(): return MetadataViews.Display( name: self.name, description: self.description, thumbnail: MetadataViews.HTTPFile( url: self.thumbnail ) ) ``` If the thumbnail is a HTTP resource: ```cadence thumbnail : MetadataViews.HTTPFile(url: *Please put your url here) ``` If the thumbnail is an IPFS resource: ```cadence // thumbnail : MetadataViews.IPFSFile( cid: thumbnail cid, // Type path: ipfs path // Type specify path if the cid is a folder hash, otherwise use nil here ) ``` ![MetadataViews.Display](display.png 'Display') :::info SVG field should be sent as `thumbnailURL`, should be base64 encoded, and should have a dataURI prefix, like so: ``` data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InJlZCIvPjwvc3ZnPg== ``` ::: ### Editions The `Editions` view provides intricate details about the particular release of an NFT within a set of NFTs with the same metadata. This can include information about the number of copies in an edition, the specific NFT's sequence number within that edition, or its inclusion in a limited series. When the `Editions` view is queried, it retrieves this data, and provides collectors with the information they need to comprehend the rarity and exclusivity of the NFT they are interested in. An NFT can also be part of multiple editions, which is why the `Editions` view can hold any number of `Edition` structs in an array. For example, if an NFT is number 11 of 30 of an exclusive edition, the code to return the `Editions` view would look like this: ```cadence case Type(): let editionInfo = MetadataViews.Edition( name: "Example NFT Edition", number: 11, max: 30 ) return MetadataViews.Editions([editionInfo]) ``` ### Serial number metadata The `Serial` metadata provides the unique serial number of the NFT, akin to a serial number on a currency note or a VIN on a car. This serial number is a fundamental attribute that certifies the individuality of each NFT and is critical for identification and verification processes. Serial numbers are expected to be unique among other NFTs from the same project. Many projects already use the NFT resource's [resource's globally unique UUID] as the ID, so they will typically also use that as the serial number. ```cadence case Type(): return MetadataViews.Serial(self.uuid) ``` ### Royalties metadata Royalty information is vital for the sustainable economics of the creators in the NFT space. [The `Royalties` metadata view] defines the specifics of any royalty agreements in place, such as the percentage of sales revenue that will go to the original creator or other stakeholders on secondary sales. Each royalty view contains a fungible token receiver capability where royalties should be paid: ```cadence access(all) struct Royalty { access(all) let receiver: Capability<&{FungibleToken.Receiver}> access(all) let cut: UFix64 } ``` here is an example of how an NFT might return a `Royalties` view: ```cadence case Type(): // Assuming each 'Royalty' in the 'royalties' array has 'cut' and 'description' fields let royalty = MetadataViews.Royalty( // The beneficiary of the royalty: in this case, the contract account receiver: ExampleNFT.account.capabilities.get<&AnyResource{FungibleToken.Receiver}>(/public/GenericFTReceiver), // The percentage cut of each sale cut: 0.05, // A description of the royalty terms description: "Royalty payment to the original creator" ) } return MetadataViews.Royalties(detailedRoyalties) ``` If someone wants to make a listing for their NFT on a marketplace, the marketplace can check to see if the royalty receiver accepts the seller's desired fungible token by calling the `receiver.getSupportedVaultTypes(): {Type: Bool}` function via the `receiver` reference: ```cadence let royaltyReceiverRef = royalty.receiver.borrow() ?? panic("Could not borrow a reference to the receiver") let supportedTypes = receiverRef.getSupportedVaultTypes() if supportedTypes[**royalty.getType()**] { // The type is supported, so you can deposit recieverRef.deposit(<-royalty) } else { // if it is not supported, you can do something else, // like revert, or send the royalty tokens to the seller instead } ``` If the desired type is not supported, the marketplace has a few options. They could either get the address of the receiver with the `receiver.owner.address` field and check to see if the account has a receiver for the desired token, they could perform the sale without a royalty cut, or they could abort the sale since the token type isn't accepted by the royalty beneficiary. You can see example implementations of royalties in the `ExampleNFT` contract and the associated transactions and scripts. NFTs are often sold for a variety of currencies, so the royalty receiver should ideally be a [fungible token switchboard] receiver that forwards any received tokens to the correct vault in the receiving account. #### Important instructions for royalty receivers If you plan to set your account as a receiver of royalties, you'll likely want to be able to accept as many token types as possible. This is possible with the `FungibleTokenSwitchboard`. If you initialize a switchboard in your account, it can accept any generic fungible token and route it to the correct vault in your account. Therefore, if you want to receive royalties, you should set up your account with the [`setup_royalty_account_by_paths.cdc`] transaction. This will link generic public path from `MetadataViews.getRoyaltyReceiverPublicPath() to the capability paths and types that you provide as arguments. Then you can use that public path and capability for your royalty receiver. ### External URL metadata The ExternalURL view returns to an associated webpage URL, which provides additional content or information about the NFT. This can be a website, social media page, or anything else related to the project that uses a URL. ```cadence case Type(): return MetadataViews.ExternalURL("".concat(self.id.toString())) ``` ### Traits metadata The [`Trait`] view type encapsulates the unique attributes of an NFT, like any visual aspects or category-defining properties. These can be essential for marketplaces that need to sort or filter NFTs based on these characteristics. When you return trait views as recommended, you can fit the data in the places you want. ```cadence access(all) struct Trait { // The name of the trait. Like Background, Eyes, Hair, etc. access(all) let name: String // The underlying value of the trait access(all) let value: AnyStruct // displayType is used to show some context about what this name and value represent // for instance, you could set value to a unix timestamp, and specify displayType as "Date" to tell // platforms to consume this trait as a date and not a number access(all) let displayType: String? // Rarity can also be used directly on an attribute. // This is optional because not all attributes need to contribute to the NFT's rarity. access(all) let rarity: Rarity? ``` The traits view is extremely important to get right, because many third-party apps and marketplaces are heavily reliant on it to properly display the entirety of your NFTs. For example, the names and values of the traits likely display on a user-facing website, so it is important to return them in a presentable form, such as `First Name`, instead of `first_name` or `firstName`. Additionally, limit your `value` field to primitive types like `String`, `Int`, or `Bool`. Furthermore, the `displayType` is important as well, because it tells websites how to display the trait properly. Developers should not just default to `String` or `Integer` for all their display types. When applicable, the display types to accurately reflect the data that needs to be displayed. ![MetadataViews.Traits](traits_String.png 'traits_String') #### Note: always prefer wrappers over single views When you expose a view that could have multiple occurrences on a single NFT, such as `Edition`, `Royalty`, `Media` or `Trait`, always use the wrapper view (such as `Editions`, `Royalties`, etc), even if there is only a single occurrence. The wrapper view is always the plural version of the single view name and can be found below the main view definition in the `MetadataViews` contract. When you resolve the view, the wrapper view should be the returned value, instead of return the single view or just an array of several occurrences of the view. ```cadence access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil) let editionList: [MetadataViews.Edition] = [editionInfo] // return the wrapped view return MetadataViews.Editions( editionList ) } } ``` ## Contract-level metadata implementation Contract-level metadata provides a holistic view of an NFT collection. It captures complete attributes and contextual information about the entire set, rather than specifics of individual tokens. These views describe attributes at the collection or series level rather than individual NFTs. These views should still should be queryable via individual NFTs though. To accomplish this, you can forward the call from the NFT's `resolveView()` method to the contract's `resolveView()` method, like so: ```cadence /// this line is in `ExampleNFT.NFT.resolveView()` case Type(): return ExampleNFT.getCollectionDisplay(nftType: Type<@ExampleNFT.NFT>()) ``` ### NFTCollectionData This view provides paths and types related to the NFT collection's storage and access within the smart contract. The information in this view is critical for you to understand how to interact with a collection. ```cadence case Type(): return MetadataViews.NFTCollectionData( // where should the collection be saved? storagePath: ExampleNFT.CollectionStoragePath, // where to borrow public capabilities from? publicPath: ExampleNFT.CollectionPublicPath, // Important types for how the collection should be linked publicCollection: Type<&ExampleNFT.Collection>(), publicLinkedType: Type<&ExampleNFT.Collection>(), // function that can be accessed to create an empty collection for the project createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} { return <-ExampleNFT.createEmptyCollection(nftType: Type<@ExampleNFT.NFT>()) }) ) ``` Here, `NFTCollectionData` specifies several important elements related to how the collection is stored and accessed on the Flow blockchain. It provides information on storage paths and access control paths for both public and private data, as well as linked types that specify what capabilities are publicly available (like collection, receiver, or provider interfaces). ### NFTCollectionDisplay This view describes the collection with visual elements and metadata that are useful for display purposes, such as in a marketplace or gallery. Many third party apps need this in order to display high-level information about an NFT project properly. ```cadence case Type(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "" ), mediaType: "image/svg+xml" ) return MetadataViews.NFTCollectionDisplay( name: "The Example Collection", description: "This collection is used as an example to help you develop your next Flow NFT.", externalURL: MetadataViews.ExternalURL(""), squareImage: media, bannerImage: media, socials: { "twitter": MetadataViews.ExternalURL("") } ) ``` In the example above, the `NFTCollectionDisplay` not only offers fundamental metadata like the collection's name and description but also provides image URLs for visual representations of the collection (`squareImage` and `bannerImage`) and external links, including social media profiles. ![MetadataViews.CollectionDisplay](collectionDisplay.png 'CollectionDisplay') ### Contract-borrowing Metadata With the contract borrowing feature, the [ViewResolver] interface on contracts can be borrowed directly without needing to import the contract first. Views can be resolved directly from there. As an example, you might want to allow your contract to resolve `NFTCollectionData` and `NFTCollectionDisplay` so that platforms do not need to find an NFT that belongs to your contract to get information about how to set up or show your collection. ```cadence import ViewResolver from 0xf8d6e0586b0a20c7 import MetadataViews from 0xf8d6e0586b0a20c7 access(all) fun main(addr: Address, name: String): StoragePath? { let t = Type() let borrowedContract = getAccount(addr).contracts.borrow<&ViewResolver>(name: name) ?? panic("contract could not be borrowed") let view = borrowedContract.resolveView(t) if view == nil { return nil } let cd = view! as! MetadataViews.NFTCollectionData return cd.storagePath } ``` Will Return ```cadence {"domain":"storage","identifier":"exampleNFTCollection"} ``` ## More It's crucial that developers who want to deploy NFTs on Flow understand `MetadataViews` and the core functions associated with it With these views and functions, NFTs can maintain a consistent presentation across various platforms and marketplaces and foster interoperability between contracts and applications in the Flow ecosystem. To gain a deeper understanding of implementing the MetadataView standard, check out our documentation on "How to Create an NFT Project on Flow". It provides an introduction for how to integrate these standards into your NFT contracts. - See the [API reference for a complete list of Metadata functions]. - Check out [an Example NFT project] which implements `MetadataViews`. - Read [the NFT Guide]for an introduction to implementation. [scheduled transactions]: ./scheduled-transactions.md [Royalty specification]: https://github.com/onflow/flow-nft?tab=readme-ov-file#royalty-view [rarity description]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc#L614 [ExampleNFT contract]: https://github.com/onflow/flow-nft/blob/master/contracts/ExampleNFT.cdc [`ViewResolver` contract]: https://github.com/onflow/flow-nft/blob/master/contracts/ViewResolver.cdc [`MetadataViews` contract]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc [a useful guide]: https://docs.flowty.io/developer-docs/ [Interfaces]: https://cadence-lang.org/docs/language/interfaces [composite type]: https://cadence-lang.org/docs/language/composite-types [resource's globally unique UUID]: https://cadence-lang.org/docs/language/resources#resource-identifier [The `Royalties` metadata view]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc#L295 [fungible token switchboard]:https://github.com/onflow/flow-ft?tab=readme-ov-file#fungible-token-switchboard [`setup_royalty_account_by_paths.cdc`]: https://github.com/onflow/flow-ft/blob/master/transactions/switchboard/setup_royalty_account_by_paths.cdc [`Trait`]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc#L655 [ViewResolver]: https://github.com/onflow/flow-nft/blob/master/contracts/ViewResolver.cdc [API reference for a complete list of Metadata functions]: https://developers.flow.com/build/cadence/core-contracts/flow-nft/MetdataViews/MetadataViews [an Example NFT project]: https://github.com/onflow/flow-nft/blob/master/contracts/ExampleNFT.cdc [the NFT Guide]: ../../../blockchain-development-tutorials/tokens/nft-cadence.md --- # File: /md/build/cadence/advanced-concepts/passkeys.md --- title: Passkeys description: Implement passkeys on Flow using WebAuthn, covering key extraction, challenges, signature formatting for Flow, and signature extensions. keywords: - passkeys - WebAuthn - authentication - ECDSA P256 - ES256 - Flow account keys - wallet integration - credential management - signature verification - biometric authentication - FIDO2 - multi-factor authentication - passwordless authentication - Flow transactions - public key cryptography sidebar_position: 9 --- # Passkeys This is a wallet‑centric, high‑level guide (per [FLIP 264: WebAuthn Credential Support]) with code snippets covering passkey registration and signing on Flow, focusing on nuances for passkey signing and account keys: 1. Create a passkey and add a Flow account key 2. Sign a transaction with the user's passkey (includes conversion, extension, and submission) It accompanies the [PoC demo] for reference and cites the FLIP where behavior is normative. :::note Platform-specific APIs This tutorial focuses on the **Web Authentication API** (WebAuthn) for browser-based applications. Other platforms such as iOS, Android, and desktop applications will require platform-specific APIs (such as Apple's [Authentication Services] or Android's [Credential Manager]), but the underlying concepts—credential creation, challenge signing, and signature formatting—remain the same across all platforms. ::: ## What you'll learn After you complete this guide, you'll be able to: - Create a passkey and derive a Flow‑compatible public key. - Generate the correct challenge for signing transactions (wallet sets SHA2‑256(signable)). - Convert a WebAuthn ECDSA DER signature into Flow's raw `r||s` format and attach the transaction signature extension. ## Passkey benefits **Sign transactions securely** Users can sign Flow transactions with passkeys while the private key stays securely stored within the authenticator. This reduces the risk of key extraction attacks and phishing attempts. **Authenticate across devices** Users can scan a QR code displayed on a desktop browser with a mobile device to approve transactions. Cloud-synchronized passkeys (such as those stored in Apple iCloud or Google Password Manager) allow authentication across multiple devices without manual key transfers. **Authenticate with platform-based security** Users can sign transactions directly on devices with built-in authenticators, such as Face ID on iPhones or Windows Hello on Windows PCs. This approach allows native transaction signing without the need for an external security key. **Recover access with cloud-synced passkeys** Cloud-synced passkeys help users recover access if they lose a device, though this introduces trade-offs between convenience and self-custody (see [Limitations of passkeys]. **Work with multi-key accounts** Combine passkeys with other authentication types with Flow's native [multi-key account support] to build secure recovery options and shared access patterns with weighted keys. ## Prerequisites - Working knowledge of modern frontend (React/Next.js) and basic backend. - Familiarity with WebAuthn/Passkeys concepts and platform constraints. - Flow Command Line (FCL) installed and configured for your app. - Flow accounts and keys: [Signature and Hash Algorithms]. ## Registration When a user generates a passkey via [navigator.credentials.create()] with `{ publicKey }`, the authenticator returns an attestation that contains the new credential's public key. On Flow, you can register that public key on an account if the algorithm of the requested passkey is either `ES256` or `ES256k`. This guide demonstrates an `ES256` passkey which translates to an `ECDSA_P256` Flow key paired with `SHA2_256` hashing. Alternatively, an `ES256k` passkey translates to an `ECDSA_secp256k1` Flow key paired with `SHA2_256` hashing. High‑level steps: 1. On the client, generate `PublicKeyCredentialCreationOptions` with: - `pubKeyCredParams`'s `alg` equal to `ES256` (`-7`) - the RP id is derived from to the web origin - the challenge equal to an arbitrary constant 2. On the client, call `navigator.credentials.create()`. 3. Verify attestation if necessary and extract the public key (P‑256 in this guide). Convert it to raw uncompressed 64‑byte `X||Y` hex string as expected by Flow. 4. Submit a transaction to add the key to the Flow account with weight and algorithms: - Signature algorithm: `ECDSA_P256` - Hash algorithm: `SHA2_256` :::info Libraries like SimpleWebAuthn can parse the COSE key and produce the raw public key bytes required for onchain registration. Ensure you normalize into the exact raw byte format Flow expects before it writes to the account key. ::: ### Build creation options and create credential Minimum example — wallet‑mode registration: This builds `PublicKeyCredentialCreationOptions` for a wallet RP with a constant registration challenge and ES256 (P‑256) so you can register the newly-created public key on a Flow account. ```tsx // In a wallet (RP = wallet origin). The challenge satisfies API & correlates request/response. // Use a stable, opaque user.id per wallet user (do not randomize per request). const rp = { name: "Passkey Wallet", id: window.location.hostname } as const const user = { id: getStableUserIdBytes(), // Uint8Array (16–64 bytes) stable per user name: "flow-user", displayName: "Flow User", } as const const creationOptions: PublicKeyCredentialCreationOptions = { challenge: new TextEncoder().encode("flow-wallet-register"), // constant is acceptable in wallet-mode; wallet providers may choose and use a constant value as needed for correlation rp, user, pubKeyCredParams: [ { type: "public-key", alg: -7 }, // ES256 (ECDSA on P-256 with SHA-256) // Optionally ES256K (ECDSA on secp256k1 with SHA-256) if the device supports secp256k1 keys: // { type: "public-key", alg: -47 }, ], authenticatorSelection: { userVerification: "preferred" }, timeout: 60_000, attestation: "none", } const credential = await navigator.credentials.create({ publicKey: creationOptions }) // Send to wallet-core (or local) to extract COSE ECDSA P-256 public key (verify attestation if necessary) // Then register the raw uncompressed key bytes on the Flow account as ECDSA_P256/SHA2_256 (this guide's choice) ``` :::tip RP ID for non-browser platforms For web applications, `rpId` is set to `window.location.hostname`. For native mobile and desktop applications, use your app's identifier instead: - **iOS**: Use your app's bundle identifier (such as `com.example.wallet`) or an associated domain. - **Android**: Use your app's package name (such as `com.example.wallet`) or an associated domain. - **Desktop**: Use your application identifier or registered domain. The `rpId` should remain consistent across credential creation and assertion for the same user account. However, Flow does not validate or enforce this consistency. ::: ### Extract and normalize public key Client-side example — extract COSE ECDSA public key (no verification) and derive raw uncompressed 64-byte `X||Y` hex suitable for Flow key registration: This parses the `attestationObject` to locate the COSE EC2 `credentialPublicKey`, reads the x/y coordinates, and returns raw uncompressed 64-byte `X||Y` hex suitable for Flow key registration. Attestation verification is intentionally omitted here. ```tsx // Uses a small CBOR decoder (e.g., 'cbor' or 'cbor-x') to parse attestationObject import * as CBOR from 'cbor' function toHex(bytes: Uint8Array): string { return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('') } function extractCosePublicKeyFromAttestation(attObj: Uint8Array): Uint8Array { // attestationObject is a CBOR map with 'authData' const decoded: any = CBOR.decode(attObj) const authData = new Uint8Array(decoded.authData) // Parse authData (WebAuthn spec): // rpIdHash(32) + flags(1) + signCount(4) = 37 bytes header let offset = 37 // aaguid (16) offset += 16 // credentialId length (2 bytes, big-endian) const credIdLen = (authData[offset] << 8) | authData[offset + 1] offset += 2 // credentialId (credIdLen bytes) offset += credIdLen // The next CBOR structure is the credentialPublicKey (COSE key) return authData.slice(offset) } function coseEcP256ToUncompressedXYHex(coseKey: Uint8Array): string { // COSE EC2 key is a CBOR map; for P-256, x = -2, y = -3 const m: Map = CBOR.decode(coseKey) const x = new Uint8Array(m.get(-2)) const y = new Uint8Array(m.get(-3)) if (x.length > 32 || y.length > 32) throw new Error('Invalid P-256 coordinate lengths') const xy = new Uint8Array(64) xy.set(x, 32 - x.length) xy.set(y, 64 - y.length) return toHex(xy) // 64-byte X||Y hex, no 0x or 0x04 prefix } // Usage const cred = (await navigator.credentials.create({ publicKey: creationOptions })) as PublicKeyCredential const att = cred.response as AuthenticatorAttestationResponse const attObj = new Uint8Array(att.attestationObject as ArrayBuffer) const cosePubKey = extractCosePublicKeyFromAttestation(attObj) const publicKeyHex = coseEcP256ToUncompressedXYHex(cosePubKey) ``` ### Add key to account Now that you have the user's public key, provision a Flow account with that key. Account creation (or to add key to an account) requires payment. In practice, account instantiation typically occurs on the wallet provider's backend service. In the PoC demo, we used a test API to provision an account with the public key: ```ts const ACCOUNT_API = "https://wallet.example.com/api/accounts/provision" export async function createAccountWithPublicKey( publicKeyHex: string, _opts?: {signAlgo?: number; hashAlgo?: number; weight?: number} ): Promise { const trimmed = publicKeyHex const body: ProvisionAccountRequest = { publicKey: trimmed, signatureAlgorithm: "ECDSA_P256", hashAlgorithm: "SHA2_256", } const res = await fetch(ACCOUNT_API, { method: "POST", headers: {Accept: "application/json", "Content-Type": "application/json"}, body: JSON.stringify(body), }) if (!res.ok) throw new Error(`Account API error: ${res.status}`) const json = (await res.json()) as ProvisionAccountResponse if (!json?.address) throw new Error("Account API missing address in response") return json.address } ``` :::note In production, this would be a service owned by the wallet provider that creates the account and attaches the user's public key, for reasons like payment handling, abuse prevention, telemetry, and correlation as needed. ::: ## Signing ### Generate the challenge - Assertion (transaction signing): Wallet sets `challenge` to the SHA2‑256 of the signable transaction message (payload or envelope per signer role). No server‑sent or random challenge is used. Flow includes a domain‑separation tag in the signable bytes. Minimal example — derive signable message and hash (per FLIP): Compute the signer‑specific signable message and hash it with SHA2‑256 to produce the WebAuthn `challenge` (no server‑generated nonce is used in wallet mode). ```tsx // Imports for helpers used to build the signable message import { encodeMessageFromSignable, encodeTransactionPayload } from '@onflow/fcl' // Hash/encoding utilities (example libs) import { sha256 } from '@noble/hashes/sha256' import { hexToBytes } from '@noble/hashes/utils' // Inputs: // - signable: object containing the voucher/payload bytes (e.g., from a ready payload) // - address: the signing account address (hex string) declare const signable: any declare const address: string // 1) Encode the signable message for this signer (payload vs envelope) const msgHex = encodeMessageFromSignable(signable, address) const payloadMsgHex = encodeTransactionPayload(signable.voucher) const role = msgHex === payloadMsgHex ? "payload" : "envelope" // 2) Compute SHA2-256(msgHex) -> 32-byte challenge const signableHash: Uint8Array = sha256(hexToBytes(msgHex)) // 3) Call navigator.credentials.get with challenge = signableHash // (see next subsection for a full getAssertion example) ``` :::info `encodeMessageFromSignable` and `encodeTransactionPayload` are FCL‑specific helpers. If you don't use FCL, construct the Flow signable transaction message yourself (payload for proposer/authorizer, envelope for payer, prepended by the transaction domain tag), then compute `SHA2‑256(messageBytes)` for the challenge. The payload encoding shown here applies regardless of wallet implementation; the helper calls are simply conveniences from FCL. ::: ### Request assertion Minimal example — wallet assertion: Build [PublicKeyCredentialRequestOptions] and request an assertion with the transaction hash as `challenge`. `rpId` must match the wallet domain. When the wallet has mapped the active account to a credential, include `allowCredentials` with that credential ID to avoid extra prompts. You can omit it, which is permissible for discoverable credentials. You will invoke [navigator.credentials.get()]. ```tsx // signableHash is SHA2-256(signable message: payload or envelope) declare const signableHash: Uint8Array declare const credentialId: Uint8Array // Credential ID for the active account (from prior auth) const requestOptions: PublicKeyCredentialRequestOptions = { challenge: signableHash, rpId: window.location.hostname, userVerification: "preferred", timeout: 60_000, allowCredentials: [ { type: "public-key", id: credentialId, }, ], } const assertion = (await navigator.credentials.get({ publicKey: requestOptions, })) as PublicKeyCredential const { authenticatorData, clientDataJSON, signature } = assertion.response as AuthenticatorAssertionResponse ``` :::info - **Credential selection**: Wallets typically know which credential corresponds to the user's active account (selected during authentication/authorization), so they should pass that credential via `allowCredentials` to scope selection and minimize prompts. For discoverable credentials, you can omit `allowCredentials`, which lets the authenticator surface available credentials. See [WebAuthn specifications] for guidance. - **RP ID consistency**: The `rpId` used here should match what was used during credential creation. However, Flow does not validate or enforce this (transactions would still pass even if different). For non-browser platforms, use the same app identifier (bundle ID, package name, and so on.) as in registration. ::: ### Convert and attach signature WebAuthn assertion signatures in this guide are ECDSA P‑256 over SHA‑256 and are typically returned in ASN.1/DER form. Flow expects raw 64‑byte signatures: `r` and `s` each 32 bytes, concatenated (`r || s`). - Convert the DER `signature` to Flow raw `r||s` (64 bytes) and attach with `addr` and `keyId`. - Build the transaction signature extension as specified: `extension_data = 0x01 || RLP([authenticatorData, clientDataJSON])`. Minimal example — convert and attach for submission: Convert the DER signature to Flow raw `r||s` and build `signatureExtension = 0x01 || RLP([authenticatorData, clientDataJSON])` per the FLIP, then compose the Flow transaction signature object for inclusion in your transaction. ```tsx import { encode as rlpEncode } from 'rlp' import { bytesToHex } from '@noble/hashes/utils' // Inputs from previous steps declare const address: string // 0x-prefixed Flow address declare const keyId: number // Account key index used for signing declare const signature: Uint8Array // DER signature from WebAuthn assertion declare const clientDataJSON: Uint8Array declare const authenticatorData: Uint8Array // 1) DER -> raw r||s (64 bytes), implementation below or similar const rawSig = derToRawRS(signature) // 2) Build extension_data per FLIP: 0x01 || RLP([authenticatorData, clientDataJSON]) const rlpPayload = rlpEncode([authenticatorData, clientDataJSON]) as Uint8Array | Buffer const rlpBytes = rlpPayload instanceof Uint8Array ? rlpPayload : new Uint8Array(rlpPayload) const extension_data = new Uint8Array(1 + rlpBytes.length) extension_data[0] = 0x01 extension_data.set(rlpBytes, 1) // 3) Compose Flow signature object const flowSignature = { addr: address, // e.g., '0x1cf0e2f2f715450' keyId, // integer key index signature: '0x' + bytesToHex(rawSig), signatureExtension: extension_data, } ``` #### Submit the signature Return the signature data to the application that initiated signing. The application should attach it to the user transaction for the signer (`addr`, `keyId`) and submit the transaction to the network. See [Transactions] for how signatures are attached per signer role (payload vs envelope) and how submissions are finalized. #### Helper: derToRawRS ```tsx // Minimal DER ECDSA (r,s) -> raw 64-byte r||s function derToRawRS(der: Uint8Array): Uint8Array { let offset = 0 if (der[offset++] !== 0x30) throw new Error("Invalid DER sequence") const seqLen = der[offset++] // assumes short form if (seqLen + 2 !== der.length) throw new Error("Invalid DER length") if (der[offset++] !== 0x02) throw new Error("Missing r INTEGER") const rLen = der[offset++] let r = der.slice(offset, offset + rLen) offset += rLen if (der[offset++] !== 0x02) throw new Error("Missing s INTEGER") const sLen = der[offset++] let s = der.slice(offset, offset + sLen) // Strip leading zeros and left-pad to 32 bytes r = stripLeadingZeros(r) s = stripLeadingZeros(s) const r32 = leftPad32(r) const s32 = leftPad32(s) const raw = new Uint8Array(64) raw.set(r32, 0) raw.set(s32, 32) return raw } function stripLeadingZeros(bytes: Uint8Array): Uint8Array { let i = 0 while (i < bytes.length - 1 && bytes[i] === 0x00) i++ return bytes.slice(i) } function leftPad32(bytes: Uint8Array): Uint8Array { if (bytes.length > 32) throw new Error("Component too long") const out = new Uint8Array(32) out.set(bytes, 32 - bytes.length) return out } ``` ## Notes from the PoC - The [PoC demo] demonstrates reference flows for passkey creation and assertion, such as: - Extract and normalize the ECDSA P‑256 public key for Flow. - Build the correct challenge . - Convert DER signatures to raw `r||s`. - Package WebAuthn fields as signature extension data. > Align your implementation with the FLIP to ensure your extension payloads and verification logic match network expectations. ## Security and UX considerations - Use `ES256` or `ES256k` as algorithms to create Flow account compatible keys. - Clearly communicate platform prompts and recovery paths; passkeys UX can differ across OS/browsers. - Replay protection: Flow uses on‑chain proposal‑key sequence numbers; see [Replay attacks]. - Optional wallet backend: store short‑lived correlation data or rate‑limits as needed (not required). ## Limitations of passkeys **Functionality varies by authenticator** Some security keys do not support biometric authentication, which requires users to enter a PIN instead. Because WebAuthn does not provide access to private keys, users must either store their passkey securely or turn on cloud synchronization for recovery. **Cloud synchronization introduces risks** Cloud-synced passkeys improve accessibility but also create risks if a cloud provider is compromised or if a user loses access to their cloud account. Users who prefer full self-custody can use hardware-based passkeys that do not rely on cloud synchronization. **Passkeys cannot be exported** Users cannot transfer a passkey between different authenticators. For example, a passkey created on a security key cannot move to another device unless it syncs through a cloud provider. To avoid losing access, users should set up authentication on multiple devices or combine passkeys with [multi-key account configurations] for additional recovery options. ## Credential management (wallet responsibilities) Wallet providers should persist credential metadata to support seamless signing, rotation, and recovery: - Map `credentialId` ↔ Flow `addr` (and `keyId`) for the active account. - Store `rpId`, user handle, and (optionally) `aaguid`/attestation info for risk decisions. - Support multiple credentials per account and revocation/rotation workflows. - Enforce nonce/sequence semantics and rate limits server-side as needed. See [WebAuthn Credential Support (FLIP)] for rationale and wallet‑mode guidance. ## Conclusion In this tutorial, you integrated passkeys (WebAuthn) with Flow for both registration and signing. Now that you have completed the tutorial, you should be able to: - Create a WebAuthn credential and derive a Flow‑compatible public key. - Generate the correct challenge for signing transactions (wallet sets SHA2‑256(signable)). - Convert a WebAuthn ECDSA DER signature into Flow's raw `r||s` format and attach the transaction signature extension. ### Further reading - Review signing flows and roles: [Transactions] - Account keys: [Signature and Hash Algorithms] - Web Authentication API (MDN): [Web Authentication API] - Flow Client Library (FCL): [Flow Client Library] - Wallet Provider Spec: [Wallet Provider Spec] - Track updates: [FLIP 264: WebAuthn Credential Support] [WebAuthn Credential Support (FLIP)]: https://github.com/onflow/flips/blob/cfaaf5f6b7c752e8db770e61ec9c180dc0eb6543/protocol/20250203-webauthn-credential-support.md [PoC demo]: https://github.com/onflow/passkey-wallet-demo [FLIP 264: WebAuthn Credential Support]: https://github.com/onflow/flips/blob/cfaaf5f6b7c752e8db770e61ec9c180dc0eb6543/protocol/20250203-webauthn-credential-support.md [Authentication Services]: https://developer.apple.com/documentation/authenticationservices, [Credential Manager]: https://developer.android.com/identity/sign-in/credential-manager [Web Authentication API]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API [navigator.credentials.create()]: https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create [PublicKeyCredentialCreationOptions]: https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions [PublicKeyCredentialRequestOptions]: https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialRequestOptions [navigator.credentials.get()]: https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get [PublicKeyCredential]: https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential [AuthenticatorAttestationResponse]: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAttestationResponse [AuthenticatorAssertionResponse]: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse [Replay attacks]: https://github.com/onflow/flips/blob/cfaaf5f6b7c752e8db770e61ec9c180dc0eb6543/protocol/20250203-webauthn-credential-support.md#replay-attacks [Transactions]: ../basics/transactions.md [Signature and Hash Algorithms]: ../basics/accounts.md [Flow Client Library]: ../../tools/clients/fcl-js/index.md [Wallet Provider Spec]: ../../tools/wallet-provider-spec/index.md [WebAuthn specifications]: https://www.w3.org/TR/webauthn-3 [Limitations of passkeys]: #limitations-of-passkeys [multi-key account support]: ../basics/accounts.md#account-keys [PoC demo]: https://github.com/onflow/passkey-wallet-demo [multi-key account configurations]: ../basics/accounts.md#account-keys --- # File: /md/build/cadence/advanced-concepts/randomness.md --- title: Flow Onchain Randomness in Cadence description: Learn how Flow provides native, secure onchain randomness at the protocol level, enabling developers to build applications with verifiable, unpredictable outcomes without external oracles. keywords: - randomness - VRF - onchain randomness - revertibleRandom - random beacon - commit-reveal - random number generation - blockchain randomness - secure randomness - Flow protocol - randomness beacon - PRNG sidebar_label: VRF (Randomness) in Cadence --- # Randomness on FLOW Flow provides native onchain randomness at the protocol level, which enhances blockchain functionality and eliminates reliance on external oracles. This secure, decentralized feature empowers developers to build a variety of applications with truly unpredictable, transparent, and fair outcomes, achieved with greater efficiency. Flow onchain randomness delivers immediate random values within transactions and bypasses the latency and complexity of oracle integration. Developers can obtain verifiably random results with a single line of Cadence code, which streamlines the development process and enhances the performance of decentralized applications. ## Use Cases of onchain randomness - **Gaming:** Integrates fairness and unpredictability into gameplay, which enhances user engagement without delays. - **NFTs:** Facilitates the creation of uniquely randomized traits in NFTs quickly, which adds to their rarity and value. - **Lotteries & Draws:** Offers instant and verifiably fair random selection for lotteries, which solidifies trust in real-time. - **DeFi Protocols:** Allows rapid and innovative random reward systems within decentralized finance. - **DAOs:** Assists in unbiased voting and task assignments through immediate randomness. - **Broad Applications:** Extends to any domain requiring impartial randomization, from asset distribution to security mechanisms, all with the added benefit of on-demand availability. - **Flow protocol:** Contributes to the proof of stake consensus security by selecting which validator gets to propose the next block, and assigns verification nodes to check block computations. ## Flow distributed randomness beacon Within the Flow protocol, the heart of randomness generation lies in the "Distributed Randomness Beacon". This module generates randomness that is distributed across the network and adheres to established cryptographic and security standards. The output from the randomness beacon is a random source for each block that is unpredictable and impartial. Any node or external client can validate the block random source and verify it was generated fairly, which makes the randomness beacon a Verifiable Random function (VRF). Since Flow mainnet launched, the beacon has ensured protocol security. To do this, it selects which consensus node gets to propose the next block and assigns verification nodes to oversee block computations. For those interested in a more detailed exploration of the randomness beacon and its inner workings, you can read [the technical deep dive on the Flow forum]. The randomness beacon is also used to provide the Flow Virtual Machine (FVM) with random numbers, which allows both Cadence and EVM to access fresh, secure and instant randomness at every block and transaction. ## Revertible randomness For usage of randomness where result abortion is not an issue, it is recommended to use the built-in function `revertibleRandom.` `revertibleRandom` returns a pseudo-random number and is backed by the Distributed Randomness Beacon. The function is available for both Cadence and EVM. ```cadence // Language reference: // https://cadence-lang.org/docs/language/built-in-functions#revertiblerandom access(all) fun main(): UInt64 { // Simple assignment using revertibleRandom - keep reading docs for safe usage! let rand: UInt64 = revertibleRandom() return rand } ``` It is notable that the random number generation process is unpredictable (for miners unpredictable at block construction time and for cadence logic unpredictable at time of call), verifiable, uniform, as well as safe from bias by miners and previously-running Cadence code. See the [Cadence documentation]for more details about the function usage. Although Cadence and EVM exposes safe randomness generated by the Flow protocol via `revertibleRandom`, there is an additional safety-relevant aspect that developers need to be mindful about. The `revertibleRandom` function can be used safely in some applications where the transaction results are _not_ deliberately reverted after the random number is revealed (such as a trusted contract that distributes random NFTs to registered users or onchain lucky draw). However, if applications require a non-trusted party (for instance app users) to submit a transaction that calls a randomized (non-deterministic) contract, the developer must explicitly protect the stream of random numbers to not break the security guarantees: :::warning 🚨 A transaction can atomically revert all its action during its runtime and abort. Therefore, it is possible for a transaction that calls into your smart contract to post-select favorable results and revert the transaction for unfavorable results. In other words, if you write a lottery function that immediately draws a random number that may or may not be a winner, a clever attacker can get infinite guesses for free. Use commit-reveal and sell them a ticket instead! ::: In other words, transactions submitted by a non-trusted party are able to reject their results after the random is revealed. :::info **Post-selection** - the ability for transactions to reject results they don't like - is inherent to any smart contract platform that allows transactions to roll back atomically. See this very similar [Ethereum example]. ::: The risky scenario that a contract developer needs to think about is the following: - Imagine an adversarial user that sends a transaction that calls your smart contract. - The transaction includes code that runs after your smart contract returns and inspects the outcome. - If the outcome is unfavorable (based on some criteria codified in the transaction), the transaction aborts itself. As an example, consider a simple coin toss randomized contract where users can bet any amount of tokens against a random binary output. If the coin toss contract outputs `1`, the user doubles their bet. If the coin toss contract outputs `0`, the user loses their bet in favor of the coin toss. Although the user (or the honest coin toss contract) cannot predict or bias the outcome, the user transaction can check the randomized result and cancel the transaction if they are losing their bet. To do this, call an exception that causes the transaction to error (such as division by zero). All temporary state changes are cancelled and the user can repeat the process until they double their bet. ## Commit-reveal scheme The recommended way to mitigate the problems above is via a commit-reveal scheme. The scheme involves two steps: commit and reveal. During the commit phase, the user transaction commits to accept the future output of a smart contract where the last input is an unknown random source. The user transaction does not know the random source at commit time. The smart contract stores this commitment on the blockchain. The reveal phase can start as early as the next block, when the committed beacon's source of randomness becomes available. The reveal phase can be executed at any block after that, now that the commitment to a past block is stored onchain. With a second transaction, the smart contract can be executed to explicitly generate the random outputs. ### Commit-reveal pattern on Flow [FLIP 123: Onchain Random beacon history for commit-reveal schemes] was introduced to provide a safe pattern to use randomness in transactions so that it's not possible to revert unfavorable randomized transaction results. We recommend this approach as a best-practice example for implementing a commit-reveal scheme in Cadence or EVM. The `RandomBeaconHistory` contract provides a convenient archive, where for each past block height the respective "source of randomness" can be retrieved. The `RandomBeaconHistory` contract is automatically executed by the system at each block to store the next source of randomness. The history table can be used to query the user's committed random source from the past. :::info While the commit-and-reveal scheme mitigates post-selection of results by adversarial clients, secure randomness on Flow additionally protects against any pre-selection vulnerabilities (like biasing attacks by byzantine miners). ::: A commit-reveal scheme can be implemented as follows. To illustrate, we'll revisit the coin toss example discussed earlier: - When a user submits a bidding transaction, the bid amount is transferred to the coin toss contract, and the block height where the bid was made is stored. This is a commitment by the user to use the Source of Randomness (`SoR`) at the current block. Note that the current block's `SoR` isn't known to the transaction execution environment, and therefore the transaction has no way to inspect the random outcome and predict the coin toss result. The current block's `SoR` is only available once added to the history core-contract, which only happens at the end of the block's execution. The user may also commit to using an SoR of some future block, which is equally unknown at the time the bid is made. - The coin toss contract may grant the user a limited window of time (such as a block height range) to send a second transaction for resolving the results and claim any winnings. If it fails to do so, the bid amount remains in the coin toss contract. - Within that reveal transaction, the user calls the coin toss contract, looks us up the block height at which the block was committed and checks that it has already passed. The contract queries that block's `SoR` from the core-contract `RandomBeaconHistory` via block height. - The coin toss contract uses a PRG seeded with the queried `SoR` and diversified with a specific information to the use-case (a user ID or resource ID for instance). Diversification does not add new entropy, but it avoids generating the same outcome for different use-cases. If a diversifier (or salt) isn't used, all users that committed a bid on the same block would either win or lose. - The PRG is used to generate the random result and resolve the bid. Note that the user can make the transaction abort after inspecting a losing result. However, the bid amount would be lost anyway when the allocated window expires. The following lines of code illustrate a random coin toss that cannot be gamed or biased. The commit-reveal scheme prevent clients from post-selecting favorable outcomes. ```cadence // The code below is taken from the example CoinToss contract found in the project repo // https://github.com/onflow/random-coin-toss /// --- Commit --- /// In this method, the caller commits a bet. The contract takes note of the /// block height and bet amount, returning a Receipt resource which is used /// by the better to reveal the coin toss result and determine their winnings. access(all) fun flipCoin(bet: @{FungibleToken.Vault}): @Receipt { let receipt <- create Receipt( betAmount: bet.balance ) // commit the bet // `self.reserve` is a `@FungibleToken.Vault` field defined on the app contract // and represents a pool of funds self.reserve.deposit(from: <-bet) emit CoinFlipped(betAmount: receipt.betAmount, commitBlock: receipt.commitBlock, receiptID: receipt.uuid) return <- receipt } /// --- Reveal --- /// Here the caller provides the Receipt given to them at commitment. The contract /// then "flips a coin" with randomCoin(), providing the committed block height /// and salting with the Receipts unique identifier. /// If result is 1, user loses, if it's 0 the user doubles their bet. /// Note that the caller could condition the revealing transaction, but they've /// already provided their bet amount so there's no loss for the contract if /// they do access(all) fun revealCoin(receipt: @Receipt): @FungibleToken.Vault { pre { receipt.commitBlock <= getCurrentBlock().height: "Cannot reveal before commit block" } let betAmount = receipt.betAmount let commitBlock = receipt.commitBlock let receiptID = receipt.uuid // self.randomCoin() errors if commitBlock <= current block height in call to // RandomBeaconHistory.sourceOfRandomness() let coin = self.randomCoin(atBlockHeight: receipt.commitBlock, salt: receipt.uuid) destroy receipt if coin == 1 { emit CoinRevealed(betAmount: betAmount, winningAmount: 0.0, commitBlock: commitBlock, receiptID: receiptID) return <- FlowToken.createEmptyVault() } let reward <- self.reserve.withdraw(amount: betAmount * 2.0) emit CoinRevealed(betAmount: betAmount, winningAmount: reward.balance, commitBlock: commitBlock, receiptID: receiptID) return <- reward } ``` ## Revertible random or commit-reveal? While both methods are backed by the Flow Randomness Beacon, it is important for developers to mindfully choose between `revertibleRandom` or seeding a PRNG using the `RandomBeaconHistory` smart contract: - With `revertibleRandom` a user has the power to abort and revert if it doesn't like `revertibleRandom`'s outputs. `revertibleRandom` is only suitable for smart contract functions that exclusively run within trusted transactions emitted by trusted parties. You can think of a lottery contract that picks a winning user, where that transaction is emitted by the lottery developer who is trusted to not add the abortion logic into the transaction. Users are able to check the transaction code after it is submitted and make sure the lottery developer acted fairly. - In contrast, the commit-reveal method with the `RandomBeaconHistory` is necessary in cases where the transaction is submitted by non-trusted users and may revert the random outputs. You can think of a user minting a randomized NFT and can add a logic to their transaction to check the random traits and abandon the NFT if they are not happy with the result. Another user playing a betting game, adds a logic to check the bet result and abort whenever they lose the bet. General users are not guaranteed to act honestly when they submit transactions to play. Commit-reveal patterns are the way to limit their actions. During the commit phase, the user commits to proceed with a future source of randomness, which is only revealed after the commit transaction concluded. If you add a safe pattern to reveal randomness without the possibility of conditional transaction reversion, it unlocks applications that rely on randomness. Since we've provided examples of commit-reveal implementations, we hope to foster a more secure ecosystem of decentralized applications and encourage developers to build with best practices. ## An invitation to build Flow onchain randomness opens new doors for innovation, and offers developers the tools to create fair and transparent decentralized applications. With this feature, new possibilities emerge—from enhanced gameplay in decentralized gaming to confirmed integrity of smart contract-driven lotteries or introducing novel mechanisms in DeFi. This is an invitation for builders and creators: leverage onchain randomness on Flow to distinguish your projects and push the boundaries of what's possible. Your imagination and code have the potential to forge new paths in the web3 landscape. So go ahead and build; the community awaits the next big thing that springs from true randomness. ## Learn More If you'd like to dive deeper into onchain randomness on Flow, here's a list of resources: - To learn more about how the randomness beacon works under the hood, see [the forum post]. - These FLIPs provide a more in-depth technical understanding of recent updates related to randomness: - **[FLIP 120: Update unsafeRandom function:]** describes how the beacon provides randoms to `revertibleRandomness`. - **[FLIP 123: Onchain Random beacon history for commit-reveal schemes:]** describes the commit-reveal design and why it is secure. - To see funcational Cadence and EVM code, explore the [coin toss example on GitHub]. [coin toss example on GitHub]: https://github.com/onflow/random-coin-toss [FLIP 123: Onchain Random beacon history for commit-reveal schemes:]: https://github.com/onflow/flips/blob/main/protocol/20230728-commit-reveal.md#flip-123-onchain-random-beacon-history-for-commit-reveal-schemes [FLIP 120: Update unsafeRandom function:]: https://github.com/onflow/flips/blob/main/cadence/20230713-random-function.md#flip-120-update-unsaferandom-function [the forum post]: https://forum.flow.com/t/secure-random-number-generator-for-flow-s-smart-contracts/5110 [the technical deep dive on the Flow forum]: https://forum.flow.com/t/secure-random-number-generator-for-flow-s-smart-contracts/5110 [Cadence documentation]: https://cadence-lang.org/docs/language/built-in-functions#revertiblerandom [Ethereum example]: https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/ [FLIP 123: Onchain Random beacon history for commit-reveal schemes]: https://github.com/onflow/flips/blob/main/protocol/20230728-commit-reveal.md#flip-123-onchain-random-beacon-history-for-commit-reveal-schemes --- # File: /md/build/cadence/advanced-concepts/scaling.md --- title: Scaling Transactions from a Single Account description: Learn how to scale transactions from a single account on Flow using multiple proposer keys, enabling concurrent transaction processing for system-level operations. keywords: - scaling - transactions - proposer keys - sequence numbers - concurrent transactions - system transactions - transaction scaling - Flow blockchain - account scaling - transaction workers - batch operations sidebar_label: Scaling Transactions from a Single Account --- # Scaling Transactions from a Single Account Flow is designed for consumer-scale internet applications and is one of the fastest blockchains globally. Transaction traffic on deployed contracts can be divided into two main categories: 1. **User Transactions** These are transactions initiated by users, such as: - Buying or selling NFTs. - Transferring tokens. - Swapping tokens on decentralized exchanges (DEXs). - Staking or unstaking tokens. In this category, each transaction originates from a unique account and is sent to the Flow network from a different machine. Developers don't need to take special measures to scale for this category, beyond ensuring their logic is primarily onchain and their supporting systems (for example, frontend, backend) can handle scaling if they become bottlenecks. Flow's protocol inherently manages scaling for user transactions. 2. **System Transactions** These are transactions initiated by an app's backend or various tools, such as: - Minting thousands of tokens from a single minter account. - Creating transaction workers for custodians. - Running maintenance jobs and batch operations. In this category, many transactions originate from the same account and are sent to the Flow network from the same machine, which can make scaling tricky. This guide focuses on strategies for scaling transactions from a single account. In the following sections, we'll explore how to execute concurrent transactions from a single account on Flow with multiple proposer keys. :::info This guide is specific to non-EVM transactions. For EVM-compatible transactions, you can use any EVM-compatible scaling strategy. ::: ## Problem Blockchains use sequence numbers, also known as nonces, for each transaction to prevent [replay attacks] and allow users to specify the order of their transactions. The Flow network requires a specific sequence number for each incoming transaction and will reject any transaction where the sequence number does not exactly match the expected next value. This behavior challenges scaling, as sending multiple transactions does not guarantee that the netwrok executes them in the order users send them. This is a fundamental aspect of Flow's resistance to MEV (Maximal Extractable Value), as transaction ordering is randomized within each block. If a transaction arrives out of order, the network rejects it and return an error message similar to the following: ``` * checking sequence number failed: [Error Code: 1007] invalid proposal key: public key X on account 123 has sequence number 7, but given 6 ``` Our objective is to execute multiple concurrent transactions and not encounter the sequence number error described above. When we design a solution, we must consider the following key factors: - **Reliability** Ideally, we want to avoid local sequence number management, as it is error-prone. In a local sequence number implementation, the sender must determine which error types increment the sequence number and which do not. For instance, network issues do not increment the sequence number, but application errors do. Furthermore, if the sender's sequence number becomes unsynchronized with the network, multiple transactions may fail. The most reliable approach to manage sequence numbers is to query the network for the latest sequence number before you sign and send each transaction. - **Scalability** If you allow multiple workers to manage the same sequence number, it can introduce coupling and synchronization challenges. To address this, we aim to decouple workers so that they can operate independently and not interfere with one another. - **Capacity Management** To ensure reliability, the system must recognize when it has reached capacity. Additional transactions should be queued and executed when there is sufficient throughput. Fire-and-forget strategies are unreliable to handle arbitrary traffic, as they do not account for system capacity. ## Solution Flow's transaction model introduces a unique role called the proposer. Each Flow transaction is signed by three roles: authorizer, proposer, and payer. The proposer key determines the sequence number for the transaction, which effectively decouples sequence number management from the authorizer and allows independent scaling. You can learn more about this concept [here]. We can leverage this model to design an ideal system transaction architecture as follows: - **Multiple Proposer Keys** Flow accounts can have multiple keys. If you assign a unique proposer key to each worker, each worker can independently manage its own sequence number without interference from others. - **Sequence Number Management** To ensure each worker uses the correct sequence number, they fetch the latest sequence number from the network. Since workers operate with different proposer keys, there are no conflicts or synchronization issues. - **Queue and Process Workflow** - Each worker picks a transaction request from the incoming requests queue, signs it with its assigned proposer key, and submits it to the network. - The worker remains occupied until the transaction is finalized by the network. - If all workers are busy, the incoming requests queue holds additional requests until there is enough capacity to process them. - **Key reuse for optimization** To simplify the system further, we can reuse the same cryptographic key multiple times within the same account by adding it as a new key. These additional keys can have a weight of 0 since they do not need to authorize transactions. Here's a visual example of how such an [account configuration] might look: ![Example.Account](scaling-example-account.png 'Example Account') As shown, the account includes additional weightless keys designated for proposals, each with its own independent sequence number. This setup ensures that multiple workers can operate concurrently without conflicts or synchronization issues. In the next section, we'll demonstrate how to implement this architecture with the [Go SDK]. ## Example implementation An example implementation of this architecture can be found in the [Go SDK Example]. This example deploys a simple `Counter` contract: ```cadence access(all) contract Counter { access(self) var count: Int init() { self.count = 0 } access(all) fun increase() { self.count = self.count + 1 } access(all) view fun getCount(): Int { return self.count } } ``` The goal is to invoke the `increase()` function 420 times concurrently from a single account. When you add 420 concurrency keys and use 420 workers, all these transactions can be executed almost simultaneously. ### Prerequisites We use Testnet to demonstrate real network conditions. To run this example, you need to create a new testnet account. To start, generate a key pair: ```bash flow keys generate ``` You can use the generated key with the [faucet] to create a testnet account. Update the corresponding variables in the `main.go` file: ```go const PRIVATE_KEY = "123" const ACCOUNT_ADDRESS = "0x123" ``` ### Code walkthrough When the example starts, it will deploy the `Counter` contract to the account and add 420 proposer keys with the following transaction: ```cadence transaction(code: String, numKeys: Int) { prepare(signer: auth(AddContract, AddKey) &Account) { // deploy the contract signer.contracts.add(name: "Counter", code: code.decodeHex()) // copy the main key with 0 weight multiple times // to create the required number of keys let key = signer.keys.get(keyIndex: 0)! var count: Int = 0 while count < numKeys { signer.keys.add( publicKey: key.publicKey, hashAlgorithm: key.hashAlgorithm, weight: 0.0 ) count = count + 1 } } } ``` Next, the main loop starts. Each worker will process a transaction request from the queue and execute it. Here's the code for the main loop: ```go // populate the job channel with the number of transactions to execute txChan := make(chan int, numTxs) for i := 0; i < numTxs; i++ { txChan <- i } startTime := time.Now() var wg sync.WaitGroup // start the workers for i := 0; i < numProposalKeys; i++ { wg.Add(1) // worker code // this will run in parallel for each proposal key go func(keyIndex int) { defer wg.Done() // consume the job channel for range txChan { fmt.Printf("[Worker %d] executing transaction\n", keyIndex) // execute the transaction err := IncreaseCounter(ctx, flowClient, account, signer, keyIndex) if err != nil { fmt.Printf("[Worker %d] Error: %v\n", keyIndex, err) return } } }(i) } close(txChan) // wait for all workers to finish wg.Wait() ``` The `IncreaseCounter` function calls the `increase()` function on the `Counter` contract: ```go // Increase the counter by 1 by running a transaction using the given proposal key func IncreaseCounter(ctx context.Context, flowClient *grpc.Client, account *flow.Account, signer crypto.Signer, proposalKeyIndex int) error { script := []byte(fmt.Sprintf(` import Counter from 0x%s transaction() { prepare(signer: &Account) { Counter.increase() } } `, account.Address.String())) tx := flow.NewTransaction(). SetScript(script). AddAuthorizer(account.Address) // get the latest account state including the sequence number account, err := flowClient.GetAccount(ctx, flow.HexToAddress(account.Address.String())) if err != nil { return err } tx.SetProposalKey( account.Address, account.Keys[proposalKeyIndex].Index, account.Keys[proposalKeyIndex].SequenceNumber, ) return RunTransaction(ctx, flowClient, account, signer, tx) } ``` The above code is executed concurrently by each worker. Since each worker operates with a unique proposer key, there are no conflicts or synchronization issues. Each worker independently manages its sequence number, which ensures smooth execution of all transactions. Finally, the `RunTransaction` function serves as a helper utility to send transactions to the network and wait for them to be finalized. It is important to note that the proposer key sequence number is set within the `IncreaseCounter` function before it calls `RunTransaction`. ```go // Run a transaction and wait for it to be sealed. Note that this function does not set the proposal key. func RunTransaction(ctx context.Context, flowClient *grpc.Client, account *flow.Account, signer crypto.Signer, tx *flow.Transaction) error { latestBlock, err := flowClient.GetLatestBlock(ctx, true) if err != nil { return err } tx.SetReferenceBlockID(latestBlock.ID) tx.SetPayer(account.Address) err = SignTransaction(ctx, flowClient, account, signer, tx) if err != nil { return err } err = flowClient.SendTransaction(ctx, *tx) if err != nil { return err } txRes := examples.WaitForSeal(ctx, flowClient, tx.ID()) if txRes.Error != nil { return txRes.Error } return nil } ``` ### Run the Example Run the example to execute 420 transactions at the same time: ```bash → cd ./examples → go run ./transaction_scaling/main.go . . . Final Counter: 420 ✅ Done! 420 transactions executed in 11.695372059s ``` It takes roughly the time of 1 transaction to run all 420 without any errors. [replay attacks]: https://en.wikipedia.org/wiki/Replay_attack [here]: https://developers.flow.com/build/cadence/basics/transactions#proposal-key [account configuration]: https://www.flowscan.io/account/18eb4ee6b3c026d2?tab=keys [Go SDK]: https://github.com/onflow/flow-go-sdk [Go SDK Example]: https://github.com/onflow/flow-go-sdk/blob/master/examples/transaction_scaling/main.go [faucet]: https://faucet.flow.com/fund-account --- # File: /md/build/cadence/advanced-concepts/scheduled-transactions.md --- title: Scheduled Transactions description: Learn about Flow Scheduled Transactions, enabling smart contracts to autonomously execute predefined logic at specific future times without external triggers. keywords: - scheduled transactions - autonomous execution - Flow blockchain - smart contracts - transaction scheduling - FlowTransactionScheduler - time-based automation - transaction handlers - blockchain automation sidebar_position: 8 --- # Flow Scheduled Transactions Documentation ## Introduction :::info Scheduled transactions were part of the Forte network upgrade and are available on Flow Mainnet, Flow Emulator (CLI v2.7.0+) and Flow Testnet. For more information, see [Forte: Introducing Actions & Agents]. ::: Scheduled transactions on the Flow blockchain allow users and smart contracts to autonomously execute predefined logic at specific future times without external triggers. This powerful feature allows developers to create "wake up" patterns where contracts can schedule themselves to run at predetermined block timestamps, which allows novel blockchain automation patterns. Key benefits include: - **Autonomous execution**: no need for external services or manual intervention. - **Time-based automation**: execute transactions based on blockchain time. - **Predictable scheduling**: guaranteed execution within specified time windows. Common use cases include recurring payments, automated arbitrage, time-based contract logic, delayed executions, and periodic maintenance tasks. :::info Flow provides a scheduled transaction manager to help you manage your scheduled transactions more easily. Check out the [scheduled transactions intro] for a tutorial on how to schedule some basic transactions with the manager. ::: ## Concepts ### Create a scheduled transaction To create a scheduled transaction, the logic that executes in the transaction must already be defined in a function that the scheduler calls when it is time for the transaction to execute. Therefore, all scheduled transactions must include a capability to a resource that conforms to this Transaction Handler interface defined in the Scheduler contract and includes getters that conform to the [Flow metadata views standard]: ```cadence access(all) resource interface TransactionHandler { // Called by the protocol to executed the scheduled transaction // **Transaction ID**: Unique identifier for tracking, returned during scheduling // **Data**: The optional data provided during scheduling that may relate // to the specific scheduled transaction access(Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) // Allows querying this handler to get metadata about it // See the flow metadata views standard for more info access(all) view fun getViews(): [Type] access(all) fun resolveView(_ view: Type): AnyStruct? } ``` To schedule a transaction, store an instance of this resource in your account storage and pass a capability to the scheduler contract as part of the schedule request. Here is a simple example implementation for a Handler's `executeTransaction()` function that transfers FLOW at the scheduled time: ```cadence access(all) contract TransferFLOWHandler { access(all) let HandlerStoragePath: StoragePath access(all) let HandlerPublicPath: PublicPath access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(all) var from: Capability access(all) var amount: UFix64 // other functions left out for simplicity // The actual logic that is executed when the scheduled transaction // is executed access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { if let to = data as Address { let providerRef = self.from.borrow() ?? panic("Could not borrow a reference to the provider FlowToken Vault") // Get a reference to the recipient's Receiver let receiverRef = getAccount(to) .capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Could not borrow a Receiver reference to the FlowToken Vault in account \(to.toString())") // Deposit the withdrawn tokens in the recipient's receiver receiverRef.deposit(from: <-providerRef.withdraw(amount: self.amount)) } else { panic("Unable to transfer FLOW because the data provided when scheduling the transaction is not a Flow address!") } } } // A user would call this to get an instance of this handler // for their own scheduling use access(all) fun createHandler(amount: UFix64, from: Capability): @Handler { return <- create Handler(name: "Transfer FLOW Handler Resource", amount: amount, from: from) } // other functions left out for simplicity } ``` ### Scheduling In scheduling, you create the transaction that executes at a specified future timestamp. The system uses three priority levels: - **High Priority**: guarantees execution in the first block with the scheduled time or fails scheduling, requires the highest fees. - **Medium Priority**: best-effort execution as close as possible to the scheduled time known during scheduling. - **Low Priority**: opportunistic execution when network capacity allows, lowest fees but no guarantee about timing. Each transaction requires: - **Handler Capability**: a capability to a resource implementing `TransactionHandler` interface, like the FLOW transfer one above. - **Timestamp**: future Unix timestamp when execution should occur (fractional seconds ignored). - **Execution Effort**: computational resources allocated (computation unit limit for the transaction). - **Fees**: Flow tokens to cover execution costs and storage costs for the transaction data. - **Optional Data**: arbitrary data that's possibly relevant to the transaction forwarded to the handler during execution. These arguments are required by the [`FlowTransactionScheduler.schedule()` function]. This function returns a `ScheduledTransaction` resource object. The Scheduled Transaction Manager standard (mentioned in the intro) provides an easy way for developers and users to manage their scheduled transactions from a central place in their account. Users are strongly encouraged to use this. More information about the Scheduled Transaction manager is in the [section at the end of this document]. When a transaction is scheduled, the [`FlowTransactionScheduler.Scheduled` event] is emitted with information about the scheduled transaction and handler. ### Fees Fee calculation includes: - **Base execution fee**: based on computational effort with standard Flow fee structure. - **Priority multiplier**: higher priorities pay more (High: 10x, Medium: 5x, Low: 2x base rate). - **Storage fee**: cost to store transaction data on-chain. Fees are paid upfront and are used in full. There are no refunds if the cost of execution was lower. Please keep in mind the priority multiplier can change in the future. You can obtain the fee configuration from the contract, and you can use the estimate function check the fees upfront. ### Execution of transaction handlers When the scheduled time arrives, the Flow blockchain calls the `executeTransaction` method on your handler resource. If the transaction succeeds, the [`FlowTransactionScheduler.Executed` event] is emitted with information about the executed transaction. If the scheduled transaction fails at any point during execution, the `Executed` event is not emitted. ### Cancel transactions You can cancel scheduled transactions before execution. When you cancel a transaction, it returns a portion of the fees (configurable refund percentage, 50% as of now). Please keep in mind the refund percentage can change in the future. To cancel, you need the `ScheduledTransaction` resource that was returned during scheduling. The scheduled transaction manager also makes scheduled transaction cancellation easier. ### Transaction lifecycle Scheduled transactions follow a specific lifecycle with corresponding events: 1. **Scheduled**: Transaction is created and queued for future execution. - Event: `FlowTransactionScheduler.Scheduled` - Status: `Scheduled` 2. **Pending Execution**: Transaction timestamp has arrived and it's ready for execution. - Event: `FlowTransactionScheduler.PendingExecution` - Status: `Executed` (Executed does not necessarily mean it succeeded, just that execution was attempted.) 3. **Executed**: The blockchain processed the transaction. - Event: `FlowTransactionScheduler.Executed` - Status: `Executed` 4. **Canceled**: Transaction was canceled before execution (optional path). - Event: `FlowTransactionScheduler.Canceled` - Status: `Canceled` ### Contracts The `FlowTransactionScheduler` contract is deployed to the service account and manages all scheduled transactions across the network. The `FlowTransactionSchedulerUtils` contract provides utilities for scheduled transactions, such as the transaction `Manager` resource, common handlers, and metadata views related to scheduled transactions. Below are listed the addresses of both transaction scheduler contracts on each network they are deployed: - **Emulator**: `0xf8d6e0586b0a20c7` - \*\*Cadence Testing Framework: `0x0000000000000001` - **Testnet**: `0x8c5303eaa26202d6` ## Examples ### 1. Example test handler contract This contract implements the `TransactionHandler` interface and is used in the following examples. It emits events when scheduled transactions are executed. ```cadence // TestFlowCallbackHandler.cdc - Simple test handler import "FlowTransactionScheduler" access(all) contract TestFlowScheduledTransactionHandler { access(all) let HandlerStoragePath: StoragePath access(all) let HandlerPublicPath: PublicPath access(all) event TransactionExecuted(data: String) access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { if let string: String = data as? String { emit TransactionExecuted(data: string) } else { emit TransactionExecuted(data: "bloop") } } // public functions that anyone can call to get information about // this handler access(all) view fun getViews(): [Type] { return [Type(), Type(), Type()] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return TestFlowScheduledTransactionHandler.HandlerStoragePath case Type(): return TestFlowScheduledTransactionHandler.HandlerPublicPath case Type(): return MetadataViews.Display( name: "Basic Scheduled Transaction Handler", description: "Emits a TransactionExecuted event when the scheduled transaction is executed", thumbnail: MetadataViews.HTTPFile( url: "" ) ) default: return nil } } } access(all) fun createHandler(): @Handler { return <- create Handler() } init() { self.HandlerStoragePath = /storage/testCallbackHandler self.HandlerPublicPath = /public/testCallbackHandler } } ``` ### 2. Schedule a transaction with the scripts manager This example shows how to create and schedule a transaction that will execute at a future timestamp with the [`TestFlowCallbackHandler`] from Example 1. ```cadence // schedule.cdc import "FlowTransactionScheduler" import "FlowTransactionSchedulerUtils" import "TestFlowScheduledTransactionHandler" import "FlowToken" import "FungibleToken" transaction(timestamp: UFix64, feeAmount: UFix64, effort: UInt64, priority: UInt8, testData: AnyStruct?) { prepare(account: auth(BorrowValue, SaveValue, IssueStorageCapabilityController, PublishCapability, GetStorageCapabilityController) &Account) { // if a transaction scheduler manager has not been created for this account yet, create one if !account.storage.check<@{FlowTransactionSchedulerUtils.Manager}>(from: FlowTransactionSchedulerUtils.managerStoragePath) { let manager <- FlowTransactionSchedulerUtils.createManager() account.storage.save(<-manager, to: FlowTransactionSchedulerUtils.managerStoragePath) // create a public capability to the callback manager let managerRef = account.capabilities.storage.issue<&{FlowTransactionSchedulerUtils.Manager}>(FlowTransactionSchedulerUtils.managerStoragePath) account.capabilities.publish(managerRef, at: FlowTransactionSchedulerUtils.managerPublicPath) } // If a transaction handler has not been created for this account yet, create one, // store it, and issue a capability that will be used to create the transaction if !account.storage.check<@TestFlowScheduledTransactionHandler.Handler>(from: TestFlowScheduledTransactionHandler.HandlerStoragePath) { let handler <- TestFlowScheduledTransactionHandler.createHandler() account.storage.save(<-handler, to: TestFlowScheduledTransactionHandler.HandlerStoragePath) account.capabilities.storage.issue(TestFlowScheduledTransactionHandler.HandlerStoragePath) let publicHandlerCap = account.capabilities.storage.issue<&{FlowTransactionScheduler.TransactionHandler}>(TestFlowScheduledTransactionHandler.HandlerStoragePath) account.capabilities.publish(publicHandlerCap, at: TestFlowScheduledTransactionHandler.HandlerPublicPath) } // Get the entitled capability that will be used to create the transaction // Need to check both controllers because the order of controllers is not guaranteed var handlerCap: Capability? = nil if let cap = account.capabilities.storage .getControllers(forPath: TestFlowScheduledTransactionHandler.HandlerStoragePath)[0] .capability as? Capability { handlerCap = cap } else { handlerCap = account.capabilities.storage .getControllers(forPath: TestFlowScheduledTransactionHandler.HandlerStoragePath)[1] .capability as! Capability } // borrow a reference to the vault that will be used for fees let vault = account.storage.borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow FlowToken vault") let fees <- vault.withdraw(amount: feeAmount) as! @FlowToken.Vault let priorityEnum = FlowTransactionScheduler.Priority(rawValue: priority) ?? FlowTransactionScheduler.Priority.High // borrow a reference to the callback manager let manager = account.storage.borrow(from: FlowTransactionSchedulerUtils.managerStoragePath) ?? panic("Could not borrow a Manager reference from \(FlowTransactionSchedulerUtils.managerStoragePath)") // Schedule the regular transaction with the main contract manager.schedule( handlerCap: handlerCap!, data: testData, timestamp: timestamp, priority: priorityEnum, executionEffort: effort, fees: <-fees ) } } ``` ### 3. Query transaction information Get Status: [The get_status script] demonstrates how to check the current status of a scheduled transaction with the global status function. Get all Tx Info: [The get_transaction_data script] gets all the internal information about a scheduled transaction. #### Manager scripts The manager provides many different ways to get information about all of your scheduled transactions. Check out all the scripts you can use with your [manager]. ### 4. Cancel a scheduled transaction This transaction shows how to cancel a scheduled transaction and receive a partial refund of the fees paid. ```cadence // cancel_transaction.cdc import "FlowTransactionScheduler" import "FlowToken" transaction(transactionId: UInt64) { prepare(account: auth(BorrowValue, SaveValue, LoadValue) &Account) { // borrow a reference to the manager let manager = account.storage.borrow(from: FlowTransactionSchedulerUtils.managerStoragePath) ?? panic("Could not borrow a Manager reference from \(FlowTransactionSchedulerUtils.managerStoragePath)") // Get the vault where the refund should be deposited let vault = account.storage.borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow FlowToken vault") // cancel the transaction vault.deposit(from: <-manager.cancel(id: id)) } } ``` ### 5. Fee estimation This script helps estimate the cost of scheduling a transaction before you actually submit it. This is useful for budget and validation. ```cadence // estimate_fees.cdc - Script to estimate scheduling costs import "FlowTransactionScheduler" access(all) fun main( dataSize: AnyStruct?, timestamp: UFix64, priority: UInt8, executionEffort: UInt64 ): FlowTransactionScheduler.EstimatedScheduledTransaction { let priorityEnum = FlowTransactionScheduler.Priority(rawValue: priority) ?? FlowTransactionScheduler.Priority.Medium return FlowTransactionScheduler.estimate( data: dataSize, timestamp: timestamp, priority: priorityEnum, executionEffort: executionEffort ) } ``` ### 6. Monitor execution events Use the Flow Command Line Interface (CLI) to monitor all scheduled transaction events in real-time (example for testnet - account addresses may differ): ```bash flow events get \ A.8c5303eaa26202d6.FlowTransactionScheduler.Scheduled \ A.8c5303eaa26202d6.FlowTransactionScheduler.PendingExecution \ A.8c5303eaa26202d6.FlowTransactionScheduler.Executed \ A.8c5303eaa26202d6.FlowTransactionScheduler.Canceled \ A.373ce83aef691d2d.TestFlowCallbackHandler.TransactionExecuted \ --last 200 \ -n testnet ``` This command fetches the last 200 blocks of events for: - **Scheduled**: when a transaction is scheduled. - **PendingExecution**: when a transaction is ready for execution. - **Executed**: when a transaction has been executed. - **Canceled**: when a transaction is canceled. - **TransactionExecuted**: custom event from the test handler. These examples demonstrate the complete lifecycle of scheduled transactions: create handlers, schedule execution, monitor events, and manage cancellations. The system provides flexibility for various automation scenarios while you maintain network stability through resource limits and priority management. ## Tools Support for scheduled transactions in different tools is still work in progress and is coming soon. The Flow CLI and Access Node API will support specific commands and APIs to query scheduled transactions by ID, which it easier to manage and monitor your scheduled transactions programmatically. The [flow-go-sdk] will also add support for these new commands. It provides native integration for Go applications that work with scheduled transactions. Block explorer support for scheduled transactions is also coming, which will provide a visual interface to view and track scheduled transaction execution on the Flow blockchain. For feature requests and suggestions for scheduled transaction tooling, visit [github.com/onflow/flow] and create an issue with the tag `scheduled_transactions`. Read [FLIP 330: Scheduled Callbacks] for more details. [FLIP 330: Scheduled Callbacks]: https://github.com/onflow/flips/blob/main/protocol/20250609-scheduled-callbacks.md [flow-go-sdk]: ../../tools/clients/flow-go-sdk/index.md [Flow metadata views standard]: metadata-views.md [Forte: Introducing Actions & Agents]: https://flow.com/post/forte-introducing-actions-agents-supercharging-composability-and-automation [github.com/onflow/flow]: https://github.com/onflow/flow [manager]: https://github.com/onflow/flow-core-contracts/tree/master/transactions/transactionScheduler/scripts/manager [The get_status script]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/transactionScheduler/scripts/get_status.cdc [The get_transaction_data script]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/transactionScheduler/scripts/get_transaction_data.cdc [`TestFlowCallbackHandler`]: #1-example-test-handler-contract [`FlowTransactionScheduler.Executed` event]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowTransactionScheduler.cdc#L78 [`FlowTransactionScheduler.Scheduled` event]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowTransactionScheduler.cdc#L52 [section at the end of this document]: #2-scheduling-a-transaction-with-the-manager [`FlowTransactionScheduler.schedule()` function]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowTransactionScheduler.cdc#L732 [scheduled transactions intro]: ../../../blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md --- # File: /md/build/cadence/basics/accounts.md --- sidebar_position: 2 title: Accounts description: Learn about Flow blockchain accounts, including their structure, key management, multi-sig capabilities, and creation process. Understand how accounts store contracts, manage storage, and handle transaction signing. keywords: - Flow accounts - blockchain accounts - account keys - multi-sig - public keys - account storage - account creation - keyless accounts - service accounts - account address - account balance - signature algorithms - hash algorithms - account contracts --- :::info Are you an EVM developer looking for information about EVM Accounts on Flow? If so, check out the EVM specific documentation [here](../../../build/evm/accounts.md) ::: # Accounts An account on Flow is a record in the chain state that holds the following information: - Address: unique identifier for the account. - Public Keys: public keys authorized on the account. - Code: Cadence contracts deployed to the account. - Storage: area of the account used to store resource assets. Accounts and their keys are needed to sign transactions that change the Flow blockchain state. To execute a transaction, a small amount of Flow, called a ["Fee"] must be paid by the account or subsidized by a wallet or service. Flow allocates a [fixed amount of storage] to each account to save data structures and Resources. An account may also contain contract code which transactions and scripts can interact with to query or mutate the state of the blockchain. A simple representation of an account: ![Screenshot 2023-08-16 at 16.43.07.png](_accounts_images/Screenshot_2023-08-16_at_16.43.07.png) ## Address A Flow address is represented as 16 hex-encoded characters (usually prefixed with `0x` to indicate hex encoding). Unlike Bitcoin and Ethereum, Flow addresses are not derived from cryptographic public keys. Instead, each Flow address is assigned by the Flow protocol via an onchain deterministic sequence. The sequence uses an error detection code to guarantee that all addresses differ with at least two hex characters. This makes typos that result in accidental loss of assets impossible. This decoupling is a unique advantage of Flow, as it allows for multiple public keys to be associated with one account, or for a single public key to be used across several accounts. ## Balance Each Flow account created on Mainnet will by default [hold a Flow vault that holds a balance and is part of the FungibleToken standard]. This balance is used to pay for [transaction fees and storage fees]. :::warning The minimum amount of FLOW an account can have is **0.001**. ::: This minimum storage fee is provided by the account creator and covers the cost of storage up to 100kB of data in perpetuity. This fee is applied only once and can be "topped up" to add additional storage to an account. The minimum account reservation ensures that most accounts won't run out of storage capacity if anyone deposits anything (like an NFT) to the account. ### Maximum available balance Due to the storage restrictions, there is a maximum available balance that user can withdraw from the wallet. The core contract [`FlowStorageFees`] provides a function to retrieve that value: ```cadence import "FlowStorageFees" access(all) fun main(accountAddress: Address): UFix64 { return FlowStorageFees.defaultTokenAvailableBalance(accountAddress) } ``` Alternatively developers can use `availableBalance` property of the `Account` ```cadence access(all) fun main(address: Address): UFix64 { let acc = getAccount(address) let balance = acc.availableBalance return balance } ``` ## Contracts An account can optionally store multiple [Cadence contracts]. The code is stored as a human-readable UTF-8 encoded string which makes it easy for anyone to inspect the contents. ## Storage Each Flow account has an associated storage and capacity. The account's storage used is the byte size of all the data stored in the account's storage. An account's [storage capacity is directly tied to the balance of Flow tokens] an account has. An account can, without any additional cost, use any amount of storage up to its storage capacity. If a transaction puts an account over storage capacity or drops an account's balance below the minimum 0.001 Flow tokens, that transaction fails and reverts. ## Account **keys** Flow accounts can be configured with multiple public keys that are used to control access. Owners of the associated private keys can sign transactions to mutate the account's state. During account creation, public keys can be provided which will be used when owners interact with the account. You can send a transaction to add, remove, or revoke account keys. This is radically different from blockchains like Ethereum where an account is tied to a single public/private key pair. Each account key has a weight that determines the signing power it holds. :::warning A transaction is not authorized to access an account unless it has a total signature weight greater than or equal to **1000**, the weight threshold. ::: For example, an account might contain three keys, each with 500 weight: ![Screenshot 2023-08-16 at 16.28.58.png](_accounts_images/Screenshot_2023-08-16_at_16.28.58.png) This represents a 2-of-3 multi-sig quorum, in which a transaction is authorized to access the account if it receives signatures from _at least_ two out of three keys. An account key contains the following attributes: - **ID** used to identify keys within an account. - **Public Key** raw public key (encoded as bytes). - **Signature algorithm** (see below). - **Hash algorithm** (see below). - **Weight** integer between 0-1000. - **Revoked** whether the key has been revoked or it's active. - **Sequence Number** is a number that increases with each submitted transaction signed by this key. ### Signature and hash algorithms The signature and hashing algorithms are used during the transaction signing process and can be set to certain predefined values. There are two curves commonly used with the ECDSA algorithm, secp256r1 ([OID 1.2.840.10045.3.1.7], also called the "NIST P-256." this curve is common for mobile secure enclave support), and secp256k1 ([OID 1.3.132.0.10], the curve used by "Bitcoin"). Be sure to double-check which parameters you use before you register a key, because if you present a key that uses one of the curves under the code and format of the other, an error will occur. | Algorithm | Curve | ID | Code | | --------- | --------- | --------------- | ---- | | ECDSA | P-256 | ECDSA_P256 | 2 | | ECDSA | secp256k1 | ECDSA_secp256k1 | 3 | _Note that the codes listed here are for the signature algorithms as used by the node API, and they are different from the ones [defined in Cadence]_ | Algorithm | Output Size | ID | Code | | --------- | ----------- | -------- | ---- | | SHA-2 | 256 | SHA2_256 | 1 | | SHA-3 | 256 | SHA3_256 | 3 | Both hashing and signature algorithms are compatible with each other, so you can freely choose from the set. ### **Locked / keyless accounts** An account on Flow doesn't require keys in order to exist, but this makes the account immutable since no transaction can be signed that can change the account. This can be useful if we want to freeze an account contract code and it elegantly solves the problem of when you have multiple account types (as that is the case for Ethereum). ![Screenshot 2023-08-16 at 18.59.10.png](_accounts_images/Screenshot_2023-08-16_at_18.59.10.png) To achieve keyless accounts, you can remove a public key from an account signing with that same key and repeat that action until an account has no keys left. You can also create a new account that has no keys assigned. With account linking, you can also have a child account that has no keys, but that the parent controls. :::danger Be careful when you remove keys from an account, because after an account's total key weights sum to less than 1000, it can no longer be modified. ::: ### **Multi-sig accounts** To create a multi-signature account, you can manage the account keys and their corresponding weight. To repeat, in order to sign a transaction the keys used to sign it must have weights that sum up to at least 1000. With this information, we can easily see how we can achieve the following cases: #### 2-of-3 multi-sig quorum ![Screenshot 2023-08-16 at 19.34.44.png](_accounts_images/Screenshot_2023-08-16_at_19.34.44.png) #### 3-of-3 multi-sig quorum ![Screenshot 2023-08-16 at 19.34.55.png](_accounts_images/Screenshot_2023-08-16_at_19.34.55.png) #### 1-of-2 signature ![Screenshot 2023-08-16 at 19.34.51.png](_accounts_images/Screenshot_2023-08-16_at_19.34.51.png) ### Key format We support ECDSA with the curves `P-256` and `secp256k1`. For these curves, the public key is encoded into 64 bytes as `X||Y` where `||` is the concatenation operator. - `X` is 32 bytes and is the big endian byte encoding of the `x`-coordinate of the public key padded to 32, i.e. `X=x_31||x_30||...||x_0` or `X = x_31*256^31 + ... + x_i*256^i + ... + x_0`. - `Y` is 32 bytes and is the big endian byte encoding of the `y`-coordinate of the public key padded to 32, i.e. `Y=y_31||y_30||...||y_0` or `Y = y_31*256^31 + ... + y_i*256^i + ... + y_0` ## Account creation To create accounts on the Flow blockchain, we call a special [create account Cadence function]. After an account is created, we can associate a new key with that account. Of course, all that can be done within a single transaction. Keep in mind that there is an account creation fee that needs to be paid. Account creation fees are relatively low, and we expect that wallet providers and exchanges will cover the cost when a user converts fiat to crypto for the first time. For development purposes, [you can use Flow CLI to easily create emulator, testnet and mainnet accounts]. The account creation fee is paid by a funding wallet, so you don't need a pre-existing account to create it. ### **Key generation** You should generate keys in a secure manner, and take different levels of caution for each key's purpose. :::warning Anyone who obtains access to a private key can modify the account the key is associated with (assuming it has enough weight). Be very careful how you store the keys. ::: For secure production keys, we suggest that you use key management services such as [Google key management] or [Amazon KMS], which are also supported by our CLI and SDKs. Those services are mostly great when integrated into your application. However, for personal use, you can securely use any [existing wallets] as well as a [hardware Ledger wallet]. ## Service accounts ### Flow Service Account The Service Account is a special account in Flow that has special permissions to manage system contracts. It is able to mint tokens, set fees, and update network-level contracts. ### Tokens and fees The Service Account has administrator access to the FLOW token smart contract, so it has authorization to mint and burn tokens. It also has access to the transaction fee smart contract and can adjust the fees charged for transactions execution on Flow. ### Network management The Service Account administers other smart contracts that manage various aspects of the Flow network, such as epochs and (in the future) validator staking auctions. ### Governance Besides its special permissions, the Service Account is an account like any other in Flow. The service account is currently controlled by a smart contract governed by the Flow community. No single entity has the ability to unilaterally execute a transaction from the service account because it requires four signatures from controlling keys. The Flow foundation only controls three of the keys and the others are controlled by trusted community members and organizations. ## Accounts retrieval You can use the Flow Command Line Interface (CLI) to get account data. To do this, run the following command: ```sh flow accounts get 0xf919ee77447b7497 -n mainnet ``` Find [more about the command in the CLI docs]. Accounts can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: **gRPC API** [building-on-flow/nodes/access-api#accounts] **REST API** [http-api#tag/Accounts] There are multiple SDKs implementing the above APIs for different languages: **Javascript SDK** [tools/clients/fcl-js] **Go SDK** [tools/clients/flow-go-sdk] Find a list of all SDKs here: [tools/clients] ["Fee"]: ./fees.md [fixed amount of storage]: ./fees.md#storage [hold a Flow vault that holds a balance and is part of the FungibleToken standard]: ./flow-token.md [transaction fees and storage fees]: ./fees.md [`FlowStorageFees`]: ../core-contracts/05-flow-fees.md#flowstoragefees [Cadence contracts]: https://cadence-lang.org/docs/language/contracts [storage capacity is directly tied to the balance of Flow tokens]: ./fees.md#storage [OID 1.2.840.10045.3.1.7]: http://oid-info.com/get/1.2.840.10045.3.1.7 [OID 1.3.132.0.10]: http://oid-info.com/get/1.3.132.0.10 [defined in Cadence]: https://cadence-lang.org/docs/language/crypto#signing-algorithms [create account Cadence function]: https://cadence-lang.org/docs/language/accounts#account-creation [you can use Flow CLI to easily create emulator, testnet and mainnet accounts]: ../../../build/tools/flow-cli/accounts/create-accounts.md [Google key management]: https://cloud.google.com/security-key-management [Amazon KMS]: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.Keys.html [existing wallets]: ../../../ecosystem/wallets.md [hardware Ledger wallet]: ../../../ecosystem/wallets.md) [more about the command in the CLI docs]: ../../../build/tools/flow-cli/accounts/get-accounts.md [building-on-flow/nodes/access-api#accounts]: ../../../protocol/access-onchain-data/index.md#accounts [http-api#tag/Accounts]: /http-api#tag/Accounts [tools/clients/fcl-js]: ../../../build/tools/clients/fcl-js/index.md [tools/clients/flow-go-sdk]: ../../../build/tools/clients/flow-go-sdk/index.md [tools/clients]: ../../../build/tools/clients/index.md --- # File: /md/build/cadence/basics/blocks.md --- sidebar_position: 1 title: Blocks description: Learn about Flow blockchain blocks, their structure, lifecycle, and how they maintain the blockchain's state. Understand block headers, payloads, and the finalization process. keywords: - blocks - blockchain blocks - block header - block payload - block seals - block finalization - block status - consensus - collection guarantees - block retrieval - block ID - block height - Flow blockchain - blockchain state --- # Blocks Blocks are entities that make up the Flow blockchain. Each block contains a list of [transactions] that were executed and as a result, changed the global blockchain state. Each block is identified by a unique ID which is a cryptographic hash of the block contents. Block also includes a link to the parent block ID, which creates a linked list of blocks called the Flow blockchain. The unique block ID serves as proof of the block contents which any oberver can independently validate. Interesting cryptographic properties of the hash that make up the block ID guarantee that if any change is made to the block data, it would produce a different hash. Because blocks are linked, a different hash would break the link as it would no longer be referenced in the next block. A very basic representation of blocks is: ![Screenshot 2023-08-16 at 15.16.38.png](_blocks_images/Screenshot_2023-08-16_at_15.16.38.png) Blocks are ordered from the genesis block 0 up to the latest block. Each block contains an ordered list of transactions. This is how the Flow blockchain preserves the complete history of all the changes made to the state from the start to the current state. Each block contains more data which is divided into **block header** and **block payload**. There are many representations of block data within the Flow protocol. APIs, node types, and specific components within the node may view a block from different perspectives. For the purpose of this document, we will talk about block data we expose through APIs to the clients. ![Screenshot 2023-08-16 at 10.50.53.png](_blocks_images/Screenshot_2023-08-16_at_10.50.53.png) ### Block header The Block header contains the following fields: - **ID** represents the block's unique identifier, which is derived from the hashing block header, which includes the payload hash. The algorithm used on Flow to hash the content and get an identifier is SHA3 256. This ID is a commitment to all the values in the block staying the same. - **Parent ID** is a link to the previous block ID in the list that makes up the blockchain. - **Height** is the block sequence number, where block 0 was the first block produced, and each next block increments the value by one. - **Timestamp** is the timestamp at which this block was proposed by the consensus node. Depending on your use case, this time might not be accurate enough, [read more about how to measure time on the Flow blockchain]. - **Payload Hash** represents the payload hash that is included when producing the ID of the block. To calculate the payload hash take Merkle root hashes of collection guarantees, seals, execution receipts, and execution results and hash them together. For more information, see the block payload section below. ### Block payload The block payload contains the following fields: - **Collection Guarantees** is a list of collection IDs with the signatures from the collection nodes that produced the collections. This acts as a guarantee by collection nodes that [transactions]data in the collection will be available on the collection node if requested by other nodes at a later time. Flow purposely skips including transaction data in a block, makes blocks as small as possible, and produces new blocks by consensus nodes fast, because consensus nodes have to sync the proposed block between nodes, and that data should be the smallest possible. The consensus nodes don't really care what will a transaction do as long as it's valid, they only need to define an order of those transactions in a block. - **Block Seals** is the attestation by verification nodes that the transactions in a previously executed block have been verified. This seals a previous block referenced by the block ID. It also references the result ID and execution root hash. It contains signatures of the verification nodes that produced the seal. ## Lifecycle and status Block status is not a value stored inside the block itself, but it represents the lifecycle of a block. We derive this value based on the block inclusion in the Flow blockchain and present it to the user as it acts as an important indicator of the finality of the changes the block contains. Here, we'll give an overview of the different phases a block goes through. [More details can be found in the whitepaper]. Also, a lot of the block states are not necessarily important to the developer but only important to the Flow blockchain's function. New blocks are constantly being proposed even if no new transactions are submitted to the network. Consensus nodes are in charge of blocks production. They use a consensus algorithm (an implementation of HotStuff) to agree on what the new block will be. A block contains the ordered list of collections and each collection contains an ordered list of transactions. This is an important fact to reiterate. A block serves as a list of transitions to the Flow state machine. It documents, as an ordered list, all the changes transactions will make to the state. A block that is [agreed upon by the consensus nodes via an implementation of HotStuff consensus algorithm] to be the next block is **finalized**. This means the block won't change anymore and it will next be executed by the execution node. Be careful because until a block is **sealed** the changes are not to be trusted. After verification nodes validate and agree on the correctness of execution results, a block is sealed and consensus nodes will include these seals in the new block. In summary, a block can be either **finalized** which guarantees transactions included in the block will stay the same and will be executed, and **sealed** which means the block execution was verified. ![Screenshot 2023-08-16 at 10.48.26.png](_blocks_images/Screenshot_2023-08-16_at_10.48.26.png) ## Block Retrieval You can use the Flow CLI to get the block data by running: ```sh flow blocks get latest -network mainnet ``` Find [more about the command in the CLI docs] Blocks can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: [**gRPC Block API**] [**REST Block API**] There are multiple SDKs that implement the above APIs for different languages: [**Javascript SDK**] [**Go SDK**] Find a list of all SDKs [here]. [read more about how to measure time on the Flow blockchain]: https://cadence-lang.org/docs/measuring-time#time-on-the-flow-blockchain [read more about measuring time on the Flow blockchain]: https://cadence-lang.org/docs/measuring-time#time-on-the-flow-blockchain [transactions]: ./transactions.md [More details can be found in the whitepaper]: https://flow.com/technical-paper [agreed upon by the consensus nodes via an implementation of HotStuff consensus algorithm]: https://arxiv.org/pdf/2002.07403.pdf [more about the command in the CLI docs]: ../../../build/tools/flow-cli/get-flow-data/get-blocks.md [**gRPC Block API**]: ../../../protocol/access-onchain-data/index.md#blocks [**REST Block API**]: /http-api#tag/Blocks [**Javascript SDK**]: ../../../build/tools/clients/fcl-js/index.md [**Go SDK**]: ../../../build/tools/clients/flow-go-sdk/index.md [here]: ../../../build/tools/clients/index.md --- # File: /md/build/cadence/basics/collections.md --- sidebar_position: 1 title: Collections description: Learn about Flow blockchain collections, how they optimize data transfer by linking blocks and transactions, and their role in the network architecture. Understand how collection nodes create and manage transaction collections. keywords: - collections - blockchain collections - transaction collections - collection nodes - HotStuff consensus - transaction hashes - network optimization - collection clusters - transaction payload - Flow architecture - consensus nodes - collection retrieval - blockchain scaling - data optimization --- # Collections Collections link blocks and transactions together. Collection node clusters make these collections (via the HotStuff consensus algorithm), made up of an ordered list of one or more hashes of [signed transactions]. In order to optimize data, blocks don't contain transactions (as they do on Ethereum). The benefits are transaction data does not get transferred to consensus nodes on the network which optimizes transfer speed, and this architecture allows you to add collection clusters, which scales ingestion speed. Consensus nodes need to only agree on the order of transactions to be executed, they don't need to know the transaction payload, which makes blocks and collections lightweight. Collection nodes hold transaction payloads for anyone who requests them (for example, execution nodes). ![Screenshot 2023-08-17 at 19.50.39.png](_collection_images/Screenshot_2023-08-17_at_19.50.39.png) ## Collection retrieval To use the Flow CLI to get the collection data, run the following command: ```sh flow collections get caff1a7f4a85534e69badcda59b73428a6824ef8103f09cb9eaeaa216c7d7d3f -n mainnet ``` Find [more about the command in the CLI docs]. Collections can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: [**gRPC Collection API**] [**REST Collection API**] There are multiple SDKs that implement the above APIs for different languages: [**Javascript SDK**] [**Go SDK**] Find a list of all SDKs [here]. [signed transactions]: ./transactions.md [more about the command in the CLI docs]: ../../../build/tools/flow-cli/get-flow-data/get-blocks.md [**gRPC Collection API**]: ../../../protocol/access-onchain-data/index.md#collections [**REST Collection API**]: /http-api#tag/Collections [**Javascript SDK**]: ../../../build/tools/clients/fcl-js/index.md [**Go SDK**]: ../../../build/tools/clients/flow-go-sdk/index.md [here]: ../../../build/tools/clients/index.md --- # File: /md/build/cadence/basics/events.md --- sidebar_position: 6 title: Events description: Learn about Flow blockchain events, including core events and user-defined events. Understand how events are emitted during transaction execution and how they can be observed by off-chain applications. keywords: - events - blockchain events - Flow events - core events - user-defined events - event emission - event payload - fungible token events - fee events - event listeners - transaction events - event types - event naming - Flow network - event observation --- # Events Flow events are special values that are emitted on the network during the execution of a Cadence program and can be observed by off-chain observers. Events are defined as Cadence code and you should [read the Cadence documentation] to understand how to define them. Since transactions don't have return values you can leverage events to broadcast certain changes the transaction caused. Clients taht listen on Flow networks (apps) can listen to these events that are emitted and react. ![Screenshot 2023-08-18 at 14.09.33.png](_events_images/Screenshot_2023-08-18_at_14.09.33.png) There are two types of events emitted on the Flow network: - Core events - User-defined events Events consist of the **event name** and an optional **payload**. ![Screenshot 2023-08-18 at 13.59.01.png](_events_images/Screenshot_2023-08-18_at_13.59.01.png) ## Core events Core events are events emitted directly from the FVM (Flow Virtual Machine). The events have the same name on all networks and do not follow the same naming as user-defined events (they have no address). A list of events that are emitted by the Flow network is: | Event Name | Description | | --------------------------- | ----------------------------------------------------------------------- | | flow.AccountCreated | Event that is emitted when a new account gets created. | | flow.AccountKeyAdded | Event that is emitted when a key gets added to an account. | | flow.AccountKeyRemoved | Event that is emitted when a key gets removed from an account. | | flow.AccountContractAdded | Event that is emitted when a contract gets deployed to an account. | | flow.AccountContractUpdated | Event that is emitted when a contract gets updated on an account. | | flow.AccountContractRemoved | Event that is emitted when a contract gets removed from an account. | | flow.InboxValuePublished | Event that is emitted when a Capability is published from an account. | | flow.InboxValueUnpublished | Event that is emitted when a Capability is unpublished from an account. | | flow.InboxValueClaimed1 | Event that is emitted when a Capability is claimed by an account. | For more details [on the core events, you can read Cadence reference documentation]. ## User-defined events Events that are defined inside contracts and when emitted follow a common naming schema. The schema consists of four parts: ```cadence A.{contract address}.{contract name}.{event type} ``` An example event would look like: ![Screenshot 2023-08-18 at 14.30.36.png](_events_images/Screenshot_2023-08-18_at_14.30.36.png) The first `A` means the event originates from a contract, which will always be the case for user-defined events. The contract address as the name implies is the location of a contract deployed on the Flow network. Next, is the name of the contracted event originates from, and last is the event type defined in the contract. There is an unlimited amount of events that can be defined on Flow, but you should know about the most common ones. ### Fungible token events All fungible token contracts, which includes [The FLOW Token contract], use the [fungible token standard on Flow]. As with any contract, the standard emits events when interacted with. When any fungible token is transferred, standard events are emitted. You can find a lot of details on the events emitted in the [Fungible Token documentation]. The most common events are when tokens are transferred which is accomplished with two actions: withdraw tokens from the payer and deposit tokens in the receiver. Each of those actions has a corresponding event: **Withdraw tokens** Event name: `FungibleToken.Withdrawn` ```cadence event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64, balanceAfter: UFix64) ``` Mainnet event: `A.f233dcee88fe0abe.FungibleToken.Withdrawn` Testnet event: `A.9a0766d93b6608b7.FungibleToken.Withdrawn` **Deposit tokens** ```cadence event Deposited(type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64, balanceAfter: UFix64) ``` Event name: `FungibleToken.Deposited` Mainnet event: `A.f233dcee88fe0abe.FungibleToken.Deposited` Testnet event: `A.9a0766d93b6608b7.FungibleToken.Deposited` ### **Fee Events** Since fees are governed by a contract deployed on the Flow network, that contract also emits events when fees are deducted. Charging fees consists of a couple of steps: - Calculate and deduct fees - Withdraw Flow tokens from the payer account - Deposit Flow tokens to the fees contract These events are very common since they accommodate all transactions on Flow. Each fee deduction will result in three events: the withdrawal of Flow tokens, the deposit of Flow tokens, and the fee deduction. An example of fee events: ```yml Events: - Index: 0 Type: A.f233dcee88fe0abe.FungibleToken.Withdrawn Tx ID: 1ec90051e3bc74fc36cbd16fc83df08e463dda8f92e8e2193e061f9d41b2ad92 Values: - type (String): '1654653399040a61.FlowToken.Vault' - amount (UFix64): 0.00000100 - from (Address?): b30eb2755dca4572 - Index: 1 Type: A.f233dcee88fe0abe.FungibleToken.Deposited Tx ID: 1ec90051e3bc74fc36cbd16fc83df08e463dda8f92e8e2193e061f9d41b2ad92 Values: - type (String): '1654653399040a61.FlowToken.Vault' - amount (UFix64): 0.00000100 - to (Address?): f919ee77447b7497 - Index: 2 Type: A.f919ee77447b7497.FlowFees.FeesDeducted Tx ID: 1ec90051e3bc74fc36cbd16fc83df08e463dda8f92e8e2193e061f9d41b2ad92 Values: - amount (UFix64): 0.00000100 - inclusionEffort (UFix64): 1.00000000 - executionEffort (UFix64): 0.00000000 ``` [read the Cadence documentation]: https://cadence-lang.org/docs/language/events [The FLOW Token contract]: ../core-contracts/03-flow-token.md [fungible token standard on Flow]: ../core-contracts/02-fungible-token.md [Fungible Token documentation]: ../core-contracts/02-fungible-token.md [on the core events, you can read Cadence reference documentation]: https://cadence-lang.org/docs/language/core-events --- # File: /md/build/cadence/basics/fees.md --- sidebar_position: 5 title: Fees description: Learn about Flow blockchain fees, including transaction fees, storage fees, and how they work to protect the network. Understand fee structures, calculation methods, and optimization strategies. keywords: - fees - transaction fees - storage fees - execution fees - inclusion fees - surge factor - fee calculation - storage capacity - fee optimization - network protection - blockchain fees - gas fees - compute unit fees - Flow token - fee structure - cost estimation --- :::info Are you an EVM developer who wants information about EVM Accounts on Flow? If so, check out the EVM specific documentation [here] ::: # Fees ## Transaction fees A transaction fee is a cost paid in Flow by the payer account and is required for a transaction to be included in the Flow blockchain. Fees are necessary to protect the network against spam or infinite running transactions and to provide monetary incentives for participants that make up the Flow network. On Flow, transaction fees are determined with `compute units (CU)`, a metric that captures the effort needed to include and execute a transaction. Compute units function similarly to gas in EVM systems but are derived differently. To calculate the final fee paid by the user, multiply the transaction’s total compute units by the current compute-unit price, with all fees payable in FLOW. A transaction fee is paid regardless of whether a transaction succeeds or fails. If the payer account doesn't have sufficient Flow balance to pay for the transaction fee, the transaction will fail. We can provide the compute unit limit value when you submit the transaction, which helps limit the transaction fee. ### Understand the need for transaction fees One of the ways that a single computer that no one owns and anyone can use from being monopolized by any part is to charge a fee measured by the amount of computation needed to execute code. All blockchains implement a form of this system. Segmented transaction fees are essential to ensure fair pricing based on the impact on the network. For instance, heavier operations will require more resources to process and propagate transactions. Common operations, however, will stay reasonably priced. To improve the overall security of the network, fees makie malicious actions (such as spam) on the network less viable. They also prevent a computer crash if an infinite loop is started maliciously or accidentally. The unique Flow architecture is targeted at high throughput. It makes it easier to have slack in the system, so short spikes can be handled more gracefully. ### **Fee structure** Each transaction fee consists of three components: execution fee, inclusion fee, and network surge factor. ![Screenshot 2023-08-17 at 17.16.32.png](_fees_images/Screenshot_2023-08-17_at_17.16.32.png) **Execution fee** The execution effort for a transaction is determined by the code path the transaction takes and the actions it does. The actions that have an associated execution effort cost can be separated into four broad buckets: - Normal lines of cadence, loops, or function calls. - Reading data from storage, charged per byte read. - Writing data to storage, charged per byte written. - Account creation. | | Computation Units | Flow | |-----------------------------------|-------------------|-------------| | FT transfer | 19 | 8.60E-04 | | Mint a small NFT (size-dependent) | 25 | 1.10E-03 | | Empty Transaction | 0 | 1.00E-04 | | Create 1 Account | 45 | 1.90E-03 | | Create 10 Accounts | 363 | 1.46E-02 | | Deploy contract (~50kb) | 319 | 1.29E-02 | | Add key to an account | 9 | 4.60E-04 | **Inclusion fee** The inclusion effort of a transaction represents the work needed to: - Include the transaction in a block - Transport the transaction information from node to node - Verify transaction signatures Right now, the inclusion effort is always 1.0 and the inclusion effort cost is fixed to `0.0001`. Fees were last revised as per [FLIP 351](https://github.com/onflow/flips/blob/main/governance/20251119-transaction-fee-update.md) **Surge factor** A network surge factor is applied when the network is busy due to an increased influx of transactions required to be processed or a decrease in the ability to process transactions. See [FLIP 336] for details on this calculation. **Estimat transaction costs** Cost estimation is a two-step process. First, you need to gather the execution effort with either the emulator, on testnet, or on mainnet. Second, you use the execution effort for a transaction to calculate the final fees with one of the JavaScript or Go FCL SDKs. ## Storage Flow's approach to storage capacity is a bit similar to some banks' pricing models, where you maintain a minimum balance to prevent monthly account fees. Here, the amount of data in your account determines your minimum balance. If you fall below the minimum balance, you can't transact with your account, except for deposits or data deletion. The essence of storage fee model is that it ensures data availability but doesn't continuously charge fees for storage, and also prevents abuses that could burden the network's storage resources. This distinction between current state and blockchain history is crucial for you to understand storage requirements and limitations. Each Flow account has associated storage used. The account's storage used is the byte size of all the data stored in the account's storage. Accounts also have a storage capacity, which is directly tied to the amount of Flow tokens an account has. The account can, without any additional cost, use any amount of storage up to its storage capacity. :::warning If a transaction puts an account over storage capacity, that transaction fails and is reverted. Likewise, if a transaction would drop an account's balance below 0.001 Flow tokens, which is the minimum an account can have, the transaction would also fail. ::: **Storage capacity** The storage capacity of an account is dictated by the amount of FLOW it has. :::danger The **minimum amount of FLOW an account can have is 0.001**. This minimum is provided by the account creator at account creation. ::: The minimum account reservation ensures that most accounts won't run out of storage capacity if anyone deposits anything (like an NFT) to the account. Currently, the amount required to store 100 MB in account storage is one Flow. ![Screenshot 2023-08-17 at 17.27.50.png](_fees_images/Screenshot_2023-08-17_at_17.27.50.png) When you store data in an account on Flow, it doesn't charge tokens from the account. Rather, it just makes sure you will keep the tokens as a reserve. After the storage is freed up, you can transfer the Flow tokens. ### Storage capacity of the payer The storage capacity of the Payer of a transaction is generally computed the same way as the capacity of any other account. However, the system needs to account for the transaction fees the payer will incur at the end of the transaction. The final transaction fee amount is not fully known at this step, only when accounts are checked for storage compliance. If their storage used is more than their storage capacity, the transaction will fail. Because of this, the payer's balance is conservatively considered to be lower by the maximum possible transaction fees, when checking for storage compliance. The maximum transaction fee of a specific transaction is the transaction fee as if the transaction would have used up all of its execution effort limit. ### Storage used All data that is in an account's storage counts towards storage used. Even when an account is newly created, it is not empty. There are already some items in its storage: - Metadata that marks that the account exists. - An empty FLOW vault, and stored receiver capability. - Public keys to the account if the account was created with keys. - Smart contracts deployed on the account if the account was created with contracts. - The value of the account's storage used as an unsigned integer. When you add additional keys, smart contracts, capabilities, resources, and so on to the account, it counts towards storage used. Data stored on the Flow blockchain is stored in a key-value ledger. Each item's key contains the address that owns the item and the path to the item. An account can have many keys, therefore flow considers the account key items are stored with. This means that the storage used by each item is the byte length of the item plus the byte length of the item's key. ### Maximum available balance Due to the storage restrictions, there is a maximum available balance that user can withdraw from the wallet. The core contract [`FlowStorageFees`] provides a function to retrieve that value: ```cadence import "FlowStorageFees" access(all) fun main(accountAddress: Address): UFix64 { return FlowStorageFees.defaultTokenAvailableBalance(accountAddress) } ``` Alternatively developers can use `availableBalance` property of the `Account` ```cadence access(all) fun main(address: Address): UFix64 { let acc = getAccount(address) let balance = acc.availableBalance return balance } ``` ## Practically understand fees **Use the Flow emulator** You can start the [emulator with the Flow CLI]. Run your transaction and take a look at the events emitted: ```shell 0|emulator | time="2022-04-06T17:13:22-07:00" level=info msg="⭐ Transaction executed" computationUsed=3 txID=a782c2210c0c1f2a6637b20604d37353346bd5389005e4bff6ec7bcf507fac06 ``` You should see the `computationUsed` field. Take a note of the value, you will use it in the next step. **On testnet or mainnet** When a transaction is completed, you can use an explorer like [Flowscan] to review the transaction details and events emitted. For Flowscan, you can open the transaction in question and look for the event `FeesDeducted` from the [`FlowFees`] contract: ![flowscan-fees](./_fees_images/flowscan-fees.png) In the event data on the right side, you will see a set of fields that represent [the fees for a specific transaction.]: - Total Fees Paid - Inclusion Effort - Execution Effort Take a note of the last value in the list - the `executionEffort` value. You will use it in the next step. ### Calculate final costs The cost for transactions can be calculated with the following FCL scripts on mainnet/testnet respectively. **On mainnet** ```cadence import FlowFees from 0xf919ee77447b7497 access(all) fun main( inclusionEffort: UFix64, executionEffort: UFix64 ): UFix64 { return FlowFees.computeFees(inclusionEffort: inclusionEffort, executionEffort: executionEffort) } ``` **On testnet** ```cadence import FlowFees from 0x912d5440f7e3769e access(all) fun main( inclusionEffort: UFix64, executionEffort: UFix64 ): UFix64 { return FlowFees.computeFees(inclusionEffort: inclusionEffort, executionEffort: executionEffort) } ``` ## Configure execution limits FCL SDKs allow you to set the execution effort limit for each transaction. Based on the execution effort limit determined in the previous step, you should set a reasonable maximum to avoid unexpected behavior and protect your users. The final transaction fee is computed from the actual execution effort used up to this maximum. :::info Keep in mind that the limits are not for the final fees that the user will have to pay. The limits are for the execution efforts specifically. ::: It is important to set a limit that isn't too high or too low. If it is set too high, the payer needs to have more funds in their account before they send the transaction. If it is too low, the execution could fail and all state changes are dropped. **Use FCL JS SDK** You need to set the `limit` parameter for the `mutate` function, for example: ```js import * as fcl from "@onflow/fcl" const transactionId = await fcl.mutate({ cadence: ` transaction { execute { log("Hello from execute") } } `, proposer: fcl.currentUser, payer: fcl.currentUser, limit: 100 }) const transaction = await fcl.tx(transactionId).onceExecuted(); console.log(transaction;) ``` **Use FCL Go SDK** You need to call the `SetComputeLimit` method to set the fee limit, for example: ```go import ( "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" ) var ( myAddress flow.Address myAccountKey flow.AccountKey myPrivateKey crypto.PrivateKey ) tx := flow.NewTransaction(). SetScript([]byte("transaction { execute { log(\"Hello, World!\") } }")). SetComputeLimit(100). SetProposalKey(myAddress, myAccountKey.Index, myAccountKey.SequenceNumber). SetPayer(myAddress) ``` ### Maximum transaction fees of a transaction The maximum possible fee imposed on the payer for a transaction can be calculated as the **inclusion cost plus the execution cost**. The execution cost is the fee calculated to run the transaction based on the [execution effort limit maximum specified]. The payer will never pay more than this amount for the transaction. ## Optimize Cadence code to reduce effort Several optimizations can lead to reduced execution time of transactions. Below is a list of some practices. This list is not exhaustive but rather exemplary. **Limit functions calls** Whenever you make function calls, make sure these are absolutely required. In some cases, you might be able to check prerequisites and avoid additional calls: ```cadence for obj in sampleList { /// check if call is required if obj.id != nil { functionCall(obj) } } ``` **Limit loops and iterations** Whenever you want to iterate over a list, make sure it is necessary to iterate through all elements as opposed to a subset. Avoid loops to grow in size too much over time. Limit loops when possible. ```cadence // Iterating over long lists can be costly access(all) fun sum(list: [Int]): Int { var total = 0 var i = 0 // if list grows too large, this might not be possible anymore while i < list.length { total = total + list[i] } return total } // Consider designing transactions (and scripts) in a way where work can be "chunked" into smaller pieces access(all) fun partialSum(list: [Int], start: Int, end: Int): Int { var partialTotal = 0 var i = start while i < end { partialTotal = partialTotal + list[i] } return partialTotal } ``` **Understand the impact of function calls** Some functions will require more execution efforts than others. You should carefully review what function calls are made and what execution they involve. ```cadence // be aware functions that call a lot of other functions // (or call themselves) might cost a lot access(all) fun fib(_ x: Int): Int { if x == 1 || x== 0 { return x } // + 2 function calls each recursion return fib(x-1) + fib(x-2) } // consider inlining functions with single statements, to reduce costs access(all) fun add(_ a: Int, _ b: Int): Int { // single statement; worth inlining return a + b } ``` **Avoid excessive load and save operations** Avoid costly loading and storage operations and [borrow references] where possible, for example: ```cadence transaction { prepare(acct: auth(BorrowValue) &Account) { // Borrows a reference to the stored vault, much less costly operation that removing the vault from storage let vault <- acct.storage.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) let burnVault <- vault.withdraw(amount: 10) destroy burnVault // No `save` required because we only used a reference } } ``` > **Note**: If the requested resource does not exist, no reading costs are charged. **Limit accounts created per transaction** Try to only create accounts and add keys when necessary, because there are costs associated with these actions. **Check user's balance before you execute transactions** You should ensure that the user's balance has enough balance to cover the highest possible fees. For FT transfers, you need to cover the amount to transfer in addition to the highest possible fees. ## Educate users Wallets will handle the presentation of the final transaction costs, but you can still facilitate the user experience when you educate them within your application. If your user has self-custody wallets, they may have to pay the transaction and want to understand the fees. Here are some suggestions. **Explain that costs can vary depending on the network usage** Suggested message: "Fees improve the security of the network. They are flexible to ensure fair pricing based on the impact on the network." **Explain that they can wait for the network surge to pass** Inevitably, network surges will cause higher fees. Users who might want to submit a transaction while the network usage surges may want to send the transaction at a later time to reduce costs. **Explain that the wallet might not allow the transaction due to a lack of funds** If dynamic fees increase to the highest possible level, the user's fund might not be enough to execute the transaction. Let the users know that they should either add funds or try when the network is less busy. ## How to learn more There are several places to learn more about transaction fees: - [FLIP-660] - [FLIP-753] - [Flow Fees Contract] :::info If you have thoughts on the implementation of transaction fees on Flow, you can [leave feedback on this forum post]. ::: ## FAQs **When will the fee update go into effect?** The updates were rolled out with the [Spork on April 6, 2022], and were turned on [June 1st] during the [weekly epoch transition]. **Why are fees collected even when transactions fail?** Costs are deducted appropriately when you or your users broadcast and verify a transaction. **What execution costs are considered above average?** There is no average for execution costs. Every function will vary significantly based on the logic implemented. You should review the optimization best practices to determine if you could reduce your costs. **Do hardware wallets like Ledger support segmented fees?** Yes. **What is the lowest execution cost?** The lowest execution cost is one. This means your transaction included one function call or loop that didn't read or write any date. **Can I determine how much a transaction will cost on mainnet without actually paying?** You can estimate the costs in a two-way process: 1. Determine execution costs for transactions (emulator or testnet). 2. Use an FCL SDK method to calculate the final transaction fees. **How accurate will testnet fees be to mainnet fees?** Final fees are determined by the surge factor on the network. The surge factor for the testnet will be different from the factor for the mainnet, so you need to expect a variation between mainnet and testnet estimates. **I use Blocto and I haven't paid any fees yet. Why is that?** That is because Blocto acts as the payer for transactions. Self-custody wallets may have the user pay the transaction. Additionally, apps can sponsor the transaction if they choose. **Why would the same transaction have different fees when executed for different accounts?** Execution costs, among other things, include the cost to read data from account storage and since the data stored varies from account to account, so does the execution costs, and subsequently the transaction fees. Additional Details: - The most expensive operations in Cadence are reading and writing to storage. This isn't punitive! Every read needs to be sent to all Verification nodes for verification (with Merkel proofs), and every write requires a path of Merkel hashes to be updated. It's inherently expensive to read and write on any blockchain. - The way data is stored in accounts is as a tree (the hint is in the name "atree" :wink:). So, the more elements in the account, the more levels of the tree, and therefore the more nodes of that tree that need to be read and updated. So, looking at the byte size of an account is a decent proxy for figuring out how much it's going to cost. - Because it's a tree, the cost of reads and writes grows with log(n), but does scale. - atree has an update queued up for [Crescendo] that will improve this. The previous version erred on the side of adding new levels to the tree (to keep the code simple), while the new version tries to pack more data at each level. This will result in fewer levels for the same byte size. Additionally, it includes a more compact encoding leading to a reduction in the byte size of most accounts. - Even with these improvements, this relationship is likely to remain indefinitely. The bigger the account, the more maintenance the nodes have to do, which will result in somewhat larger tx fees. [here]: ../../../build/evm/fees.md [FLIP 336]: https://github.com/onflow/flips/blob/main/governance/20250727-dynamic-transaction-fees.md [`FlowStorageFees`]: ../core-contracts/05-flow-fees.md#flowstoragefees [emulator with the Flow CLI]: ../../../build/tools/emulator/index.md#running-the-emulator-with-the-flow-cli [Flowscan]: https://flowscan.io/ [`FlowFees`]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowFees.cdc: [the fees for a specific transaction.]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowFees.cdc#L14 [execution effort limit maximum specified]: #configuring-execution-limits [borrow references]: https://cadence-lang.org/docs/design-patterns#avoid-excessive-load-and-save-storage-operations-prefer-in-place-mutations [FLIP-660]: https://github.com/onflow/flow/pull/660 [FLIP-753]: https://github.com/onflow/flow/pull/753 [Flow Fees Contract]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowFees.cdc [leave feedback on this forum post]: https://forum.flow.com/t/variable-transaction-fees-are-coming-to-flow/2941 [Spork on April 6, 2022]: ../../../protocol/node-ops/node-operation/past-upgrades#mainnet-17 [June 1st]: https://forum.flow.com/t/permissionless-contract-deployment-progress/2981 [weekly epoch transition]: https://github.com/onflow/service-account/tree/main/transactions/set-execution-effort-weights/2022/jun-1 [Crescendo]: https://flow.com/upgrade/crescendo --- # File: /md/build/cadence/basics/flow-token.md --- title: FLOW Coin sidebar_position: 10 description: Learn about the FLOW coin, its role as the native token of the Flow blockchain, and how to acquire, use, and build with it. Understand staking, delegation, and transaction fee mechanisms. keywords: - FLOW coin - FLOW token - native token - fungible token - staking - delegation - transaction fees - Flow protocol - Flow rewards - token utility - Flow wallet - token custody - Flow transactions - Flow governance - Flow ecosystem --- # FLOW Coin This section contains information about the FLOW Coin for individual backers, wallet providers, custodians and node operators. ### FLOW as a Native Coin FLOW is the default coin for the Flow protocol, meaning it is used for all protocol-level fee payments, rewards and staking transactions. FLOW implements the standard [Flow Fungible Token interface], which all other onchain fungible tokens also conform to. This interface is defined in Cadence, Flow's native smart-contract programming language, which makes it easy to write applications that interact with FLOW. ## How to Get FLOW There are two ways to acquire FLOW Coins as yield: 1. [Earn FLOW as a Validator or Delegator]: Receive newly-minted FLOW as a reward when you run a node. 1. [Earn FLOW as a Community Contributor]: Flow offers grants for selected proposals as well as RFPs for teams to submit proposals for funded development ## How to use FLOW With FLOW, you can: - Spend - Stake - Delegate - Hold - Vote - Send and share - Create, develop, and grow your dapp ### Spend FLOW All you need to spend FLOW is an account and a tool to sign transactions (a wallet, custodian, or other signing service). The FCL (Flow Client Library) makes it super duper easy to go to any dapp, login with your account, have a great time, and then sign with the wallet of your choice only once you decide to make a purchase. ### Stake FLOW [You can use FLOW to operate a staked node.] Node operators receive newly-minted FLOW as a reward for helping to secure the network. ### Delegate FLOW [You can use FLOW for stake delegation.] Delegators receive newly-minted FLOW as a reward for helping to secure the network. ### Hold FLOW If you have already purchased FLOW and wish to hold it, you have a couple of options: - For relatively small, short term holdings - most people use a wallet. Wallets are used to help you sign transactions (verify your actions) when you use your FLOW tokens. - For larger, long term holdings - you may want to use a custody provider to keep your funds safe. You can find wallets and custodians that support Flow in the [Flow Port] ### Vote with FLOW Participation in the Flow community means more than just run a node or build a dapp. It's also about engaging in discussion, debate, and decision making about the protocol, the content on it, and the people that it impacts. You can use your Flow account to submit votes to community polls and other governance related activities. ### Send and share FLOW If you simply want to share the love and bring your friends to Flow, it's easier than an edible arrangement. It is possible to use the Flow blockchain and not hold any FLOW coins yourself. Free to play games, trials, community polls, and other community activities can all take place with only an account (which may be created on a person's behalf) and a small fixed fee which may be paid by a user agent. The protocol requires some FLOW coins to process these transactions, but (and this is the cool part!) a product can support users who do not themselves hold FLOW and still provide that user with all the underlying security guarantees the Flow protocol provides. It's easy to transfer FLOW, create accounts, and update keys on [Flow Port] ### Submit transactions and update users Transactions are submitted with a Flow SDK via the Access API. On Flow, a transaction is identified by its hash - the hash that exists as soon as that transaction is signed and submitted to an Access or Collection node. Results of transactions can be queried by transaction hash through the Access API. A user can check the status of a transaction at any time via the [Flow Block Explorer]. To expose these results natively in your app, you can use a Flow SDK to [fetch transaction results]. With a Flow SDK, you can also [fetch account state by address] from a Flow Access API. After the transaction is sealed, an event is emitted and you will be able to read transaction events and update the user. The Flow SDKs also allow [polling for events] with the Flow Access API. ## How to build with FLOW To get started with Flow, see the [Flow App Quickstart] [Flow Fungible Token interface]: https://github.com/onflow/flow-ft [Earn FLOW as a Validator or Delegator]: ../../../protocol/staking/06-technical-overview.md [Earn FLOW as a Community Contributor]: https://github.com/onflow/developer-grants [You can use FLOW to operate a staked node.]: ../../../protocol/staking/06-technical-overview.md [You can use FLOW for stake delegation.]: ../../../protocol/staking/06-technical-overview.md [Flow Port]: https://port.flow.com/ [Flow Block Explorer]: https://flowscan.io/ [fetch transaction results]: https://github.com/onflow/flow-go-sdk#querying-transaction-results [fetch account state by address]: https://github.com/onflow/flow-go-sdk#querying-accounts [polling for events]: https://github.com/onflow/flow-go-sdk#querying-events) [Flow App Quickstart]: ../../../blockchain-development-tutorials/cadence/getting-started/building-a-frontend-app.md --- # File: /md/build/cadence/basics/mev-resistance.md --- title: MEV Resistance description: How Flow’s unique architecture minimizes Maximal Extractable Value (MEV) to ensure fair and equitable access. sidebar_position: 5 keywords: - MEV - Maximal Extractable Value - Flow Blockchain - Equitable Access - Transaction Ordering - Blockchain Security --- # How Flow Suppresses MEV to Ensure Equitable Access
    ## The hidden cost of MEV in decentralized systems One of the most under-discussed benefits of decentralization is **equitable access**. Ideally, the value and quality-of-service you receive from a decentralized platform should not depend on your identity, computing power, or personal connections. However, **Maximal Extractable Value (MEV)** poses a significant threat to this principle. MEV allows block producers to manipulate transaction ordering for profit—often at the direct expense of users. The ability to front-run, back-run, or sandwich transactions can extract value from ordinary users, which reinforces inequalities instead of eliminating them. In most blockchain networks, MEV is not just an unfortunate side effect; it is structurally embedded in how transactions are processed. ## Why MEV persists on most blockchains MEV is difficult to prevent on most blockchains because **each block has a single builder**. This builder must have: - A full copy of the blockchain state. - The ability to simulate transactions before they are finalized. - Absolute control over transaction selection and ordering. In practice, this means that **the entity who adds your transaction to the blockchain can first simulate it to identify profit opportunities**. They can test hundreds or thousands of ways to rearrange transactions, and insert their own to extract MEV—often at **your** expense. For example, if a block builder can sandwich your transaction and earn $10, it means **you** likely lose $10 in value. This is functionally theft, and the worst part? If your transaction is airtight and offers no MEV opportunities, the block builder has no obligation to include it at all. Pay the toll, or get locked out. ## How Flow accomplishes MEV resilience Unlike many blockchains, **Flow was designed from the ground up to minimize MEV** through a unique multi-role architecture. Flow introduces key design choices that break the typical MEV-enabling structure: ### 1. **Separate transaction selection from execution** On Flow, **Collection Nodes** select transactions, but they do not have access to the execution state or computing power to simulate them. Meanwhile, **Execution Nodes** run transactions but cannot choose or reorder them. This separation significantly reduces the ability of block builders to test transactions before execution. Even if an attacker controls both a Collection Node and an Execution Node, they cannot easily extract MEV. ### 2. **Separate transaction ordering from execution** Flow further decentralizes control by introducing **Consensus Nodes** that determine transaction order. These nodes are separate from both Collection Nodes and Execution Nodes. For an attacker to perform MEV, they would need to: - Control a **Collection Node** to insert a transaction. - Control a **Consensus Node** to place it in the desired order. - Have execution state access to predict its effects. This makes it vastly more difficult to extract MEV compared to traditional blockchains, where a single entity often controls all three functions. ### 3. **Strict transaction execution rules** Execution Nodes on Flow have a **simple, enforceable rule**: They **must** execute transactions exactly as ordered by Consensus Nodes, or they get slashed. Unlike traditional blockchains, where the same party both orders and executes transactions, Flow ensures that Execution Nodes cannot manipulate transaction order for profit. ### 4. **Parallel processing for extra MEV resistance** Flow’s unique **pipelined execution model** adds another layer of complexity for potential attackers. While one block is executed, the next block undergoes consensus, and a third block continues to collect transactions. This means that **to front-run or sandwich attack on Flow, an attacker must successfully predict the outcome of at least two unexecuted blocks—one of which hasn’t even been built yet**. Even with significant resources, this makes profitable MEV attacks incredibly difficult. ## The end result: a fairer blockchain Flow’s architecture ensures that: - The nodes that select transactions **don’t know** their order. - The nodes that order transactions **don’t know** the blockchain state. - The nodes that execute transactions **can’t** modify the order. By **intentionally separating powers**, Flow eliminates MEV at its root rather than merely mitigate its effects. This level of protection against MEV is not an afterthought—it has been a fundamental design goal of Flow since day one. If equitable access matters, **why settle for anything less?** --- # File: /md/build/cadence/basics/network-architecture.md --- sidebar_position: 0 title: Network Architecture ↗️ --- ; --- # File: /md/build/cadence/basics/scripts.md --- sidebar_position: 4 title: Scripts description: Learn about Flow scripts - read-only Cadence code that can query blockchain state without fees. Understand how to write, execute, and optimize scripts for accessing Flow network data. keywords: - scripts - Cadence scripts - blockchain queries - read operations - Flow scripts - script execution - state queries - Flow API - script limitations - best practices - historic data - script arguments - script returns - Flow CLI - computation limits --- # Scripts A script provides a light-weight method to query chain data. It is executable Cadence code that can query for Flow execution state data but cannot modify it in any way. Unlike a Flow transaction, a script is not signed and requires no transaction fees. Also unlike a transaction, a script can return a value back to the caller. You can think of script execution as a read-only operation, very similar to the `eth_call` RPC method on Ethereum. Scripts are currently executed on either the Access Nodes or the Execution Nodes based on the Access node configuration. Scripts are defined by the following the Cadence code: ```cadence // The 'main' function is the entry point function and every script needs to have one. access(all) fun main() { // Cadence statements to be executed go here } ``` Scripts can return a typed value: ```cadence access(all) fun main(): Int { return 1 + 2 } ``` Scripts can also accept arguments: ```cadence access(all) fun main(arg: String): String { return "Hello ".concat(arg) } ``` Scripts can call contract functions and query the state of a contract. To call a function on another contract, import it from its address and invoke the function: ```cadence import World from 0x01 access(all) fun main(): String { return World.hello() } ``` Scripts can also be run against previous blocks, which allows you to query historic data from the Flow network. This is particularly useful to retrieve historical states of contracts or track changes over time. ## When to use a script? Scripts can be used for the following: 1. Validate a transaction before you submit it (for example, confirm that if the payer has sufficient balance, the receiver account is setup correctly to receive a token or NFT). 2. Collect chain data over time. 3. Continuously verify accounts through a background job, such as a Discord bot that verifies users by their Flow account. 4. Query core contracts. For an example, see how to [query staking and epoch related information]. Also, see the scripts directory under each of the [core contract transactions] for other core contracts related scripts. ## Execute Scripts ### Access API To execute a script, submit it to the Access API provided by access nodes. Currently, there are three API endpoints that allow a user to execute scripts at the latest sealed block, a previous block height, or a previous block ID. [**gRPC Script API**] [**REST Script API**] There are multiple SDKs implementing the above APIs for different languages: [**Javascript SDK**] [**Go SDK**] Find a list of all SDKs [here] ### Flow CLI You can also execute a script with the [Flow CLI]: ```sh flow scripts execute ./helloWorld.cdc ``` A user can define their own scripts or can use already defined scripts by the contract authors that can be found with the [FLIX] service. ## Best Practices The following are some recommendations for how to write efficient scripts: 1. **Simpler and shorter scripts**: Scripts, like transactions, are subject to computation limits (see [limitations]).We recommend that you run shorter and simpler scripts which have low time complexity for a faster response. If you have a script with several nested loops, long iteration, or that queries many onchain fields, we recommend that you simplify the script logic. 2. **Fewer state reads**: A script reads execution state and to get a faster response, it is best to limit the amount of state that is read by the script. 3. **Smaller length of array or dictionary type arguments**: If your script requires an array or a dictionary as an argument where each element causes a state lookup, instead of a single script call that passes in a long list, make multiple calls with a smaller subset of the array or dictionary. 4. **NFTCatalog**: If your script uses the [NFTCatalog] functions, ensure that you use the [latest functions] and do not use any of the deprecated functions such as `getCatalog()`. ## Limitations 1. **Rate limit** - Script execution is subjected to API rate-limits imposed by the Access nodes and the Execution nodes. The rate limits for the Public Access nodes hosted by QuickNode are outlined [outlined here]. 2. **Computation limit** - Similar to a transaction, each script is also subjected to a computation limit. The specific value can be configured by individual Access and Execution node operators. Currently, the default compute unit (gas) limit for a script is 100,000. 3. **Historic block data limit** 1. Script execution on execution nodes is restricted to approximately the last 100 blocks. Any request for script execution on an execution node on a past block (specified by block ID or block height) will fail if that block is more than 100 blocks in the past. 2. Script execution on an access node can go much beyond the last 100 blocks but is restricted to the height when the [last] network upgrade ([HCU] or spork) occurred. [query staking and epoch related information]: ../../../protocol/staking/07-staking-scripts-events.md [core contract transactions]: https://github.com/onflow/flow-core-contracts/tree/master/transactions [**gRPC Script API**]: ../../../protocol/access-onchain-data/index.md#scripts [**REST Script API**]: /http-api#tag/Scripts [**Javascript SDK**]: ../../../build/tools/clients/fcl-js/index.md [**Go SDK**]: ../../../build/tools/clients/flow-go-sdk/index.md [here]: ../../../build/tools/clients/index.md [Flow CLI]: ../../../build/tools/flow-cli/scripts/execute-scripts [FLIX]: ../../../build/tools/flow-cli/flix [limitations]: #limitations [NFTCatalog]: https://github.com/onflow/nft-catalog [latest functions]: https://github.com/onflow/nft-catalog?tab=readme-ov-file#using-the-catalog-for-marketplaces-and-other-nft-applications [outlined here]: https://www.quicknode.com/docs/flow#endpoint-rate-limits [last]: https://developers.flow.com/protocol/node-ops/node-operation/past-upgrades [HCU]: https://developers.flow.com/protocol/node-ops/node-operation/hcu --- # File: /md/build/cadence/basics/smart-contracts.md --- slug: /build/cadence/basics/smart-contracts redirect: /build/cadence/smart-contracts/overview title: Smart Contracts ↙ description: Redirect page to comprehensive Flow smart contracts documentation and overview. keywords: - smart contracts - Flow contracts - Cadence - contract development - blockchain development - Flow programming - contract deployment - contract overview --- # Smart Contracts Go to [Smart Contracts](../smart-contracts/overview.md) import {Redirect} from '@docusaurus/router'; ; --- # File: /md/build/cadence/basics/transactions.md --- sidebar_position: 3 title: Transactions description: Learn about Flow blockchain transactions, their structure, lifecycle, and signing process. Understand transaction roles, statuses, finality, and how to optimize transaction execution. keywords: - Flow transactions - transaction speed - transaction time - transaction signing - transaction roles - transaction status - transaction finality - blockchain transactions - multi-sig - transaction fees - sequence numbers - proposal key - authorizers - transaction payload - transaction lifecycle - transaction submission --- # Transactions Transactions are cryptographically signed data messages that contain a set of instructions that update the Flow state. They are a basic unit of computation that gets executed by execution nodes. In order for a transaction to be included in the Flow blockchain a fee is required from the payer. ![Screenshot 2023-08-17 at 13.57.36.png](_transactions_images/Screenshot_2023-08-17_at_13.57.36.png) :::tip Transactions on Flow are fundamentally different from those on Ethereum. The main purpose of a transaction is not to send funds, but to contain code that gets executed. This makes transactions very flexible and powerful. In addition to being able to access the authorizing accounts private assets, transactions can also read and call functions in public contracts, and access public domains in other users' accounts. Transactions on Flow also feature different roles, such as define third-party payer accounts, proposer accounts, and authorizers, which we will talk about in detail soon. ::: In order for a transaction to be valid and executed, it must contain signatures from accounts involved as well as some other information, let's take a look at all the required fields. ![Screenshot 2023-08-17 at 14.52.56.png](_transactions_images/Screenshot_2023-08-17_at_14.52.56.png) ### Script The script section contains instructions for transaction execution. This is a Cadence program in source code form (human-readable), and encoded as UTF-8. The transaction program must contain a `transaction` declaration. A transaction includes multiple optional phases `prepare`, `pre`, `execute`, and `post` phase. You can read more about it in the Cadence [documentation on transactions]. Each phase has a purpose, the two most important phases are `prepare` and `execute`. In the `prepare` phase, we have access to `&Account` objects, which gives us the power to interact with those accounts. The accounts are called authorizers of transactions, so each account we want to interact with in the `prepare` phase must sign the transaction as an authorizer. The `execute` phase does exactly what it says, it executes the main logic of the transaction. This phase is optional, but it is a best practice to add your main transaction logic in the section, so it is explicit. Again, make sure to read Cadence [documentation on transactions] This is an example of a transaction script: ```cadence transaction(greeting: String) { execute { log(greeting.concat(", World!")) } } ``` ### Arguments Transactions may declare parameters it needs during execution, these must be provided as input arguments when you send a transaction. You can think of them as function arguments. Currently, we provide [arguments in the JSON-Cadence Data Interchange Format]. Which is a human-readable JSON format. The sample script from above accepts a single `String` argument. ### Reference block A reference to a recent block used for expiry. A transaction is considered expired if it is submitted to Flow after reference block height + N, where N is a constant defined by the network. On mainnet current setting for N is 600 which amounts to approximately 10 minutes for expiry (this is subject to change). :::caution We recommend that you use the latest finalized block as the reference block, as it is the most recent block guaranteed to be included by the network. We don't recommend that you use the latest sealed block, as sealing occurs after finalization and could cause the transaction to fall outside of the expiration window. ::: ### Gas limit When a transaction is executed each operation consumes a predefined amount of computational units (we define more about that in the Fees documentation). This defines the maximum amount of computation that is allowed to be done during this transaction. If a transaction completes execution with fewer computational units than the limit, it remains unaffected. However, if it hits this limit during execution, the transaction will fail, its changes will be reverted, but fees will still be applied. The maximum computational limit for Flow mainnet is currently at 9999, but this might change. The maximum network limit is defined to protect the network from transactions that would run forever. ### Proposal key Each transaction must declare a proposal key, which can be an account key from any Flow account (App, User or Wallet). The account that owns the proposal key is referred to as the _proposer_. Proposer is a role in a transaction that defines who proposes the transaction, the effect of the transaction being submitted on the proposer is that it will increment the sequence number for the provided proposer key. This is done to ensure transactions are not resubmitted (replay attack) and thus sequence actions. A proposal key definition declares the address, key ID, and up-to-date sequence number for the account key. A single proposer can have many transactions executed in parallel only limited by the key they use to propose the transaction. ![Screenshot 2023-08-17 at 15.10.33.png](_transactions_images/Screenshot_2023-08-17_at_15.10.33.png) - Address identifies the account that will act as a proposer of this transaction. - Key ID is an index number (starting at 0) that identifies the key on the account provided in the address. - Sequence Number is a number on each key that increments by one with each transaction. This ensures that each transaction executes at most once and prevents many unwanted situations, such as [transaction replay attacks]. Each key in an account has a dedicated sequence number associated with it. Unlike Ethereum, there is no sequence number for the entire account. ### Authorizers Authorizers are accounts that authorize a transaction to read and mutate their state. A transaction can specify zero or more authorizers, and this depends on how many accounts the transaction needs to access. The number of authorizers on the transaction must match the number of &Account parameters declared in the prepare statement of the Cadence script. Example transaction with multiple authorizers: ```cadence transaction { prepare(authorizer1: auth(Capabilities) &Account, authorizer2: auth(Storage) &Account) { } } ``` Each account defined as an authorizer must sign the transaction with its own key, and when it does so, it acknowledges the transaction it signed will have access to that account and may modify it. How it will modify it is understood from the list of account entitlements that are granted in the `prepare` argument list and when it reads the transaction script. In an transaction, developers should only give the minimum set of account entitlements that are required for the transaction to execute properly. This ensures that users who sign transactions can understand what parts of their account a transaction can access. ### Payer A payer is the account that pays the fees for the transaction. A transaction must specify exactly one payer. The payer only has to pay the network and compute unit fees; the transaction is not authorized to access resources or code stored in the payer account. When you explicitly specify a payer, third-party services such as wallet providers can pay a transaction. ## Transaction lifecycle After a transaction has been submitted to the Flow network with the Access node APIs, it will begin its lifecycle and eventually reach a finality. Each submitted transaction can be identified with an ID. **Transaction ID** A transaction ID is a hash of the encoded transaction payload and can be calculated at any time. We don't submit transaction ID as part of the transaction payload as it can be derived from the data and thus would mean duplication of data. ### Transaction status The transaction status represents the state of a transaction on the Flow blockchain. Some statuses are mutable and some are immutable, they usually follow a timeline like so: ![Screenshot 2023-08-17 at 16.08.18.png](_transactions_images/Screenshot_2023-08-17_at_16.08.18.png) - Unknown - The transaction has not yet been seen by the section of the network you communicate with. - Pending - The transaction has been received by a collection node but has not yet been finalized in a block. - Finalized - The consensus nodes have included the transaction in a block, but it has not been executed by execution nodes. - Executed - Execution nodes have produced a result for the transaction. - Sealed - The verification nodes have verified and agreed on the result of the transaction and the consensus node has included the seal in the latest block. - Expired - The transaction was submitted past its expiration block height. :::danger It is **important to differentiate the transaction status and transaction result**. Transaction status will only provide you with information about the inclusion of the transaction in the blockchain, not whether the transaction was executed the way you intended. **A transaction can still fail to execute the way you intended and be sealed.** ::: ### Transaction result After a transaction is executed, its result will be available, which provides details on its success or any errors encountered during execution. It also includes events the transaction may have emitted. ![Screenshot 2023-08-17 at 16.29.30.png](_transactions_images/Screenshot_2023-08-17_at_16.29.30.png) :::danger From a developer perspective, a transaction is only successful if: - It is sealed - It didn't encounter errors ::: ## Transaction time It's crucial that developers and users understand how transaction times work across different blockchains to optimize their operations and expectations. Flow's multi-node architecture allows for some of the fastest transaction times and finality times across chains. Read on for more detail on how it works and what it means for developers and users. ### Two key transaction questions Whenever a transaction is processed, two primary questions come to mind: 1. **Inclusion**: Will this transaction be included in the final chain? 2. **Result**: What is the outcome of the transaction? Different blockchains tackle these questions in varied sequences. For instance, Bitcoin and Ethereum provide answers simultaneously. Layer 2 solutions (L2s) can sometimes address the outcome before it confirms inclusion. But, there's a catch: you can have an answer to those questions that might be wrong. Flow, on the other hand, prioritizes the inclusion question. ### Transaction finality Drawing a parallel to traditional finance, a vendor might instantly know if Visa approves a transaction, but the possibility of chargebacks lingers for weeks. This uncertainty introduces the concept of "finality" in blockchain transactions. In the dominant Proof-of-Stake (PoS) environment, which includes most chains except for Bitcoin, there are three key finality stages: - **Preliminary result**: It's an initial answer to the aforementioned questions. The preliminary result doesn't ensure correctness, and there are no economic penalties (like "slashing") if the informant provides false information. - **Soft economic finality**: This stage provides an answer backed by cryptographic proof. If the informant is deceptive, they face economic repercussions or "slashing." - **Hard economic finality**: The provided answer either holds true, or the entire blockchain requires a restart. The latter case sees at least one-third of the nodes face economic penalties. ![finality.png](./_transactions_images/finality.png) ### Chain comparisons | Chain | Preliminary | Soft finality | Hard finality | | -------- | ----------- | ------------- | ------------- | | Solana | 100ms | n/a | ~13s | | Ethereum | 15s | n/a | ~13m | | Flow | bypass | 4s | ~10s | #### Flow Flow bypasses preliminary results entirely. It reaches soft finality ("Executed") in about four seconds and hard finality ("Sealed") in around 10 seconds. If an Access Node on Flow states a transaction has occurred, it's either correct or cryptographic proof exists that can lead to the node's slashing. ![transaction-time.png](_transactions_images/chain-comparison.png) ## Sign a transaction Due to the existence of **weighted keys** and **split signing roles**, Flow transactions sometimes need to be signed multiple times by one or more parties. That is, multiple unique signatures may be needed to authorize a single transaction. A transaction can contain two types of signatures: **payload signatures** and **envelope signatures**. ![Screenshot 2023-08-17 at 14.52.51.png](_transactions_images/Screenshot_2023-08-17_at_14.52.51.png) ### Signer Roles - **Proposer**: the account that specifies a proposal key. - **Payer**: the account that pays for the transaction fees. - **Authorizers**: zero or more accounts that authorize the transaction to mutate their state. ### Payload The transaction payload is the innermost portion of a transaction and contains the data that uniquely identifies the operations applied by the transaction as we have defined them above. In Flow, two transactions with the same payload will never be executed more than once. :::warning ⚠️ The transaction proposer and authorizer are only required to sign the transaction payload. These signatures are the payload signatures. ::: ### Authorization envelope The transaction authorization envelope contains both the transaction payload and the payload signatures. The transaction payer is required to sign the authorization envelope. These signatures are **envelope signatures**. :::danger Special case: if an account is both the payer and either a proposer or authorizer, it is required only to sign the envelope. ::: ### Payment envelope The outermost portion of the transaction, which contains the payload and envelope signatures, is referred to as the payment envelope. :::danger Special case: if an account is both the payer and either a proposer or authorizer, it is required only to sign the envelope. ::: ### Payer signs last The payer must sign the portion of the transaction that contains the payload signatures, which means that the payer must always sign last. This ensures the payer that they are signing a valid transaction with all of the required payload signatures. :::danger Special case: if an account is both the payer and either a proposer or authorizer, it is required only to sign the envelope. ::: ### Signature structure A transaction signature is a composite structure that contains three fields: - Address - Key ID - Signature Data The _address_ and _key ID_ fields declare the account key that generated the signature, which is required in order to verify the signature against the correct public key. ### Sequence numbers Flow uses sequence numbers to ensure that each transaction executes at most once. This prevents many unwanted situations such as [transaction replay attacks]. Sequence numbers work similarly to transaction nonces in Ethereum, but with several key differences: - **Each key in an account has a dedicated sequence number** associated with it. Unlike Ethereum, there is no sequence number for the entire account. - When creating a transaction, only the **proposer must specify a sequence number**. Payers and authorizers are not required to. :::tip The transaction proposer is only required to specify a sequence number for a single account key, even if it signs with multiple keys. This key is referred to as the proposal key. ::: Each time an account key is used as a proposal key, its sequence number is incremented by one. The sequence number is updated after execution, even if the transaction fails (reverts) during execution. A transaction is failed if its proposal key does not specify a sequence number equal to the sequence number stored on the account _at execution time._ ## Common signing scenarios Below are several scenarios in which different signature combinations are required to authorize a transaction. ### Single party, single signature The simplest Flow transaction declares a single account as the proposer, payer and authorizer. In this case, the account can sign the transaction with a single signature. This scenario is only possible if the signature is generated by a key with full signing weight. | Account | Key ID | Weight | | ------- | ------ | ------ | | 0x01 | 1 | 1000 | ```json { "payload": { "proposalKey": { "address": "0x01", "keyId": 1, "sequenceNumber": 42 }, "payer": "0x01", "authorizers": ["0x01"] }, "payloadSignatures": [], // 0x01 is the payer, so only needs to sign envelope "envelopeSignatures": [ { "address": "0x01", "keyId": 1, "sig": "0xabc123" } ] } ``` ### Single party, multiple signatures A transaction that declares a single account as the proposer, payer and authorizer may still specify multiple signatures if the account uses weighted keys to achieve multi-sig functionality. | Account | Key ID | Weight | | ------- | ------ | ------ | | 0x01 | 1 | 500 | | 0x01 | 2 | 500 | ```json { "payload": { "proposalKey": { "address": "0x01", "keyId": 1, "sequenceNumber": 42 }, "payer": "0x01", "authorizers": ["0x01"] }, "payloadSignatures": [], // 0x01 is the payer, so only needs to sign envelope "envelopeSignatures": [ { "address": "0x01", "keyId": 1, "sig": "0xabc123" }, { "address": "0x01", "keyId": 2, "sig": "0xdef456" } ] } ``` ### Multiple parties A transaction that declares different accounts for each signing role will require at least one signature from each account. | Account | Key ID | Weight | | ------- | ------ | ------ | | 0x01 | 1 | 1000 | | 0x02 | 1 | 1000 | ```json { "payload": { "proposalKey": { "address": "0x01", "keyId": 1, "sequenceNumber": 42 }, "payer": "0x02", "authorizers": ["0x01"] }, "payloadSignatures": [ { "address": "0x01", // 0x01 is not payer, so only signs payload "keyId": 1, "sig": "0xabc123" } ], "envelopeSignatures": [ { "address": "0x02", "keyId": 1, "sig": "0xdef456" } ] } ``` ### Multiple parties, multiple signatures A transaction that declares different accounts for each signing role may require more than one signature per account if those accounts use weighted keys to achieve multi-sig functionality. | Account | Key ID | Weight | | ------- | ------ | ------ | | 0x01 | 1 | 500 | | 0x01 | 2 | 500 | | 0x02 | 1 | 500 | | 0x02 | 2 | 500 | ```json { "payload": { "proposalKey": { "address": "0x01", "keyId": 1, "sequenceNumber": 42 }, "payer": "0x02", "authorizers": ["0x01"] }, "payloadSignatures": [ { "address": "0x01", // 0x01 is not payer, so only signs payload "keyId": 1, "sig": "0xabc123" }, { "address": "0x01", // 0x01 is not payer, so only signs payload "keyId": 2, "sig": "0x123abc" } ], "envelopeSignatures": [ { "address": "0x02", "keyId": 1, "sig": "0xdef456" }, { "address": "0x02", "keyId": 2, "sig": "0x456def" } ] } ``` ## Transaction submission and retrieval You can use the Flow CLI to get a current transaction by ID: ```sh flow transactions get 1ec90051e3bc74fc36cbd16fc83df08e463dda8f92e8e2193e061f9d41b2ad92 -n mainnet ``` Find [more about the command in the CLI docs]. A user can define their own transactions or it can use already defined transactions by the contract authors that can be found by with the FLIX service. Transactions can be submitted and obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: [**gRPC Transaction API**] [**REST Transaction API**] There are multiple SDKs that the above APIs for different languages: [**Javascript SDK**] [**Go SDK**] Find a list of all SDKs [here] [documentation on transactions]: https://cadence-lang.org/docs/language/transactions [arguments in the JSON-Cadence Data Interchange Format]: https://cadencelang.dev/docs/1.0/json-cadence-spec [transaction replay attacks]: https://en.wikipedia.org/wiki/Replay_attack [more about the command in the CLI docs]: ../../../build/tools/flow-cli/get-flow-data/get-blocks.md [**gRPC Transaction API**]: ../../../protocol/access-onchain-data/index.md#transactions [**REST Transaction API**]: /http-api#tag/Transactions [**Javascript SDK**]: ../../../build/tools/clients/fcl-js/index.md [**Go SDK**]: ../../../build/tools/clients/flow-go-sdk/index.md [here]: ../../../build/tools/clients/index.md --- # File: /md/build/cadence/core-contracts.md --- title: Flow Core Contracts description: The smart contracts that power the Flow protocol sidebar_label: Core Smart Contracts sidebar_position: 9 sidebar_custom_props: icon: 📝 description: Explore the foundational contracts driving the Flow blockchain and learn how to utilize these vital building blocks for your own smart contract development. keywords: - core contracts - Flow protocol - smart contracts - FungibleToken - FlowToken - ServiceAccount - FlowFees - FlowIDTableStaking - NonFungibleToken - MetadataViews - StakingCollection - NFTStorefront - AccountLinking - EVM - protocol contracts - Flow standards - blockchain infrastructure - contract standards --- # Flow Core Contracts Flow relies on a set of core contracts that define key portions of the Flow protocol. These contracts control the following: - Standard fungible token behavior. ([FungibleToken, FungibleTokenMetadataViews, FungibleTokenSwitchboard, Burner]) - Flow Protocol Token. ([FlowToken](./03-flow-token.md)) - Flow Service Account. ([ServiceAccount, NodeVersionBeacon, RandomBeaconHistory]) - Account, transaction and storage fee payments. ([FlowFees and FlowStorageFees]) - Staking and delegation ([FlowIDTableStaking]) - Epochs ([FlowEpoch, FlowClusterQC, FlowDKG]) There are other important contracts that aren't part of the core protocol but are nevertheless important to developers on Flow: - Standard Non-Fungible Token Behavior. ([NonFungibleToken]) - NFT Metadata Standard. ([MetadataViews, ViewResolver]) - Staking Collection. ([StakingCollection]) - NFT Storefronts. ([NFTStorefront]) - Account linking and Hybrid Custody. ([AccountLinking]) - EVM interfacing contract. ([EVM]) [FungibleToken, FungibleTokenMetadataViews, FungibleTokenSwitchboard, Burner]: ./02-fungible-token.md [FlowToken]: ./03-flow-token.md [ServiceAccount, NodeVersionBeacon, RandomBeaconHistory]: ./04-service-account.md [FlowFees and FlowStorageFees]: ./05-flow-fees.md [FlowIDTableStaking]: ./06-staking-contract-reference.md [FlowEpoch, FlowClusterQC, FlowDKG]: ./07-epoch-contract-reference.md [NonFungibleToken]: ./08-non-fungible-token.md)) [MetadataViews, ViewResolver]: ./09-nft-metadata.md)) [StakingCollection]: ./11-staking-collection.md [NFTStorefront]: ./10-nft-storefront.md [AccountLinking]: ./12-hybrid-custody.md [EVM]: ./13-evm.md) --- # File: /md/build/cadence/core-contracts/02-fungible-token.md --- title: Fungible Token Contract sidebar_position: 2 sidebar_label: Fungible Token description: Learn about Flow's Fungible Token standard contract, its implementation, events, and how to interact with fungible tokens on the Flow blockchain. keywords: - fungible token - FT standard - token contract - Flow tokens - token events - token transactions - token metadata - token standard - Flow FT - token implementation - token interface - token deployment - token addresses - token events - token specification --- # Fungible Token Contract The `FungibleToken` contract implements the Fungible Token Standard. It is the second contract ever deployed on Flow. - [Basic Fungible Token Tutorial] - [Fungible Token Guide] - [Fungible Token Standard Repo] The `FungibleTokenMetadataViews` and `FungibleTokenSwitchboard` contracts are also deployed to the same account as `FungibleToken`. Source: [FungibleToken.cdc](https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc) | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xee82856bf20e2aa6` | | Cadence Testing Framework | `0x0000000000000002` | | Testnet | `0x9a0766d93b6608b7` | | Mainnet | `0xf233dcee88fe0abe` | # Transactions All `FungibleToken` projects are encouraged to use the generic token transactions and scripts in the `flow-ft` [repo]. They can be used for any token that implements the fungible token standard properly without any code change besides import addresses on different networks. # Events Events emitted from all contracts follow a standard format: ``` A.{contract address}.{contract name}.{event name} ``` The components of the format are: - `contract address` - the address of the account the contract has been deployed to. - `contract name` - the name of the contract in the source code. - `event name` - the name of the event as declared in the source code. ## FungibleToken events Contracts that implement the Fungible Token standard get access to standard events that are emitted every time a relevant action occurs, like deposit and withdraw tokens. This means that projects do not have to implement their own custom events unless the standard events do not satisfy requirements they have for events. The `FungibleToken` events will have the following format: ``` A.{contract address}.FungibleToken.Deposited A.{contract address}.FungibleToken.Withdrawn ``` Where the `contract address` is the `FungibleToken` address on the network being queried. The addresses on the various networks are shown above. ### FungibleToken.Deposited ```cadence access(all) event Deposited ( type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64, balanceAfter: UFix64 ) ``` Whenever `deposit()` is called on a resource type that implements `FungibleToken.Vault`, the `FungibleToken.Deposited` event is emitted with the following arguments: - `type: String`: The type identifier of the token being deposited. - Example: `A.4445e7ad11568276.FlowToken.Vault` - `amount: UFix64`: The amount of tokens that were deposited. - Example: `0.00017485` - `to: Address?`: The address of the account that owns the Vault that received the tokens. If the vault is not stored in an account, `to` will be `nil`. - Example: `0x4445e7ad11568276` - `toUUID: UInt64`: The UUID of the Vault that received the tokens. - Example: `177021372071991` - `depositedUUID`: The UUID of the Vault that was deposited (and therefore destroyed). - Example: `177021372071991` - `balanceAfter: UFix64`: The balance of the Vault that received the tokens after the deposit happened. - Example: `1.00047545` ### FungibleToken.Withdrawn ```cadence access(all) event Withdrawn ( type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64, balanceAfter: UFix64 ) ``` Whenever `withdraw()` is called on a resource type that implements `FungibleToken.Vault`, the `FungibleToken.Withdrawn` event is emitted with the following arguments: - `type: String`: The type identifier of the token being withdrawn. - Example: `A.4445e7ad11568276.FlowToken.Vault` - `amount: UFix64`: The amount of tokens that were withdrawn. - Example: `0.00017485` - `from: Address?`: The address of the account that owns the Vault that the tokens were withdrawn from. If the vault is not stored in an account, `to` will be `nil`. - Example: `0x4445e7ad11568276` - `fromUUID: UInt64`: The UUID of the Vault that the tokens were withdrawn from. - Example: `177021372071991` - `withdrawnUUID`: The UUID of the Vault that was withdrawn. - Example: `177021372071991` - `balanceAfter: UFix64`: The balance of the Vault that the tokens were withdrawn from after the withdrawal. - Example: `1.00047545` ### FungibleToken.Burned ```cadence access(all) event Burned ( type: String, amount: UFix64, fromUUID: UInt64 ) ``` Whenever a fungible token that implements `FungibleToken.Vault` is burned via the `Burner.burn()` method, this event is emitted with the following arguments: - `type: String`: The type identifier of the token that was burnt. - Example: `A.4445e7ad11568276.FlowToken.Vault` - `amount: UFix64`: The amount of tokens that were burnt. - Example: `0.00017485` - `fromUUID: UInt64`: The UUID of the Vault that was burnt. - Example: `177021372071991` [Basic Fungible Token Tutorial]: https://cadence-lang.org/docs/tutorial/fungible-tokens [Fungible Token Guide]: ../../../blockchain-development-tutorials/tokens/fungible-token-cadence.md [Fungible Token Standard Repo]: https://github.com/onflow/flow-ft [repo]: https://github.com/onflow/flow-ft/tree/master/transactions --- # File: /md/build/cadence/core-contracts/03-flow-token.md --- title: Flow Token Contract sidebar_position: 3 sidebar_label: Flow Token description: Learn about the FLOW token smart contract, its implementation, events system, and deployment addresses across different networks. Understand how to interact with the native token of the Flow blockchain. keywords: - FLOW token - Flow contract - token events - token transactions - token minting - token burning - core contracts - native token - Flow protocol - token implementation - token deployment - contract addresses - token initialization - Flow mainnet - Flow testnet --- # Flow Token Contract The `FlowToken` contract defines the FLOW network token. Source: [FlowToken.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0x0ae53cb6e3f42a79` | | Cadence Testing Framework | `0x0000000000000003` | | Testnet | `0x7e60df042a9c0868` | | Mainnet | `0x1654653399040a61` | ## Transactions Transactions and scripts for `FlowToken` are in the `flow-core-contracts` [repo]. As mentioned in the `FungibleToken` page, developers are encouraged to use the generic token transactions in the [`flow-ft` repo] instead. ## Events Flow relies on a set of core contracts that define key portions of the Flow protocol. Those contracts are core contracts and are made to emit the events documented below. You can read about the [core contracts here] and view their source code and event definitions. Events emitted from core contracts follow a standard format: ``` A.{contract address}.{contract name}.{event name} ``` The components of the format are: - `contract address` - the address of the account the contract has been deployed to. - `contract name` - the name of the contract in the source code. - `event name` - the name of the event as declared in the source code. ### Flow token contract Description of events emitted from the [FLOW Token contract](./03-flow-token.md). The contract defines the fungible FLOW token. Please note that events for the fungible token contracts are the same if deployed to a different account but the `contract address` is changed to the address of the account the contract has been deployed to. ### Tokens initialized Event that is emitted when the contract gets created. - Event name: `TokensInitialized` - Mainnet event: `A.1654653399040a61.FlowToken.TokensInitialized` - Testnet event: `A.7e60df042a9c0868.FlowToken.TokensInitialized` ```cadence access(all) event TokensInitialized(initialSupply: UFix64) ``` | Field | Type | Description | | ------------- | ------ | -------------------------------- | | initialSupply | UFix64 | The initial supply of the tokens | ### Tokens withdrawn Event that is emitted when tokens get withdrawn from a Vault. - Event name: `TokensWithdrawn` - Mainnet event: `A.1654653399040a61.FlowToken.TokensWithdrawn` - Testnet event: `A.7e60df042a9c0868.FlowToken.TokensWithdrawn` ```cadence access(all) event TokensWithdrawn(amount: UFix64, from: Address?) ``` | Field | Type | Description | | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | | amount | UFix64 | The amount of tokens withdrawn | | from | Address? | Optional address of the account that owns the vault where tokens were withdrawn from. `nil` if the vault is not in an account's storage | ### Tokens deposited Event that is emitted when tokens get deposited to a Vault. - Event name: `TokensDeposited` - Mainnet event: `A.1654653399040a61.FlowToken.TokensDeposited` - Testnet event: `A.7e60df042a9c0868.FlowToken.TokensDeposited` ```cadence access(all) event TokensDeposited(amount: UFix64, to: Address?) ``` | Field | Type | Description | | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | | amount | UFix64 | The amount of tokens withdrawn | | to | Address? | Optional address of the account that owns the vault where tokens were deposited to. `nil` if the vault is not in an account's storage | ### Tokens minted Event that is emitted when new tokens gets minted. - Event name: `TokensMinted` - Mainnet event: `A.1654653399040a61.FlowToken.TokensMinted` - Testnet event: `A.7e60df042a9c0868.FlowToken.TokensMinted` ```cadence access(all) event TokensMinted(amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | ---------------------------- | | amount | UFix64 | The amount of tokens to mint | ### Tokens burned Event that is emitted when tokens get destroyed. - Event name: `TokensBurned` - Mainnet event: `A.1654653399040a61.FlowToken.TokensBurned` - Testnet event: `A.7e60df042a9c0868.FlowToken.TokensBurned` ```cadence access(all) event TokensBurned(amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | ---------------------------- | | amount | UFix64 | The amount of tokens to burn | ### Minter created Event that is emitted when a new minter resource gets created. - Event name: `MinterCreated` - Mainnet event: `A.1654653399040a61.FlowToken.MinterCreated` - Testnet event: `A.7e60df042a9c0868.FlowToken.MinterCreated` ```cadence access(all) event MinterCreated(allowedAmount: UFix64) ``` | Field | Type | Description | | ------------- | ------ | ------------------------------------------------------- | | allowedAmount | UFix64 | The amount of tokens that the minter is allowed to mint | ### Burner Created Event that is emitted when a new burner Resource gets created. - Event name: `BurnerCreated` - Mainnet event: `A.1654653399040a61.FlowToken.BurnerCreated` - Testnet event: `A.7e60df042a9c0868.FlowToken.BurnerCreated` ```cadence access(all) event BurnerCreated() ``` ### Staking events To learn more about staking events, read [staking/events/] [FlowToken.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowToken.cdc [repo]: https://github.com/onflow/flow-core-contracts/tree/master/transactions/flowToken [`flow-ft` repo]: https://github.com/onflow/flow-ft/tree/master/transactions [core contracts here]: ./index.md [staking/events/]: ../../../protocol/staking/07-staking-scripts-events.md --- # File: /md/build/cadence/core-contracts/04-service-account.md --- title: Service Account Contracts sidebar_position: 4 sidebar_label: Service Account description: Learn about Flow's service account contracts that manage core protocol requirements, including transaction fees, deployment permissions, random beacon history, and node versioning. keywords: - service account - core contracts - FlowServiceAccount - transaction fees - deployment permissions - RandomBeaconHistory - NodeVersionBeacon - protocol versions - Flow protocol - core protocol - contract addresses - protocol management - Flow governance - network configuration --- # Service Account Contracts The service account is the account that manages the core protocol requirements of Flow. | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x8c5303eaa26202d6` | | Mainnet | `0xe467b9dd11fa00df` | Here are three important contracts deployed to the service account: ## FlowServiceAccount `FlowServiceAccount` tracks transaction fees, deployment permissions, and provides some convenience methods for Flow Token operations. Source: [FlowServiceAccount.cdc] ### Events Important events from `FlowServiceAccount` are: ```cadence access(all) event TransactionFeeUpdated(newFee: UFix64) access(all) event AccountCreationFeeUpdated(newFee: UFix64) ``` ## RandomBeaconHistory - `RandomBeaconHistory` stores the history of random sources generated by the Flow network. The defined Heartbeat resource is updated by the Flow Service Account at the end of every block with that block's source of randomness. Source: [RandomBeaconHistory.cdc] ### Events Important events from `RandomBeaconHistory` are: ```cadence // Event emitted when missing SoRs from past heartbeats are detected and will be backfilled: // - `blockHeight` is the height where the gap is detected // - `gapStartHeight` is the height of the first missing entry detected access(all) event RandomHistoryMissing(blockHeight: UInt64, gapStartHeight: UInt64) // Event emitted when missing SoRs are backfilled on the current heartbeat: // - `blockHeight` is the height where the backfill happened, it also defines the SoR used to backfill // - `gapStartHeight` is the height of the first backfilled entry // - `count` is the number of backfilled entries // Note that in very rare cases, the backfilled gap may not be contiguous. This event does not // fully define the backfilled entries in this case. access(all) event RandomHistoryBackfilled(blockHeight: UInt64, gapStartHeight: UInt64, count: UInt64) ``` ## NodeVersionBeacon - `NodeVersionBeacon` holds the past and future protocol versions that should be used to execute or handle blocks at a given block height. Source: [NodeVersionBeacon.cdc] ### Events Important events from `NodeVersionBeacon` are: ```cadence /// Event emitted when the version table is updated. /// It contains the current version and all the upcoming versions /// sorted by block height. /// The sequence increases by one each time an event is emitted. /// It can be used to verify no events were missed. access(all) event VersionBeacon( versionBoundaries: [VersionBoundary], sequence: UInt64 ) /// Event emitted any time the version boundary freeze period is updated. /// freeze period is measured in blocks (from the current block). access(all) event NodeVersionBoundaryFreezePeriodChanged(freezePeriod: UInt64) ``` [FlowServiceAccount.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowServiceAccount.cdc [RandomBeaconHistory.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/RandomBeaconHistory.cdc [NodeVersionBeacon.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/NodeVersionBeacon.cdc --- # File: /md/build/cadence/core-contracts/05-flow-fees.md --- title: Flow Fees Contract sidebar_position: 5 sidebar_label: Flow Fees description: Learn about Flow's fees contracts that handle transaction and storage fees, including fee collection, parameters, and storage capacity management. Understand how fees are processed and managed on the Flow blockchain. keywords: - Flow fees - transaction fees - storage fees - fee collection - fee parameters - FlowFees - FlowStorageFees - fee events - surge factor - execution fees - storage capacity - fee management - Flow protocol - core contracts - blockchain fees --- # Flow Fees Contract The `FlowFees` contract is where all the collected flow fees are gathered. Source: [FlowFees.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xe5a8b7f23e8b548f` | | Cadence Testing Framework | `0x0000000000000004` | | Testnet | `0x912d5440f7e3769e` | | Mainnet | `0xf919ee77447b7497` | ### Events Important events for `FlowFees` are: ```cadence // Event that is emitted when tokens are deposited to the fee vault access(all) event TokensDeposited(amount: UFix64) // Event that is emitted when tokens are withdrawn from the fee vault access(all) event TokensWithdrawn(amount: UFix64) // Event that is emitted when fees are deducted access(all) event FeesDeducted(amount: UFix64, inclusionEffort: UFix64, executionEffort: UFix64) // Event that is emitted when fee parameters change access(all) event FeeParametersChanged(surgeFactor: UFix64, inclusionEffortCost: UFix64, executionEffortCost: UFix64) ``` ## FlowStorageFees The `FlowStorageFees` contract defines the parameters and utility methods for storage fees. Source: [FlowStorageFees.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x8c5303eaa26202d6` | | Mainnet | `0xe467b9dd11fa00df` | ### Events Important events for `FlowStorageFees` are: ```cadence // Emitted when the amount of storage capacity an account has per reserved Flow token changes access(all) event StorageMegaBytesPerReservedFLOWChanged(_ storageMegaBytesPerReservedFLOW: UFix64) // Emitted when the minimum amount of Flow tokens that an account needs to have reserved for storage capacity changes. access(all) event MinimumStorageReservationChanged(_ minimumStorageReservation: UFix64) ``` [FlowFees.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowFees.cdc [FlowStorageFees.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowStorageFees.cdc --- # File: /md/build/cadence/core-contracts/06-staking-contract-reference.md --- title: Flow Staking Contract Reference sidebar_position: 6 sidebar_label: Staking Table description: Learn about Flow's staking contract (FlowIDTableStaking) that manages staked nodes, delegation, and rewards. Understand how to interact with staking functionality through transactions and scripts. keywords: - staking contract - Flow staking - node staking - delegation - staking rewards - FlowIDTableStaking - staking scripts - staking events - node management - token delegation - staking table - epoch events - staking transactions - Flow protocol - staking requirements --- # Flow Staking Contract Reference The `FlowIDTableStaking` contract is the central table that manages staked nodes, delegation and rewards. Source: [FlowIDTableStaking.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x9eca2b38b18b5dfe` | | Mainnet | `0x8624b52f9ddcd04a` | ## Transactions and scripts Transactions for the staking contract are in the `flow-core-contracts` repo. Developers and users are advised to use [the staking collection transactions] to stake tokens instead of the basic transactions that are used for tests. ### Getting staking info with scripts These scripts are read-only and get info about the current state of the staking contract. | ID | Name | Source | | ----------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`SC.01`** | Get Delegation Cut Percentage | [idTableStaking/get_cut_percentage.cdc] | | **`SC.02`** | Get Minimum Stake Requirements | [idTableStaking/get_stake_requirements.cdc] | | **`SC.03`** | Get Total Weekly Reward Payout | [idTableStaking/get_weekly_payout.cdc] | | **`SC.04`** | Get Current Staked Node Table | [idTableStaking/get_current_table.cdc] | | **`SC.05`** | Get Proposed Staked Node Table | [idTableStaking/get_proposed_table.cdc] | | **`SC.06`** | Get Total Flow Staked | [idTableStaking/get_total_staked.cdc] | | **`SC.07`** | Get Total Flow Staked by Node Type | [idTableStaking/get_total_staked_by_type.cdc] | | **`SC.08`** | Get All Info about a single NodeID | [idTableStaking/get_node_info.cdc] | | **`SC.09`** | Get a node's total Commitment (delegators) | [idTableStaking/get_node_total_commitment.cdc] | | **`SC.10`** | Get All Info about a single Delegator | [idTableStaking/delegation/get_delegator_info.cdc] | | **`SC.11`** | Get a node's total Commitment | [idTableStaking/get_node_total_commitment_without_delegators.cdc] | ### Delegator transactions Documentation for token delegation is described in the staking documentation for [the staking collection]. ## Events The `FlowIDTableStaking` contract emits an event whenever an important action occurs. See the [staking events Documentation]for more information about each event. ```cadence /// Epoch access(all) event NewEpoch( totalStaked: UFix64, totalRewardPayout: UFix64, newEpochCounter: UInt64 ) access(all) event EpochTotalRewardsPaid( total: UFix64, fromFees: UFix64, minted: UFix64, feesBurned: UFix64, epochCounterForRewards: UInt64 ) /// Node access(all) event NewNodeCreated(nodeID: String, role: UInt8, amountCommitted: UFix64) access(all) event TokensCommitted(nodeID: String, amount: UFix64) access(all) event TokensStaked(nodeID: String, amount: UFix64) access(all) event NodeTokensRequestedToUnstake(nodeID: String, amount: UFix64) access(all) event TokensUnstaking(nodeID: String, amount: UFix64) access(all) event TokensUnstaked(nodeID: String, amount: UFix64) access(all) event NodeRemovedAndRefunded(nodeID: String, amount: UFix64) access(all) event RewardsPaid(nodeID: String, amount: UFix64, epochCounter: UInt64) access(all) event UnstakedTokensWithdrawn(nodeID: String, amount: UFix64) access(all) event RewardTokensWithdrawn(nodeID: String, amount: UFix64) access(all) event NetworkingAddressUpdated(nodeID: String, newAddress: String) access(all) event NodeWeightChanged(nodeID: String, newWeight: UInt64) /// Delegator access(all) event NewDelegatorCreated(nodeID: String, delegatorID: UInt32) access(all) event DelegatorTokensCommitted(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorTokensStaked(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorTokensRequestedToUnstake(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorTokensUnstaking(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorTokensUnstaked(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorRewardsPaid(nodeID: String, delegatorID: UInt32, amount: UFix64, epochCounter: UInt64) access(all) event DelegatorUnstakedTokensWithdrawn(nodeID: String, delegatorID: UInt32, amount: UFix64) access(all) event DelegatorRewardTokensWithdrawn(nodeID: String, delegatorID: UInt32, amount: UFix64) /// Contract Fields access(all) event NewDelegatorCutPercentage(newCutPercentage: UFix64) access(all) event NewWeeklyPayout(newPayout: UFix64) access(all) event NewStakingMinimums(newMinimums: {UInt8: UFix64}) access(all) event NewDelegatorStakingMinimum(newMinimum: UFix64) ``` [FlowIDTableStaking.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc [the staking collection transactions]: ../../../protocol/staking/14-staking-collection.md [idTableStaking/get_cut_percentage.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_cut_percentage.cdc [idTableStaking/get_stake_requirements.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_stake_requirements.cdc [idTableStaking/get_weekly_payout.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_weekly_payout.cdc [idTableStaking/get_current_table.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_current_table.cdc [idTableStaking/get_proposed_table.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_proposed_table.cdc [idTableStaking/get_total_staked.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_total_staked.cdc [idTableStaking/get_total_staked_by_type.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_total_staked_by_type.cdc [idTableStaking/get_node_info.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_node_info.cdc [idTableStaking/get_node_total_commitment.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_node_total_commitment.cdc [idTableStaking/delegation/get_delegator_info.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/delegation/get_delegator_info.cdc [idTableStaking/get_node_total_commitment_without_delegators.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_node_total_commitment_without_delegators.cdc [the staking collection]: ../../../protocol/staking/14-staking-collection.md [staking events Documentation]: ../../../protocol/staking/07-staking-scripts-events.md --- # File: /md/build/cadence/core-contracts/07-epoch-contract-reference.md --- title: Flow Epoch Contracts Reference sidebar_position: 7 sidebar_label: Epoch Contracts description: Learn about Flow's epoch-related contracts that manage network phases, quorum certificates, and distributed key generation. Understand how FlowEpoch, FlowClusterQC, and FlowDKG contracts work together. keywords: - epoch contracts - FlowEpoch - FlowClusterQC - FlowDKG - epoch phases - quorum certificates - distributed key generation - epoch metadata - epoch counter - epoch scripts - QC voting - DKG participants - network phases - Flow protocol - epoch management --- # Contract The `FlowEpoch` contract is the state machine that manages Epoch phases and emits service events. The `FlowClusterQC` and `FlowDKG` contracts manage the processes that happen during the Epoch Setup phase. These contracts are all deployed to the same account as the `FlowIDTableStaking` contract. Sources: - [FlowEpoch.cdc] - [FlowClusterQC.cdc] - [FlowDKG.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x9eca2b38b18b5dfe` | | Mainnet | `0x8624b52f9ddcd04a` | ## Transactions ### Get epoch info These scripts are read-only and get info about the current state of the epoch contract. | ID | Name | Source | | ----------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **`EP.01`** | Get Epoch Metadata | [epoch/get_epoch_metadata.cdc] | | **`EP.02`** | Get Configurable Metadata | [epoch/get_config_metadata.cdc] | | **`EP.03`** | Get Epoch Counter | [epoch/get_epoch_counter.cdc] | | **`EP.04`** | Get Epoch Phase | [epoch/get_epoch_phase.cdc] | ## Quorum certificate transactions and scripts | ID | Name | Source | | ----------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`QC.01`** | Create QC Voter | [quorumCertificate/get_epoch_metadata.cdc] | | **`QC.02`** | Submit QC Vote | [quorumCertificate/get_config_metadata.cdc] | | **`QC.03`** | Get Collector Cluster | [quorumCertificate/scripts/get_cluster.cdc] | | **`QC.04`** | Get QC Enabled | [quorumCertificate/scripts/get_qc_enabled.cdc] | | **`QC.05`** | Get Node Has Voted | [quorumCertificate/scripts/get_node_has_voted.cdc] | | **`QC.06`** | Get QC Voting Complete | [quorumCertificate/scripts/get_voting_completed.cdc] | ## DKG transactions and scripts | ID | Name | Source | | ------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`DKG.01`** | Create DKG Participant | [dkg/create_participant.cdc] | | **`DKG.02`** | Get Configurable Metadata | [dkg/send_whiteboard_message.cdc] | | **`DKG.03`** | Send Final Submission | [dkg/send_final_submission.cdc] | | **`DKG.04`** | Get DKG Enabled | [dkg/scripts/get_dkg_enabled.cdc] | | **`DKG.05`** | Get DKG Completed | [dkg/scripts/get_dkg_completed.cdc] | | **`DKG.06`** | Get Whiteboard Messages | [dkg/scripts/get_whiteboard_messages.cdc] | | **`DKG.07`** | Get Final Submissions | [dkg/scripts/get_final_submissions.cdc] | | **`DKG.08`** | Get Node Has Submitted | [dkg/scripts/get_node_has_submitted.cdc] | # Events See the [epoch documentation] for a list and documentation for important `FlowEpoch` events. [FlowEpoch.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowEpoch.cdc [FlowClusterQC.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowClusterQC.cdc [FlowDKG.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowDKG.cdc [epoch documentation]: ../../../protocol/staking/05-epoch-scripts-events.md [epoch/get_epoch_metadata.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_epoch_metadata.cdc) [epoch/get_config_metadata.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_config_metadata.cdc [epoch/get_epoch_counter.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_epoch_counter.cdc [epoch/get_epoch_phase.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_epoch_phase.cdc) [quorumCertificate/get_epoch_metadata.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/create_voter.cdc) [quorumCertificate/get_config_metadata.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/submit_vote.cdc [quorumCertificate/scripts/get_cluster.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/scripts/get_cluster.cdc [quorumCertificate/scripts/get_qc_enabled.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/scripts/get_qc_enabled.cdc [quorumCertificate/scripts/get_node_has_voted.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/scripts/get_node_has_voted.cdc [quorumCertificate/scripts/get_voting_completed.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/quorumCertificate/scripts/get_voting_completed.cdc [dkg/create_participant.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/create_participant.cdc [dkg/send_whiteboard_message.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/send_whiteboard_message.cdc [dkg/send_final_submission.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/send_final_submission.cdc [dkg/scripts/get_dkg_enabled.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/scripts/get_dkg_enabled.cdc [dkg/scripts/get_dkg_completed.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/scripts/get_dkg_completed.cdc [dkg/scripts/get_whiteboard_messages.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/scripts/get_whiteboard_messages.cdc [dkg/scripts/get_final_submissions.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/scripts/get_final_submissions.cdc [dkg/scripts/get_node_has_submitted.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/dkg/scripts/get_node_has_submitted.cdc --- # File: /md/build/cadence/core-contracts/08-non-fungible-token.md --- title: Non-Fungible Token Contract sidebar_position: 8 sidebar_label: Non-Fungible Token description: Learn about Flow's Non-Fungible Token (NFT) standard contract interface, its implementation, events system, and how to build NFT contracts on Flow. Understand the core NFT functionality and event handling. keywords: - NFT - non-fungible token - NFT standard - token contract - NFT events - NFT transactions - token interface - Flow NFT - NFT implementation - token collection - NFT deposits - NFT withdrawals - token updates - NFT metadata - digital assets --- # Non-Fungible Token Contract The `NonFungibleToken` contract interface implements the Fungible Token Standard. All NFT contracts are encouraged to import and implement this standard. - [Basic Non-Fungible Token Tutorial] - [Non Fungible Token Guide] - [Non Fungible Token Standard Repo] Source: [NonFungibleToken.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x631e88ae7f1d7c20` | | Mainnet | `0x1d7e57aa55817448` | ## Transactions All `NonFungibleToken` projects are encouraged to use the generic token transactions and scripts in the `flow-nft` [repo]. You can use them for any token that implements the non-fungible token standard properly, and you won't have to change any code besides import addresses on different networks. ## Events Events emitted from all contracts follow a standard format: ``` A.{contract address}.{contract name}.{event name} ``` The components of the format are: - `contract address` - the address of the account the contract has been deployed to. - `contract name` - the name of the contract in the source code. - `event name` - the name of the event as declared in the source code. ## NonFungibleToken events Contracts that implement the Non-Fungible Token standard get access to standard events that are emitted every time a relevant action occurs, like token deposits and withdrawls. This means that projects do not have to implement their own custom events unless the standard events do not satisfy requirements they have for events. The `NonFungibleToken` events will have the following format: ``` A.{contract address}.NonFungibleToken.Deposited A.{contract address}.NonFungibleToken.Withdrawn ``` Where the `contract address` is the `NonFungibleToken` address on the network being queried. The addresses on the various networks are shown above. ### NonFungibleToken.Deposited ```cadence access(all) event Deposited ( type: String, id: UInt64, uuid: UInt64, to: Address?, collectionUUID: UInt64 ) ``` Whenever `deposit()` is called on a resource type that implements `NonFungibleToken.Collection`, the `NonFungibleToken.Deposited` event is emitted with the following arguments: - `type: String`: The type identifier of the token being deposited. - Example: `A.4445e7ad11568276.TopShot.NFT` - `id: UInt64`: The ID of the token that was deposited. Note: This may or may not be the UUID. - Example: `173838` - `uuid: UInt64`: The UUID of the token that was deposited. - Example: `177021372071991` - `to: Address?`: The address of the account that owns the Collection that received the token. If the collection is not stored in an account, `to` will be `nil`. - Example: `0x4445e7ad11568276` - `collectionUUID: UInt64`: The UUID of the Collection that received the token. - Example: `177021372071991` ### NonFungibleToken.Withdrawn ```cadence access(all) event Withdrawn ( type: String, id: UInt64, uuid: UInt64, from: Address?, providerUUID: UInt64 ) ``` Whenever `withdraw()` is called on a resource type that implements `NonFungibleToken.Collection`, the `NonFungibleToken.Withdrawn` event is emitted with the following arguments: - `type: String`: The type identifier of the token being withdrawn. - Example: `A.4445e7ad11568276.TopShot.NFT` - `id: UInt64`: The id of the token that was withdrawn. Note: May or may not be the UUID. - Example: `113838` - `uuid: UInt64`: The UUID of the token that was withdrawn. - Example: `177021372071991` - `from: Address?`: The address of the account that owns the Collection that the token was withdrawn from. If the collection is not stored in an account, `to` will be `nil`. - Example: `0x4445e7ad11568276` - `providerUUID: UInt64`: The UUID of the Collection that the token was withdrawn from. - Example: `177021372071991` ### NonFungibleToken.Updated ```cadence access(all) event Updated( type: String, id: UInt64, uuid: UInt64, owner: Address? ) ``` Whenever a non-fungible token is updated for whatever reason, projects should call the `NonFungibleToken.emitNFTUpdated()` function to emit this event. It indicates to event listeners that they should query the NFT to update any stored information they have about the NFT in their database. - `type: String`: The type identifier of the token that was updated. - Example: `A.4445e7ad11568276.TopShot.NFT` - `id: UInt64`: The ID of the token that was updated. This may or may not be the UUID. - Example: `173838` - `uuid: UInt64`: The UUID of the token that was updated. - Example: `177021372071991` - `owner: Address?`: The address of the account that owns the Collection that owns the token. If the collection is not stored in an account, `to` will be `nil`. - Example: `0x4445e7ad11568276` [repo]: https://github.com/onflow/flow-nft/tree/master/transactions [Basic Non-Fungible Token Tutorial]: https://cadence-lang.org/docs/tutorial/non-fungible-tokens-1) [Non Fungible Token Guide]: ../../../blockchain-development-tutorials/tokens/nft-cadence.md) [Non Fungible Token Standard Repo]: https://github.com/onflow/flow-nft) [NonFungibleToken.cdc]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc) --- # File: /md/build/cadence/core-contracts/09-nft-metadata.md --- title: NFT Metadata Contract sidebar_position: 9 sidebar_label: NFT Metadata description: Learn about Flow's NFT metadata standards implemented through ViewResolver and MetadataViews contracts. Understand how to attach and manage onchain metadata for NFTs and integrate with the Flow NFT Catalog. keywords: - NFT metadata - ViewResolver - MetadataViews - metadata standard - NFT catalog - onchain metadata - NFT interoperability - metadata views - FLIP-0636 - NFT discovery - metadata implementation - Flow NFT - NFT standards - metadata integration - NFT optimization --- # NFT Metadata Contract The `ViewResolver` and `MetadataViews` contracts implement a standard to attach onchain metadata to NFTs. This standard was originally proposed in [FLIP-0636]. It is deployed at the same address as the `NonFungibleToken` contract interface. Source: [ViewResolver.cdc] Source: [MetadataViews.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x631e88ae7f1d7c20` | | Mainnet | `0x1d7e57aa55817448` | There exists a tool, [Flow NFT Catalog], which allows dapp developers to unlock interoperability of your NFT collection across the Flow ecosystem. This will help make your NFT collection's metadata more discoverable and interoperable. To optimize your NFT collections for this catalog, you'll need to: 1. Update your NFT contract to support `ViewResolver` and `MetadataViews` with implementation of the [core NFT views]. 2. Deploy the updated contract to both testnet and mainnet. 3. Afterwards, onboard your NFT to the Flow NFT catalog at [https://flow-nft-catalog.com]. [FLIP-0636]: https://github.com/onflow/flips/blob/main/application/20210916-nft-metadata.md [ViewResolver.cdc]: https://github.com/onflow/flow-nft/blob/master/contracts/ViewResolver.cdc [MetadataViews.cdc]: https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc [Flow NFT Catalog]: https://flow-nft-catalog.com [core NFT views]: ../advanced-concepts/metadata-views.md [https://flow-nft-catalog.com]: https://flow-nft-catalog.com --- # File: /md/build/cadence/core-contracts/10-nft-storefront.md --- title: NFT Storefront Smart Contract sidebar_position: 10 sidebar_label: NFT Storefront description: Learn about Flow's NFT Storefront contracts that enable non-custodial NFT marketplaces. Understand how to list NFTs for sale, handle purchases, manage commissions, and implement creator royalties. keywords: - NFT marketplace - NFT storefront - NFT listings - NFT sales - creator royalties - marketplace commission - non-custodial - NFT trading - NFT commerce - listing management - sale cuts - NFT purchases - marketplace integration - NFT transactions - Flow marketplace --- # NFT Storefront Smart Contract The NFT Storefront contracts implement a standard way to list NFTs for sale and buy them from listings. `NFTStorefrontV2` is the more powerful and full-featured version, so developers and users are encouraged to use it instead of `NFTStorefront` or their own implementation. Source: [NFTStorefrontV2.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x2d55b98eb200daef` | | Mainnet | `0x4eb8a10cb9f87357` | Source: [NFTStorefront.cdc] | Network | Contract Address | | ------- | -------------------- | | Testnet | `0x94b06cfca1d8a476` | | Mainnet | `0x4eb8a10cb9f87357` | ## Primer The `NFTStorefrontV2` contract lets you create a _non-custodial Resource (NFT) marketplace_ on the FLOW blockchain. `NFTStorefrontV2` makes it simple for Sellers to list NFTs in dApp specific marketplaces. dApp developers leverage the APIs provided by the contract to manage listings for sale and to transact NFT trades. ![dapps_1](https://user-images.githubusercontent.com/14581509/191749748-714f9d8f-cb41-4be4-a3d2-ec84cb8b5ffb.png) Developers should use the `NFTStorefrontV2` to create their marketplace and to alloq p2p purchases. The diagram below shows how dApps can facilitate the creation of NFT listings for different marketplaces and how marketplaces can filter their listings. Listings made through a specific dApp storefront can be simultaneously listed on 3rd party marketplaces beyond that dApp. Well known 3rd party marketplaces listen for compatible NFT listing events, which allows the automation of listings into their marketplace dashboards. ![dapps_2](https://user-images.githubusercontent.com/14581509/191753605-e1c48a57-0c3c-4509-808b-8fee4e7d32e8.png) With the `NFTStorefrontV2`, marketplaces can instantly and easily tap into the vibrant FLOW NFT ecosystem and allow NFT holders to list their NFTs and allows creator royalties. Marketplaces then interact with seller storefronts directly to process an NFT trade. Flow's account based model ensures that NFTs listed for sale always reside in the Seller account until traded, regardless of how many Listings are posted across any number of marketplaces, for the same NFT. ![marketplace_1](https://user-images.githubusercontent.com/14581509/191755699-fe0570cb-80a3-408c-8eef-4051e3209481.png) ## Functional overview A general purpose sale support contract for NFTs that implement the Flow [`NonFungibleToken`] standard. Each account that wants to list NFTs for sale creates a `Storefront` resource to store in their account and lists individual sales within that Storefront as listings. There is usually one Storefront per account held at the `/storage/NFTStorefrontV2`. Each listing can define one or more sale cuts taken out of the sale price to go to one or more addresses. Listing fees, royalties, or other considerations can be paid with sale cuts. Also, the listing can include a commission as one of these sale cuts is paid to whoever facilitates the purchase. Listings can have an optional list of marketplace [receiver capabilities] used to receive the commission after the seller fulfills the listing. An NFT may be listed in one or more listings, and the validity of each listing can easily be checked. Interested parties can globally track listing events onchain and filter by NFT types, IDs and other characteristics to determine which to make available for purchase within their own marketplace UIs." ## Sell NFTs `NFTStorefrontV2` offers a generic process for creating the listing for an NFT. It provides all the essential APIs to manage those listings independently. Many marketplaces create a single storefront resource to manage different individual listings. We recommend that you create the listing under the user-owned storefront resource to make it trustless and platform-independent. Users should possess the `Storefront` resource under their account to create the listing using the storefront contract. ## Create a successful listing with the NFTStorefrontV2 contract. As recommended above, the first step is to create and store the [Storefront resource] in the user account with the [setup_account] transaction. The next step is to create a listing under the newly-created storefront resource. If the user (repetitive) already holds the storefront resource, then use the current resource. The seller can come with multiple requirements to list their NFTs, and we try our best to cover most of them below. ### **Scenario 1:** Sell NFTs that correspond to more than one cryptocurrency, such as FLOW, USDC etc. The `NFTStorefrontV2` contract doesn't support an NFT sale for multiple different currencies with a single listing. However, to achieve this, you can create multiple listings for the same NFT for each different currency. **Example -** Alice wants to sell a kitty and is open to receive FLOW and USDC ![scenario_1](./scenario_1.png) A seller puts an NFT on sale, a process called listing. To create a listing with the [sell_item] transaction, the seller provides some required details to list an NFT, such as the receiving currency type, and the [Capability] from where the network deducts the NFT. For more information, see [`createListing`]. To receive a different currency, the seller must provide a different **Receiver currency type** , such as `salePaymentVaultType`. As depicted in the above diagram, there are two listing formations with almost the same inputs. The only differentiator is the `salePaymentVaultType` parameter that needs to be different when the seller creates duplicate NFT listings with different sale currency types. ### **Scenario 2:** Peer-to-Peer (p2p) listing of NFT: A listing anyone can fulfill. dApps can leverage the **NFTStorefrontV2** to facilitate a listing's creation for the seller independent of any marketplace. dApps or marketplaces can list those listings on their platforms, or seller can settle it p2p. The seller can use [sell_item] transaction to create a p2p listing. To do this, they provide the `marketplacesAddress` with an empty array. The seller can provide [commission] to the facilitator of sale, which can also act as a discount if the facilitator and the purchaser are the same. ### **Scenario 3:** The seller wants to list its NFT in different marketplaces. `NFTStorefrontV2` offers two different ways to do it. - The seller can create a listing and provide the `marketplacesAddress` that it wants to have a listing on with the [sell_item] transaction. Marketplaces can listen to `ListingAvailable` events and check whether their address is included in the `commissionReceivers` list; If yes, the marketplace would be rewarded during the successful fulfilment of the listing. Example - Bob wants to list on marketplace 0xA, 0xB & 0xC and will offer 10% commission on the sale price of the listing to the marketplaces. ![scenario_3](https://user-images.githubusercontent.com/14581509/190966834-8eda4ec4-e9bf-49ef-9dec-3c47a236d281.png) - Another way to accomplish this is to create separate listings for each marketplace on which a user wants their listing with the [sell_item_with_marketplace_cut] transaction. In this case, the marketplace would earn one part of the [`saleCut`] as an incentive. It appends the marketplace saleCut in the `saleCuts` array when it creates the listing. ### Considerations 1. **Ghost listings -** _Ghost listings are listings which don't have an underlying NFT in the seller's account. However, the listing is still available for buyers to attempt to purchase_. StorefrontV2 is not immune to ghost listings. Usually, ghost listings will cause a purchaser's transaction to fail, which is annoying but isn't a significant problem. Ghost listings become a problem for the seller when the listed NFT comes back to the seller's account after its original sale. The ghost listing will no longer be invalid when it comes back, and anyone can purchase it even if the seller doesn't want to sell it at that price anymore. :::info _We recommend that marketplaces and p2p dApps create an off-chain notification service that tells their users (sellers) to remove the listings if they don't hold the NFT anymore in the same account._ ::: 2. **Expired listings -** `NFTStorefrontV2` introduces a safety measure to specify that a listing will expire after a certain period that can be set during the creation so no one can purchase the listing anymore. It is not a fool-proof safety measure, but it does give some safe ground to the sellers for the ghost listings & stale listings. :::info _We recommended for marketplaces and p2p dApps not to show the expired listings on their dashboards._ ::: ## Purchase NFTs To purchase NFTs through the `NFTStorefrontV2` is simple. The buyer has to provide the payment vault and the `commissionRecipient` , if applicable, during the purchase. p2p dApps don't need any intermediaries to facilitate the purchase of listings. [`purchase`] API offered by the `Listing` resource gets used to facilitate the purchase of NFT. During the listing purchase, all saleCuts are paid automatically. This also includes royalty distribution for that NFT, if applicable. If the vault provided by the buyer lacks sufficient funds, then the transaction will fail. ### Considerations 1. **Auto cleanup -** `NFTStorefrontV2` offers a unique ability to do auto cleanup of duplicate listings during a purchase. It comes with a drawback if one NFT has thousands of duplicate listings. It will become the bottleneck during purchases one of the listings as it will likely trigger an out-of-compute error. :::info _We recommended NOT to have more than 50 duplicate listings of any given NFT._ ::: 2. **Unsupported receiver capability** - A common pitfall during the purchase of an NFT that some saleCut receivers don't have a supported receiver capability because that entitled sale cut would transfer to first valid sale cut receiver. However, it can be partially solved if you provide the generic receiver with the [`FungibleTokenSwitchboard`] contract and add all the currency capabilities the beneficiary wants to receive. For more information about the `FungibleTokenSwitchboard`, see [Fungible Token Switchboard] ## Allow creator royalties for NFTs The `NFTStorefrontV2` contract optionally supports royalty payments to the minter account for secondary resales of that NFT after the original sale. Marketplaces decide for themselves whether to support creator royalties when they validate listings for sale eligibility. We encourage all marketplaces to support creator royalties and support community creators in the **FLOW** ecosystem. If a seller's NFT supports the [Royalty Metadata View] standard, then marketplaces can honor royalties payments at time of purchase. `NFTStorefrontV2` dynamically calculates the royalties owed at the time of listing creation and applies it as a saleCut of the listing at the time of purchase. ```cadence // Check whether the NFT implements the MetadataResolver or not. if nft.getViews().contains(Type()) { // Resolve the royalty view let royaltiesRef = nft.resolveView(Type()) ?? panic("Unable to retrieve the royalties view for the NFT with type " .concat(nft.getType().identifier).concat(" and ID ") .concat(nft.id.toString()).concat(".") // Fetch the royalties. let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties() // Append the royalties as the salecut for royalty in royalties { self.saleCuts.append(NFTStorefrontV2.SaleCut(receiver: royalty.receiver, amount: royalty.cut * effectiveSaleItemPrice)) totalRoyaltyCut = totalRoyaltyCut + royalty.cut * effectiveSaleItemPrice } } ``` You can view a complete transaction in [sell_item]. saleCut only supports a single token receiver type and therefore beneficiaries of a `saleCut` can also only receive the token type used for the purchase. To support different token types for saleCuts, we recommend that you use the [`FungibleTokenSwitchboard`] contract. The contract defines a generic receiver for fungible tokens which itself handles routing of tokens to the respective vault for that token type. You can learn more about this in [Fungible Token Switchboard]. ## Allow marketplace commissions for NFT sales `NFTStorefrontV2` allows optional commissions on trades for marketplaces which require it as a condition to list a NFT for sale. The seller sets commission and commission receivers during initial listing creation. At time of purchase, the commission amount is paid once only to the commission receiver that matches the marketplace receiver address which facilitated the sale. For NFT listings in marketplaces which don't require commission, you can set commission receivers as `nil`. If you set the buyer of the NFT and `commissionRecipient` to `nil`, it applies a discount for the buyer. ![scenario_2](https://user-images.githubusercontent.com/14581509/190966499-c176203f-b6a6-4422-860f-1bf6f2bcdbb6.png). ## APIs and events offered by NFTStorefrontV2 ## Resource Interface `ListingPublic` ```cadence resource interface ListingPublic { access(all) fun borrowNFT(): &NonFungibleToken.NFT? access(all) fun purchase( payment: @FungibleToken.Vault, commissionRecipient: Capability<&{FungibleToken.Receiver}>?, ): @NonFungibleToken.NFT access(all) fun getDetails(): ListingDetail access(all) fun getAllowedCommissionReceivers(): [Capability<&{FungibleToken.Receiver}>]? } ``` An interface that provides a useful public interface to a Listing. ### Functions #### **fun `borrowNFT()`** ```cadence fun borrowNFT(): &NonFungibleToken.NFT? ``` This will assert in the same way as the NFT standard borrowNFT() if the NFT is absent, for example if it has been sold via another listing. --- #### **fun `purchase()`** ```cadence fun purchase(payment FungibleToken.Vault, commissionRecipient Capability<&{FungibleToken.Receiver}>?): NonFungibleToken.NFT ``` Facilitates the purchase of the listing by providing the payment vault and the commission recipient capability if there is a non-zero commission for the given listing. Respective saleCuts are transferred to beneficiaries and funtion return underlying or listed NFT. --- #### **fun `getDetails()`** ```cadence fun getDetails(): ListingDetails ``` Fetches the details of the listings. --- #### **fun `getAllowedCommissionReceivers()`** ```cadence fun getAllowedCommissionReceivers(): [Capability<&{FungibleToken.Receiver}>]? ``` Fetches the allowed marketplaces capabilities or commission receivers for the underlying listing. If it returns `nil`, then commission is up for grabs by anyone. --- ## Resource `Storefront` ```cadence resource Storefront { access(all) fun createListing( nftProviderCapability: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>, nftType: Type, nftID: UInt64, salePaymentVaultType: Type, saleCuts: [SaleCut], marketplacesCapability: [Capability<&{FungibleToken.Receiver}>]?, customID: String?, commissionAmount: UFix64, expiry: UInt64 ): UInt64 access(all) fun removeListing(listingResourceID: UInt64) access(all) fun getListingIDs(): [UInt64] access(all) fun getDuplicateListingIDs(nftType: Type, nftID: UInt64, listingID: UInt64): [UInt64] access(all) fun cleanupExpiredListings(fromIndex: UInt64, toIndex: UInt64) access(all) fun borrowListing(listingResourceID: UInt64): &Listing{ListingPublic}? } ``` A resource that allows its owner to manage a set of listings, and anyone to interact with them in order to query their details and purchase the NFTs that they represent. Implemented Interfaces: - `StorefrontManager` - `StorefrontPublic` ### Initializer ```cadence fun init() ``` ### Functions #### **fun `createListing()`** ```cadence fun createListing(nftProviderCapability Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>, nftType Type, nftID UInt64, salePaymentVaultType Type, saleCuts [SaleCut], marketplacesCapability [Capability<&{FungibleToken.Receiver}>]?, customID String?, commissionAmount UFix64, expiry UInt64): UInt64 ``` insert Create and publish a listing for an NFT. --- #### **fun `removeListing()`** ```cadence fun removeListing(listingResourceID UInt64) ``` removeListing Remove a listing that has not yet been purchased from the collection and destroy it. --- #### **fun `getListingIDs()`** ```cadence fun getListingIDs(): [UInt64] ``` getListingIDs Returns an array of the listing resource IDs that are in the collection. --- #### **fun `getDuplicateListingIDs()`** ```cadence fun getDuplicateListingIDs(nftType Type, nftID UInt64, listingID UInt64): [UInt64] ``` getDuplicateListingIDs Returns an array of listing IDs that are duplicates of the given `nftType` and `nftID`. --- #### **fun `cleanupExpiredListings()`** ```cadence fun cleanupExpiredListings(fromIndex UInt64, toIndex UInt64) ``` cleanupExpiredListings Iterate over the provided range of indexes to clean up the expired listings. --- #### **fun `borrowListing()`** ```cadence fun borrowListing(listingResourceID: UInt64): &{ListingPublic}? ``` borrowListing Returns a read-only view of the listing for the given listingID if it is contained by this collection. --- ## Resource Interface `StorefrontPublic` ```cadence resource interface StorefrontPublic { access(all) fun getListingIDs(): [UInt64] access(all) fun getDuplicateListingIDs(nftType: Type, nftID: UInt64, listingID: UInt64): [UInt64] access(all) fun cleanupExpiredListings(fromIndex: UInt64, toIndex: UInt64) access(all) fun borrowListing(listingResourceID: UInt64): &Listing{ListingPublic}? access(all) fun cleanupPurchasedListings(listingResourceID: UInt64) access(all) fun getExistingListingIDs(nftType: Type, nftID: UInt64): [UInt64] } ``` StorefrontPublic An interface to allow listing and borrowing listings, and purchase items via listings in a Storefront. ### Functions #### **fun `getListingIDs()`** ```cadence fun getListingIDs(): [UInt64] ``` getListingIDs Returns an array of the listing resource IDs that are in the collection --- #### **fun `getDuplicateListingIDs()`** ```cadence fun getDuplicateListingIDs(nftType Type, nftID UInt64, listingID UInt64): [UInt64] ``` getDuplicateListingIDs Returns an array of listing IDs that are duplicates of the given nftType and nftID. --- #### **fun `borrowListing()`** ```cadence fun borrowListing(listingResourceID UInt64): &Listing{ListingPublic}? ``` borrowListing Returns a read-only view of the listing for the given listingID if it is contained by this collection. --- #### **fun `cleanupExpiredListings()`** ```cadence fun cleanupExpiredListings(fromIndex UInt64, toIndex UInt64) ``` cleanupExpiredListings Iterates over the provided range of indexes to clean up the expired listing. --- #### **fun `cleanupPurchasedListings()`** ```cadence fun cleanupPurchasedListings(listingResourceID: UInt64) ``` cleanupPurchasedListings Allows anyone to remove already purchased listings. --- #### **fun `getExistingListingIDs()`** ```cadence fun getExistingListingIDs(nftType Type, nftID UInt64): [UInt64] ``` getExistingListingIDs Returns an array of listing IDs of the given `nftType` and `nftID`. --- ## Events **event `StorefrontInitialized`** ```cadence event StorefrontInitialized(storefrontResourceID: UInt64) ``` A Storefront resource was created. Consumers can now expect events from this Storefront. We do not specify an address: we cannot and should not. Created resources do not have an owner address, and may be moved after creation in ways we cannot check. `ListingAvailable` events can be used to determine the address of the owner of the Storefront at the time of the listing but only at that precise moment in that precise transaction. If the seller moves the Storefront while the listing is valid, that is on them. --- **event `StorefrontDestroyed`** ```cadence event StorefrontDestroyed(storefrontResourceID: UInt64) ``` A Storefront has been destroyed. Event consumers can now stop processing events from this Storefront. We do not specify an address. --- **event `ListingAvailable`** ```cadence event ListingAvailable(storefrontAddress: Address, listingResourceID: UInt64, nftType: Type, nftUUID: UInt64, nftID: UInt64, salePaymentVaultType: Type, salePrice: UFix64, customID: String?, commissionAmount: UFix64, commissionReceivers: [Address]?, expiry: UInt64) ``` Above event gets emitted when a listing is created and added to a Storefront resource. The Address values here are valid when the event is emitted, but the state of the accounts they refer to may change outside of the `NFTStorefrontV2` workflow, so be careful to check when you use them. --- **event `ListingCompleted`** ```cadence event ListingCompleted(listingResourceID: UInt64, storefrontResourceID: UInt64, purchased: Bool, nftType: Type, nftUUID: UInt64, nftID: UInt64, salePaymentVaultType: Type, salePrice: UFix64, customID: String?, commissionAmount: UFix64, commissionReceiver: Address?, expiry: UInt64) ``` The listing was resolved. It was either purchased, removed or destroyed. --- **event `UnpaidReceiver`** ```cadence event UnpaidReceiver(receiver: Address, entitledSaleCut: UFix64) ``` A entitled receiver wasn't paid during the sale of the NFT. --- **Holistic process flow diagram of NFTStorefrontV2 -** ![NFT Storefront Process flow](https://user-images.githubusercontent.com/14581509/191960793-ff153e5d-2934-410c-b724-5c5dffd2c20f.png) [NFTStorefrontV2.cdc]: https://github.com/onflow/nft-storefront/blob/main/contracts/NFTStorefrontV2.cdc [NFTStorefront.cdc]: https://github.com/onflow/nft-storefront/blob/main/contracts/NFTStorefront.cdc [`NonFungibleToken`]: https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc [receiver capabilities]: https://cadence-lang.org/docs/language/capabilities [Storefront resource]: #resource-storefront [setup_account]: https://github.com/onflow/nft-storefront/blob/main/transactions/setup_account.cdc [sell_item]: https://github.com/onflow/nft-storefront/blob/main/transactions/sell_item.cdc [Capability]: https://cadence-lang.org/docs/language/capabilities [`createListing`]: #fun-createlisting [Fungible Token Switchboard]: https://github.com/onflow/flow-ft#fungible-token-switchboard [commission]: #enabling-marketplace-commissions-for-nft-sales [sell_item_with_marketplace_cut]: https://github.com/onflow/nft-storefront/blob/main/transactions/sell_item_with_marketplace_cut.cdc [`saleCut`]: https://github.com/onflow/nft-storefront/blob/160e97aa802405ad26a3164bcaff0fde7ee52ad2/contracts/NFTStorefrontV2.cdc#L104 [`purchase`]: #fun-purchase [`FungibleTokenSwitchboard`]: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleTokenSwitchboard.cdc [Royalty Metadata View]: https://github.com/onflow/flow-nft/blob/21c254438910c8a4b5843beda3df20e4e2559625/contracts/MetadataViews.cdc#L335 --- # File: /md/build/cadence/core-contracts/11-staking-collection.md --- title: Flow Staking Collection Contract Reference sidebar_position: 11 sidebar_label: Staking Collection description: Learn about Flow's Staking Collection contract that manages user stake and delegation resources. Understand how to interact with nodes, delegators, and locked tokens through the collection interface. keywords: - staking collection - Flow staking - node management - delegation - locked tokens - machine accounts - staking operations - node registration - delegator registration - stake management - token delegation - staking interface - Flow protocol - staking transactions - collection events --- # Flow Staking Collection Contract Reference The `FlowStakingCollection` contract is a contract that manages a resource which contain a user's stake and delegation objects. The `FlowStakingCollection` allows a user to manage multiple active nodes or delegators and interact with node or delegator objects stored in either their optional locked account or in the StakingCollection itself (stored in the main account). If a user has locked tokens, StakingCollection allows a user to interact with their locked tokens to perform staking actions for any of their nodes or delegators. The staking collection also manages a node's machine accounts creation process if they have any collector or consensus nodes. It also allows them to deposit and withdraw tokens from any of their machine accounts through the staking collection. See the [Staking Collection Docs] for more information on the design of the staking collection contract. Source: [FlowStakingCollection.cdc] | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0x95e019a17d0e23d7` | | Mainnet | `0x8d0e87b65159ae63` | ## Transactions Use the following transactions to interact with the StakingCollection. :::info The StakingCollection differentiates between stake and delegation requests through passing an optional DelegatorID argument. For example, if you wish to Stake New Tokens for an active node, pass `nil` as the optional DelegatorID argument to the Stake New Tokens transaction. The same applies for all the other staking operation transactions. ::: | ID | Name | Source | | ------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`SCO.01`** | Setup Staking Collection | [stakingCollection/setup_staking_collection.cdc] | | **`SCO.02`** | Register Delegator | [stakingCollection/register_delegator.cdc] | | **`SCO.03`** | Register Node | [stakingCollection/register_node.cdc] | | **`SCO.04`** | Create Machine Account | [stakingCollection/create_machine_account.cdc] | | **`SCO.05`** | Request Unstaking | [stakingCollection/request_unstaking.cdc] | | **`SCO.06`** | Stake New Tokens | [stakingCollection/stake_new_tokens.cdc] | | **`SCO.07`** | Stake Rewarded Tokens | [stakingCollection/stake_rewarded_tokens.cdc] | | **`SCO.08`** | Stake Unstaked Tokens | [stakingCollection/stake_unstaked_tokens.cdc] | | **`SCO.09`** | Unstake All | [stakingCollection/unstake_all.cdc] | | **`SCO.10`** | Withdraw Rewarded Tokens | [stakingCollection/withdraw_rewarded_tokens.cdc] | | **`SCO.11`** | Withdraw Unstaked Tokens | [stakingCollection/withdraw_unstaked_tokens.cdc] | | **`SCO.12`** | Close Stake | [stakingCollection/close_stake.cdc] | | **`SCO.13`** | Transfer Node | [stakingCollection/transfer_node.cdc] | | **`SCO.14`** | Transfer Delegator | [stakingCollection/transfer_delegator.cdc] | | **`SCO.15`** | Withdraw From Machine Account | [stakingCollection/withdraw_from_machine_account.cdc] | | **`SCO.22`** | Update Networking Address | [stakingCollection/update_networking_address.cdc] | ## Scripts | ID | Name | Source | | ------------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`SCO.16`** | Get All Delegator Info | [stakingCollection/scripts/get_all_delegator_info.cdc] | | **`SCO.15`** | Get All Node Info | [stakingCollection/scripts/get_all_node_info.cdc] | | **`SCO.22`** | Get Delegator Ids | [stakingCollection/scripts/get_delegator_ids.cdc] | | **`SCO.17`** | Get Node Ids | [stakingCollection/scripts/get_node_ids.cdc] | | **`SCO.18`** | Get Does Stake Exist | [stakingCollection/scripts/get_does_stake_exist.cdc] | | **`SCO.19`** | Get Locked Tokens Used | [stakingCollection/scripts/get_locked_tokens_used.cdc] | | **`SCO.20`** | Get Unlocked Tokens Used | [stakingCollection/scripts/get_unlocked_tokens_used.cdc] | | **`SCO.21`** | Get Machine Accounts | [stakingCollection/scripts/get_machine_accounts.cdc] | ## Setup Transaction To setup the Staking Collection for an account, use the `SC.01` transaction. The setup process finds any node or delegator records already stored in the main account's storage, as well as any in the associated locked account if an associated locked account exists. It connects these node and delegator records with the new Staking Collection, and you ue the Staking Collection API to interact with them. ## Events The `StakingCollection` contract emits an event whenever an important action occurs. ```cadence access(all) event NodeAddedToStakingCollection(nodeID: String, role: UInt8, amountCommitted: UFix64, address: Address?) access(all) event DelegatorAddedToStakingCollection(nodeID: String, delegatorID: UInt32, amountCommitted: UFix64, address: Address?) access(all) event NodeRemovedFromStakingCollection(nodeID: String, role: UInt8, address: Address?) access(all) event DelegatorRemovedFromStakingCollection(nodeID: String, delegatorID: UInt32, address: Address?) access(all) event MachineAccountCreated(nodeID: String, role: UInt8, address: Address) ``` [Staking Collection Docs]: ../../../protocol/staking/14-staking-collection.md [FlowStakingCollection.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowStakingCollection.cdc [stakingCollection/setup_staking_collection.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/setup_staking_collection.cdc [stakingCollection/register_delegator.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/register_delegator.cdc [stakingCollection/register_node.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/register_node.cdc [stakingCollection/request_unstaking.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/request_unstaking.cdc [stakingCollection/stake_new_tokens.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/stake_new_tokens.cdc [stakingCollection/stake_rewarded_tokens.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/stake_rewarded_tokens.cdc [stakingCollection/stake_unstaked_tokens.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/stake_unstaked_tokens.cdc [stakingCollection/unstake_all.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/unstake_all.cdc [stakingCollection/withdraw_rewarded_tokens.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/withdraw_rewarded_tokens.cdc [stakingCollection/withdraw_unstaked_tokens.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/withdraw_unstaked_tokens.cdc [stakingCollection/close_stake.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/close_stake.cdc [stakingCollection/transfer_node.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/transfer_node.cdc [stakingCollection/transfer_delegator.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/transfer_delegator.cdc [stakingCollection/withdraw_from_machine_account.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/withdraw_from_machine_account.cdc [stakingCollection/update_networking_address.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/update_networking_address.cdc [stakingCollection/scripts/get_all_delegator_info.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_all_delegator_info.cdc [stakingCollection/scripts/get_all_node_info.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_all_node_info.cdc [stakingCollection/scripts/get_delegator_ids.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_delegator_ids.cdc [stakingCollection/scripts/get_node_ids.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_node_ids.cdc [stakingCollection/scripts/get_does_stake_exist.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_does_stake_exist.cdc [stakingCollection/scripts/get_locked_tokens_used.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_locked_tokens_used.cdc [stakingCollection/scripts/get_unlocked_tokens_used.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_unlocked_tokens_used.cdc [stakingCollection/scripts/get_machine_accounts.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/scripts/get_machine_accounts.cdc [stakingCollection/create_machine_account.cdc]: https://github.com/onflow/flow-core-contracts/blob/master/transactions/stakingCollection/create_machine_account.cdc --- # File: /md/build/cadence/core-contracts/12-hybrid-custody.md --- title: Flow Account Linking Contract Address sidebar_position: 12 sidebar_label: Account Linking description: Learn about Flow's Account Linking contracts that enable hybrid custody solutions. Understand how to manage child accounts and share specific resources between parent and child accounts securely. keywords: - account linking - hybrid custody - child accounts - parent accounts - account management - resource sharing - custody solution - account security - wallet integration - Flow accounts - contract browser - account permissions - resource access - account control - custody model --- # Flow Account Linking Contract Address The Account Linking contracts manage ChildAccounts to permit hybrid custody in scenarios where apps only want to share a subset of resources on their accounts with various parents. In many cases, this will be a user's primary wallet outside of the application a child account came from. You can see the docs for account linking [here] | Network | Contract Address | | ------- | ------------------------------------------------------------------------------ | | Testnet | [`0x294e44e1ec6993c6`] | | Mainnet | [`0xd8a7e05a7ac670c0`] | [here]: https://developers.flow.com/build/cadence/advanced-concepts/account-linking [`0x294e44e1ec6993c6`]: https://contractbrowser.com/account/0x294e44e1ec6993c6 [`0xd8a7e05a7ac670c0`]: https://contractbrowser.com/account/0xd8a7e05a7ac670c0 --- # File: /md/build/cadence/core-contracts/13-evm.md --- title: Flow EVM sidebar_position: 13 sidebar_label: EVM description: Learn about Flow's EVM contract that enables Ethereum Virtual Machine compatibility on Flow. Understand how to interact with EVM from Cadence and access Flow's EVM equivalence features. keywords: - Flow EVM - EVM compatibility - Ethereum Virtual Machine - EVM tooling - Cadence EVM - EVM contract - EVM integration - blockchain interop - smart contracts - EVM equivalence - contract deployment - Flow protocol - cross-chain - EVM support - FLIP 223 --- # Flow EVM The `EVM` contract is the entrypoint from Cadence to Flow EVM. While many developers may choose to interact with EVM via [EVM-equivalent tooling paths], all access to Flow EVM ultimately interfaces via Cadence at some level. If you would like to interact with EVM directly from Cadence, you can use the `EVM` contract and it's constructs. Read more about the EVM contract and its role in Flow's EVM equivalence in [FLIP #223]. Mainnet/Testnet Source: [`EVM.cdc`] | Network | Contract Address | | ------------------------- | -------------------------------------------------------------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | [`0x8c5303eaa26202d6`] | | Mainnet | [`0xe467b9dd11fa00df`] | [EVM-equivalent tooling paths]: ../../../build/evm/using.mdx [FLIP #223]: https://github.com/onflow/flips/blob/main/protocol/20231116-evm-support.md [`EVM.cdc`]: https://github.com/onflow/flow-go/blob/master/fvm/evm/stdlib/contract.cdc [`0x8c5303eaa26202d6`]: https://contractbrowser.com/A.8c5303eaa26202d6.EVM [`0xe467b9dd11fa00df`]: https://contractbrowser.com/A.e467b9dd11fa00df.EVM --- # File: /md/build/cadence/core-contracts/14-burner.md --- title: Flow Burner Contract Address sidebar_position: 14 sidebar_label: Burner description: Learn about Flow's Burner contract that enables custom destruction logic for resources. Understand how to implement the Burnable interface and safely destroy resources with custom callbacks. keywords: - resource burning - Burner contract - resource destruction - burn callback - Burnable interface - resource management - Flow resources - custom destruction - safe burning - resource cleanup - Flow protocol - contract safety - resource lifecycle - destruction logic - burn operations --- # Flow Burner Contract Address The [Burner](https://github.com/onflow/flow-ft/blob/master/contracts/utility/Burner.cdc) contract provides a way for resources to define custom logic that is executed when the resource is destroyed. Resources that want to use this functionality should implement the `Burner.Burnable` interface which requires that they include a `burnCallback()` function that includes the custom logic. We recommend that, regardless of the resource, all users and developers should use `Burner.burn()` when they destroy a resource instead of `destroy`. | Network | Contract Address | | ------- | ------------------------------------------------------------------------------ | | Cadence Testing Framework | `0x0000000000000001` | | Emulator | `0xee82856bf20e2aa6` | | Testnet | [`0x294e44e1ec6993c6`] | | Mainnet | [`0xd8a7e05a7ac670c0`] | [Burner]: https://github.com/onflow/flow-ft/blob/master/contracts/utility/Burner.cdc [`0x294e44e1ec6993c6`]: https://contractbrowser.com/account/0x294e44e1ec6993c6 [`0xd8a7e05a7ac670c0`]: https://contractbrowser.com/account/0xd8a7e05a7ac670c0 --- # File: /md/build/cadence/core-contracts/15-bridge.md --- title: VM Bridge Contracts sidebar_position: 15 sidebar_label: VM Bridge description: Learn about Flow's bridge contracts that manage bridging tokens between the Cadence and EVM environments. keywords: - core contracts - transaction fees - EVM - bridge - fungible tokens - non fungible tokens - nft --- # VM Bridge Contracts The Flow VM bridge is the account and series of smart contracts that manage how assets are safely bridged between the Cadence and EVM Flow Environments. | Network | Contract Address | | ------------------------- | -------------------- | | Emulator | `0xf8d6e0586b0a20c7` | | Cadence Testing Framework | `0x0000000000000001` | | Testnet | `0xdfc20aee650fcbdf` | | Mainnet | `0x1e4aa0b87d10b141` | # Contracts There are many important contracts deployed to the bridge account. You should refer to [the bridge repo] and [the bridge guides] for more detailed information about the bridge and tutorials for how to use the bridge properly. Here is a list of each Cadence contract used for the bridge: | Contract | Purpose | | ------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `CrossVMNFT` | Contract that defines cross-VM NFT-related interfaces. | | `CrossVMToken` | Contract that defines cross-VM Fungible Token Vault interfaces. | | `FlowEVMBridgeHandlerInterfaces` | Defines interface for custom bridged token handlers. | | `IBridgePermissions` | Defines an interface to prevent bridging for a specific token. | | `ICrossVM` | Defines an interface to get EVM contract addresses. | | `ICrossVMAsset` | Defines an interface to represent a Cadence bridged version of an EVM asset. | | `IEVMBridgeNFTMinter` | Defines an interface that allows the bridge to mint NFTs. | | `IEVMBridgeTokenMinter` | Defines an interface that allows the bridge to mint FTs. | | `IFlowEVMNFTBridge` | Defines core methods for bridging NFTs. | | `IFlowEVMTokenBridge` | Defines core methods for bridging FTs. | | `FlowEVMBridge` | The main entrypoint for briding tokens across Flow VMs. | | `FlowEVMBridgeAccessor` | Defines methods to route bridge requests from the EVM contract to the Flow-EVM bridge contract. | | `FlowEVMBridgeConfig` | Used to store configuration options for the VM Bridge. | | `FlowEVMBridgeCustomAssociations` | Stores configuration information about custom bridged asset configurations. | | `FlowEVMBridgeCustomAssociationTypes` | Defines interfaces used to specify custom bridged asset associations. | | `FlowEVMBridgeHandlers` | Defines mechanisms to handle assets with custom associations (Deprecated). | | `FlowEVMBridgeNFTEscrow` | Handles locking of NFTs that are bridged from Flow to EVM and back. | | `FlowEVMBridgeResolver` | Defines methods to resolve Metadata Views for bridged assets. | | `FlowEVMBridgeTemplates` | Serves Cadence code from chunked templates for bridge-deployed assets. | | `FlowEVMBridgeTokenEscrow` | Handles locking of FTs that are bridged from Flow to EVM and back. | | `FlowEVMBridgeUtils` | Defines many different utility methods that are used by bridge contracts. | | `ArrayUtils` | Provides useful utility functions for array manipulation. | | `ScopedFTProviders` | Provides utilities to create provider capabilities for tokens that are restricted to a specific amount. | | `Serialize` | Provides utilities to serialize common types to JSON compatible strings. | | `SerializeMetadata` | Provides methods to serialize NFT metadata as a JSON compatible string. | | `StringUtils` | Provides useful utility functions for string manipulation. | # EVM bridge Solidity contracts There are also Solidity contracts that are deployed in Flow EVM that are needed for the bridge. Here are their addresses: | Contracts | Testnet | Mainnet | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `FlowEVMBridgeFactory.sol` | [`0xf8146b4aef631853f0eb98dbe28706d029e52c52`] | [`0x1c6dea788ee774cf15bcd3d7a07ede892ef0be40`] | | `FlowEVMBridgeDeploymentRegistry.sol` | [`0x8781d15904d7e161f421400571dea24cc0db6938`] | [`0x8fdec2058535a2cb25c2f8cec65e8e0d0691f7b0`] | | `FlowEVMBridgedERC20Deployer.sol` | [`0x4d45CaD104A71D19991DE3489ddC5C7B284cf263`] | [`0x49631Eac7e67c417D036a4d114AD9359c93491e7`] | | `FlowEVMBridgedERC721Deployer.sol` | [`0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56`] | [`0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df`] | And below are the bridge escrow's EVM addresses. These addresses are [`CadenceOwnedAccount`s (COA)] and they are stored in the same Flow account as you'll find the Cadence contracts (see above). | Network | Address | | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Testnet | [`0x0000000000000000000000023f946ffbc8829bfd`] | | Mainnet | [`0x00000000000000000000000249250a5c27ecab3b`] | [the bridge repo]: https://github.com/onflow/flow-evm-bridge [the bridge guides]: ../../../blockchain-development-tutorials/cross-vm-apps/vm-bridge.md [`0xf8146b4aef631853f0eb98dbe28706d029e52c52`]: https://evm-testnet.flowscan.io/address/0xF8146B4aEF631853F0eB98DBE28706d029e52c52 [`0x1c6dea788ee774cf15bcd3d7a07ede892ef0be40`]: https://evm.flowscan.io/address/0x1C6dEa788Ee774CF15bCd3d7A07ede892ef0bE40 [`0x8781d15904d7e161f421400571dea24cc0db6938`]: https://evm-testnet.flowscan.io/address/0x8781d15904d7e161f421400571dea24cc0db6938 [`0x8fdec2058535a2cb25c2f8cec65e8e0d0691f7b0`]: https://evm.flowscan.io/address/0x8FDEc2058535A2Cb25C2f8ceC65e8e0D0691f7B0 [`0x4d45CaD104A71D19991DE3489ddC5C7B284cf263`]: https://evm-testnet.flowscan.io/address/0x4d45CaD104A71D19991DE3489ddC5C7B284cf263 [`0x49631Eac7e67c417D036a4d114AD9359c93491e7`]: https://evm.flowscan.io/address/0x49631Eac7e67c417D036a4d114AD9359c93491e7 [`0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56`]: https://evm-testnet.flowscan.io/address/0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56 [`0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df`]: https://evm.flowscan.io/address/0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df [`CadenceOwnedAccount`s (COA)]: https://developers.flow.com/blockchain-development-tutorials/cross-vm-apps/interacting-with-coa#coa-interface [`0x0000000000000000000000023f946ffbc8829bfd`]: https://evm-testnet.flowscan.io/address/0x0000000000000000000000023f946FFbc8829BFD [`0x00000000000000000000000249250a5c27ecab3b`]: https://evm.flowscan.io/address/0x00000000000000000000000249250a5C27Ecab3B --- # File: /md/build/cadence/differences-vs-evm.md --- sidebar_position: 2 title: Differences vs. EVM sidebar_custom_props: icon: ↔️ description: Understand the key differences between Flow and EVM-based blockchains. Learn about Flow's unique account model, Cadence programming language, and transaction structure compared to Ethereum. keywords: - Flow vs EVM - Cadence vs Solidity - blockchain differences - account model - smart contracts - transactions - Flow nodes - development tools - Flow architecture - blockchain comparison - Flow capabilities - resource orientation - Flow security model - blockchain SDK - developer tools --- # Differences vs. EVM Flow [Cadence] is designed with many improvements over prior blockchain networks. As a result, you'll notice many differences between Flow vs. other blockchains, especially Ethereum. This document will be most useful to developers who are already familiar with building on the EVM, but contains details useful to all developers. Check out [Why Flow] for a more general overview of the Flow blockchain. :::tip Remember, Flow also supports full [EVM] equivalence! To start, you can move over your current contracts, then start to build new features that take advantage of the power of Cadence. ::: ## The Flow Cadence account model Key pairs establish ownership on blockchains. In other blockchains (such as Bitcoin and Ethereum), the user's address is also calculated based on their public key, which establishes a unique one-to-one relationship between accounts (addresses) and public keys. This also means there is no concrete "account creation" process other than to generate a valid key pair. With the advent of smart contracts, Ethereum introduced a new account type to deploy contracts that can use storage space (for example, to store contract bytecode). You can learn more about the distinction between EOA and Contract [accounts on Ethereum]. The [Flow account model] combines the concepts of EOAs and Contract Accounts into a single account model and decouples accounts and public keys. Flow accounts are associated with one or more public keys of various weights that specify interested parties that need to produce valid cryptographic signatures for each transaction authorized by that account. ![Screenshot 2023-08-16 at 16.43.07.png](../basics/_accounts_images/Screenshot_2023-08-16_at_16.43.07.png) This natively allows interesting use cases, like key revocation, rotation, and multi-signature transactions. All Flow accounts can use network storage (for example, to deploy contracts and store resources like NFTs) based on the number of FLOW tokens they hold. :::warning You must run an explicit account creation transaction on Flow to create a new account. [Flow CLI] can create an account on any network with a given public key. This requires a [very small fee] to be paid in FLOW. ::: Another key difference is that [storage] for data and assets related to an account are stored in the account, **not** in the contract as with the EVM. Check out the [Accounts] concept document to learn more about Flow accounts. ## Smart contracts On Flow, smart contracts can be written in [Cadence], or Solidity. Cadence syntax is user-friendly and inspired by modern languages like Swift. Notable features of Cadence that make it unique and the key power of the Flow blockchain are: - **Resource-oriented**: Cadence introduces a new type called Resources. Resources enable onchain representation of digital assets natively and securely. Resources can only exist in one location at a time and are strictly controlled by the execution environment to avoid common mishandling mistakes. Each resource has a unique `uuid` associated with it on the blockchain. Examples of usage are fungible tokens, NFTs, or any custom data structure representing a real-world asset. Check out [Resources] to learn more. - **Capability-based**: Cadence offers a [Capability-based Security] model. This also allows the use of Resources as structures to build access control. Capabilities and [Entitlements] can provide fine-grained access to the underlying objects for better security. For example, when users list an NFT on a Flow marketplace, they create a new Capability to the stored NFT in their account so the buyer can withdraw the asset when they provide the tokens. Check out [Capability-based Access Control] to learn more about Capabilities on Cadence. :::warning Cadence is not compiled. All contracts are public and unobfuscated on Flow. This isn't that different from the EVM, where it's trivial to decompile a contract back into Solidity. ::: Check out the [Cadence] website to learn the details of the Cadence programming language. If you are a Solidity developer, we recommend you start with Cadence's [Guide for Solidity Developers] to dive deeper into the differences between the two languages. Here are some additional resources that can help you get started with Cadence: - [The Cadence tutorial] - ERC-20 equivalent on Flow is the Flow Fungible Token Standard - [Flow FT Repository] - [FT Tutorial] - ERC-721 equivalent on Flow is the Flow Non-Fungible Token Standard - [Flow NFT Repository] - [NFT Tutorial] - Asset marketplaces with Cadence - [Marketplace Setup Tutorial] - [NFT Storefront] is an example marketplace standard ## Transactions and scripts To interact with the state on most other blockchains, you can cryptographically authorize smart contract function calls. On Flow, transactions offer rich functionality through Cadence code. This allows you to seamlessly combine multiple contracts and function calls into a single transaction that updates the blockchain state - which all execute together as one unified operation. Here is a sample transaction that mints an NFT from `ExampleNFT` contract on Testnet: ```cadence import NonFungibleToken from 0x631e88ae7f1d7c20 import ExampleNFT from 0x2bd9d8989a3352a1 /// Mints a new ExampleNFT into recipient's account transaction(recipient: Address) { /// Reference to the receiver's collection let recipientCollectionRef: &{NonFungibleToken.Collection} /// Previous NFT ID before the transaction executes let mintingIDBefore: UInt64 prepare(signer: &Account) { self.mintingIDBefore = ExampleNFT.totalSupply // Borrow the recipient's public NFT collection reference self.recipientCollectionRef = getAccount(recipient) .capabilities.get<&{NonFungibleToken.Collection}>(ExampleNFT.CollectionPublicPath) .borrow() ?? panic("The recipient does not have a NonFungibleToken Receiver at " .concat(ExampleNFT.CollectionPublicPath.toString()) .concat(" that is capable of receiving an NFT.") .concat("The recipient must initialize their account with this collection and receiver first!")) } execute { let currentIDString = self.mintingIDBefore.toString() // Mint the NFT and deposit it to the recipient's collection ExampleNFT.mintNFT( recipient: self.recipientCollectionRef, name: "Example NFT #".concat(currentIDString), description: "Example description for #".concat(currentIDString), thumbnail: "https://robohash.org/".concat(currentIDString), royalties: [] ) } post { self.recipientCollectionRef.getIDs().contains(self.mintingIDBefore): "The next NFT ID should have been minted and delivered" ExampleNFT.totalSupply == self.mintingIDBefore + 1: "The total supply should have been increased by 1" } } ``` ### Authorize transactions The process to authorize a transaction on Flow Cadence is more complex, but also much more powerful than an EVM transaction: - [Accounts] can have multiple keys with different weights. - Multiple accounts can sign a single transaction (`prepare` takes any number of arguments). - Transaction computation fees can be paid by a different account, called the `Payer` account. - The [transaction nonce] is provided by the `Proposer` account. This enables rate control and order to be dictated by a different party if needed. - All of the above roles can be the same account. The same powerful concept also exists to query the blockchain state with Scripts. Here is a sample script that fetches the `ExampleNFT` IDs owned by a given account on Testnet: ```cadence /// Script to get NFT IDs in an account's collection import NonFungibleToken from 0x631e88ae7f1d7c20 import ExampleNFT from 0x2bd9d8989a3352a1 access(all) fun main(address: Address, collectionPublicPath: PublicPath): [UInt64] { let account = getAccount(address) let collectionRef = account .capabilities.get<&{NonFungibleToken.Collection}>(collectionPublicPath) .borrow() ?? panic("The account with address " .concat(address.toString()) .concat("does not have a NonFungibleToken Collection at " .concat(ExampleNFT.CollectionPublicPath.toString()) .concat(". The account must initialize their account with this collection first!"))) return collectionRef.getIDs() } ``` Check out [Transactions] and [Scripts] to learn more about the concepts. You can also read the Cadence language reference on [Transactions] to dive deeper. ## Flow nodes Developers need a blockchain node to send transactions and fetch state. Flow is based on a multi-node architecture that separates tasks like consensus and computation into separate nodes. You can learn more about the Flow architecture in the [Flow Primer]. Access Nodes are the node type that are most useful for developers, as they provide access to the Flow network [via an API]. ## SDKs and tools If you're already familiar with blockchain development, here's a comparison between popular software packages and Flow's tooling: - [Hardhat] / [Truffle] / [Foundry] - [Flow CLI] provides local development tools and the [Flow Emulator]. - [OpenZeppelin] - [Emerald OZ] - [go-ethereum] - [Flow Go SDK] - [FCL] also provides Backend API for Flow in JS. - [web3.js] - [FCL] - [flow-cadut] provides more utilities to use Flow on Web. - [Remix] - [Flow Playground] provides basic experimentation on the web - [Cadence VSCode Extension] is strongly suggested to install for local development. - [Testing Smart Contracts] - [Cadence testing framework] allows native tests in Cadence. - [overflow] for testing in Go. [Why Flow]: ../../flow.md [EVM]: ../../../build/evm/quickstart.md [accounts on Ethereum]: https://ethereum.org/en/developers/docs/accounts [Flow CLI]: ../../../build/tools/flow-cli/accounts/create-accounts.md [very small fee]: ../basics/fees.md#fee-structure [Flow account model]: ../basics/accounts.md [Accounts]: ../basics/accounts.md [storage]: ../basics/accounts.md#storage [Cadence]: https://cadence-lang.org/ [Resources]: https://cadence-lang.org/docs/language/resources [Capability-based Security]: https://en.wikipedia.org/wiki/Capability-based_security [Entitlements]: https://cadence-lang.org/docs/language/access-control#entitlements [Capability-based Access Control]: https://cadence-lang.org/docs/language/capabilities [Guide for Solidity Developers]: https://cadence-lang.org/docs/solidity-to-cadence [The Cadence tutorial]: https://cadence-lang.org/docs/tutorial/first-steps [transaction nonce]: https://ethereum.org/en/developers/docs/accounts/#an-account-examined [Transactions]: ../basics/transactions.md [Scripts]: ../basics/scripts.md [Transactions]: https://cadence-lang.org/docs/language/transactions [Flow Primer]: https://flow.com/primer#primer-how-flow-works [via an API]: ../../../protocol/flow-networks/index.md [Flow FT Repository]: https://github.com/onflow/flow-ft [FT Tutorial]: https://cadence-lang.org/docs/tutorial/fungible-tokens [Flow NFT Repository]: https://github.com/onflow/flow-nft [NFT Tutorial]: https://cadence-lang.org/docs/tutorial/non-fungible-tokens-1 [Marketplace Setup Tutorial]: https://cadence-lang.org/docs/tutorial/marketplace-setup [NFT Storefront]: https://github.com/onflow/nft-storefront/ [Hardhat]: https://hardhat.org/) [Truffle]: https://trufflesuite.com/) [Foundry]: https://getfoundry.sh/) [Flow CLI]: https://github.com/onflow/flow-cli/) [Flow Emulator]: https://github.com/onflow/flow-emulator) [OpenZeppelin]: https://www.openzeppelin.com/) [Emerald OZ]: https://oz.ecdao.org/overview) [go-ethereum]: https://geth.ethereum.org/) [Flow Go SDK]: https://github.com/onflow/flow-go-sdk/) [FCL]: https://github.com/onflow/fcl-js/) [web3.js]: https://github.com/web3/web3.js) [flow-cadut]: https://github.com/onflow/flow-cadut) [Remix]: https://remix.ethereum.org/) [Flow Playground]: https://play.flow.com/) [Cadence VSCode Extension]: https://marketplace.visualstudio.com/items?itemName=onflow.cadence) [Testing Smart Contracts]: https://ethereum.org/en/developers/docs/smart-contracts/testing/) [Cadence testing framework]: https://cadence-lang.org/docs/testing-framework) [overflow]: https://github.com/bjartek/overflow) --- # File: /md/build/cadence/quickstart.mdx --- title: Quickstart ↙ sidebar_position: 1 --- # Quickstart Go to [Quickstart](../../blockchain-development-tutorials/cadence/getting-started) import {Redirect} from '@docusaurus/router'; ; --- # File: /md/build/cadence/smart-contracts/best-practices/contract-upgrades.md --- title: Contract Upgrades with Incompatible Changes sidebar_position: 4 description: Learn best practices for handling incompatible contract upgrades on Flow. Understand the risks and recommended approaches for upgrading smart contracts while maintaining data integrity. keywords: - contract upgrades - smart contracts - incompatible changes - contract migration - upgrade strategy - contract deployment - Flow blockchain - contract versioning - data migration - contract paths - resource management - upgrade transactions - contract compatibility - best practices - contract security --- # Contract Upgrades with Incompatible Changes ## Problem I have an incompatible upgrade for a contract. How can I deploy this? ## Solution Please don't perform incompatible upgrades between contract versions in the same account. There is too much that can go wrong. You can make [compatible upgrades] and then run a post-upgrade function on the new contract code if needed. If you must replace your contract rather than update it, the simplest solution is to add or increase a suffix on any named paths in the contract code (for example, `/public/MyProjectVault` becomes `/public/MyProjectVault002`) in addition to making the incompatible changes, then create a new account and deploy the updated contract there. ⚠️ Flow identifies types relative to addresses, so you will also need to provide _upgrade transactions_ to exchange the old contract's resources for the new contract's ones. Make sure to inform users as soon as possible when and how they will need to perform this task. If you absolutely must keep the old address when you make an incompatible upgrade, then you do so at your own risk. Make sure you perform the following actions in this exact order: 1. Delete any resources used in the contract account, such as an Admin resource. 2. Delete the contract from the account. 3. Deploy the new contract to the account. ⚠️ If any user accounts contain `structs` or `resources` from the _old_ version of the contract that have been replaced with incompatible versions in the new one, **they will not load and will cause transactions that attempt to access them to crash**. For this reason, after any users have received `structs` or `resources` from the contract, do not attempt an incompatible upgrade with this method! [compatible upgrades]: https://cadence-lang.org/docs/language/contract-updatability --- # File: /md/build/cadence/smart-contracts/best-practices/project-development-tips.md --- title: Flow Smart Contract Project Development Standards sidebar_label: Development Standards sidebar_position: 5 description: Learn best practices for organizing and managing Cadence smart contract projects. Understand key aspects of design, development, testing, deployment, and community engagement. keywords: - development standards - smart contracts - project management - best practices - Cadence development - testing standards - documentation - deployment process - project organization - code review - security practices - community engagement - open source - technical leadership - Flow development --- # Smart Contract Project Development Standards ## Context Smart Contracts are the bedrock piece of security for many important parts of the Flow blockchain, as well as for any project that is deployed to a blockchain. They are also the most visible technical parts of any project, since users will query them for data, build other smart contracts that interact with them, and use them as materials to learn and templates for future projects. Furthermore, when deployed they are publicly available code on the blockchain and often also in public Github repos. Therefore, the process to design, build, test, document, and manage these projects needs to reflect the critical importance they hold in the ecosystem. Every software project strikes a balance between effort spent on product or feature delivery versus the many other demands of the software development lifecycle, whether testing, technical debt, automation, refactoring, or documentation. Since we build in Web3, we face the same trade-offs, but in a higher risk and consequence environment than what is typical for most software. A mismanaged or untested smart contract may result in **significant** financial losses because of overlooked and exploited vulnerabilities. We highly recommend builders adopt these best practices to help mitigate these risks. If they do so, they can build better smart contracts, avoid potential bugs, support user and third-party adoption of their projects, and increase their chances of success as a model for good software design. Additionally, the more projects that adopt good software design and management standards normalizes this behavior, and encourages other projects in the ecosystem to do the same, which creates a healthier and more vibrant community. When you ensure appropriate levels of testing, it results in better smart contracts which have pro-actively modeled threats and engineered against them. dApp builders who ensure appropriate levels of standards adoption ([FungibleToken](https://github.com/onflow/flow-ft), [NFT Metadata], [NFT StoreFront], and so on) amplify the network effects for all in the ecosystem. NFTs in one dApp can be readily consumed by other dApps through onchain events with no new integration required. With your help and participation, we can further accelerate healthy and vibrant network effects across the Flow ecosystem! Some of these suggestions might seem somewhat unnecessary, but it is important to model what a project can do to manage its smart contracts the best so that hopefully all of the other projects follow suit. This also assumes standard software design best practices also apply. Indeed, many of these suggestions are more general software design best practices, but there may be others that are assumed but not included here. ### Implement These Practices This document serves as mostly an outline of best practices the projects should follow. As with all best practices, teams will choose which applies to them and their work process. However, we recommend that teams explicitly define a minimum acceptable set of standards for themselves along with the mechanisms to ensure that they are observed. Some teams may also have their own set of development standards that achieve a similar goal to these. These recommendations are not meant to be the only paths to success, so if a team disagrees with some of these and wants to do things their own way, they are welcome to pursue that. This document just shows some generic suggestions for teams who might not know how they want to manage their project. ## Design Process Smart contracts usually manage a lot of value, have many users, and are difficult to upgrade for a variety of reasons. Therefore, it is important to have a clearly defined design process for the smart contracts before much code is written so that the team can set themselves up for success. Here are some recommendations for how projects can organize the foundations of their projects. ### Projects should ensure that there is strong technical leadership for their smart contracts To develop a dApp requires a clear vision for the role of the smart contract and how it's integrated. Security vulnerabilities may arise from bugs directly in smart contract code (and elsewhere in the system). Asynchronous interaction vectors may lead to forms of malicious abuse, Denial of Service (DOS), and so on in a contract that trigger explosive compute unit costs for the developer or other problems. We recommend that engineers who lead a project and deploy to mainnet understand software and security engineering fundamentals and have been thorough in their Cadence skills development. For more in-depth resources to help learn Cadence, see the [Cadence documentation]. The technical leader should be someone who understands Cadence well and has written Cadence smart contracts before. Production-level smart contracts are not the place for beginners to get their start. It should be this person's responsibility to lead design discussions with product managers and the community, write most of the code and tests, solicit reviews, make requested changes and make sure the project gets completed in a timely manner. The leader should also understand how to sign transactions with the CLI to deploy and upgrade smart contracts, run admin transactions, troubleshoot problems, and so on. If something goes wrong in relation to the smart contract that needs to be handled with a bespoke transaction, it is important that the owner knows how to build and run transactions and scripts safely to address the issues and upgrade the smart contracts. The project should also have a clear plan of succession in case the original owner is not available or leaves the project. It is important that there are others who can fill in who clearly understand the code and requirements so they can give good feedback, perform effective reviews, and make changes where needed. ### Projects should maintain a well-organized open source Repo for their smart contracts As projects like NBA Topshot have shown, when a blockchain product becomes successful others can and do to build on top of what you are doing. Whether that is analytics, tools, or other value adds that could help grow your project ecosystem, composability is key and that depends on open source development. If there isn't already an open source repo, builders should consider creating one. Builders can start from the [the Flow open source template] and make sure all of their repo is set up with some initial documentation for what the repo is for before any code is written. External developers and users should have an easily accessible home page to go to to understand any given project. The repo should also have some sort of high-level design document that lays out the intended design and architecture of the smart contract. The project leads should determine what is best for them to include in the document, but some useful things to include are basic user stories, architecture of the smart contracts, and any questions that still need to be answered about it. Where applicable, diagrams should be made that describe state machines, user flows, etc. - This document should be shared in an issue in the open source repo where the contracts or features are developed, then later moved to the `.README` or another important docs page. A high level design is a key opportunity to model threats and understand the risks of the system. When we collaborate and review designs together, it helps ensure that we capture and address more edge-cases. It's also a lot less effort to iterate on a design than on hundreds of lines of Cadence. ## Development process recommendations ### The development process should be iterative, if possible The project should develop an MVP first, get reviews, and test thoroughly, then add additional features with tests. This ensures that the core features are designed thoughtfully and makes the review process easier because they can focus on each feature one at a time, rather than get overwhelmed by a huge block of code. ### Comments and field or function descriptions are essential! Our experience writing many Cadence smart contracts has taught us how important documentation is. It especially matters what is documented and for whom, and in that way we are no different from any software language. The "why" is super important, if for example something - an event - that happens in one contract leads to outcomes in a different contract. The "what" helps give context, the reason that the code turned out the way it is. The "how" you don't document - you've written the code. Comments should be directed to those who will change the code after you. Write comments at the same time (or even before) the code is written. This helps the developer and reviewers understand the work-in-progress code better, as well as the intentions of the design (for test and review). Comment functions with: - A description - Parameter descriptions - Return value descriptions Top-Level comments and comments for types, fields, events, and functions should use `///` (three slashes) to be recognised by the [Cadence Documentation Generator]. Regular comments within functions should only use two slashes (`//`) ## Test Recommendations Summarized below is a list of test-related recommendations for a typical smart contract project. Popular testing frameworks to use for Cadence are listed here: - Cadence: [Cadence Testing Framework] - Go: [Overflow] The same person who writes the code should also write the tests. They have the clearest knowledge of the code paths and edge cases. Tests should be **mandatory**, not optional, even if the contract is copied from somewhere else. There should be thorough emulator unit tests in the public repo. [See the flow fungible token repo] for an example of unit tests in javascript. Every time there is a new Cadence version or emulator version, make sure to update the repo dependencies to confirm the tests all still pass. Tests should avoid being monolithic; you should set up individual test cases for each part of the contract to test them in isolation There are some exceptions, like contracts that have to run through a state machine to test different cases. Positive and negative cases need to be tested. You should also write integration tests to ensure that your app or backend can interact properly with the smart contracts. ## Manage Project Keys and deployments Smart contract keys and deployments are very important and need to be treated as such. ### Store Private Keys securely Do not keep Private Keys for the contract and admin accounts in plain text format anywhere. Projects should determine a secure solution that works best for them to store their private keys. We recommend that you them in a secure key store such as Google KMS or something similar. ### Handle deployments to Testnet or Mainnet As projects become more successful, communities around them grow. In a trustless ecosystem, that also means more of others building on your contracts. Before you deploy or upgrade a contract, it is important to maintain clear community communications with sufficient notice, since changes will always bring added risk. When you give community members time to review and address issues with upgrades before they happen, it builds trust and confidence in projects. Here are a few suggestions for how to manage a deployment or upgrade: - Communicate to all stake-holders well in advance - Share the proposal with the community at least a week in advance (unless it is a critical bug fix). - Examples of places to share are your project's chat, forum, blog, email list, and so on. - This will allow the community and other stakeholders to have plenty of time to view the upcoming changes and provide feedback if necessary. - Share the time of the deployment and the deployment transaction with branch and commit hash information to ensure the transaction itself is correct. - Coordinate deployment with stakeholders to make sure it is done correctly and on time. ## Responsibilities to the Community Web3 brings tremendous possibilities for engineering applications with trustlessness and composability in mind, and Cadence and Flow offer unique features to achieve this. If every project treats their community and the Flow community with respect and care, the things we can all build together will be very powerful. ### Projects should have thorough documentation Encouraging adoption of project contracts to the broader ecosystem raises the bar around code to provides clear high-level descriptions, with detailed and useful comments within contracts, transactions, and scripts. The more that users can understand a project, that it adheres to standards, and can be built upon with ease, the more likely others will build against it in turn. Each project should have a detailed README.md with these sections: - Explanation of the project itself with links to the app. - Addresses on various networks. - High-level technical description of the contracts with emphasis on important types and functionality - Architecture diagram (if applicable) - Include links to tutorials if they are external - Flow smart contract standards that a project implements Additionally, each contract, transaction, and script should have high-level descriptions at the top of their files. This way, anyone in the community can easily come in and understand what each one is doing without the need to parse confusing code. ### Projects should engage with and respond to their own Community After a contract is deployed, the work doesn't stop there. Project communities require continuous nurturing and support. As the developer of a public project on a public blockchain, the owners have an obligation to be helpful and responsive to the community so that they can encourage composability and third party interactions. - Keep issues open in the repo. - The owner should turn on email notifications for new issue creation in the repo. - Respond to issues quickly and clean up unimportant ones. - Consider blog posts to share more details on technical aspects of the project and upcoming changes. ### Projects should contribute to the greater Flow and Cadence community Flow has a vibrant and growing community of contributors around the world. Through our mutual collaboration, we've had numerous community Flow Improvement Proposals ([FLIP]s) shipped. If you have an interest in a particular improvement for Flow or Cadence, we host open meetings which you are welcome to join (announced on discord) and can participate anytime on any of the FLIPs [already proposed]. Responsible project maintainers should contribute to discussions about important proposals (new cadence features, standard smart contracts, metadata, and so on), and generally be aware about current best practices and anti-pattern knowledge. Projects who contribute to these discussions are able to influence them to ensure that the language and protocol changes are favorable to them and the rest of the app developers in the ecosystem. It also helps the owner to promote the project and themselves. Resources for Best Practices: - [cadence/design-pattern] - [cadence/anti-patterns] - [cadence/security-best-practices] Composability and extensibility should also be priorities while they design, develop, and document their projects. If you have any feedback about these guidelines, create an issue in the [onflow/cadence-style-guide] repo or make a PR to update the guidelines so we can start a discussion. [cadence/design-pattern]: https://cadence-lang.org/docs/design-patterns [cadence/anti-patterns]: https://cadence-lang.org/docs/anti-patterns [cadence/security-best-practices]: ./security-best-practices.md) [FLIP]: https://github.com/onflow/flow/tree/master/flips [already proposed]: https://github.com/onflow/flow/pulls?q=is%3Aopen+is%3Apr+label%3AFLIP [onflow/cadence-style-guide]: https://github.com/onflow/cadence-style-guide [See the flow fungible token repo]: https://github.com/onflow/flow-ft/tree/master/lib/js/test [Cadence Testing Framework]: ../../smart-contracts/testing.md [Overflow]: https://github.com/bjartek/overflow [Cadence Documentation Generator]: https://github.com/onflow/cadence-tools/tree/master/docgen [the Flow open source template]: https://github.com/onflow/open-source-template [Cadence documentation]: https://cadence-lang.org/docs/ [FungibleToken]: https://github.com/onflow/flow-ft [NFT Metadata]: ../../advanced-concepts/metadata-views.md [NFT StoreFront]: https://github.com/onflow/nft-storefront --- # File: /md/build/cadence/smart-contracts/best-practices/security-best-practices.md --- title: Cadence Security Best Practices sidebar_label: Security Best Practices sidebar_position: 3 description: Learn essential security practices for writing secure Cadence smart contracts. Understand how to handle references, account storage, capabilities, transactions, and access control safely. keywords: - security practices - Cadence security - smart contract security - secure coding - reference safety - account storage - capabilities - access control - transaction security - type safety - authorization - secure development - Flow security - best practices - security guidelines --- # Cadence Security Best Practices This is an opinionated list of best practices Cadence developers should follow to write more secure Cadence code. Some practices listed below might overlap with advice in the [Cadence Anti-Patterns] section, which is a recommended read as well. ## References [References] are ephemeral values and cannot be stored. If persistence is required, store a capability and borrow it when needed. References allow free upcasting and downcasting. For example, a restricted type can cast to its unrestricted type, which exposes all `access(all)` functions and fields of the type. So, even if your capability uses an interface to restrict its functionality, it can still downcast to expose all other public functionality. Therefore, any privileged functionality in a resource or struct that will have a public capability needs to have entitled accecss, for example `access(Owner)`. Then, the only way to access that functionality would be through an entitled reference, like ``. ## Account Storage Don't trust a users' [account storage]. Users have full control over their data and may reorganize it as they see fit. Users may store values in any path, so paths may store values of "unexpected" types. These values may be instances of types in contracts that the user deployed. Always [borrow] with the specific type that is expected. Or, check if the value is an instance of the expected type. ## Authorized Accounts Access to an `&Account` gives access to whatever is specified in the account entitlements list when that account reference is created. Therefore, [don't use Account references] as a function parameter or field unless absolutely necessary and only use the minimum set of entitlements required for the specified functionality so that other account functionality cannot be accessed. It is preferable to use capabilities over direct `&Account` references when you expose account data. Capabilities revoke access by unlinking and limits the access to a single value with a certain set of functionality. ## Capabilities Don't store anything under the [public capability storage] unless strictly required. Anyone can access your public capability with `Account.capabilities.get`. If something needs to be stored under `/public/`, restrict privileged functions with entitlements to make sure only read functionality is provided. When you publish a capability, the capability might already be present at the given `PublicPath`. In that case, Cadence will panic with a runtime error to not override the already published capability. It is a good practice to check if the public capability already exists with `account.capabilities.get().check` before you create it. This function will return `nil` if the capability does not exist. If there's a case where borrowing a capability might fail, use the `account.check` function to verify that the target exists and has a valid type. Ensure that unauthorized parties cannot access capabilities. For example, capabilities should not be accessible through a public field, such as public dictionaries or arrays. When you expose a capability in such a way, anyone can borrow it and perform all actions that the capability allows. ## Transactions Audits of Cadence code should also include [transactions], as they may contain arbitrary code, just, like in contracts. In addition, they are given full access to the accounts of the transaction's signers, i.e. the transaction is allowed to manipulate the signers' account storage, contracts, and keys. Signing a transaction gives access to the `&Account`, i.e. access to the account's storage, keys, and contracts depending on what entitlements are specified. Do not blindly sign a transaction. The transaction could for example change deployed contracts by upgrading them with malicious statements, revoking or adding keys, transferring resources from storage, etc. ## Types Use [restricted types and interfaces]. Always use the most specific type possible, following the principle of least privilege. Types should always be as restrictive as possible, especially for resource types. If given a less-specific type, cast to the more specific type that is expected. For example, when implementing the fungible token standard, a user may deposit any fungible token, so the implementation should cast to the expected concrete fungible token type. ## Access Control Declaring a field as [`access(all)`] only protects from replacing the field's value, but the value itself can still be mutated if it is mutable. Remember that containers, like dictionaries, and arrays, are mutable. Prefer non-public access to a mutable state. That state may also be nested. For example, a child may still be mutated even if its parent exposes it through a field with non-settable access. Do not use the `access(all)` modifier on fields and functions unless necessary. Prefer `access(self)`, `acccess(Entitlement)`, or `access(contract)` and `access(account)` when other types in the contract or account need to have access. [Cadence Anti-Patterns]: https://cadence-lang.org/docs/design-patterns [References]: https://cadence-lang.org/docs/language/references [account storage]: https://cadence-lang.org/docs/language/accounts#account-storage [borrow]: https://cadence-lang.org/docs/language/capabilities [don't use Account references]: https://cadence-lang.org/docs/anti-patterns#avoid-using-authaccount-as-a-function-parameter [public capability storage]: https://cadence-lang.org/docs/language/capabilities [restricted types and interfaces]: https://cadence-lang.org/docs/language/restricted-types [`access(all)`]: https://cadence-lang.org/docs/language/access-control [transactions]: https://cadence-lang.org/docs/language/transactions --- # File: /md/build/cadence/smart-contracts/deploying.md --- title: Deploying Contracts sidebar_label: Deploying Contracts description: Learn how to deploy and update smart contracts on Flow Mainnet and Testnet. Understand account creation, key management, and deployment best practices. sidebar_position: 3 sidebar_custom_props: icon: 🥇 keywords: - contract deployment - Flow mainnet - Flow testnet - account creation - key management - Flow CLI - smart contracts - deployment guide - contract updates - sporks - network migration - testnet faucet - deployment security - contract addresses - Flow deployment --- # Deploying Contracts Deploying smart contracts to Flow's networks is the final step for you to bring your blockchain application to life. This guide covers everything you need to know to deploy your Cadence contracts to both Flow Testnet and Mainnet, from account creation to contract updates. ## What you'll learn After you complete this guide, you'll be able to: - **Create and fund accounts** on Flow Testnet and Mainnet. - **Deploy contracts** with Flow CLI with proper configuration. - **Update current contracts** and preserve their addresses. - **Understand the differences** between testnet and mainnet deployment. - **Follow security best practices** for production deployments. ## Prerequisites Before you deploy contracts, make sure you have: - **Flow CLI installed** and configured. - **A Flow project** with contracts ready for deployment. - **Basic understanding** of Cadence smart contracts. - **Completed testing** of your contracts locally. ## Deployment Workflow The recommended deployment workflow follows this progression: 1. **Emulator Deployment** - Deploy and test your contracts locally (free, instant). 2. **Testnet Deployment** - Deploy and test your contracts on Flow Testnet (free). 3. **Mainnet Deployment** - Deploy to Flow Mainnet after testing is complete (costs FLOW tokens). 4. **Contract Updates** - Update contracts as needed with the `update` command. This approach ensures your contracts work correctly before you commit real resources to mainnet deployment. ## Deploy to emulator The Flow Emulator is your local development environment where you can deploy and test contracts instantly without any network costs or delays. This is the first step in your deployment journey. ### Start the emulator First, start the [Flow Emulator]. In a second terminal: ```zsh flow emulator start ``` ### Create an emulator account Create a local account for testing: ```zsh flow accounts create --network emulator ``` When prompted: 1. **Account name**: Enter `emulator-account` 2. Select `emulator` as the network when prompted This creates a new account on the emulator and adds it to your `flow.json` configuration. ### Configure emulator deployment Update your `flow.json` to include emulator deployment configuration: ```zsh flow config add deployment ``` Follow the prompts: 1. **Network**: `emulator` 2. **Account**: `emulator-account` 3. **Contract**: `YourContract` 4. **Deploy more contracts**: `no` (or `yes` if you have multiple contracts) Your `flow.json` will now include an emulator deployment section: ```json { "deployments": { "emulator": { "emulator-account": ["YourContract"] } } } ``` ### Deploy contract to emulator Deploy your contract to the local emulator: ```zsh flow project deploy --network emulator ``` :::warning You cannot deploy the same contract to multiple accounts on the same network with one deployment command. If you attempt to do so, you will see: ❌ Command Error: the same contract cannot be deployed to multiple accounts on the same network Edit `flow.json` to remove the duplicate. ::: You will see output similar to: ```zsh Deploying 1 contracts for accounts: emulator-account YourContract -> 0xf8d6e0586b0a20c7 (contract deployed successfully) 🎉 All contracts deployed successfully ``` ### Test your emulator deployment Verify your contract works via these scripts and transactions: ```zsh # Run a script to read contract state flow scripts execute cadence/scripts/YourScript.cdc --network emulator # Send a transaction to interact with your contract flow transactions send cadence/transactions/YourTransaction.cdc --network emulator --signer emulator-account ``` :::info The emulator provides instant feedback and is perfect for rapid development and testing. All transactions are free and execute immediately. ::: ## Deploy to Testnet For a more complete quickstart, visit the [Getting Started] guide. - You should test your contracts, transactions and scripts on Testnet, have strong smart contract test coverage and follow the additional guidelines set out here: [Smart Contract Testing Guidelines]. - Use `flow init` to [Create a Project] if you need one to practice deployment with. ### Create a Testnet account First, you'll need a testnet account to deploy your contracts. Create one with: ```zsh flow accounts create --network testnet ``` :::info For security reasons, Flow Cadence does not allow accounts to have the same address on testnet, mainnet, and/or the emulator. :::: When prompted: 1. **Account name**: Enter `testnet-account` 2. Select `testnet` as the network when prompted This creates a new account on testnet and adds it to your `flow.json` configuration. It also saves the private key for the new account in `.pkey` and uses this file to import the key because `flow.json` is visible in the repo. :::danger As with any other blockchain network, **anyone** with access to the private key for an account can access that account at any time without your knowledge. ::: ### Fund your Testnet account To deploy contracts and send transactions on testnet, you need FLOW tokens. Flow provides a faucet service to get free testnet tokens. ```zsh flow accounts fund testnet-account ``` This will open the faucet in your browser. You can also navigate there manually. 1. Visit the [Testnet Faucet]. 2. Enter your testnet account address. 3. Complete any required verification (captcha, and so on). 4. Request tokens (you'll receive 100000 testnet FLOW tokens). Check your account balance: ```zsh flow accounts list ``` You will see your account details with a balance of FLOW tokens. ### Configure Testnet deployment Update your `flow.json` to include testnet deployment configuration: ```zsh flow config add deployment ``` Follow the prompts: 1. **Network**: `testnet` 2. **Account**: `testnet-account` 3. **Contract**: `YourContract` 4. **Deploy more contracts**: `no` (or `yes` if you have multiple contracts) Your `flow.json` will now include a testnet deployment section: ```json { "deployments": { "testnet": { "testnet-account": ["YourContract"] } } } ``` ### Deploy contract to Testnet Deploy your contract to the public testnet: ```zsh flow project deploy --network testnet ``` You will see output similar to: ```zsh Deploying 1 contracts for accounts: testnet-account YourContract -> 0x9942a81bc6c3c5b7 (contract deployed successfully) 🎉 All contracts deployed successfully ``` ## Deploy to Mainnet After you've successfully tested your contracts on testnet, you can deploy to mainnet. You'll need a mainnet account with real FLOW tokens. ### Create a Mainnet account For mainnet, you'll need to acquire FLOW tokens through exchanges or other means, as there's no faucet. ```zsh flow accounts create --network mainnet ``` When prompted: 1. **Account name**: Enter `mainnet-account` 2. **Select "Mainnet" Network** ### Acquire FLOW tokens You can purchase FLOW tokens from major exchanges. Make sure your mainnet account has sufficient FLOW tokens to cover deployment costs. Flow is a very efficient network, so even 1.0 FLOW is sufficient to deploy large numbers of contracts. ### Configure Mainnet deployment Add mainnet deployment configuration to your `flow.json`: ```zsh flow config add deployment --network mainnet ``` Follow the prompts: 1. **Network**: `mainnet` 2. **Account**: `mainnet-account` 3. **Contract**: `YourContract` 4. **Deploy more contracts**: `no` (or `yes` if you have multiple contracts) Your `flow.json` will now include mainnet configuration: ```json { "deployments": { "mainnet": { "mainnet-account": ["YourContract"] } } } ``` ### Deploy to Mainnet Deploy your contracts to mainnet: ```zsh flow project deploy --network mainnet ``` :::warning This deployment costs (a relatively small amount of) real FLOW tokens and you cannot undo it. You can, however, redeploy your contracts to update them, or delete them. ::: You will see output similar to: ```zsh Deploying 1 contracts for accounts: mainnet-account YourContract -> 0xABC123DEF456789 (contract deployed successfully) 🎉 All contracts deployed successfully ``` :::info All your contract deployment addresses are stored in `flow.json`. Mainnet, Testnet and local (emulator) are stored as well. ::: ## Deploy updated contracts on mainnet You can update contracts and retain the contract address. To do this, use the [Flow CLI contract update command] to redeploy an updated version of your contract: ```zsh flow accounts update-contract ./YourContract.cdc --signer mainnet-account --network mainnet ``` [Flow CLI]: ../../../build/tools/flow-cli/install [Getting Started]: ../../../blockchain-development-tutorials/cadence/getting-started/smart-contract-interaction [Smart Contract Testing Guidelines]: ./testing.md [Create a Project]: ../../../build/tools/flow-cli/index.md [Flow CLI contract update command]: ../../../build/tools/flow-cli/accounts/account-update-contract.md [Flow CLI get account command]: ../../../build/tools/flow-cli/accounts/get-accounts.md [Sporks]: ../../../protocol/node-ops/node-operation/network-upgrade [Flow Emulator]: ../../../build/tools/emulator [Testnet Faucet]: https://faucet.flow.com/ [core contracts]: ../core-contracts/index.md [some code examples from the Flow Go SDK]: https://github.com/onflow/flow-go-sdk/tree/master/examples [Forum]: https://forum.flow.com/ [Discord]: https://discord.com/invite/J6fFnh2xx6 --- # File: /md/build/cadence/smart-contracts/learn-cadence.md --- sidebar_position: 1 slug: /build/cadence/learn-cadence title: Learn Cadence ↗️ --- ; --- # File: /md/build/cadence/smart-contracts/overview.md --- title: Smart Contracts on Flow sidebar_label: Smart Contracts on Flow sidebar_position: 2 sidebar_custom_props: icon: 🛠️ description: Learn about smart contract development on Flow blockchain. Understand data storage, standards implementation, and best practices for building decentralized applications using Cadence. keywords: - smart contracts - Flow blockchain - Cadence - dApp development - NFT standards - fungible tokens - contract storage - IPFS storage - blockchain data - Flow standards - contract interfaces - development tools - Flow CLI - Flow emulator - decentralized apps --- # Smart Contracts on Flow At its core, a decentralized application is defined by the [smart contracts] it uses on the blockchain. Rather than rely on centralized application servers and databases, apps model their core application logic with smart contracts, often referred to as the "onchain" code. It is therefore helpful to develop a clear model for your app that takes into account the data and logic that will exist in your smart contracts. In particular, it is important to differentiate between the parts of your app that must live on chain and those that should live off chain. ## How to write smart contracts on Flow Smart contracts on the Flow blockchain are implemented in [Cadence], a resource-oriented programming language specifically designed for smart contract development. ### Onboard to Cadence To get started with Cadence, we recommended that you cover the introductory tutorials available in the [Flow Playground], a simple web IDE designed for you to learn Cadence. ### Configure your local environment To build confidently, you will want to set up the appropriate local environment and have an adequate test suite to ensure your smart contracts operate as intended. To do this, familiarize yourself with the following tools: - [Flow CLI]: A utility to directly interact with the chain and manage accounts and contracts. - [Flow Emulator] A lightweight server that simulates the Flow blockchain (strongly recommended during development). - [Flow Dev Wallet] A utility to simulate user wallets in development. - [Visual Studio Code Extension] An IDE integration used to develop smart contracts. ## Store data on Flow All apps will store important data on the blockchain, and some more than others -- especially NFT apps. You'll want to consider the following when you store data on the Flow blockchain. ### What does your data need to represent? Permanence is a key property of blockchains; users trust that the data they store will continue to exist for years to come, and this is a defining characteristic of assets like NFTs. Therefore, well-designed digital assets store the information necessary to retain their value without external dependencies. ### Storage limits and fees However, there are practical constraints to data storage on a blockchain. Developer and user accounts must retain a small amount of FLOW tokens, known as the storage fee, for bytes of data stored in their accounts. The minimum storage fee will grant each account a minimum storage amount. If an account holds assets that demand more bytes of storage, the account will need to retain more FLOW tokens to increase the storage amount according to Flow's [fee schedule]. A more compact data model can keep storage needs down. Furthermore, a single Flow transaction has a size limit of 4MB, which limits the rate at which large amounts of data can be transferred to the blockchain. Lastly, a blockchain is not a content delivery network and therefore cannot serve media assets, such as videos, at the speeds expected by modern applications. For these reasons, it usually isn't practical to store large media assets such as videos and high-definition images on the Flow blockchain. Instead, consider an external storage solution. ### External storage networks Decentralized storage networks such as IPFS allow you to store large digital assets off chain, but with no need to rely on centralized servers. Instead of save an entire asset to the Flow blockchain, you can save the content hash (known as a CID on IPFS) on the blockchain and then store the source file off-chain. This way, users can verify that the media file matches the digital asset. IPFS files can be uploaded via a pinning service such as Pinata; see their [NFT tutorial] for an example of how to use Pinata with Flow. IPFS files are served through [gateways], many of which leverage caching to provide fast response times. Cloudflare provides a [public IPFS Gateway], and Pinata also supports [dedicated gateways with custom domains]. ## Use current standards The Flow blockchain has smart contract standards for both fungible and non-fungible tokens that you should implement when you build your contracts. ### Non-Fungible Tokens (NFTs) All NFTs on the Flow blockchain implement the [NonFungibleToken] interface, which allows them to be compatible with wallets, marketplaces and other cross-app experiences. See the [NFT Guide] for a guide on how to create a basic NFT contract that conforms to the standard. - [NonFungibleToken] (NFT) contract interface ### NFT sales and trading Flow has a standard contract to facilitate both the direct sales and peer-to-peer trading of NFTs. The NFT storefront contract is useful for apps that want to provide an NFT marketplace experience. - [NFT Storefront contract] ### Fungible Tokens Fungible tokens (that is, coins, currencies) on the Flow blockchain, which includes the default cryptocurrency token FLOW, implement the [FungibleToken] interface. See the [FT Guide] for a guide on how to create a basic fungible token contract that conforms to the standard. - [FungibleToken] contract interface: [smart contracts]: https://en.wikipedia.org/wiki/Smart_contract [Cadence]: https://github.com/onflow/cadence [Flow Playground]: https://play.flow.com/ [Flow CLI]: ../../../build/tools/flow-cli/index.md [Flow Emulator]: ../../../build/tools/emulator/index.md [Flow Dev Wallet]: https://github.com/onflow/fcl-dev-wallet/ [Visual Studio Code Extension]: ../../../build/tools/vscode-extension/index.md [fee schedule]: ../basics/fees.md#storage [NFT tutorial]: https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf [gateways]: https://docs.ipfs.io/concepts/ipfs-gateway/ [public IPFS Gateway]: https://developers.cloudflare.com/distributed-web/ipfs-gateway [dedicated gateways with custom domains]: https://medium.com/pinata/announcing-dedicated-ipfs-gateways-60f599949ce [NonFungibleToken]: ../core-contracts/08-non-fungible-token.md [NFT Guide]: ../../../blockchain-development-tutorials/tokens/nft-cadence.md [NFT Storefront contract]: https://github.com/onflow/nft-storefront [FungibleToken]: ../core-contracts/02-fungible-token.md [FT Guide]: ../../../blockchain-development-tutorials/tokens/fungible-token-cadence.md --- # File: /md/build/cadence/smart-contracts/testing-strategy.md --- title: Testing Smart Contracts sidebar_label: Testing Smart Contracts sidebar_position: 3 description: A layered testing strategy for Flow—unit tests, forked integration, and a forked emulator sandbox. Guidance for reproducibility and simple CI setup. keywords: - testing strategy - unit testing - integration testing - fork testing - flow test --fork - flow emulator --fork - emulator - testnet - CI pipeline - continuous integration - reproducibility - fork-height - block height pinning - test selection - smoke tests - E2E testing - account impersonation - test troubleshooting - golden files - test automation - Flow CLI - Cadence tests - spork boundaries --- # Testing Smart Contracts This document describes a single, pragmatic strategy to test on Flow. Use layers that are deterministic and isolated by default, add realism with forks when needed, and keep a minimal set of live network checks before release. ## At a glance - **Unit & Property — Test Framework**: Hermetic correctness and invariants. - **Integration — Fork Testing**: Real contracts and data; mutations stay local. - **Local integration sandbox (interactive, `flow emulator --fork`)**: Drive apps/E2E against production-like state. - **Staging (testnet)**: Final plumbing and config checks. - **Post-deploy (read-only)**: Invariant dashboards and alerts. ## Layers ### Unit and property — test framework - Use `flow test` - **Use when**: You validate Cadence logic, invariants, access control, error paths, footprint. - **Why**: Fully deterministic and isolated; highest-regression signal. - **Run**: Every commit/PR; wide parallelism. - **Notes**: Write clear success andfailure tests, add simple “this should always hold” rules when helpful, and avoid external services. See also: [Running Cadence Tests]. ### Integration — fork testing - **Use when**: You interact with real on-chain contracts or data (FTand NFT standards, AMMs, wallets, oracles, bridges), upgrade checks, historical repro. - **Why**: Real addresses, capability paths, and resource schemas; catches drift early. - **Run**: On Pull Requests (PRs), run the full forked suite if practical (pinned), or a small quick set; run more cases nightly or on main. - **How**: Configure with `#test_fork(network: "mainnet", height: nil)` in your test file, or use `flow test --fork` CLI flags. - **Notes**: - Pin with `height: 85432100` in the pragma (or `--fork-height` CLI flag) where reproducibility matters. - Prefer local deployment + impersonation over real mainnet accounts. - Mutations are local to the forked runtime; the live network is never changed. - Be mindful of access-node availability and rate limits. - External oracles and protocols: forked tests do not call off-chain services or other chains; mock these or run a local stub. See also: [Fork Testing with Cadence], [Fork Testing Flags]. ### Local integration sandbox — `flow emulator --fork` - **Use when**: You drive dApps, wallets, bots, indexers, or exploratory debugging outside the test framework. - **Why**: Production-like state with local, disposable control; great for end to end (E2E) and migrations. - **Run**: Dev machines and focused E2E CI jobs. - **Notes**: - Pin height; run on dedicated ports; impersonation is built-in; mutations are local; off-chain/oracle calls are not live—mock or run local stubs - What to run: Manual exploration and debugging of flows against a forked state; frontend connected to the emulator (for example, `npm run dev` pointed at `http://localhost:8888`); automated E2E/FE suites (for example, Cypress or Playwright) against the local fork; headless clients, wallets/bots/indexers, and migration scripts. - Not for the canonical Cadence test suite—prefer fork testing with `flow test` for scripted Cadence tests (see [Fork Testing Flags] and [Running Cadence Tests]) Quick start example: ```bash # Start a fork (pinning height recommended for reproducibility) flow emulator --fork mainnet --fork-height ``` ```javascript // In your root component (e.g., App.tsx) import { FlowProvider } from '@onflow/react-sdk'; import flowJSON from './flow.json'; function App() { return ( {/* Your app components */} ); } ``` ```bash # Run app npm run dev # Run E2E tests npx cypress run ``` See also: [Interactive Testing with Forked Emulator], [Flow Emulator]. ### Staging — Testnet - **Use when**: Final network plumbing and configuration checks before release. - **Why**: Validates infra differences you cannot fully simulate. - **Run**: Pre-release and on infra changes. - **Notes**: - Keep canaries minimal and time-boxed; protocol and partner support may be limited on testnet (not all third-party contracts are deployed or up to date). - What to run: Minimal app smoke tests (login and auth, key flows, mint and transfer, event checks); frontend connected to Testnet with a small Cypress/Playwright smoke set; infra or config checks (endpoints, contract addresses oraliases, env vars, service or test accounts) - Not for the canonical Cadence test suite — prefer fork testing with `flow test` for scripted tests (see [Fork Testing Flags] and [Running Cadence Tests]) Quick start example: ```javascript // In your root component (e.g., App.tsx) import { FlowProvider } from '@onflow/react-sdk'; function App() { return ( {/* Your app components */} ); } ``` ```bash # Run app npm run dev # Run smoke tests npx cypress run --spec "cypress/e2e/smoke.*" ``` See also: [Flow Networks]. ### Post-deploy monitoring (read-only) - **Use when**: After releases to confirm invariants and event rates. - **Why**: Detects real-world anomalies quickly. - **Run**: Continuous dashboards and alerts tied to invariants. ## Reproducibility and data management - **Pin where reproducibility matters**: Use `--fork-height ` for both `flow test --fork` and `flow emulator --fork`. Pins are per‑spork; historical data beyond spork boundaries is unavailable. For best results, keep a per‑spork stable pin and also run a "latest" freshness job. - **Named snapshots**: Maintain documented pin heights (for example, in CI vars or a simple file) with names per dependency or protocol - **Refresh policy**: Advance pins via a dedicated “freshness” PR; compare old vs. new pins - **Goldens**: Save a few canonical samples (for example, event payloads, resource layouts, key script outputs) as JSON in your repo, and compare them in CI to catch accidental schema/shape changes. Update the samples intentionally as part of upgrades. ## CI tips - PRs: Run emulator unit or property and forked integration (pinned). Full suite is fine if practical; otherwise a small quick set. - Nightly/Main: Add a latest pin job and expand fork coverage as needed. - E2E (optional): Use `flow emulator --fork` at a stable pin and run your browser tests. ## Test selection and tagging - **Optional naming helpers**: Use simple suffixes in test names like `_fork`, `_smoke`, `_e2e` if helpful. - Pass files and directories to run the tests you care about: `flow test FILE1 FILE2 DIR1 ...` (most common). - Optionally, use `--name ` to match test functions when it’s convenient. - **Defaults**: PRs can run the full fork suite (pinned) or a small quick set; nightly runs broader coverage (and optional E2E). ## Troubleshooting tips - Re-run at the same `--fork-height`, then at latest - Compare contract addresses/aliases in `flow.json` - Diff event or resource shapes against your stored samples - Check access-node health and CI parallelism or sharding ## Dos and Don’ts - **Do**: Keep a fast, hermetic base; pin forks; tag tests; maintain tiny PR smoke sets; document pins and set a simple refresh schedule (for example, after each spork or monthly). - **Don't**: Make "latest" your default in CI; create or rely on real mainnet accounts; conflate fork testing (`flow test`) with the emulator's fork mode (`flow emulator --fork`). ## Related docs - Guide → Running tests: [Running Cadence Tests] - Guide → How-to: [Cadence Testing Framework] - Tutorial → Step-by-step: [Fork Testing with Cadence] - Tool → Emulator (including fork mode): [Flow Emulator] - Reference → Fork testing flags: [Fork Testing Flags] [Running Cadence Tests]: ../../tools/flow-cli/tests.md [Cadence Testing Framework]: ./testing.md [Fork Testing with Cadence]: ../../../blockchain-development-tutorials/cadence/fork-testing/index.md [Interactive Testing with Forked Emulator]: ../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md [Flow Emulator]: ../../tools/emulator/index.md [Fork Testing Flags]: ../../tools/flow-cli/tests.md#fork-testing-flags [Flow Networks]: ../../../protocol/flow-networks/index.md [Network Upgrade (Spork) Process]: ../../../protocol/node-ops/node-operation/network-upgrade.md [Flow CLI Configuration (flow.json)]: ../../tools/flow-cli/flow.json/initialize-configuration.md [Dependency Manager]: ../../tools/flow-cli/dependency-manager.md [Cadence Testing Framework]: https://cadence-lang.org/docs/testing-framework --- # File: /md/build/cadence/smart-contracts/testing.md --- title: Cadence Testing Framework sidebar_label: Cadence Testing Framework description: Learn how to write and run tests for Cadence contracts, scripts, and transactions using the Cadence Testing Framework and Flow CLI. sidebar_position: 4 sidebar_custom_props: icon: 📝 keywords: - Cadence testing - unit tests - code coverage - Flow CLI - test assertions - test helpers --- # Testing Smart Contracts Testing is an essential part of smart contract development to ensure the correctness and reliability of your code. The Cadence Testing Framework provides a convenient way to write tests for your contracts, scripts and transactions which allows you to verify the functionality and correctness of your smart contracts. :::info Looking for high‑level guidance on when to use emulator, forks, or testnet? See [Testing Smart Contracts](./testing-strategy.md). ::: ## Install Flow CLI The [Flow CLI] is the primary tool for you to develop, test, and deploy smart contracts to the Flow network. If you haven't installed the Flow CLI yet and have [homebrew](https://brew.sh/) installed, simply run `brew install flow-cli`. Alternatively, refer to the Flow CLI [installation instructions]. ## Create a new project In your preferred code editor, create a new directory for your project and navigate to it in the terminal. Then initialize a new Flow project with the `flow init` command. This will create a `flow.json` config file that contains the [project's configuration]. ```bash mkdir test-cadence cd test-cadence flow init ``` ## Write a simple smart contract In your code editor, create a new file called `calculator.cdc` and add the following code: ```cadence calculator.cdc access(all) contract Calculator { access(all) fun add(a: Int, b: Int): Int { return a + b } access(all) fun subtract(a: Int, b: Int): Int { return a - b } access(all) fun multiply(a: Int, b: Int): Int { return a * b } } ``` ## Add the smart contract to the config Next up, we need to add our new contract in the `contracts` key in the `flow.json` config file. More specifically, we need to add the contract name, location and an address alias for the `testing` environment. ```json { "contracts": { "Calculator": { "source": "./calculator.cdc", "aliases": { "testing": "0x0000000000000007" } } }, "networks": {...}, "accounts": {...}, "deployments": {...} } ``` For the time being, the address for the `testing` alias, can be one of: - `0x0000000000000005` - `0x0000000000000006` - `0x0000000000000007` - `0x0000000000000008` - `0x0000000000000009` - `0x000000000000000a` - `0x000000000000000b` - `0x000000000000000c` - `0x000000000000000d` - `0x000000000000000e` In the next release, there will be `20` addresses for contract deployment during testing. ## Write unit tests In the same directory, create a new file called `calculator_test.cdc` and add the following code: ```cadence calculator_test.cdc import Test import "Calculator" // contract name from the previous step access(all) fun setup() { let err = Test.deployContract( name: "Calculator", path: "./calculator.cdc", arguments: [] ) Test.expect(err, Test.beNil()) } access(all) fun testAdd() { Test.assertEqual(5, Calculator.add(a: 2, b: 3)) } access(all) fun testSubtract() { Test.assertEqual(2, Calculator.subtract(a: 5, b: 3)) } ``` This code: - imports the `Calculator` contract from the `calculator.cdc` file (according to `flow.json`) - deploys the `Calculator` contract to the address specified in the `testing` alias - defines two test cases: `testAdd()` and `testSubtract()` - calls `add()` and `subtract()` methods with different input values respectively. ## Run the test cases To run the test cases, use the following command in the terminal: ```bash flow test --cover --covercode="contracts" calculator_test.cdc ``` This command uses the Flow CLI to run the test cases and display the output. You will see the following output: ```bash Test results: "calculator_test.cdc" - PASS: testAdd - PASS: testSubtract Coverage: 66.7% of statements ``` This output indicates that both test cases ran successfully, and the two smart contract methods work as expected. With the supplied flags (`--cover` & `--covercode="contracts"`), we also get code coverage insights for the contracts under testing. The code coverage percentage is `66.7%`, because we have not added a test case for the `multiply` method. When we view the auto-generated `coverage.json` file, we see: ```json { "coverage": { "A.0000000000000007.Calculator": { "line_hits": { "14": 0, "4": 1, "9": 1 }, "missed_lines": [14], "statements": 3, "percentage": "66.7%" } } } ``` Line 14 from the `Calculator` smart contract is marked as missed. This is the line: ```cadence return a * b ``` which is the `multiply` method. When we add a test case for the above method: ```cadence calculator_test.cdc ... access(all) fun testMultiply() { Test.assertEqual(10, Calculator.multiply(a: 2, b: 5)) } ``` our code coverage percentage goes to `100%`: ```bash flow test --cover --covercode="contracts" calculator_test.cdc Test results: "calculator_test.cdc" - PASS: testAdd - PASS: testSubtract - PASS: testMultiply Coverage: 100.0% of statements ``` ## Advanced testing techniques The Cadence testing framework provides various features and techniques used to write comprehensive test scenarios. Some of these include: - [**Code Coverage**]: You can use the `--cover` flag with the `flow test` command to view code coverage results when you run your tests. This allows you to identify areas of your code that are not adequately covered by your test inputs. - **Test Helpers**: Test helpers are reusable functions that help you set up the initial state for your test files. You can define test helpers in a Cadence program and use them in your test files by importing it whenever you need it. - [**Assertions**]: The testing framework provides built-in assertion functions, such as `assertEqual`, `beNil`, `beEmpty`, `contain`, to help you verify the expected behavior of your smart contracts. - **Test Suites**: You can organize your test files into test suites to improve the readability and maintainability of your test code. Test suites allow you to group related test cases and set up common test helpers for all the tests in the suite. - [**Integration tests**]: In our previous example, we would directly call the available methods on the contract under test. This is generally categorized as unit testing. You can also write integration testswhen you execute scripts and transactions to interact with the contracts under testing. If you would like to write your tests in Go, instead of Cadence, you can use [Overflow tool] to run integration tests against either an local emulator, testnet, mainnet or an in memory instance of the flow-emulator. When you leverage these advanced testing techniques, you can write more robust and reliable smart contracts in Cadence. In this example, we set up a basic testing environment, wrote a simple smart contract in Cadence, and created a test file to verify its functionality. We then used the Flow CLI to run the test file and confirm that the smart contract works correctly. This is a basic example, and there are many more advanced features and techniques you can explore when you work with the Cadence Testing Framework. For more in-depth tutorials and documentation, refer to the official [Cadence language documentation] and the [Flow CLI documentation]. ## Testing Requirements It is suggested to follow the following best practices: - Every publicly exposed feature of a contract and its resources should have unit tests that check both for success with correct input _and_ for failure with incorrect input. These tests should be capable of being run locally with the Flow emulator, with no or minimal extra resources or configuration, and with a single command. - Each user story or workflow that uses the smart contracts should have an integration test that ensures that the series of steps required to complete it does so successfully with test data. Make sure you test all contracts - and the integration into your application extensively before proceeding to the mainnet. You should aim to replicate all conditions as closely as possible to the usage patterns on mainnet. ## Write Tests There are official SDKsand frameworks for Flow in Cadence, Go and JavaScript. In all three cases, the test code will need to deploy the contracts, configure accounts to interact with them and send transactions to them. It will then have to wait for the transactions to be sealed and check the results. To do this, it catches exceptions, checks for events, and querys state via scripts. ### Cadence tests Cadence comes with built-in support for code coverage, as well as a native testing framework which allows developers to write their tests with Cadence. This framework is bundled with the [Flow CLI] tool, which includes a dedicated command to run tests (`flow test`). You can find examples of Cadence tests in the following projects: [hybrid-custody], [flow-nft], [flow-ft]. Visit the [Cadence documentation] to view all the available features. The [Hybrid Custody] project is a prime example which utilizes both the Cadence testing framework and code coverage in its CI. ![Hybrid Custody CI](./hybrid-custody-ci.png) There is also a [repository] which contains some sample contracts and their tests. ![Automated CI Coverage Report](./codecov-in-pr.png) ![Coverage Report Visualization](./codecov-insights.png) The Cadence testing framework utilizes the emulator under the hood. ### Fork testing (overview) For running tests against a fork of mainnet/testnet, see the dedicated tutorial: [Fork Testing with Cadence (Step-by-Step)]. For available flags, see [Fork Testing Flags]. To interactively explore a forked state outside the test framework, see [Flow Emulator]. For when to use forks vs emulator, see [Testing Strategy on Flow]. ## References - [Cadence documentation] for testing. - [Overflow tool] is a powerful Golang-based DSL for efficient testing and execution of blockchain interactions - Projects that have good examples of robust test cases: - [hybrid-custody] - [flow-nft] - [flow-ft] [Testing Strategy on Flow]: ./testing-strategy.md [Flow CLI]: ../../../build/tools/flow-cli/index.md [installation instructions]: ../../../build/tools/flow-cli/install.md [project's configuration]: ../../../build/tools/flow-cli/flow.json/configuration.md [Flow CLI documentation]: ../../../build/tools/flow-cli/index.md [Network Upgrade (Spork) Process]: ../../../protocol/node-ops/node-operation/network-upgrade.md [Fork Testing Flags]: ../../tools/flow-cli/tests.md#fork-testing-flags [Flow Emulator]: ../../tools/emulator/index.md [Fork Testing with Cadence (Step-by-Step)]: ../../../blockchain-development-tutorials/cadence/fork-testing/index.md [**Code Coverage**]: https://github.com/m-Peter/flow-code-coverage [**Assertions**]: https://cadence-lang.org/docs/testing-framework#assertions [**Integration tests**]: https://github.com/bjartek/overflow [Overflow tool]: https://github.com/bjartek/overflow [Cadence language documentation]: https://cadence-lang.org/ [hybrid-custody]: https://github.com/onflow/hybrid-custody/tree/main/test [flow-nft]: https://github.com/onflow/flow-nft/tree/master/tests [flow-ft]: https://github.com/onflow/flow-ft/tree/master/tests [Cadence documentation]: https://cadence-lang.org/docs/testing-framework [Hybrid Custody]: https://github.com/onflow/hybrid-custody#readme [repository]: https://github.com/m-Peter/flow-code-coverage#readme --- # File: /md/build/evm/accounts.md --- title: Accounts sidebar_label: Accounts sidebar_position: 7 --- :::info Are you a Cadence developer who wants information about Accounts on Cadence? If so, check out the Cadence specific documentation [here] ::: # Accounts There are three types of accounts used for Flow EVM. 1. **Externally Owned Accounts (EOA)**: EOAs are controlled by private individuals with cryptographic keys and can initiate transactions directly. They are the primary account type for users to interact with the blockchain, hold and send cryptocurrency, or call smart contract functions. 2. **Contract Accounts**: These accounts hold smart contract code and are governed by this code's logic. Unlike EOAs, Contract Accounts do not initiate transactions on their own but can execute transactions in response to calls they receive from EOAs or other contracts. 3. **Cadence Owned Accounts (COA)**: This is an account type unique to Flow EVM. These accounts are managed by [Cadence resources] and you can use them to interact with the Flow EVM from within the Cadence environment. EOAs and Contract accounts function the same as on other EVM networks. Users may interact with these accounts with the standard EVM JSON-RPC API ([see endpoints here]). You can read more about EOAs and Contract accounts on the [Ethereum docs]. However, to leverage all the features of Cadence, developers will need to use Cadence Owned Accounts. :::danger 🚨🚨🚨 **ASSET LOSS RISK** 🚨🚨🚨 Cadence-Owned Accounts, easily identifiable by the leading zeroes (`0x00000000000000000000000`) **only exist on Flow**. The keys to these addresses are generated in a way that is not compatible with other networks. As a result, any assets sent to one of these addresses on another network **will be lost permanently!** We're working with major wallet providers to block such transfers, and recommend that all app and wallet developers do the same. ::: ## Cadence Owned Accounts A COA is a natively supported EVM smart contract wallet type that allows a Cadence resource to own and control an EVM address. This native wallet type provides the primitives needed to bridge or control assets across Flow EVM and Cadence, which facilitates composability between environments. ![Account-Model](./flow-evm-account-model.png) ### Why use COAs? COAs create powerful new opportunities to improve the UX, functionality and utility of EVM applications by taking advantage of Cadence. Key benefits include: - **Enhanced Composability**: Within Cadence, developers can extend and compose upon applications written in Solidity. This allows developers to build upon current EVM applications and deliver a more feature-rich user experience. - **Atomic Interactions**: Developers can execute multiple EVM transactions atomically from a COA. This is particularly useful for applications that require multiple transactions to be executed within a single block, or require all prior transactions' state changes to revert if a single transaction in the batch fails. This is not possible natively with EOAs or with `UserOperations` when they use the ERC-4337 standard. In both cases, each individual transaction is distinct and cannot be reverted back after state changes. - **Native Account Abstraction**: COAs are controlled by Cadence resources, which are in turn owned by Flow accounts. [Flow accounts] have built-in support for multi-signature authentication, key rotation, and account recovery. As a Cadence resource, COAs naturally inherit [these features]. - **Fine-Grained Access Control**: As Cadence resources, access to a COA can be governed by more sophisticated policies than those available with basic EVM accounts. When developers use powerful Cadence access control primitives such as [capabilities and entitlements], they can restrict who can interact with a COA and what actions they can perform. ### Differences from traditional EVM accounts COAs are smart contracts that are deployed to, and are fully accessible within, Flow EVM. However, unlike traditional EVM accounts (for example, EOAs or smart contract accounts), a Cadence resource owns COAs. This means that the Cadence execution can natively create and control COAs. Unlike EOAs, COAs do not have an associated key, but are assigned a 20-byte EVM address when they're created from Cadence. This address is based on the UUID of the Cadence resource and is prefixed with `0x000000000000000000000002`. This address determines the location of the COA smart contract deployment and is the EVM address that is used to interact with the COA. A COA may instantiate transactions itself (where the COA's EVM address acts as `tx.origin`). This behavior differs from other EVM environments, where only EOAs may instantiate transactions. Because Cadence resources own COAs, an EVM transaction is not required to trigger a transaction from a COA (for example, a transaction to make a call to `execute` or EIP-4337's `validateUserOpMethod`). Instead, call transactions may be triggered directly from the Cadence resource that owns the COA. When developers invoke the `call` method on this resource, a transaction event will be emitted within the EVM environment. ### More information To learn how to create and interact with COAs in Cadence, see the guide for [Interacting with COAs from Cadence]. For more information about Cadence Owned Accounts, see the [Flow EVM Support FLIP]. [here]: ../cadence/basics/accounts.md [Cadence resources]: https://cadence-lang.org/docs/language/resources [see endpoints here]: ./using.mdx) [Ethereum docs]: https://ethereum.org/developers/docs/accounts [Flow accounts]: ./accounts.md [these features]: ../cadence/advanced-concepts/account-abstraction.md [capabilities and entitlements]: https://cadence-lang.org/docs/language/access-control [Interacting with COAs from Cadence]: ../../blockchain-development-tutorials/cross-vm-apps/interacting-with-coa.md [Flow EVM Support FLIP]: https://github.com/onflow/flips/pull/225/files --- # File: /md/build/evm/fees.md --- title: Fees sidebar_label: Fees sidebar_position: 6 --- :::info Are you a Cadence developer who wants information about Fees on Cadence? If so, check out the Cadence specific documentation [here](../cadence/basics/fees.md) ::: # Fees EVM transactions are ultra low-cost and use the native FLOW token as gas. [Externally Owned Accounts (EOAs)] function the same on Flow as other EVM networks like Ethereum.

    How Transaction Fees are Computed on EVM

    With Flow EVM, EVM operations can now be called within Cadence transactions. EVM operations also have an associated effort measured in gas which needs to be factored into the execution effort calculation in addition to the Flow computation for any EVM transaction. ``` Transaction fee on EVM = surge x [inclusion fee + (execution effort * unit cost)] ``` - `Surge' factor` dynamically accounts for network pressure and market conditions. - `Inclusion fee` accounts for the resources required to process a transaction due to its core properties (byte size, signatures). This is currently constant at 1E-6 FLOW, but subject to change with community approval. - `Execution fee` The fee that accounts for the operational cost of running the transaction script, processing the results, sending results for verification, generating verification receipts, and so on, and is calculated as a product of `execution effort units` and the `cost per unit`. - `Execution Effort (computation)` is based on transaction type and operations that are called during the execution of a transaction. The weights determine how costly (time consuming) each operation is. - `Execution Effort Unit Cost` = `4E-05 FLOW` (currently constant, but subject to change with community approval)

    Calculation of Execution Effort

    ``` Execution Effort (computation) = 3.271E+01 * create_account + 2.348E+01 * blsverify_pop + 7.408E+00 * get_account_balance + 6.145E+00 * blsaggregate_public_keys + 6.059E+00 * get_storage_capacity + 5.726E+00 * get_account_available_balance + 5.637E+00 * update_account_contract_code + 4.964E+00 * blsaggregate_signatures + 1.152E+00 * generate_account_local_id + 5.000E-01 * get_account_contract_names + 3.878E-01 * get_storage_used + 3.770E-01 * account_keys_count + 2.346E-01 * allocate_slab_index + 1.348E-01 * atree_map_get + 1.125E-01 * atree_map_remove + 6.659E-02 * create_array_value + 5.826E-02 * create_dictionary_value + 5.579E-02 * atree_map_set + 5.573E-02 * atree_array_insert + 5.074E-02 * atree_map_read_iteration + 4.442E-02 * encode_event + 3.598E-02 * transfer_composite_value + 2.910E-02 * atree_array_append + 2.701E-02 * statement + 2.650E-02 * atree_array_set + 2.135E-02 * function_invocation + 1.846E-02 * atree_map_pop_iteration + 1.123E-02 * atree_array_pop_iteration + 7.874E-03 * rlpdecoding + 4.242E-03 * graphemes_iteration + 3.922E-03 * ufix_parse + 3.403E-03 * fix_parse + 2.731E-03 * loop + 2.701E-03 * atree_array_batch_construction + 1.907E-03 * transfer_dictionary_value + 1.053E-03 * big_int_parse + 7.324E-04 * transfer_array_value + 7.324E-04 * set_value + 4.730E-04 * uint_parse + 4.272E-04 * int_parse + 3.510E-04 * get_value + 7.629E-05 * string_to_lower + 4.578E-05 * evmgas_usage ``` where ``` `evmgas_usage` is reported by EVM as the cost in gas for executing the transaction within the EVM, for instance, 21K gas for a simple send transaction. ```

    Demonstration of Transaction Fees on EVM

    Assume a simple Token transfer transaction: **Scenario 1 - Cadence-only Transaction** The token transfer transaction: - makes 76 atree_map_get calls, - reads 9431 bytes (get_value), - sets 2448 bytes (set_value), - invokes 55 cadence statements, - makes 2 get_storage_used calls, - makes 28 cadence function_invocation calls, - makes 8 transfer_composite_value calls, - makes 5 atree_map_set calls, - makes 4 encode_event calls, - makes 2 create_array_value calls, - makes 2 atree_array_append calls, - makes 4 atree_array_batch_construction calls, - makes 2 loop calls, - makes 2 transfer_array_value calls ``` Compute Units = 76 * 0.135 + 9431 * 0.000 + 2448 * 0.001 + 55 * 0.027 + 2 * 0.388 + 28 * 0.021 + 8 * 0.036 + 5 * 0.056 + 4 * 0.044 + 2 * 0.067 + 2 * 0.029 + 4 * 0.003 + 2 * 0.003 + 2 * 0.001 ``` But since `EVMGasUsage` is 0 for a Cadence transaction, ``` Compute Units = 19.2 ``` Thus ``` Transaction fee = [1E-4 FLOW + (19.2 * 4E-05 FLOW)] x 1 = 8.68E-04 ``` **Scenario 2 - EVM Transaction** If the EVMGasUsage can be assumed to be 21,000 gas (typical for a simple transfer): - uses 377806 evm gas (evmgas_usage), - reads 22840 bytes (get_value), - makes 1676 atree_array_batch_construction calls, - makes 30 atree_map_get calls, - makes 325 atree_array_pop_iteration calls, - sets 3182 bytes (set_value), - makes 273 rlpdecoding calls, - makes 20 atree_map_read_iteration calls, - makes 1329 transfer_array_value calls, - invokes 25 cadence statements, - makes 12 atree_map_set calls, - makes 17 transfer_composite_value calls, - makes 8 create_array_value calls, - makes 19 function_invocation calls, - makes 1 get_storage_used calls, - makes 87 graphemes_iteration calls, - makes 2 encode_event calls, - makes 2 atree_array_append calls, - makes 1 atree_map_pop_iteration calls, - makes 2 loop calls, - makes 40 string_to_lower calls ``` Compute Units = 377806 * 0.00005 + 22840 * 0.00035 + 1676 * 0.00270 + 30 * 0.13484 + 325 * 0.01123 + 3182 * 0.00073 + 273 * 0.00787 + 20 * 0.05074 + 1329 * 0.00073 + 25 * 0.02701 + 12 * 0.05579 + 17 * 0.03598 + 8 * 0.06659 + 19 * 0.02135 + 1 * 0.38782 + 87 * 0.00424 + 2 * 0.04442 + 2 * 0.02910 + 1 * 0.01846 + 2 * 0.00273 + 40 * 0.00008 = 47.8 ``` Thus ``` Transaction fee = [1E-4 FLOW + (47.8 * 4E-05 FLOW)] x 1 = 2.012E-03 FLOW ``` :::info Be aware that this example serves solely for illustrative purposes to elucidate the calculations. Actual transaction fees may differ due to various factors, such as the byte size of the transaction. :::
    ## Gasless Transactions Fees needed to execute transactions on a Web3 app are often a major challenge for new users and can be a barrier to adoption. To easily extend their apps with Cadence to create ‘gasless’ experiences, builders can specify their app as the [sponsor] instead of the user. To learn more about storage fee and transaction fee, visit [Flow Tokenomics page]. [Externally Owned Accounts (EOAs)]: ./accounts.md [sponsor]: ../cadence/advanced-concepts/account-abstraction.md#sponsored-transactions [Flow Tokenomics page]: https://flow.com/flow-tokenomics/technical-overview). --- # File: /md/build/evm/how-it-works.md --- title: How Flow EVM Works sidebar_label: How it Works sidebar_position: 3 --- # How Flow EVM Works The Flow network uses [Cadence] as its main execution environment. Cadence offers a safe, efficient, and developer-friendly experience to build smart contracts and decentralized applications. Cadence extends EVM apps built in Solidity. It unlocks gasless experiences, new business models, and fine-tuned access control. Since Flow offers full EVM support, current applications and tools already deployed in the EVM ecosystem can simply onboard to the network with [no code changes]. Flow EVM is designed with these major goals in mind: - Support EVM equivalency: Ensure that any tools and applications deployed to or run on Ethereum can also be deployed and run on Flow. - Minimize breaking changes to the Cadence ecosystem, software and tools. - Maximum composability across environments: Allowing atomic and smooth interaction between EVM and Cadence environments.
    ### EVM - a smart contract in Cadence To satisfy the design goals and thanks to the extensibility properties of the Cadence runtime, Flow EVM is designed as a higher-level environment incorporated as a smart contract deployed to Cadence. This smart contract is not owned by anyone and has its own storage space, allows Cadence to query, and is updated through EVM transactions. EVM transactions can be wrapped inside Cadence transactions and passed to the EVM contract for execution. The artifacts of EVM transaction execution (such as receipts and logs) are emitted as special Cadence events (TransactionExecuted, BlockExecuted) and available to the upstream process (Flow transaction) to turn on atomic operations. The EVM environment has its own concept of blocks, and every Flow block includes at most one EVM Block. The EVM block is formed at the end of Flow Block execution and includes all the transaction executed during the EVM block execution. Since EVM blocks are formed onchain and Flow provides fast finality, as long as the user of these events waits for Flow block finality, it doesn’t have to worry about EVM block forks, uncle chains, and other consensus-related challenges. ### No shared memory design The interaction between two environments is through atomic calls and none of the environments has access to the raw memory of the other. This maintains the security properties of each environment. Cadence can submit transactions to the EVM environment and EVM transactions can make calls to a special precompiled contract called Cadence Arch. You can read more about this in the [Precompiled contracts] section. ### No new native token Flow EVM uses the same native token as Cadence (FLOW token). No new token is minted at the genesis block of EVM and all the tokens have to be bridged over from the Cadence side into the EVM side. To facilitate this a native bridge is provided by the EVM contract. ### EVM Equivalency Under the hood, Flow EVM uses [the standard EVM implementation] and regularly applies updates through Flow’s height-coordinated updates (such as execution layer changes planned for the Ethereum Prague update). This means anything that can run on Ethereum after the Pectra upgrade can run on Flow EVM. This means many useful EIPs such as [EIP-1014], [EIP-1559], [EIP-4844], [EIP-5656], [EIP-6780], are supported automatically. Yet a small set of differences between Flow EVM and Ethereum might be seen (mostly of the nature of extension) for two reasons: - A set of extensions has been added to ensure seamless and easy interaction between the two environments. - Flow EVM is secured by the Flow network and benefits from its robust network properties, such as fast block production and finalization, which makes issues like handling uncle chains irrelevant. ## Gateways As mentioned, Flow EVM runs on top of the Flow network and its consensus model. Flow EVM does not leverage `geth` or introduce new node types to the current architecture. Operators who want help secure the network stake tokens and run one of the Flow node types. To support `web3.js` clients, the [EVM Gateway] honors the Ethereum [JSON-RPC specification]. The gateway integrates with Flow access nodes and can be run by anyone (unstaked). It serves two purposes: ### Gateway as a light client The gateway follows Flow's block production to collect, verify, and index EVM-related events. The gateway provides the necessary endpoints and services all JSON-RPC requests for third-party dApps that interact with Flow EVM. EVM events include all information needed to reconstruct the EVM state since the genesis block (replayability). When the gateway re-executes transactions, it can collect traces and maintain a local and archival copy of the EVM state over time. ### Gateway as a sequencer As mentioned, Flow EVM can be seen as a higher-level environment built on top of Cadence. Thus, all EVM transactions are ultimately handled with a Flow transaction (a wrapped call to the EVM). The gateway accepts EVM transactions, runs an internal mempool of transactions, wraps batches of EVM transactions in Flow transactions, and submits them. The safety of transaction execution is not dependent on the gateway; they only relay the transaction. The safety measures of the EVM environment (for example, Nonce) ensure that each transaction is executed at most once. Since the gateways submit Flow transactions, they have to pay the related transaction fees. Part of these fees is associated with the computation fees of the EVM transaction. To facilitate the repayment of fees, the `evm.run` function accepts a `coinbase` address, which collects gas fees from the transaction, and pays it to the address provided by the gateway node. Essentially, the transaction wrapper behaves similarly to a miner, receives the gas usage fees on an EVM address, and pays for the transaction fees. The gas price per unit of gas creates a marketplace for these third parties to compete over transactions. **Censorship resistance and MEV protection** Since Flow EVM runs on the Flow network, it benefits from Flow’s protections against censorship and MEV attacks. The Flow network natively provides censorship and MEV resistance, which it achieves by designating specific validators to build transaction bundles that are separated from the validators that propose blocks (proposer-builder separation). More details about this are available in Flow’s protocol [white papers]. For extra protection on the EVM side, the gateway software is designed to be fully configurable and as lightweight as possible. This allows anyone with an account on Flow (for example, any application) to run their own instances. **Fee market change (EIP-1559)** [EIP-1559] is supported by the Flow EVM and Gateway nodes can decide on the inclusion of the transactions based on the tips or gas fees. The parameters for the EIP 1559 are adjustable by the Flow network. Currently, the base fee is set to zero, as EVM transactions are wrapped by the Flow transactions. ## Block hash calculation difference Developers who use the GoLang Ethereum client to integrate with Flow will find that the block hash calculated by the Flow EVM RPC endpoints `eth_getBlockByNumber` and `eth_getBlockByHash` differ from the hash calculated locally by `go-ethereum`. This is due to underlying differences in the `Block` implementation to support EVM on Flow. For more information, see [the issue in GitHub]. ## Opcodes Flow EVM supports opcodes listed [here], except for the following changes: - **COINBASE** (`block.coinbase`) Similar to Ethereum, it returns the address of block’s beneficiary address. In the case of Flow EVM, it returns the address of the current sequencer's fee wallet (see the [Gateways] section for more details). - **PREVRANDAO** (`block.prevrandao`) On Ethereum, this value provides access to beacon chain randomness (see [EIP-4399]), Since Flow uses a different approach in consensus and verifiable randomness generation, this value is filled with a random number provided by the Flow protocol. While Flow EVM provides such opcode, it is recommended not to rely on this value for security-sensitive applications, as it is the case on Ethereum. In order to benefit from the full power of secure randomness on Flow, we recommend that you use the [Cadence Arch precompiles]. ## Precompiled contracts Besides all the precompiled contracts supported by Ethereum (see here for [the list]), Flow EVM has augmented this with a unique precompiled contract, the Cadence Arch, that provides access to the Cadence world. Cadence Arch is a multi-function smart contract (deployed at `0x0000000000000000000000010000000000000001`) that allows any smart contract on Flow EVM a limited set of interactions with the Cadence environment. Functions currently available on the Cadence Arch smart contract are: - `FlowBlockHeight() uint64` (signature: `0x53e87d66`) returns the current Flow block height. This could be used instead of Flow EVM block heights to trigger scheduled actions, given that it's more predictable when a block might be formed. - `VerifyCOAOwnershipProof(bytes32 _hash, bytes memory _signature)(bool success)` returns true if the proof is valid. An ownership proof verifies that a Flow wallet controls a COA account (see the next section for more details on [COA]). - `revertibleRandom() uint64` returns a safe pseudo-random value that is produced by the Flow VRF (via the Flow internal randomness beacon). The function invokes Cadence's `revertibleRandom` described [here]. Although the random value is safe, a transaction may revert its results in the case of an unfavourable outcome. The function should only be used by trusted calls where there is no issue if the result reverts. `getRandomSource` must be used instead with untrusted calls. - `getRandomSource(uint64) bytes32` should be used when you implement a [commit-reveal] scheme. It returns a secure random source from the Cadence randomness history contract. Learn more about the secure usage of randomness on Flow [here]. Here is a sample that demonstrates how to call the Cadence Arch. ```solidity address constant public cadenceArch = 0x0000000000000000000000010000000000000001; function flowBlockHeight() public view returns (uint64){ (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("flowBlockHeight()")); require(ok, "failed to fetch the flow block height through cadence arch"); uint64 output = abi.decode(data, (uint64)); return output; } ``` ## Special addresses ### Native token bridge Both Cadence and Flow EVM use the same token (FLOW) to run their operations. No new token is minted on the EVM side. The EVM smart contract natively supports easy FLOW token movement across two environments. Because the EVM have limited visibility into Cadence and to make tracking funds easier, every time Flow tokens are withdrawn from the Cadence side and deposited into an EVM address, the balance is added to a special address `0x0000000000000000000000010000000000000000` (native token bridge) and then transferred to the destination EVM address. The bridge address always maintains a balance of zero. Clearly, this EOA address is a network address and is not controlled by public key. ### Cadence-Owned Accounts (COAs) COA is a natively supported EVM smart contract wallet type that allows a Cadence resource to own and control an EVM address. This native wallet provides the primitives needed to bridge or control assets across Flow EVM and Cadence. From the EVM perspective, COAs are smart contract wallets that accept native token transfers and support several ERCs including [ERC-165], [ERC-721], [ERC-777], [ERC-1155]. These smart contract wallets are only deployable through the Cadence environment and their address starts with the prefix `0x000000000000000000000002`. The address `0x0000000000000000000000020000000000000000` is reserved for COA factory, an address that deploys contracts for COA accounts. A COA is not controlled by a key. Instead, every COA account has a unique resource accessible on the Cadence side, and anyone who owns that resource submits transactions on behalf of this address. These direct transactions have COA’s EVM address as the `tx.origin` and a new EVM transaction type (`TxType = 0xff`) is used to differentiate these transactions from other types of EVM transactions (for example, DynamicFeeTxType (`0x02`)). Currently, to make integration and tracking of these transactions byte EVM ecosystem tools, these types of transactions are encoded as legacy EVM transactions (hash computation is based on legacy tx rlp encoding). Control via a resource makes a COA a powerful smart contract wallet. It makes the transfer of ownership of the EVM address super easy without the need to transfer all the assets that an EVM address owns. It also allows a Cadence smart contract to take ownership of an EVM address and makes fully decentralized exchange and bridges across environments possible. To learn more about how to interact with a COA from the Cadence side, [see here]. ## Proofs **Inclusion proof of execution artifacts (logs and receipts)** Similar to other EVM environments, proof can be constructed for artifacts such as receipts. As mentioned earlier, all the EVM execution artifacts are collected as part of a Cadence event. Cadence events are similar to EVM logs, and the root hash of all events (event commitment) of a block is included in Flow's block content. The Cadence event inclusion proof functionality allows you to construct proofs for any artifact. For example, if you want to construct an external proof for the inclusion of a specific EVM log or receipt, here are the steps: - Flow block validation: Anyone who follows the Flow blocks can validate the Flow block headers. - EVM block validation: Since each Flow block has the root hash of Cadence events emitted during block execution, it can construct and verify the inclusion of the specific Event. In this case, every time an EVM block is executed an `evm.BlockExecuted` event is emitted that contains the full EVM block information. - Receipt inclusion: Each EVM block includes the root hash for the receipts generated during block execution. Similar to other EVM chains, a proof can be constructed to prove inclusion of a log or receipt. **Inclusion proof of transactions** Each Flow EVM block (TransactionHashRoot) includes the Merkle root hash of all the transaction hashes executed during this block. Despite similar functionality, this root is a bit different than TransactionRoot provided by Ethereum. It is a commitment over the list of transaction hashes instead of transactions, so each leaf node in the Merkle tree has the transaction hash as the value instead of full RLP encoding of transaction. So, proof verification requires an extra call to the hash function. **Account proofs** Another type of proof that EVM environments provide is proof for the state of accounts. These proofs depend on the trie structure of the execution environment. Flow EVM benefits from the advanced storage and proof system that makes Flow’s multi-role architecture possible. Flow’s state system provides ways to construct inclusion and non-inclusion proofs and you can construct proofs for EVM account’s meta data (account balances, nonce, and so on). A less common proof type is proof over the storage state of an account (mostly used for smart contracts). The first release of Flow EVM won’t support these type of proofs. [Cadence]: https://cadence-lang.org [no code changes]: https://developers.flow.com/evm/about [Precompiled contracts]: #precompiled-contracts [the standard EVM implementation]: https://github.com/ethereum/go-ethereum [EIP-1014]: https://eips.ethereum.org/EIPS/eip-1014 [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 [EIP-5656]: https://eips.ethereum.org/EIPS/eip-5656 [EIP-6780]: https://eips.ethereum.org/EIPS/eip-6780 [EVM Gateway]: https://github.com/onflow/flow-evm-gateway [JSON-RPC specification]: https://ethereum.org/en/developers/docs/apis/json-rpc/ [white papers]: https://flow.com/technical-paper [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 [the issue in GitHub]: https://github.com/onflow/flow-evm-gateway/issues/719 [here]: https://www.evm.codes/?fork=cancun [Gateways]: #gateways [EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399 [Cadence Arch precompiles]: https://github.com/onflow/docs/blob/main/docs/evm/how-it-works.md#precompiled-contracts [here]: https://developers.flow.com/build/cadence/advanced-concepts/randomness [commit-reveal]: https://developers.flow.com/build/cadence/advanced-concepts/randomness#commit-reveal-scheme [COA]: #special-addresses [ERC-165]: https://eips.ethereum.org/EIPS/eip-165) [ERC-721]: https://eips.ethereum.org/EIPS/eip-721 [ERC-777]: https://eips.ethereum.org/EIPS/eip-777 [ERC-1155]: https://eips.ethereum.org/EIPS/eip-1155 [ERC-1271]: https://eips.ethereum.org/EIPS/eip-1271) [see here]: ../../blockchain-development-tutorials/cross-vm-apps/interacting-with-coa.md --- # File: /md/build/evm/networks.md --- title: Network Information sidebar_label: Network Information sidebar_position: 5 --- # Network Information Flow EVM has the following public RPC nodes available: # Mainnet | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Mainnet | | Description | The public RPC URL for Flow Mainnet | | RPC Endpoint | https://mainnet.evm.nodes.onflow.org | | Chain ID | 747 | | Currency Symbol | FLOW | | Block Explorer | https://evm.flowscan.io | # Testnet | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Testnet | | Description | The public RPC URL for Flow Testnet | | RPC Endpoint | https://testnet.evm.nodes.onflow.org | | Chain ID | 545 | | Currency Symbol | FLOW | | Block Explorer | https://evm-testnet.flowscan.io | ## EVM specification - Flow EVM is a virtual EVM-based blockchain with the latest EVM byte-code interpreter `Geth v1.13`. - Uses `FLOW` token for transactions, with balances denominated in `Atto-FLOW` (1 `FLOW` = 10^18 `Atto-FLOW`). - The [EVM Gateway] exposes the standard EVM API (Ethereum JSON-RPC). - Read more about the implementation in [FLIP 223: EVM integration interface]. ## JSON-RPC Methods
    | Method | Status | Notes | | ----------------------------------------- | ------ | ----------- | | [web3_clientVersion] | ✅ | | [web3_sha3] | ✅ | | [net_listening] | ✅ | | [net_peerCount] | ✅ | | [net_version] | ✅ | | [eth_accounts] | 🚧 | Unsupported | | [eth_blockNumber] | ✅ | | [eth_call] | ✅ | | [eth_chainId] | ✅ | | [eth_coinbase] | ✅ | | [eth_estimateGas] | ✅ | | [eth_gasPrice] | ✅ | | [eth_getBalance] | ✅ | | [eth_getBlockByHash] | ✅ | | [eth_getBlockByNumber] | ✅ | | [eth_getBlockTransactionCountByHash] | ✅ | | [eth_getBlockTransactionCountByNumber] | ✅ | | [eth_getBlockReceipts] | ✅ | | [eth_getCode] | ✅ | | [eth_getFilterChanges] | ✅ | | [eth_getFilterLogs] | ✅ | | [eth_getLogs] | ✅ | | [eth_getProof] | 🚧 | Unsupported | | [eth_getStorageAt] | ✅ | | [eth_getTransactionByBlockHashAndIndex] | ✅ | | [eth_getTransactionByBlockNumberAndIndex] | ✅ | | [eth_getTransactionByHash] | ✅ | | [eth_getTransactionCount] | ✅ | | [eth_getTransactionReceipt] | ✅ | | [eth_getUncleByBlockHashAndIndex] | ✅ | | [eth_getUncleByBlockNumberAndIndex] | ✅ | | [eth_getUncleCountByBlockHash] | ✅ | | [eth_getUncleCountByBlockNumber] | ✅ | | | [eth_newBlockFilter] | ✅ | | [eth_newFilter] | ✅ | | [eth_newPendingTransactionFilter] | ✅ | | [eth_sendRawTransaction] | ✅ | | [eth_sendTransaction] | 🚧 | Unsupported | | [eth_sign] | 🚧 | Unsupported | | [eth_signTransaction] | 🚧 | Unsupported | | [eth_syncing] | ✅ | | [eth_uninstallFilter] | ✅ | | [eth_maxPriorityFeePerGas] | ✅ | | [eth_feeHistory] | ✅ | | [debug_traceTransaction] | ✅ | | [debug_traceBlockByNumber] | ✅ | | [debug_traceBlockByHash] | ✅ | | [debug_traceCall] | ✅ | **Legend**: ❌ = not supported. 🚧 = work in progress. ✅ = supported. Read more about the [EVM Gateway] on Flow and how it implements the Ethereum JSON-RPC API. [FLIP 223: EVM integration interface]: https://github.com/onflow/flips/blob/main/protocol/20231116-evm-support.md [web3_clientVersion]: https://ethereum.org/en/developers/docs/apis/json-rpc/#web3_clientversion [web3_sha3]: https://ethereum.org/en/developers/docs/apis/json-rpc/#web3_sha3 [net_listening]: https://ethereum.org/en/developers/docs/apis/json-rpc/#net_listening [net_peerCount]: https://ethereum.org/en/developers/docs/apis/json-rpc/#net_peercount [net_version]: https://ethereum.org/en/developers/docs/apis/json-rpc/#net_version [eth_accounts]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_accounts [eth_blockNumber]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber [eth_call]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call [eth_chainId]: https://eips.ethereum.org/EIPS/eip-695 [eth_coinbase]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_coinbase [eth_compileLLL]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_compilelll [eth_compileSerpent]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_compileserpent [eth_compileSolidity]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_compile_solidity [eth_estimateGas]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_estimategas [eth_gasPrice]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gasprice [eth_getBalance]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getbalance [eth_getBlockByHash]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbyhash [eth_getBlockByNumber]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber [eth_getBlockTransactionCountByHash]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblocktransactioncountbyhash [eth_getBlockTransactionCountByNumber]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblocktransactioncountbynumber [eth_getCode]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getcode [eth_getCompilers]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getcompilers [eth_getFilterChanges]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterchanges [eth_getFilterLogs]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterlogs [eth_getLogs]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getlogs [eth_getProof]: https://eips.ethereum.org/EIPS/eip-1186 [eth_getStorageAt]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getstorageat [eth_getTransactionByBlockHashAndIndex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyblockhashandindex [eth_getTransactionByBlockNumberAndIndex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyblocknumberandindex [eth_getTransactionByHash]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash [eth_getTransactionCount]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactioncount [eth_getTransactionReceipt]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt [eth_getUncleByBlockHashAndIndex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclebyblockhashandindex [eth_getUncleByBlockNumberAndIndex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclebyblocknumberandindex [eth_getUncleCountByBlockHash]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclecountbyblockhash [eth_getUncleCountByBlockNumber]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclecountbyblocknumber [eth_getWork]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getwork [eth_hashrate]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_hashrate [eth_mining]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_mining [eth_newBlockFilter]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newblockfilter [eth_newFilter]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newfilter [eth_newPendingTransactionFilter]: https://openethereum.github.io/JSONRPC-eth-module.html#eth_newpendingtransactionfilter [eth_pendingTransactions]: https://github.com/ethereum/wiki/issues/685 [eth_protocolVersion]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_protocolversion [eth_sendRawTransaction]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendrawtransaction [eth_sendTransaction]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction [eth_sign]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign [eth_signTransaction]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction [eth_signTypedData]: https://eips.ethereum.org/EIPS/eip-712 [eth_submitHashrate]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_submithashrate [eth_submitWork]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_submitwork [eth_syncing]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_syncing [eth_uninstallFilter]: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_uninstallfilter [db_getHex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#db_gethex [db_getString]: https://ethereum.org/en/developers/docs/apis/json-rpc/#db_getstring [db_putHex]: https://ethereum.org/en/developers/docs/apis/json-rpc/#db_puthex [db_putString]: https://ethereum.org/en/developers/docs/apis/json-rpc/#db_putstring [shh_addToGroup]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_addtogroup [shh_getFilterChanges]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_getfilterchanges [shh_getMessages]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_getmessages [shh_hasIdentity]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_hasidentity [shh_newFilter]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_newfilter [shh_newGroup]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_newgroup [shh_newIdentity]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_newidentity [shh_post]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_post [shh_uninstallFilter]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_uninstallfilter [shh_version]: https://ethereum.org/en/developers/docs/apis/json-rpc/#shh_post [txpool_content]: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content [txpool_inspect]: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect [txpool_status]: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status [parity_pendingTransactions]: https://openethereum.github.io/JSONRPC-parity-module#parity_pendingtransactions [EIP-1186]: https://eips.ethereum.org/EIPS/eip-1186 [EVM Gateway]: https://github.com/onflow/flow-evm-gateway --- # File: /md/build/evm/quickstart.md --- title: EVM Quickstart description: Deploy your first contract on Flow testnet and connect it to a rainbowkit/wagmi/viem app sidebar_position: 1 keywords: - Flow EVM - EVM quickstart - Hardhat - Flowscan - Flow Wallet - smart contracts - contract deployment - Flow Faucet - OpenZeppelin - ERC-20 --- # EVM Quickstart Flow EVM is an EVM-equivalent blockchain that combines the advantages of Flow, such as security, low-cost gas, and native VRF with compatibility with current blockchain applications tools and contracts. If it works on another EVM-equivalent blockchain, it should work on Flow EVM! This guide is a self-contained quickstart that walks you through how to deploy a contract on Flow EVM testnet with [Hardhat] and test it with [testnet Flowscan]. If you prefer, check out our tutorials for [Remix] and [Foundry] for information on how to deploy a contract with those platforms. To learn more about wallets and configurations, see [this article]. ## Network information Flow EVM has the following public RPC nodes available: ### Mainnet | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Mainnet | | Description | The public RPC URL for Flow Mainnet | | RPC Endpoint | https://mainnet.evm.nodes.onflow.org | | Chain ID | 747 | | Currency Symbol | FLOW | | Block Explorer | https://evm.flowscan.io | ### Testnet | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Testnet | | Description | The public RPC URL for Flow Testnet | | RPC Endpoint | https://testnet.evm.nodes.onflow.org | | Chain ID | 545 | | Currency Symbol | FLOW | | Block Explorer | https://evm-testnet.flowscan.io | ## Objectives After you complete this guide, you'll be able to: - Fund a wallet with testnet tokens from the [Flow Faucet]. - Deploy a contract on Flow EVM Testnet. - Interact with a contract using [Flowscan]. - Use automatically sponsored gas with the [Flow Wallet] on testnet **and mainnet**. ## Prerequisites ### Traditional cryptocurrency wallet EVM [Accounts] created by the Flow wallet have unique properties that allow for powerful features, but they do **not** have recovery phrases or private keys that you can export in a way that's compatible with [Hardhat]. As a result, you'll need to use a traditional EOA and [MetaMask] or the wallet of your choice to deploy your contracts. ## Deploy your contract For this exercise, we'll use a [Button Clicker Contract] that's relatively simple, but includes several [OpenZeppelin] contracts. This way, we can walk through the process to configure your project to use these common imports. :::info If you **really** want to speedrun this tutorial: 1. Fork the [Button Clicker Contract] repo. 2. Run `npm install`. 3. Add a `.env` with your deploy wallet key as `DEPLOY_WALLET_1`. 4. Deploy with `npx hardhat ignition deploy ./ignition/modules/ClickToken.ts --network flowTestnet`. When finished, skip to the frontend section. ::: ### Hardhat setup Open a terminal window and navigate either to the folder where you wish to create your project folder or an empty project folder: 1. Run the following command: ```bash npx hardhat init ``` ![Hardhat Init](hardhat-init.png) 2. Select **Create a TypeScript project (with Viem)**. 3. Enter `.` if you ran the command from an empty folder, or enter a path. 4. Choose the defaults for the last options, then open the project in your editor. ### Environment setup To set up an environment: 1. Add a `.env`. 2. Within it, add an environment variable called `DEPLOY_WALLET_1` with your deployment wallet's [private key]: ```text DEPLOY_WALLET_1= ``` :::danger The [private key] functions the same as the recovery phrase for a wallet. Anyone with the key can drain the wallet at any time! Use separate wallets for development and never commit a key to a repo. ::: ### Hardhat config We'll use [OpenZeppelin Contracts] in the following steps: 1. Install them and then open the project in your editor: ```bash npm install --save-dev @openzeppelin/hardhat-upgrades npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies ``` 2. Install the contracts themselves: ```bash npm install --save-dev @openzeppelin/contracts ``` 3. To better protect your wallet key, add `dotenv`: ```bash npm install dotenv ``` 4. Open `hardhat.config`. Below the imports, add the `require` statements for the contracts and `dotenv`: ```tsx require('@openzeppelin/hardhat-upgrades'); require('dotenv').config(); ``` The default config is pretty bare. We'll need to add quite a few items. We'll do these one at a time, then provide a complete copy at the end. 5. Add a `networks` property that contains the network information for Flow Testnet and Mainnet: ```tsx networks: { flow: { url: 'https://mainnet.evm.nodes.onflow.org', accounts: [process.env.DEPLOY_WALLET_1 as string], }, flowTestnet: { url: 'https://testnet.evm.nodes.onflow.org', accounts: [process.env.DEPLOY_WALLET_1 as string], }, }, ``` 6. Add an entry for `etherscan`: ```tsx etherscan: { } ``` 7. In it, add a property for `apiKey` and add keys for Flow Mainnet and Testnet. The Etherscan API requires this to be here, but currently, API keys aren't actually needed. You can use any text: ```tsx apiKey: { // Is not required by blockscout. Can be any non-empty string 'flow': "abc", 'flowTestnet': "abc" }, ``` 8. Add `customChains` and the network information for Flow: ```tsx customChains: [ { network: 'flow', chainId: 747, urls: { apiURL: 'https://evm.flowscan.io/api', browserURL: 'https://evm.flowscan.io/', }, }, { network: 'flowTestnet', chainId: 545, urls: { apiURL: 'https://evm-testnet.flowscan.io/api', browserURL: 'https://evm-testnet.flowscan.io/', }, }, ]; ``` You should end up with: ```tsx import type { HardhatUserConfig } from 'hardhat/config'; import '@nomicfoundation/hardhat-toolbox-viem'; require('@openzeppelin/hardhat-upgrades'); require('dotenv').config(); const config: HardhatUserConfig = { solidity: '0.8.28', networks: { flow: { url: 'https://mainnet.evm.nodes.onflow.org', accounts: [process.env.DEPLOY_WALLET_1 as string], }, flowTestnet: { url: 'https://testnet.evm.nodes.onflow.org', accounts: [process.env.DEPLOY_WALLET_1 as string], }, }, etherscan: { apiKey: { // Is not required by blockscout. Can be any non-empty string flow: 'abc', flowTestnet: 'abc', }, customChains: [ { network: 'flow', chainId: 747, urls: { apiURL: 'https://evm.flowscan.io/api', browserURL: 'https://evm.flowscan.io/', }, }, { network: 'flowTestnet', chainId: 545, urls: { apiURL: 'https://evm-testnet.flowscan.io/api', browserURL: 'https://evm-testnet.flowscan.io/', }, }, ], }, }; export default config; ``` ### Contract setup To set up the contract: 1. Delete `Lock.sol`. 2. Add `ClickToken.sol`. 3. Within it, add the [Button Clicker Contract]. :::warning Hardhat only installs the most current version of Solidity. `^0.8.27` means that this contract requires 0.8.27 or **higher**. You generally **don't** want to include the `^` in your contracts unless you have a specific reason for doing so. ::: We won't go into the details of the contract for this tutorial. It's a relatively simple [ERC-20] implementation that mints one token any time the `mintTo` function is called. Perfect for a Button Clicker game! ### Deployment setup To set up the deployment: 1. Delete `Lock.ts` from the `ignition/modules` folder. 2. Add `ClickToken.ts`. 3. Within it, add the following: ```tsx // This setup uses Hardhat Ignition to manage smart contract deployments. // Learn more about it at https://hardhat.org/ignition import { buildModule } from '@nomicfoundation/hardhat-ignition/modules'; const ClickerModule = buildModule('ClickTokenModule', (m) => { const clickToken = m.contract('ClickToken'); return { clickToken }; }); export default ClickerModule; ``` ### Obtain testnet funds Visit the [Flow Faucet] and follow the instructions to add testnet funds. Compared to other networks, the [Flow Faucet] grants a vast amount of tokens — enough gas for millions of transactions. :::warning EVM accounts created by the [Flow Wallet] are [Cadence-Owned Accounts] or COAs — **Not** EOAs. COAs have many advantages over EOAs, but they are generated differently, which means they don't have a key that's compatible with Hardhat. Use your [MetaMask] or similar EOA account to deploy contracts on Flow EVM. ::: ### Deploy the contract Deploy the contract with: ```bash npx hardhat ignition deploy ./ignition/modules/ClickToken.ts --network flowTestnet ``` You will see something similar to: ```bash ✔ Confirm deploy to network flowTestnet (545)? … yes Hardhat Ignition 🚀 Deploying [ ClickTokenModule ] Batch #1 Executed ClickTokenModule#ClickToken [ ClickTokenModule ] successfully deployed 🚀 Deployed Addresses ClickTokenModule#ClickToken - 0x5Ff8221DfDD1F82fd538391D231502B4b927fbD7 ``` ### Verify the contract Next, verify the contract with: ```bash hardhat ignition verify chain-545 --include-unrelated-contracts ``` You'll see something similar to: ```bash briandoyle@Mac button-clicker-contract % npx hardhat ignition verify chain-545 --include-unrelated-contracts Verifying contract "contracts/ClickToken.sol:ClickToken" for network flowTestnet... Successfully verified contract "contracts/ClickToken.sol:ClickToken" for network flowTestnet: - https://evm-testnet.flowscan.io//address/0x64366c923d5046F8417Dcd8a0Cb4a789F8722387#code ``` ## Test the contract To test the contract: 1. Click the link to open the contract in [testnet Flowscan]. 2. Click the `Connect` button and connect your wallet, then navigate to the **Contract > Read/Write contract** tab: ![read write contract](read-write.png) 3. Find the `mintTo` function and expand the UI to mint yourself a few tokens. You can click the `self` button to automatically add your address without the need to to copy and paste. 4. After you've "earned" a few tokens, use `balanceOf` to see how many tokens you have. You can also use `getAllScores` to get a list of everyone with the tokens, and how many they have. ### Test with free gas If you don't have it yet, set up the [Flow Wallet], connect, and try to mint some more tokens. You'll see that the wallet automatically sponsors your gas: ![flow-wallet](sponsored-gas.png) Even better, the [Flow Wallet] currently **sponsors transactions on mainnet** too! ## Conclusion In this tutorial, you learned how to: - Fund a wallet with testnet tokens from the [Flow Faucet]. - Deploy a contract on Flow EVM Testnet with Hardhat. - Interact with a contract using [Flowscan] - Use automatically sponsored gas with the [Flow Wallet] on testnet and mainnet. You've now mastered the basics of how to deploy and interact with EVM contracts on Flow. But this is just the beginning! Flow EVM's true power lies in its ability to combine the best of both worlds: EVM compatibility with Flow's native features. In our [Cross-VM Apps] tutorial series, you'll learn how to supercharge your EVM applications when you integrate them with Flow Cadence. You'll discover how to: - Build hybrid applications that seamlessly connect to both Flow EVM and Flow Cadence. - Use Cadence's powerful features to enhance your EVM contracts. - Enable multi-call contract writes with a single signature. - Take advantage of Flow's native features like VRF and sponsored transactions. Ready to unlock the full potential of Flow EVM? Start with our [Batched Transactions] tutorial to learn how to build your first cross-VM application. [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [wagmi]: https://wagmi.sh/ [viem]: https://viem.sh/ [rainbowkit]: https://www.rainbowkit.com/ [Hardhat]: https://hardhat.org/ [Remix]: ../../blockchain-development-tutorials/evm/development-tools/remix.md [Foundry]: ../../blockchain-development-tutorials/evm/development-tools/foundry.md [Flow Faucet]: https://faucet.flow.com/fund-account [Flowscan]: https://evm-testnet.flowscan.io/ [Flow Wallet]: https://wallet.flow.com/ [Button Clicker Contract]: https://github.com/briandoyle81/button-clicker-contract/blob/main/contracts/ClickToken.sol [OpenZeppelin]: https://www.openzeppelin.com/ [Ignition]: https://hardhat.org/ignition/docs/getting-started#overview [Accounts]: ../evm/accounts.md [MetaMask]: https://metamask.io [private key]: https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/ [ERC-20]: https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ [testnet Flowscan]: https://evm-testnet.flowscan.io/ [Cross-VM Apps]: ../../blockchain-development-tutorials/cross-vm-apps/introduction.md [Batched Transactions]: ../../blockchain-development-tutorials/cross-vm-apps/introduction.md [OpenZeppelin Contracts]: https://www.openzeppelin.com/contracts [Cadence-Owned Accounts]: ./accounts.md#cadence-owned-accounts [this article]: ../../blockchain-development-tutorials/evm/setup/integrating-metamask.mdx --- # File: /md/build/evm/using.mdx --- title: EVM Wallet Setup sidebar_label: EVM Wallet Setup sidebar_position: 4 --- import BrowserOnly from '@docusaurus/BrowserOnly'; import { AddNetworkButton } from '@site/src/components/addNetworkButton'; # EVM Wallet Setup ## Flow Native Wallet The [Flow Wallet](https://wallet.flow.com) is the preferred wallet for Flow EVM. It's also compatible with Cadence transactions, and it currently sponsors all transactions on testnet and mainnet! Flow Wallet is available on [Android](https://play.google.com/store/apps/details?id=com.flowfoundation.wallet) and [iOS](https://apps.apple.com/ca/app/flow-wallet-nfts-and-crypto/id6478996750), with desktop support using the Flow Wallet [Chrome extension](https://chromewebstore.google.com/detail/flow-reference-wallet/hpclkefagolihohboafpheddmmgdffjm). In addition to being able to transact in both EVM and Cadence environments, Flow Wallet will also allow you to view and move assets between EVM and Cadence, making it possible to manage all your assets in one place. To use the Flow Wallet Chrome extension: 1. Open the Flow Wallet browser extension and create your account. 2. Connect to an app with Flow Wallet. ## Other EVM wallets Applications deployed to Flow EVM will work with popular EVM-compatible wallets such as [MetaMask](https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn), all you need to do is add the correct [RPC endpoint](./networks) as a custom network. ### Add Flow to Browser Wallets {() => { // ******* If Chain Id changes, update the Chain ID in the AddNetworkButton component ******* return ; }} ### Add manually to MetaMask Manual method: Add Flow EVM as a custom network to MetaMask: 1. Open the MetaMask browser extension. 2. Click the dropdown at the top of the extension to open the network selection dropdown menu. 3. Click **`Add network`**. 4. Click **`Add a network manually`**. 5. In the **`Add a network manually`** dialog that appears, enter the following information: | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Mainnet | | Description | The public RPC url for Flow Mainnet | | RPC Endpoint | https://mainnet.evm.nodes.onflow.org | | Chain ID | 747 | | Currency Symbol | FLOW | | Block Explorer | https://evm.flowscan.io/ | 6. Tap Save to save Flow EVM as a network. To connect to the Flow EVM select, it from the network selection dropdown menu. To additionally add the Flow EVM Testnet to MetaMask, follow the same steps as above, but use the following information: | Name | Value | | --------------- | ------------------------------------ | | Network Name | Flow EVM Testnet | | Description | The public RPC url for Flow Testnet | | RPC Endpoint | https://testnet.evm.nodes.onflow.org | | Chain ID | 545 | | Currency Symbol | FLOW | | Block Explorer | https://evm-testnet.flowscan.io | Use the [Flow Testnet Faucet](https://faucet.flow.com/fund-account) to fund your account for testing. --- # File: /md/build/flow.md --- title: Why Flow - The Consumer DeFi Layer-One Network sidebar_label: Why Flow sidebar_position: 1 description: Flow is a purpose-built L1 blockchain designed for large-scale consumer finance applications and automated DeFi. It's the leading consumer layer-one network with Flow Actions, Scheduled Transactions, and support for both Cadence and Solidity development. keywords: - Flow blockchain - Consumer DeFi - Best Web3 Apps - Best Consumer Apps - blockchain scaling - multi-role architecture - Cadence language - Solidity language - EVM equivalence - Flow Virtual Machine - account abstraction - blockchain security - Flow features - blockchain trilemma - Flow EVM - smart contracts - Flow token - blockchain architecture - decentralization - consumer apps - MEV - miner-extractable value - maximum extractable value - VM bridge - cross-vm development - Forte upgrade - Flow Actions - Scheduled Transactions --- # Why Flow: The Consumer DeFi Layer-One Network Flow powers the future of Consumer DeFi. Flow is the home of Consumer DeFi. Flow is a purpose-built L1 blockchain designed for large-scale consumer finance applications. It is the leading consumer layer-one network, boasting over one million monthly active users across ecosystem applications built in collaboration with top global brands like NBA, Disney, PayPal, NFL, and Ticketmaster. ## The Consumer DeFi movement Today's fintech is a digital facade on analog rails, subject to the same outdated limitations of your brick-and-mortar bank. These platforms gave us the appearance of digital money without the benefits of digital-native assets. Your accounts there are mere pointers to the underlying legacy system. But that is about to change with the Consumer DeFi movement: to turn decentralized finance into personal finance. The new chapter for Flow in leading Consumer DeFi is not a sudden shift, but rather the culmination of many years of focused dedication in providing the best consumer experience. Within the past 12 months, Flow network achieved remarkable feats: nearly 10x throughput increase for consumer-scale ambitions, 600% year-over-year increase in total value locked (TVL) to over $100M, and the pivotal Forte upgrade that drastically reduced the development time for consumer finance applications from months to mere days. The next generation of consumer finance is not just facilitated by the internet; assets themselves are of the internet. Fintech apps serve as opinionated frontends to a new financial layer that is: - **Global**: Accessible everywhere, by everyone. - **Always-On**: Operating 24/7/365, unbound by business hours. - **Real-Time**: Settling transactions in seconds, not days. - **Open**: Programmable and composable across apps. The foundation for the future of consumer finance is already being laid. After years of development, DeFi technology is ready to transition from a niche market to the mainstream. However, a significant hurdle remains: current DeFi platforms are designed for crypto-savvy users, which demands steep learning curves and willingness to take on unlimited risk. We must move beyond this crypto native phase to the new consumer DeFi era with better-than-fintech user experiences, safer and sustainable yields, and most importantly delivering a tangible impact on users' daily lives. **What is Consumer DeFi?** Consumer DeFi is any app or experience that provides the benefits powered by DeFi rails to audiences with zero crypto knowledge. Apps powered by Flow win consumer mindshare because they offer features and benefits that are extremely hard to replicate with just web2 rails while ensuring users do not require crypto-specific knowledge to understand and use them. Flow has demonstrated its capability to attract global brands and institutions, and offers an innovative and safe platform for their millions of users. With the DeFi sector now ready to transition from a niche market to the mainstream, Flow is uniquely positioned to lead the Consumer DeFi charge. Dieter Shirley, Chief Architect of Flow and co-author of the [ERC-721 NFT standard], calls Flow: > **_A computer that anyone can use, everyone can trust, and no one can shut down_**
    ## Flow: automated DeFi and consumer applications Flow is a high-performance, layer-one blockchain designed for automated DeFi and large-scale consumer applications. Its multi-role architecture isolates heavy computation to Execution Nodes while keeping validation lightweight, so developers gain incredible performance and users benefit from low-cost transactions, even at scale. The Forte upgrade expands the core protocol with Flow Actions for atomic multi-step DeFi operations and Scheduled Transactions for fully onchain automation. Developers can compose swaps, staking, or yield workflows that self-execute without off-chain keepers, scripts, or relayers required. Flow delivers a unified environment for automation-heavy, composable applications that demand reliability and precision at scale. It's where DeFi, fintech, and consumer-grade performance converge in a single L1. ## Forte network upgrade: autonomous DeFi execution The Forte network upgrade marks a turning point for builders who create DeFi systems on Flow. Until now, blockchains have largely been reactive, and respond only when a user or off-chain keeper sends a transaction. Forte changes that model by giving developers a native framework for composable, autonomous execution. > **_ERC-20 and ERC-721 unlocked nouns. Actions and scheduled transactions unlock verbs._** Forte transforms Flow from a reactive blockchain into an autonomous, intelligent network that executes complex workflows without external dependencies. The upgrade introduces native time scheduling, protocol-level composability, and AI-optimized development tools that enable entirely new categories of applications. Together, these systems turn Flow into a self-governing financial runtime that is precise with 128-bit fixed-point math for lossless calculations, secure through resource-based execution, and fully composable across protocols. In this environment, DeFi logic can schedule itself, chain together across protocols, and operate autonomously—a blockchain that finally does more than react. ### Flow Actions: protocol-native composability [**Flow Actions**] are protocol-native, composable operations that enable developers to create multi-step workflows across protocols in a single atomic transaction. With Flow Actions, builders can link together standardized DeFi primitives such as sources, sinks, swappers, price oracles, and flashers into atomic, protocol-agnostic workflows. A single transaction can claim rewards, swap assets, add liquidity, and restake LP tokens without any off-chain orchestration. These building blocks eliminate custom integrations and ensure every operation either fully succeeds or safely reverts, and unlock complex strategies like automated yield farming, arbitrage, and rebalancing through simple, auditable Cadence code. This means developers can: - **Compose complex operations**: Combine multiple actions in one transaction to build sophisticated DeFi strategies. - **Eliminate integration complexity**: Use standardized interfaces instead of custom contract integrations. - **Ensure atomicity**: All operations succeed together or fail together, which eliminates partial execution risks. - **Reduce gas costs**: Execute multiple protocol interactions more efficiently than separate transactions. ### Scheduled Transactions: autonomous onchain execution [**Scheduled Transactions**] introduce the first truly onchain time scheduler. Developers can schedule or trigger transactions directly within Flow accounts, which allows recurring actions, deferred settlements, and autonomous portfolio management without external cron jobs or trusted servers. Scheduled Transactions are onchain resources that run entirely within a Flow account, which allows fully autonomous, secure transaction execution. They can self-trigger based on conditions and operate without external keepers. Key capabilities: - **Autonomous operation**: Execute transactions automatically based on programmed logic. - **Self-contained**: Run entirely onchain without external dependencies. - **Trigger-based**: React to onchain events, time schedules, or custom conditions. Combined, Actions and Scheduling allow DeFi protocols to become self-maintaining. Positions can compound automatically, vaults can adjust exposure based on time or events, and protocols can enforce predictable behavior entirely onchain. Scheduled Transactions are the first native time scheduler that lets onchain apps run tasks automatically, like cron jobs for blockchains. Applications are no longer restricted to being reactive only to user transactions. They can be used for: - **DeFi protocols** that automatically rebalance portfolios on schedule. - **AI-driven agents** that proactively settle, sweep, or optimize positions. - **Subscription services** with automatic recurring payments. - **Gaming mechanics** with time-based events and rewards. Scheduled Transactions run natively on the network, which simplifies operations, reduces off-chain dependencies, and makes behavior auditable and predictable in code. This implements [FLIP 330: Scheduled Transaction]. ### High-precision DeFi with 128-bit fixed-point types Cadence now supports **Fix128** and **UFix128** - 128-bit fixed-point types that allow precision up to **24 decimal places** for advanced DeFi, risk engines, and interest accrual workloads. The native precision in Forte with built-in 128-bit fixed-point support eliminates the need for bespoke arithmetic scaffolding and minimizes rounding-related errors common in integer-based math. This ensures lossless conversion where all existing Fix64 and UFix64 values convert seamlessly, which provides financial-grade accuracy that supports sophisticated financial calculations that require extreme precision. This implements [FLIP 341: Add 128-bit Fixed-point Types to Cadence]. ### WebAuthn and passkey support Forte adds **native WebAuthn support** such as passkeys, which allows wallets to use device-backed credentials on iOS, Android, and popular password managers to sign transactions. Native WebAuthn support on Flow eliminates seed phrases by turning on biometric authentication while preserving self-custody, with cross-device portability that securely syncs credentials across devices. The native integration requires no additional smart contract layers like ERC-4337, providing a seamless UX where users can sign transactions with Touch ID, Face ID, or hardware keys. Combined with native account abstraction on Flow, developers can build [smart wallets without relying on complex contract architectures]. This implements [FLIP 264: WebAuthn Credential Support]. ### AI-friendly Cadence errors Cadence compiler and linter errors are now **designed for AI assistance**, which makes it easier for agents and IDE copilots to fix issues automatically. Error messages: - **Explain the cause** with context-aware descriptions. - **Suggest concrete fixes** with actionable recommendations. - **Link directly** to reference docs and migration notes. - **Surface through language server** for AI-powered editors like Cursor. This enables faster feedback, fewer documentation round trips, and smoother AI agent workflows for code refactoring and migration. ### Boost efficiency and scalability #### Enhanced node performance with PebbleDB Forte upgrades node storage from BadgerDB to **PebbleDB**, delivering up to 80% memory usage reduction depending on node type and up to 60% CPU usage improvement for typical operations. The upgrade provides up to 30% annual disk usage reduction through effective pruning, higher stability under load by eliminating memory spikes, and improved ROI for operators through better resource efficiency. #### Optimized state storage with account key de-duplication Public key de-duplication eliminates redundancy while preserving multi-key account flexibility, which consolidates 53% of all keys that were duplicates. This optimization delivers a 6% reduction in the Flow execution state (saving 21 GB from 349 GB), removes 0.29 billion entries from the storage trie, and provides a 6-18% reduction in Execution Node memory usage, which results in faster state access through leaner data structures. #### Adaptive collection rate limiting for overload resilience The [assembly line architecture] on Flow gains intelligent rate limiting to prevent pipeline bottlenecks through automatic throttling when execution or sealing lags behind collection. The system maintains steady pipeline flow even at several hundred TPS while providing priority handling for governance and protocol transactions, which creates a self-regulating system that disengages after the backlog clears. #### Near real-time transaction results Building on the [data availability vision] for Flow, Access Nodes ingest account data and transaction results **before finalization**, which allows soft finality access for high-frequency DeFi applications with early state reads that include graceful rollback handling. This approach provides reduced latency for real-time applications and direct data serving without third-party dependencies. ### Protocol autonomy #### Hardened data integrity across the network A major milestone on the [protocol autonomy roadmap] ensures every data structure has a **canonical, verifiable identity** through collision-resistant hashing for all inter-node communications and immediate tampering detection by message recipients. The system provides protected data structures with custom linter validation and immutable message semantics for simplified development. This provides developers and AI agents with a simpler mental model where network messages are treated as immutable objects with stable identities. ## What makes Flow unique Flow is a fast, decentralized, and developer-friendly blockchain designed to be the foundation for a new generation of games, apps, and the [digital assets] that power them. It is based on a unique [multi-role architecture] and designed to [scale without sharding], allowing for massive improvements in speed and throughput while preserving a developer-friendly, ACID-compliant environment. Much of the protocol design is based on lessons learned from building Web3 applications while working at [Dapper Labs], particularly [CryptoKitties] — the first onchain game to reach [widespread popularity]. The game went viral, then [struggled under its own success] when it caused so much traffic that the Ethereum network itself was overwhelmed by the load. The design of Flow was guided by the need to alleviate this burden and create the best experience possible for both developers and users. Flow Foundation (the core team) and Dapper Labs (the leading ecosystem builder) work together as two integral parts of a unified effort, combining their strengths to position Flow as the leader in Consumer DeFi. This synergy brings together deep protocol and infrastructure expertise hardened by over 10 years of production experience with the highest security standards, alongside deep consumer audience expertise honed by operating multiple production apps that generated over $1 billion in revenue combined. The blockchain network of the future must be able to handle millions of users while upholding the key pillars of decentralization: 1. Verifiability 1. Predictability/Reliability 1. Equitable Access for All 1. Permissionless Composability 1. Interoperability 1. Security Flow solves the [blockchain trilemma] and represents the next generation of blockchain technology. It's built to allow seamless consumer-scale apps without compromising decentralization or user experience, and is the chosen blockchain network for [NBA Top Shot], [NFL All Day], [Mattel Creations], and [Disney Pinnacle]. ### Dual language architecture Flow is unique in that it supports two powerful programming languages for smart contract development: - **Cadence**: A modern programming language developed by smart contract application builders. - **Solidity**: The industry-standard language for EVM development, fully supported on Flow with full EVM equivalence. EVM and Cadence environments both use FLOW as gas for transactions and are connected by a native bridge that allows seamless and cheap communication between them. Fungible and non-fungible tokens can also be seamlessly transferred between environments with the native VM token bridge, taking place instantly in a single atomic transaction. This means developers can choose the language that best fits their needs while maintaining full interoperability between both environments. ### Cadence development on Flow Flow supports two smart contract languages: [Cadence] provides native resource safety, 128-bit fixed-point arithmetic for financial precision, built-in WebAuthn authentication for secure, seedless user onboarding, and vastly increased contract size, storage, and computation limits. It also grants native data availability, and Cadence transactions are written in the language itself, which allows for multiple calls to multiple functions on multiple smart contracts all with a single user signature. Key Cadence features: - **Advanced Transactions**: [Transactions] in Cadence smart contracts are not simply calls to existing functions on already deploy contracts. Instead, transactions are code written in Cadence that can **call any function (with appropriate access) on any smart contract by any author**, all in a single, atomic transaction with a single user signature. - **AI Ready**: Cadence transactions have [pre- and post-conditions] that clearly define the inputs to a transactions, such as the tokens that may be withdrawn, and outcomes, such as collectibles that must be purchased. With these definitions, Cadence transactions of immense complexity can be written safely. Regardless of code in the actual execution, the user can be sure that they get what they expected and only pay the price they authorized. - **Data Availability**: Similarly, any author can construct a **view** function to access any public data on any smart contract without needing the author of that smart contract to have anticipated the need to view that data or reliance a provider to cache it and make it available. - **Native account abstraction**: Cadence transactions have protocol-native [account abstraction]. All accounts are smart accounts, supporting scripting, multiple keys, multi-signature transactions, and walletless onboarding with social logins. - **Gasless transactions**: Cadence transactions have multiple [signing roles] for each transaction. Most notably, the payer can be set independently of the authorizer. In other words, having one account sign a transaction and another pay for that transaction is a built-in feature. - **Security**: Smart contracts on Flow are natively written in Cadence, an easier, safer, and more secure programming language for crypto assets and apps. It's the first high-level, [resource-oriented] programming language. - **Developer ergonomics**: The Flow network is designed to maximize developer productivity. Examples range from upgradeable smart contracts to built-in logging support to the Flow Emulator. ### Solidity development on Flow EVM [Solidity] allows developers to deploy current contracts on a fast and efficient, fully EVM-equivalent network and benefit from access to native VRF, batched transactions, and all the benefits of the Flow protocol. A native bridge allows seamless transfers of assets between these two networks. Flow EVM provides the best EVM experience available anywhere: - **Speed, cost, and compatibility**: Flow EVM can already run all of your audited Solidity contracts at an average of less than 1 cent per transaction ([usually way less!]). Unlike L2 solutions, Flow EVM reaches true finality in seconds — not in [a week]. - **Bridge from Other EVM networks**: You can [bridge] hundreds of assets from dozens of chains to Flow. - **VM token bridge**: Assets can be bridged between Flow Cadence and Flow EVM easily and atomically with the VM token bridge. Assets can even be bridged **and used** in a **single** transaction, allowing full composability between the EVM and Cadence environments. - **Access to Cadence features**: Access Cadence features and contracts from Flow EVM to take advantage of native [VRF], higher computation for lower cost, and any asset on Cadence Flow. You can also build [cross-vm apps] on top of the _wagmi/viem/RainbowKit_ stack, which allows batched transactions and more. - **EVM equivalence:** Flow EVM is truly _EVM Equivalent_, not just _EVM Compatible_. It runs exactly the same as EVM mainnet, which means builders do not run into _minor_ variances or endless 'quirks' when they try to integrate. If it works on Ethereum Mainnet, it works with Flow EVM. ### Seamless integration for Ethereum developers Flow EVM is designed to work out-of-the-box with the Ethereum toolchain or other clients. Native EVM transactions continue to be supported when using Metamask and other EVM-compatible clients. EVM-equivalency on Flow works behind-the-scenes by implementing a minimal transaction script in Cadence to integrate Flow features with EVM. This is made possible because EVM transactions are composed and executed within Cadence transactions, which allows novel use-cases and patterns for integration. ### Flow blockchain core features - **MEV resistance**: Flow is designed to [ensure equitable access] by resisting MEV. Maximum Extractable Value, also know as Miner-Extractable Value (MEV), is a practice common in other blockchains in which the builder of a block can profit at your expense by manipulating where and how your transaction is included. - **Native VRF**: Flow provides [onchain randomness] at the protocol level. Instead of implementing a complex setup and [paying $10+ USD per number], simply call the built-in function. - **Scalable and Secure Architecture**: The [multi-role architecture] of Flow allows the network to [scale without sharding] to serve billions of users without reducing the decentralization of consensus and verification. - **True, fast finality**: For most other networks, it takes minutes, [a day], or even [a week] to reach hard finality — the point at which a transaction cannot be reversed. On Flow, the median time for finality is [under 10 seconds], without compromising security. - **Consumer onboarding**: Flow was designed for mainstream consumers, with payment onramps catalyzing a safe and low-friction path from fiat to crypto. - **Efficient gas costs**: The Flow blockchain is extremely efficient, allowing apps to do more computation at lower costs. ### MEV resilience The [MEV Resilient] design on Flow offers DeFi builders improved market efficiency, fairness, trust, and long-term viability for their apps. Since Flow EVM transactions are composed and executed within a Cadence transaction, block production is handled by the [multi-role architecture] on Flow. This robust MEV resilience is a significant difference from other EVM-compatible networks and results in reasonably priced and predictable gas fees. The impracticality of frontrunning or other attacks improves the user experience by eliminating failed transactions and invisible fees. ### Flow track record in Consumer DeFi Flow leadership in Consumer DeFi is built on a foundation of proven expertise and real-world validation: **Consumer expertise:** - The leading consumer chain that onboarded millions of net new users onchain and still boasts over one million monthly active users - Deep consumer audience expertise honed by operating multiple production apps that generated over $1 billion in revenue combined - Vast network of global consumer brands and platforms like NBA, Disney, PayPal, Ticketmaster **DeFi and fintech expertise:** - Deep protocol and infrastructure expertise hardened by over 10 years of production experience with the highest security standards (since CryptoKitties days). - Built the first smart contract wallet (Dapper Wallet plugin) on Ethereum back in 2018, ahead of the account abstraction movement. - Managed the end-to-end infrastructure for on/off-ramp, KYC, and risk monitoring platform that handles hundreds of millions of dollars in volume (Dapper Wallet). - Flow ALP provides the best risk-adjusted yield opportunities in DeFi for consumers and institutional capital. - The best infrastructure layer that offers the fastest time to market for consumer DeFi apps. Flow is where consumers deposit their funds and access the best risk-adjusted yields, making it the ideal platform for Consumer DeFi applications that prioritize safety and sustainable returns. ### Scalability, performance, and low gas fees For sustainable user adoption, apps require the network they build on to be secure, efficient, affordable, and fast. Gas fees are ultra-low cost on the network, but Flow goes a step further, as it allows for gasless experiences through sponsored transactions. The state space on Flow is extensible to the petabyte scale, making it easy to store application data onchain. This means contracts can maintain a full working dataset — including metadata — together with contract logic. Transaction throughput on the Flow network has reached as many as 2 million daily transactions, a similar average transaction volume as Ethereum. Unlike Ethereum, Flow has always operated well under its maximum throughput ceiling, and that ceiling is scalable to even greater performance when it becomes necessary. ## Get started Whether you're ready to dive into the advantages of building with [Cadence], or are starting with Flow [EVM], we've got paths to get you up and running as quickly as possible. ### Get started with Cadence app development The [Getting Started] tutorial covers everything you need to know to build a Flow Cadence application: - Set up your local development environment (it's fast and easy!). - Deploy and interact with Flow Cadence contracts. - Build a frontend that can interact with smart contracts written by you or other developers. ### Learn Cadence [Cadence] is a modern smart contract programming language designed to work with Flow. To learn a new language is an investment, but you'll find that Cadence is safer, more explicit, and less dangerous than other blockchain languages. Plus, it unlocks the full power of the Flow protocol! :::tip If you're already comfortable with Solidity, be sure to check out how [Cadence] works in our [Guide for Solidity Developers]! ::: ### Build with Solidity on Flow EVM Not ready to take the plunge and learn [Cadence]? Try out **EVM++** by deploying current [EVM] contracts to see that Flow EVM is faster and cheaper than nearly every other EVM solution without compromising on security. Deploying on Flow EVM also gives your Solidity contracts access to many Flow Cadence features, such as native [VRF]. ## FLOW token The [FLOW] (or $FLOW) token is the native currency for the Flow network. Developers and users can use FLOW to transact on the network. Developers can integrate FLOW directly into their apps for peer-to-peer payments, service charges, or consumer rewards. FLOW can be held, transferred, or transacted peer-to-peer. - To understand more about Flow Token Economics and the FLOW token, read the [Flow Token Economics] guide. - FLOW tokens are the native Fungible Token on Flow. To learn more about how to work with them in your applications, review the [FLOW] article. ## Technical background - The [Flow Technical Primer] is a great place to start to understand how Flow works. - The [Three technical whitepapers] cover the unique innovation behind the Flow blockchain network in-depth. ## Flow Improvement Proposals (FLIPs) Those wishing to understand the technical specifics of how Flow EVM works, we recommend that you review the following improvement proposals: - Understanding [EVM Support on Flow] - Exploring the [Flow VM Bridge] - Insights into the [Flow EVM Gateway] - Integration of the [Cadence Interface] ## Build with Flow Whether you build with Cadence or Solidity, portiwant to port a current Solidity dApp or build one from scratch, Flow offers a **fast, scalable blockchain with low fees** and the tooling you already know. As a **purpose-built L1 for consumer finance applications**, Flow combines familiar development workflows with performance and UX enhancements you can't get elsewhere. Build the next generation of Consumer DeFi applications that deliver better-than-fintech user experiences, safer and sustainable yields, and tangible impact on users' daily lives. ## Join the community Want to launch a project on Flow or partner with us? Visit our weekly Flow [office hours] for discussions on project development and other opportunities for collaboration. You can also connect with us in our developers-chat in the Flow [Discord]. [a day]: https://docs.zksync.io/zk-stack/concepts/finality#finality-on-zksync-era [a week]: https://docs.optimism.io/stack/rollup/overview#fault-proofs [account abstraction]: https://flow.com/account-abstraction [best platform for consumer apps]: https://flow.com/ [blockchain trilemma]: https://coinmarketcap.com/academy/glossary/blockchain-trilemma [bridge]: ../ecosystem/bridges.md [Cadence Interface]: https://github.com/onflow/flips/blob/f646491ec895442dcccdb24d80080bab1c56188e/protocol/20231116-evm-support.md [Cadence]: https://cadence-lang.org/ [Transactions]: https://cadence-lang.org/docs/language/transactions [pre- and post-conditions]: https://cadence-lang.org/docs/language/pre-and-post-conditions [cross-vm apps]: ../blockchain-development-tutorials/cross-vm-apps/index.md [CryptoKitties]: https://www.cryptokitties.co/ [Dapper Labs]: https://www.dapperlabs.com/ [digital assets]: https://www.flow.com/post/flow-blockchain-cadence-programming-language-resources-assets [Discord]: https://discord.gg/flow [Disney Pinnacle]: https://disneypinnacle.com/ [ensure equitable access]: ./cadence/basics/mev-resistance.md [ERC-721 NFT standard]: https://github.com/ethereum/eips/issues/721 [Ethereum Virtual Machine (EVM)]: https://flow.com/upgrade/crescendo/evm.md [EVM Support on Flow]: https://github.com/onflow/flips/pull/225 [EVM]: https://flow.com/upgrade/crescendo/evm.md [Flow EVM Gateway]: https://github.com/onflow/flips/pull/235/files [Flow Technical Primer]: https://www.flow.com/primer [Flow Token Economics]: https://www.flow.com/flow-token-economics [Flow VM Bridge]: https://github.com/onflow/flips/pull/233/files/d5bc46c4b13f0b9b168a94f994c77a5a689f6b24..122e938b7acae7e774246b1b66aaf5979ca21444 [FLOW]: ./cadence/core-contracts/03-flow-token.md [Getting Started]: ../blockchain-development-tutorials/cadence/getting-started/index.md [Guide for Solidity Developers]: https://cadence-lang.org/docs/solidity-to-cadence [Mattel Creations]: https://creations.mattel.com/pages/virtual [MEV Resilient]: ./cadence/basics/mev-resistance.md [multi-role architecture]: https://flow.com/post/flow-blockchain-multi-node-architecture-advantages [multi-role architecture]: https://www.flow.com/primer [NBA Top Shot]: https://nbatopshot.com/ [NFL All Day]: https://nflallday.com/ [office hours]: https://calendar.google.com/calendar/ical/c_47978f5cd9da636cadc6b8473102b5092c1a865dd010558393ecb7f9fd0c9ad0%40group.calendar.google.com/public/basic.ics [onchain randomness]: ./cadence/advanced-concepts/randomness.md [paying $10+ USD per number]: https://docs.chain.link/vrf/v2-5/billing [resource-oriented]: https://flow.com/post/resources-programming-ownership [scale without sharding]: https://www.flow.com/post/flow-blockchain-multi-node-architecture-advantages [signing roles]: ./cadence/basics/transactions.md#signer-roles [Solidity]: https://soliditylang.org/ [struggled under its own success]: https://spectrum.ieee.org/cryptokitties [Three technical whitepapers]: https://www.flow.com/technical-paper [under 10 seconds]: ./cadence/basics/transactions.md#flow [usually way less!]: https://evm.flowscan.io/stats [VRF]: ../blockchain-development-tutorials/native-vrf/vrf-in-solidity.md [widespread popularity]: https://www.cnn.com/style/article/cryptokitty-blockchain/index.html [**Scheduled Transactions**]: ../blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md [FLIP 330: Scheduled Transaction]: https://github.com/onflow/flips/blob/main/protocol/20250609-scheduled-callbacks.md [FLIP 341: Add 128-bit Fixed-point Types to Cadence]: https://github.com/onflow/flips/blob/main/cadence/20250815-128-bit-fixed-point-types.md [smart wallets without relying on complex contract architectures]: https://flow.com/post/transforming-smartphones-into-hardware-wallets-how-secure-enclave-support-on-flow-is-ushering-in-the-next-wave-of-web3-applications [FLIP 264: WebAuthn Credential Support]: https://github.com/onflow/flips/blob/cfaaf5f6b7c752e8db770e61ec9c180dc0eb6543/protocol/20250203-webauthn-credential-support.md [protocol autonomy roadmap]: https://flow.com/protocol-autonomy-roadmap [**Flow Actions**]: ../blockchain-development-tutorials/forte/flow-actions/index.md [fundamental DeFi primitives]: ../blockchain-development-tutorials/forte/flow-actions/index.md [assembly line architecture]: https://flow.com/multi-node [data availability vision]: https://flow.com/protocol-autonomy-roadmap --- # File: /md/build/tools.mdx --- sidebar_position: 12 title: Tools description: Essential tools for the Flow blockchain ecosystem --- import DocCardList from '@theme/DocCardList'; # Tools & SDKs Flow provides a comprehensive suite of development tools to help you build, test, and deploy applications on the blockchain. ## Which Tool Should I Use? | I want to... | Use this | |--------------|----------| | Build a React frontend | [React SDK](./react-sdk/index.mdx) | | Build with vanilla JS/TypeScript | [FCL-JS](./clients/fcl-js/index.md) | | Build a Go backend | [Flow Go SDK](./clients/flow-go-sdk/index.md) | | Develop and test locally | [Flow CLI](./flow-cli/index.md) + [Emulator](./emulator/index.md) | | Write and debug Cadence | [VS Code Extension](./vscode-extension/index.md) | | Simulate wallet interactions | [Flow Dev Wallet](./flow-dev-wallet/index.md) | | Build a wallet provider | [Wallet Provider Spec](./wallet-provider-spec/index.md) | ## Core Tools ## Community Tools Tools built by the Flow community to enhance your development experience. --- # File: /md/build/tools/clients.md --- title: Client Tools description: Comprehensive guide to Flow's client tools and SDKs, including FCL-JS, Go SDK, and various language-specific implementations for interacting with the Flow blockchain. sidebar_position: 6 keywords: - FCL - Flow Client Library - SDKs - client tools - JavaScript - Go - Python - JVM - PHP - Elixir - HTTP API - blockchain - development - integration - wallets - authentication - transactions - cross-vm - EVM - Cadence --- # Client Tools Flow provides a comprehensive suite of client tools and SDKs designed to help developers build applications that interact with the Flow blockchain. These tools support various programming languages and platforms, offering different levels of abstraction and functionality. > Terminology note > > Anywhere an API or SDK accepts a Flow transaction ID, you may also provide a scheduled transaction ID: > - Transaction ID: 256-bit hash represented as a 64-character hex string > - Scheduled transaction ID: UInt64 represented as a decimal string > > For REST endpoints like `/v1/transactions/{id}` and `/v1/transaction_results/{id}`, the server treats the `id` as a transaction ID if it parses as hex; otherwise, as a scheduled transaction ID if it parses as a decimal UInt64. Both return identical response schemas. See the Protocol docs for details (`docs/protocol/access-onchain-data/index.md`). ## JavaScript (FCL) [Flow Client Library (FCL)] is the primary JavaScript/TypeScript client for Flow. It provides: - Wallet integration and authentication - Transaction and script execution - Cross-VM functionality for EVM integration - TypeScript support - Built-in security features ## Go SDK [Flow Go SDK] offers a robust set of packages for Go developers, including: - High-performance blockchain interaction - Transaction building and signing - Account management - Event subscription - Comprehensive testing utilities ## Python SDK [Flow Python SDK] provides Python developers with: - Simple blockchain interaction - Transaction management - Account handling - Event monitoring - Easy integration with Python applications ## JVM [Flow JVM SDK] supports JVM-compatible languages (Java, Kotlin, Scala) with: - Kotlin-first implementation - Transaction management - Account handling - Event subscription - Cross-platform compatibility ## PHP [PHP SDK] enables PHP developers to: - Integrate blockchain functionality - Handle transactions - Manage accounts - Monitor events - Build web applications ## Elixir [OnFlow] provides Elixir developers with: - Functional blockchain interaction - Transaction management - Account handling - Event subscription - Comprehensive documentation ## HTTP API [Flow OpenAPI] specification provides: - RESTful API endpoints - Standardized API documentation - Language-agnostic integration - Easy API testing - Swagger/OpenAPI support Each client tool is designed with specific use cases and developer needs in mind. Choose the one that best fits your development environment and requirements. [Flow Client Library (FCL)]: ./fcl-js/index.md [Flow Go SDK]: ./flow-go-sdk/index.md [Flow Python SDK]: https://github.com/janezpodhostnik/flow-py-sdk [FlowClient]: https://github.com/glucode/flow_client [Flow JVM SDK]: https://github.com/onflow/flow-jvm-sdk [flow-swift]: https://github.com/Outblock/flow-swift [flow.net]: https://github.com/tyronbrand/flow.net [Rust SDK]: https://github.com/fee1-dead/flow.rs [PHP SDK]: https://github.com/mayvenstudios/flow-php-sdk [OnFlow]: https://github.com/nkezhaya/on_flow [Flow OpenAPI]: /http-api --- # File: /md/build/tools/clients/fcl-js.md --- sidebar_position: 3 --- # Flow Client Library (FCL) :::info If you want to build a frontend, use the [React SDK]. If you're familiar with Solidity and the EVM world, the [React SDK] is to wagmi as FCL is to viem. ::: ## What is FCL? The **Flow Client Library (FCL) JS** is a package designed to facilitate interactions between apps, wallets, and the Flow blockchain. It provides a standardized way for applications to connect with users and their wallets, **eliminating the need for custom integrations**. ### Key Features: - **Universal Wallet Support** Works seamlessly with all FCL-compatible wallets, which makes authentication simple. - **Secure Authentication** Standardized authentication flow ensures a smooth user experience. - **Blockchain Interactions** Allows querying, mutating, and interacting with smart contracts on Flow. - **Full-Featured Utilities** Offers built-in functions to streamline blockchain development. - **Flexible Environment** Can run in both browser and server environments, though wallet interactions are browser-only. FCL was created to make building Flow-connected applications **easy, secure, and scalable** by defining **standardized communication patterns** between wallets, applications, and users. For iOS, we also offer [FCL Swift]. --- ## Get started ### Requirements - Node version `v12.0.0 or higher`. ### Installation To use the FCL JS in your application, install with **yarn** or **npm** ```shell npm i -S @onflow/fcl ``` ```shell yarn add @onflow/fcl ``` #### Import **ES6** ```js import * as fcl from '@onflow/fcl'; ``` **Node.js** ```js const fcl = require('@onflow/fcl'); ``` --- ## FCL for dApps #### Wallet interactions - _Wallet Discovery_ and _Sign-up/Login_: Onboard users with ease. Never worry about how to support multiple wallets. Authenticate users with any [FCL compatible wallet]. ```js // in the browser import * as fcl from '@onflow/fcl'; fcl.config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', // Endpoint set to Testnet }); fcl.authenticate(); ``` :::info For advanced configuration patterns including scoped clients and multi-tenancy, see the [`createFlowClient` reference documentation]. ![FCL Default Discovery UI](images/discovery.png) A [Dapper Wallet] developer account is required. - _Interact with smart contracts_: Authorize transactions via the user's chosen wallet. - _Prove ownership of a wallet address_: Sign and verify user signed data. ::: #### Blockchain interactions - _Query the chain_: Send arbitrary Cadence scripts to the chain and receive back decoded values ```js import * as fcl from '@onflow/fcl'; const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg('0xba1132bc08f82fe2', t.Address), // addr: Address ], }); console.log(result); // 13 ``` - _Mutate the chain_: Send arbitrary transactions with your own signatures or via a user's wallet to perform state changes on chain. ```js import * as fcl from '@onflow/fcl'; // in the browser, FCL will automatically connect to the user's wallet to request signatures to run the transaction const txId = await fcl.mutate({ cadence: ` import Profile from 0xba1132bc08f82fe2 transaction(name: String) { prepare(account: AuthAccount) { account.borrow<&{Profile.Owner}>(from: Profile.privatePath)!.setName(name) } } `, args: (arg, t) => [arg('myName', t.String)], }); ``` #### Utilities - Get account details from any Flow address - Get the latest block - Transaction status polling - Event polling - Custom authorization functions ## Typescript support FCL JS supports TypeScript. If you need to import specific types, you can do so via the [@onflow/typedefs] package. ```typescript import { CurrentUser } from '@onflow/typedefs'; const newUser: CurrentUser = { addr: null, cid: null, expiresAt: null, f_type: 'User', f_vsn: '1.0.0', loggedIn: null, services: [], }; ``` For all type definitions available, see [this file] ## Next Steps - See the [Flow App Quick Start]. - See the full [API Reference] for all FCL functionality. - Learn Flow's smart contract language to build any script or transactions: [Cadence]. - Explore all of Flow [docs and tools]. --- ## FCL for wallet providers Wallet providers on Flow have the flexibility to build their user interactions and UI through a variety of ways: - Front channel communication via Iframe, pop-up, tab, or extension. - Back channel communication via HTTP. FCL is agnostic to the communication channel and be configured to create both custodial and non-custodial wallets. This allows users to interact with wallet providers without the need to download an app or extension. The communication channels involve responding to a set of pre-defined FCL messages to deliver the requested information to the dApp. To implement a FCL compatible wallet on Flow is as simple as filling in the responses with the appropriate data when FCL requests them. If you use any of the front-channel communication methods, FCL also provides a set of [wallet utilities] to simplify this process. ### Current wallet providers - [Flow Wallet] - [NuFi Wallet] - [Blocto] - [Ledger] (limited transaction support) - [Dapper Wallet] ### Wallet discovery It can be difficult to get users to discover new wallets on a chain. To solve this, we created a [wallet discovery service] that can be configured and accessed through FCL to display all available Flow wallet providers to the user. This means: - dApps can display and support all FCL compatible wallets that launch on Flow without the need to change any code. - Users don't need to sign up for new wallets - they can carry over their current one to any dApp that uses FCL for authentication and authorization. The discovery feature can be used via API allowing you to customize your own UI or you can use the default UI without any additional configuration. :::info To get your wallet added to the discovery service, make a PR in [fcl-discovery]. ::: ### Build a FCL compatible wallet - Read the [wallet guide] to understand the implementation details. - Review the architecture of the [FCL dev wallet] for an overview. - If you want to build a non-custodial wallet, see the [Account API] and the [FLIP] on derivation paths and key generation. --- ## 🛠 Want to use the Flow SDK directly? If you prefer to interact with Flow at a **lower level** without FCL, you can use the [Flow JavaScript SDK] directly. The SDK provides raw access to Flow's API to send transactions, execute scripts, and manage accounts. FCL is built **on top of the Flow SDK**, which makes it easier to handle authentication, wallet interactions, and dApp connectivity. Choose the approach that best fits your use case. ## Support - Notice a problem or want to request a feature? [Add an issue]. - Join the Flow community on [Discord] to keep up to date and to talk to the team. - Read the [Contributing Guide] to learn how to contribute to the project. [React SDK]: ../../react-sdk/index.mdx [FCL Swift]: https://github.com/Outblock/fcl-swift [FCL compatible wallet]: #current-wallet-providers [`createFlowClient` reference documentation]: ./packages-docs/fcl/createFlowClient.md [Dapper Wallet]: https://meetdapper.com/developers [@onflow/typedefs]: https://github.com/onflow/fcl-js/tree/master/packages/typedefs [this file]: https://github.com/onflow/fcl-js/blob/master/packages/typedefs/src/index.ts [Flow App Quick Start]: ../../../../blockchain-development-tutorials/cadence/getting-started/index.md [API Reference]: ./packages-docs/fcl/index.md [Cadence]: https://cadence-lang.org [docs and tools]: https://developers.flow.com [wallet utilities]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/wallet-utils/index.js [Flow Wallet]: https://wallet.flow.com/ [NuFi Wallet]: https://nu.fi/ [Blocto]: https://blocto.portto.io/en/ [Ledger]: https://ledger.com [Dapper Wallet]: https://www.meetdapper.com/ [wallet discovery service]: https://github.com/onflow/fcl-discovery [fcl-discovery]: https://github.com/onflow/fcl-discovery [wallet guide]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/wallet-provider-spec/draft-v4.md [FCL dev wallet]: https://github.com/onflow/fcl-dev-wallet [Account API]: https://github.com/onflow/flow-account-api [FLIP]: https://github.com/onflow/flow/pull/727 [Flow JavaScript SDK]: ./packages-docs/sdk/index.md [Add an issue]: https://github.com/onflow/fcl-js/issues [Discord]: https://discord.gg/flow [Contributing Guide]: https://github.com/onflow/fcl-js/blob/master/CONTRIBUTING.md --- # File: /md/build/tools/clients/fcl-js/authentication.md # Authentication Authentication in Flow Client Library (FCL) is closely tied to the concept of `currentUser`. In fact, `fcl.authenticate` and `fcl.unauthenticate` are simply aliases for `fcl.currentUser.authenticate()` and `fcl.currentUser.unauthenticate()`, respectively. So, let’s take a closer look at `currentUser`. As an onchain app developer who uses FCL, the primary authentication functionalities revolve around how to: - Determine the `currentUser` and whether they are logged in. - Log a user in. - Log a user out. Due to the way FCL works, to log in and sign up are essentially the same process. # Retrieve information about the current user FCL provides two ways to get information about the current user: 1. **A promise-based method** that returns a snapshot of the user’s data. 2. **A subscription-based method** that triggers a callback function with the latest user information whenever it changes. ### Snapshot of the current user ```javascript import * as fcl from '@onflow/fcl'; const currentUser = await fcl.currentUser.snapshot(); console.log('The Current User:', currentUser); ``` ### Subscribe to the Current User ```javascript import * as fcl from '@onflow/fcl'; // Returns an unsubscribe function const unsubscribe = fcl.currentUser.subscribe((currentUser) => { console.log('The Current User:', currentUser); }); ``` # Authenticate and unauthenticate The TL;DR: Call `fcl.authenticate()` to log in and `fcl.unauthenticate()` to log out. On Flow mainnet, no additional configuration is needed, because your app’s users will go through the authentication process and can use any FCL-compatible wallet provider. During development, you’ll likely want to configure your app to use [`@onflow/dev-wallet`]. The [Quick Start] guide will walk you through how to set it up. We also recommend that you use the [FCL Discovery Service] to help users discover and connect to FCL-compatible wallets. Whether you're new to building onchain, or an established veteran, we’re here to help. If you run into any issues, reach out to us on [Discord] — we’re happy to assist! [`@onflow/dev-wallet`]: https://github.com/onflow/fcl-dev-wallet [Quick Start]: ../../../../blockchain-development-tutorials/cadence/getting-started/index.md [FCL Discovery Service]: discovery.md [Discord]: https://discord.gg/flow --- # File: /md/build/tools/clients/fcl-js/configure-fcl.md --- title: How to Configure FCL --- ## Configuration Flow Client Library (FCL) provides a mechanism to configure various aspects of its behavior. The key principle is that when you switch between different Flow Blockchain environments (for example, Local Emulator → Testnet → Mainnet), the only required change should be your FCL configuration. ## Set configuration values Values only need to be set once. We recommend that you do this once and as early in the life cycle as possible. To set a configuration value, the `put` method on the `config` instance needs to be called, the `put` method returns the `config` instance so they can be chained. ```javascript import * as fcl from '@onflow/fcl'; fcl .config() // returns the config instance .put('foo', 'bar') // configures "foo" to be "bar" .put('baz', 'buz'); // configures "baz" to be "buz" ``` :::info For advanced use cases that require scoped configuration, isolated client instances, or multi-tenancy support, see the [`createFlowClient` reference documentation]. ::: ## Get configuration values The `config` instance has an asynchronous `get` method. You can also pass it a fallback value in case the configuration state does not include what you want. ```javascript import * as fcl from '@onflow/fcl'; fcl.config().put('foo', 'bar').put('woot', 5).put('rawr', 7); const FALLBACK = 1; async function addStuff() { var woot = await fcl.config().get('woot', FALLBACK); // will be 5 -- set in the config before var rawr = await fcl.config().get('rawr', FALLBACK); // will be 7 -- set in the config before var hmmm = await fcl.config().get('hmmm', FALLBACK); // will be 1 -- uses fallback because this isnt in the config return woot + rawr + hmmm; } addStuff().then((d) => console.log(d)); // 13 (5 + 7 + 1) ``` ## Common configuration keys - `accessNode.api` -- API URL for the Flow Blockchain Access Node you want to communicate with. - `app.detail.title` - **(INTRODUCED `@onflow/fcl@0.0.68`)** Your applications title, can be requested by wallets and other services. Used by WalletConnect plugin and Wallet Discovery service. - `app.detail.icon` - **(INTRODUCED `@onflow/fcl@0.0.68`)** URL for your applications icon, can be requested by wallets and other services. Used by WalletConnect plugin and Wallet Discovery service. - `app.detail.description` - **(INTRODUCED `@onflow/fcl@1.11.0`)** Your applications description, can be requested by wallets and other services. Used by WalletConnect plugin and Wallet Discovery service. - `app.detail.url` - **(INTRODUCED `@onflow/fcl@1.11.0`)** Your applications URL, can be requested by wallets and other services. Used by WalletConnect plugin and Wallet Discovery service. - `challenge.handshake` -- **(DEPRECATED `@onflow/fcl@0.0.68`)** Points FCL at the Wallet or Wallet Discovery mechanism. - `discovery.wallet` -- **(INTRODUCED `@onflow/fcl@0.0.68`)** Points FCL at the Wallet or Wallet Discovery mechanism. - `discovery.wallet.method` -- Describes which service strategy a wallet should use: `IFRAME/RPC`, `POP/RPC`, `TAB/RPC`, `HTTP/POST`, `EXT/RPC` - `env` -- **(DEPRECATED `@onflow/fcl@1.0.0`)** Used in conjunction with stored interactions. Possible values: `local`, `testnet`, `mainnet` - `fcl.limit` -- Specifies fallback compute limit if not provided in transaction. Provided as integer. - `flow.network` (recommended) -- **(INTRODUCED `@onflow/fcl@1.0.0`)** Used in conjunction with stored interactions and provides FCLCryptoContract address for `testnet` and `mainnet`. Possible values: `local`, `testnet`, `mainnet`. - `service.OpenID.scopes` - **(INTRODUCED `@onflow/fcl@0.0.68`)** Open ID Connect claims for Wallets and OpenID services. - `walletconnect.projectId` -- **(INTRODUCED `@onflow/fcl@1.11.0`)** Your app's WalletConnect project ID. See [WalletConnect Cloud] to obtain a project ID for your application. - `walletconnect.disableNotifications` -- **(INTRODUCED `@onflow/fcl@1.13.0`)** Flag to disable pending WalletConnect request notifications within the application's UI. Default is `false`. ## Use contracts in scripts and transactions ### Address replacement Configuration keys that start with `0x` will be replaced in FCL scripts and transactions. This allows you to write your script or transaction Cadence code once and not have to change it when you point your application at a difference instance of the Flow Blockchain. ```javascript import * as fcl from '@onflow/fcl'; fcl.config().put('0xFungibleToken', '0xf233dcee88fe0abe'); async function myScript() { return fcl .send([ fcl.script` import FungibleToken from 0xFungibleToken // will be replaced with 0xf233dcee88fe0abe because of the configuration access(all) fun main() { /* Rest of the script goes here */ } `, ]) .then(fcl.decode); } async function myTransaction() { return fcl .send([ fcl.transaction` import FungibleToken from 0xFungibleToken // will be replaced with 0xf233dcee88fe0abe because of the configuration transaction { /* Rest of the transaction goes here */ } `, ]) .then(fcl.decode); } ``` #### Example ```javascript import * as fcl from '@onflow/fcl'; fcl .config() .put('flow.network', 'testnet') .put('accessNode.api', 'https://rest-testnet.onflow.org') .put('discovery.wallet', 'https://fcl-discovery.onflow.org/testnet/authn') .put('walletconnect.projectId', 'YOUR_PROJECT_ID') .put('app.detail.title', 'Test Harness') .put('app.detail.icon', 'https://i.imgur.com/r23Zhvu.png') .put('app.detail.description', 'A test harness for FCL') .put('app.detail.url', 'https://myapp.com') .put('0xFlowToken', '0x7e60df042a9c0868'); ``` ### Use `flow.json` A simpler way to import contracts in scripts and transactions is to use the `config.load` method to ingest your contracts from your `flow.json` file. This keeps the import syntax unified across tools and lets FCL figure out which address to use for what network based on the network provided in the config. To use `config.load` you must first import your `flow.json` file and then pass it to `config.load` as a parameter. ```javascript import { config } from '@onflow/fcl'; import flowJSON from '../flow.json'; config({ 'flow.network': 'testnet', 'accessNode.api': 'https://rest-testnet.onflow.org', 'discovery.wallet': `https://fcl-discovery.onflow.org/testnet/authn`, }).load({ flowJSON }); ``` Let's say your `flow.json` file looks like this: ``` { "contracts": { "HelloWorld": "cadence/contracts/HelloWorld.cdc" } } ``` Then in your scripts and transactions, all you have to do is: ``` import "HelloWorld" ``` FCL will automatically replace the contract name with the address for the network you use. :::info Never put private keys in your `flow.json`. Instead, use the [key/location syntax] to separate your keys into a separate git ignored file. ::: [`createFlowClient` reference documentation]: ./packages-docs/fcl/createFlowClient.md [WalletConnect Cloud]: https://cloud.walletconnect.com/sign-in [key/location syntax]: ../../flow-cli/flow.json/security.md --- # File: /md/build/tools/clients/fcl-js/cross-vm.md --- title: Cross VM Packages description: FCL packages for cross-VM (Flow + EVM) applications. --- # FCL Cross-VM Packages These packages allow you to leverage Flow’s Cadence-Owned Account (COA) within Ethereum tooling (for example, Wagmi, RainbowKit). They provide a unified approach for cross-VM apps on Flow and EVM, which lets you perform EVM-like operations will Cadence accounts. For background and motivation, see the [FCL Ethereum Provider for Cross-VM Apps FLIP #316]. | Package | Purpose | |-----------------------------------------------|-------------------------------------------------------------------------------------------------| | [@onflow/fcl-ethereum-provider] | Provides an EIP-1193-compliant Ethereum provider backed by an FCL-authenticated COA. | | [@onflow/fcl-wagmi-adapter] | Integrates Flow-based COAs with Wagmi, and exposes them as Ethereum accounts in your dApp. | | [@onflow/fcl-rainbowkit-adapter]| Allows a Flow-based wallet option in your RainbowKit wallet selection modal. | ## `@onflow/fcl-ethereum-provider` - **Description**: A drop-in EIP-1193 provider that authenticates users via [Flow Client Library (FCL)] and lets them sign transactions and messages with their COA. - **Use Cases**: - Integrate Flow EVM with any generic EVM library or framework. - Direct control over JSON-RPC calls (for example, `provider.request({ method: 'eth_sendTransaction' })`). - **Link to Docs**: [Read the @onflow/fcl-ethereum-provider Reference »] ## `@onflow/fcl-wagmi-adapter` - **Description**: A Wagmi connector that uses `@onflow/fcl-ethereum-provider` under the hood so you can sign in with your COA through standard Wagmi flows. - **Use Cases**: - Add Flow-based COAs to a current Wagmi-powered dApp as if they were Ethereum wallets. - **Link to Docs**: [Read the @onflow/fcl-wagmi-adapter Reference »] ## `@onflow/fcl-rainbowkit-adapter` - **Description**: A RainbowKit adapter that surfaces a Flow-based wallet in the wallet selection modal, wheich makes it easy to sign transactions via COAs in a RainbowKit environment. - **Use Cases**: - Offer Flow-based wallets (such as Flow Wallet) alongside popular Ethereum wallets in RainbowKit. - **Link to Docs**: [Read the @onflow/fcl-rainbowkit-adapter Reference »] [FCL Ethereum Provider for Cross-VM Apps FLIP #316]: https://github.com/onflow/flips/blob/c0fe9b71a9afb85fe70a69cf7c0870b5d327e679/application/20241223-fcl-ethereum-provider.md [@onflow/fcl-ethereum-provider]: #onflowfcl-ethereum-provider [@onflow/fcl-wagmi-adapter]: #onflowfcl-wagmi-adapter [@onflow/fcl-rainbowkit-adapter]: #onflowfcl-rainbowkit-adapter [Flow Client Library (FCL)]: https://developers.flow.com/ [Read the @onflow/fcl-ethereum-provider Reference »]: ethereum-provider.mdx [Read the @onflow/fcl-wagmi-adapter Reference »]: wagmi-adapter.mdx [Read the @onflow/fcl-rainbowkit-adapter Reference »]: rainbowkit-adapter.mdx --- # File: /md/build/tools/clients/fcl-js/cross-vm/ethereum-provider.mdx --- sidebar_position: 1 title: FCL Ethereum Provider description: An EIP-1193 provider that uses an FCL-authenticated Cadence Owned Account (COA) under the hood. --- :::info This package is currently in alpha and is subject to change. ::: # FCL Ethereum Provider Exposes a client-side [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible Ethereum provider that uses an FCL-authenticated Cadence Owned Account (COA) under the hood. If a wallet does not natively provide EVM capabilities, this provider emulates Ethereum JSON-RPC by delegating to FCL for signing and COA interactions. ## Installation ```bash npm install @onflow/fcl-ethereum-provider ``` ## Usage ```ts import * as fcl from "@onflow/fcl" import { createEthereumProvider } from "@onflow/fcl-ethereum-provider" // Configure FCL (pointing to whichever Flow network you require) fcl.config({ "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", }) // Create the EIP-1193 provider const provider = createEthereumProvider({ // Optional configuration: // service?: Service // Custom FCL service config // gateway?: Eip1193Provider | string // EVM Gateway provider or URL }) // Example: request EVM-style accounts (COA addresses) const accounts = await provider.request({ method: "eth_requestAccounts" }) console.log("EVM Accounts:", accounts) // Use the same session to sign a message const signature = await provider.request({ method: "personal_sign", params: ["0x68656c6c6f20776f726c64", accounts[0]], // hex-encoded "hello world" }) console.log("Signature:", signature) // Or send transactions const txHash = await provider.request({ method: "eth_sendTransaction", params: [ { from: accounts[0], to: "0x1234...", data: "0xabcd1234...", value: "0x0", }, ], }) console.log("Transaction Hash:", txHash) ``` ## API ### `createEthereumProvider(config?: CreateEthereumProviderConfig): Eip1193Provider` - **Parameters** - `config.service?: Service` An [FCL “Service” object][fcl-service-docs] for custom FCL authentication flows. If omitted, the default FCL discovery service is used. - `config.gateway?: Eip1193Provider | string` An EIP-1193 provider (or a string URL) pointing to a Flow EVM gateway. Defaults to the public Flow EVM gateway if omitted. - **Returns**: An [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider instance you can pass into EVM tooling or interact with directly in your app. ## Supported JSON-RPC Methods Below are the main request methods handled within the FCL Ethereum provider: 1. **`eth_requestAccounts` / `eth_accounts`** - **Behavior**: - Invokes the FCL authentication flow (if not already authenticated) - Returns the Cadence-Owned Account (COA) address - Stores the COA at `/storage/evm` (creates if missing) 2. **`eth_sendTransaction`** - **Behavior**: - Wraps the transaction in a Cadence transaction that invokes `coa.call(...)` in the Flow EVM - Uses the user’s authenticated COA for signing - Returns the resulting EVM transaction hash 3. **`personal_sign`** - **Behavior**: - Requests a user signature via FCL’s `signUserMessage` or equivalent mechanism - Returns an RLP-encoded [COA ownership proof](https://github.com/onflow/flow-go/blob/master/fvm/evm/types/proof.go#L139) in place of a raw secp256k1 signature 5. **`eth_chainId`** - **Behavior**: - Returns the numeric Flow EVM chain ID (e.g., `0x747` for Flow EVM Mainnet) 6. **`wallet_switchEthereumChain`** - **Behavior**: - Allows dApps to request switching to a different Flow EVM chain (e.g. testnet to mainnet). - Under the hood, this can trigger reconfiguration of FCL for a different Flow access node and Flow EVM gateway if recognized. - If the requested chain ID is not recognized, the call will throw an error (matching EIP-1193 standard error codes). 7. **`wallet_addEthereumChain`** - **Behavior**: - Allows a dApp to request adding a Flow EVM chain config. - If the chain is recognized by the provider or is one the provider can handle, it will register it. Otherwise, it may reject with an EIP-1193 error. - Since Flow EVM is typically a single chain per environment, usage is limited. However, in principle, custom EVM networks or local dev can be added if your provider/gateway supports them. ### Fallback Behavior Any unknown or unsupported request methods will be proxied to the `gateway` (if you provided a standard JSON-RPC URL or EIP-1193 provider). If the gateway does not handle them, an error will be returned. ## Provider Events - **`connect`**: Emitted once the user successfully authenticates via FCL, indicating that the provider is ready. - **`disconnect`**: Emitted if the FCL session ends or user explicitly logs out, severing the session. - **`accountsChanged`**: Emitted when the current user changes (e.g. re-authentication, or switching user in the wallet). - **`chainChanged`**: Emitted when the user switches to a different Flow EVM chain (e.g. testnet to mainnet). --- # File: /md/build/tools/clients/fcl-js/cross-vm/rainbowkit-adapter.mdx --- sidebar_position: 1 title: FCL Rainbowkit Adapter description: FCL adapter for using Flow wallets in RainbowKit applications. --- :::info This package is currently in alpha and is subject to change. ::: # FCL RainbowKit Adapter Offers a **RainbowKit**-compatible wallet definition that uses Flow’s COA via FCL. Once installed, RainbowKit can display a “Flow Wallet” (or other FCL-enabled wallets) in its wallet selection modal. ## Installation ```bash npm install @onflow/fcl-rainbowkit-adapter ``` ## Usage Below is a typical usage example that shows how to set up a **RainbowKit** config for the Flow testnet, using this adapter. (From your provided sample.) ```ts import * as fcl from '@onflow/fcl' import { createFclConnector, flowWallet, useIsCadenceWalletConnected } from '@onflow/fcl-rainbowkit-adapter' import { connectorsForWallets } from '@rainbow-me/rainbowkit' import { flowTestnet } from 'wagmi/chains' import { createConfig, http } from 'wagmi' // Configure FCL (Flow testnet in this example) fcl.config({ "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", "walletconnect.projectId": "9b70cfa398b2355a5eb9b1cf99f4a981", // example WC projectId }) // Create a list of connectors from your wallets const connectors = connectorsForWallets([ { groupName: "Recommended", wallets: [ flowWallet(), ], }, ], { appName: 'RainbowKit demo', projectId: '9b70cfa398b2355a5eb9b1cf99f4a981', }) // Wagmi config export const config = createConfig({ chains: [flowTestnet], connectors, ssr: true, transports: { [flowTestnet.id]: http(), } }); // In your React component function MyApp() { const isCadenceConnected = useIsCadenceWalletConnected(config) return (
    {isCadenceConnected ? (

    Cadence wallet is connected!

    ) : (

    Please connect your Cadence wallet

    )}
    ) } ``` ## API ### `flowWallet(options?: FlowWalletOptions): RainbowKitWallet` - Returns a RainbowKit-compatible wallet definition that integrates **@onflow/fcl-ethereum-provider**. - **Parameters** - `options?: FlowWalletOptions` – optional configuration, such as names/icons or custom gateway endpoints. - **Returns**: A `RainbowKitWallet` object to be included in `connectorsForWallets`. ### `createFclConnector(config?: CreateFclConnectorOptions): Connector` - A lower-level helper to build your own FCL-based EIP-1193 connectors for RainbowKit if you don't want the preconfigured `flowWallet`. - **Parameters** - `config?: CreateFclConnectorOptions` – typical Wagmi + FCL config object (i.e., chain ID, network URL, FCL services, etc.). - **Returns**: A valid Wagmi `Connector` for EVM interactions via FCL. ### `useIsCadenceWalletConnected(config: Config): boolean` A React hook that monitors the connection status of both FCL (Cadence) and Wagmi accounts to determine whether a Cadence-aware wallet is connected. - **Parameters** - `config: Config` – The Wagmi configuration object - **Returns**: `boolean` – `true` when both Cadence-aware wallet is connected, `false` if no wallet, or an EVM-only wallet is connected. --- # File: /md/build/tools/clients/fcl-js/cross-vm/wagmi-adapter.mdx --- sidebar_position: 1 title: FCL Wagmi Adapter description: FCL adapter for using Cadence-Owned Accounts (COAs) in Wagmi applications. --- :::info This package is currently in alpha and is subject to change. ::: # FCL Wagmi Adapter Provides a **Wagmi** connector that uses **@onflow/fcl-ethereum-provider** under the hood, allowing you to integrate Flow-based Cadence-Owned Accounts (COAs) seamlessly into Wagmi applications. ## Installation ```bash npm install @onflow/fcl-wagmi-adapter ``` ## Usage **Basic Example**: ```ts import { createClient, configureChains } from 'wagmi' import { fclWagmiConnector } from '@onflow/fcl-wagmi-adapter' import { flowTestnet } from 'wagmi/chains' import { publicProvider } from 'wagmi/providers/public' import * as fcl from '@onflow/fcl' // Configure FCL for Flow fcl.config({ "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", }) // Set up Wagmi for Flow Testnet const { chains, provider } = configureChains( [flowTestnet], [publicProvider()] ) // Create a connector that uses FCL under the hood const fclConnector = fclWagmiConnector({ // optional: you can pass any config your provider or FCL needs }) // Create the Wagmi client const wagmiClient = createClient({ autoConnect: true, connectors: [fclConnector], provider, }) // The rest of your dApp logic... ``` ## API ### `fclWagmiConnector(options?: FclWagmiConnectorOptions): Connector` - **Parameters** - `options?: object` – any additional configuration for the underlying FCL provider (gateway URL, custom FCL service, etc.) - **Returns**: A Wagmi `Connector` object that can be used in `createClient` or `getDefaultConfig`. **Notes**: - This connector essentially wraps `@onflow/fcl-ethereum-provider` as an EIP-1193 provider to talk to Flow EVM via Wagmi. - The user’s authenticated COA is exposed as the “account” in Wagmi context. --- # File: /md/build/tools/clients/fcl-js/discovery.md --- title: Wallet Discovery --- ## Wallet Discovery It's a challenge to know all the wallets available to users on a blockchain. Flow Client Library's (FCL) Discovery mechanism relieves much of the burden of Flow compatible wallet integration and lets developers focus on building their dApp and providing as many options as possible to their users. There are two ways an app can use Discovery: 1. The **UI version** which can be configured for display via iFrame, Popup, or Tab. 2. The **API version** which allows you to access authentication services directly in your code via `fcl.discovery.authn` method which we'll describe below. ## UI version When a user authenticates via FCL with Discovery UI, they receive a list of services they can use to login. ![FCL Default Discovery UI](./images/discovery.png) This method is the simplest way to integrate Discovery and its wallets and services into your app. All you have to do is configure `discovery.wallet` with the host endpoint for testnet or mainnet. :::info Opt-in wallets, like Ledger and Dapper Wallet, require you to explicitly state you'd like to use them. For more information on how to include opt-in wallets, [see these docs]. A [Dapper Wallet] developer account is required. To turn on Dapper Wallet inside FCL, you need to [follow this guide]. ::: ```javascript import { config } from '@onflow/fcl'; config({ 'accessNode.api': 'https://rest-testnet.onflow.org', 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', }); ``` Any time you call `fcl.authenticate` the user will be presented with that screen. To change the default view from iFrame to popup or tab set `discovery.wallet.method` to `POP/RPC` (opens as a popup) or `TAB/RPC` (opens in a new tab). For more info about service methods, see [here]. ### Branding Discovery UI As of version 0.0.79-alpha.4, dApps can now display an app title and app icon in the Discovery UI when you a few values in their FCL app config. This branding provides users with messaging that has clear intent before they authenticate to add a layer of trust. All you have to do is set `app.detail.icon` and `app.detail.title` like this: ```javascript import { config } from '@onflow/fcl'; config({ 'app.detail.icon': 'https://placekitten.com/g/200/200', 'app.detail.title': 'Kitten Dapp', }); ``` :::info If these configuration options aren't set, dApps that use the Discovery API will still display a default icon and "Unknown App" as the title when they attempt to authorize a user who is not logged in. We highly recommended that you set these values accurately before you go live. ## API version If you want more control over your authentication UI, the Discovery API is also simple to use as it exposes Discovery directly in your code via `fcl`. Setup still requires configuration of the Discovery endpoint, but when you use the API, it is set via `discovery.authn.endpoint` as shown below. ```javascript import { config } from '@onflow/fcl'; config({ 'accessNode.api': 'https://rest-testnet.onflow.org', 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', }); ``` You can access services in your Dapp from `fcl.discovery`: ```javascript import * as fcl from '@onflow/fcl'; fcl.discovery.authn.subscribe(callback); // OR fcl.discovery.authn.snapshot(); ``` To authenticate with a service (for example, when a user click's "login"), pass the selected service to the `fcl.authenticate` method described in [the API reference]: ```jsx fcl.authenticate({ service }); ``` A simple React component may end up looking like this: ```jsx import './config'; import { useState, useEffect } from 'react'; import * as fcl from '@onflow/fcl'; function Component() { const [services, setServices] = useState([]); useEffect( () => fcl.discovery.authn.subscribe((res) => setServices(res.results)), [], ); return (
    {services.map((service) => ( ))}
    ); } ``` Helpful fields for your UI can be found in the `provider` object inside of the service. Fields include the following: ```json { ..., "provider": { "address": "0xf086a545ce3c552d", "name": "Blocto", "icon": "/images/blocto.png", "description": "Your entrance to the blockchain world.", "color": "#afd8f7", "supportEmail": "support@blocto.app", "authn_endpoint": "https://flow-wallet-testnet.blocto.app/authn", "website": "https://blocto.portto.io" } } ``` ## Network configuration ### Discovery UI URLs | Environment | Example | | ----------- | ------------------------------------------------ | | Mainnet | `https://fcl-discovery.onflow.org/authn` | | Testnet | `https://fcl-discovery.onflow.org/testnet/authn` | | Local | `https://fcl-discovery.onflow.org/local/authn` | ### Discovery API endpoints | Environment | Example | | ----------- | ---------------------------------------------------- | | Mainnet | `https://fcl-discovery.onflow.org/api/authn` | | Testnet | `https://fcl-discovery.onflow.org/api/testnet/authn` | | Local | `https://fcl-discovery.onflow.org/api/local/authn` | :::info Local will return [Dev Wallet] on emulator for local development with the default port of 8701. If you'd like to override the default port, add `?port=0000` and set the port to whatever you'd like to override it to. ::: ## Other configuration :::info Configuration works across both UI and API versions of Discovery. ::: ### Include opt-in wallets **Start in FCL v0.0.78-alpha.10** Opt-in wallets are those that don't have support for authentication, authorization, and user signature services. Or, support only a limited set of transactions. You can include opt-in wallets with either **wallet UIDs** (recommended) or service account addresses: **Use wallet UIDs (recommended):** ```javascript import * as fcl from '@onflow/fcl'; fcl.config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', 'discovery.authn.include': ['dapper-wallet', 'ledger'], // Wallet UIDs }); ``` **Use service Aacount addresses:** ```javascript import * as fcl from '@onflow/fcl'; fcl.config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', 'discovery.authn.include': ['0x82ec283f88a62e65', '0x9d2e44203cb13051'], // Testnet addresses }); ``` **Opt-In Wallet Identifiers** | Wallet | Wallet UID | Mainnet Address | Testnet Address | | ------------- | --------------- | ------------------ | ------------------ | | Dapper Wallet | `dapper-wallet` | 0xead892083b3e2c6c | 0x82ec283f88a62e65 | | Ledger | `ledger` | 0xe5cd26afebe62781 | 0x9d2e44203cb13051 | To learn more about other possible configurations, check out the [Discovery Github Repo]. ### Exclude wallets To exclude wallets from FCL Discovery, you can use the `discovery.authn.exclude` configuration option. You can specify wallets with either **wallet UIDs** (recommended) or service account addresses: **Use wallet UIDs (recommended):** ```javascript import * as fcl from '@onflow/fcl'; fcl.config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', 'discovery.authn.exclude': ['nufi'], // Wallet UIDs to exclude }); ``` **Use service account addresses:** ```javascript import * as fcl from '@onflow/fcl'; fcl.config({ 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', 'discovery.authn.endpoint': 'https://fcl-discovery.onflow.org/api/testnet/authn', 'discovery.authn.exclude': ['0x123', '0x456'], // Service account addresses to exclude }); ``` **Available wallet UIDs** You can use any of the following wallet identifiers with `discovery.authn.include` or `discovery.authn.exclude`: | Wallet | UID | Mainnet Address | Testnet Address | Type | | ------------- | --------------- | ------------------ | ------------------ | ------- | | Flow Wallet | `flow-wallet` | 0x33f75ff0b830dcec | 0x33f75ff0b830dcec | Default | | NuFi | `nufi` | 0x95b85a9ef4daabb1 | - | Default | | Blocto | `blocto` | 0x55ad22f01ef568a1 | 0x55ad22f01ef568a1 | Default | | Dapper Wallet | `dapper-wallet` | 0xead892083b3e2c6c | 0x82ec283f88a62e65 | Opt-in | | Ledger | `ledger` | 0xe5cd26afebe62781 | 0x9d2e44203cb13051 | Opt-in | :::info Default wallets appear in Discovery by default. Opt-in wallets must be explicitly included with `discovery.authn.include`. You can use either the wallet UID or the service account address to filter. For the most up-to-date list of available wallets, see the [fcl-discovery wallet data] in the official repository. ::: ### WalletConnect configuration To configure WalletConnect, add a WalletConnect project ID to the FCL config: ```javascript import * as fcl from '@onflow/fcl'; fcl.config({ 'walletconnect.projectId': 'YOUR_WALLETCONNECT_PROJECT_ID', }); ``` [see these docs]: ./packages-docs/fcl/index.md#configuration [Dapper Wallet]: https://meetdapper.com/developers [follow this guide]: https://docs.meetdapper.com/quickstart [here]: https://github.com/onflow/fcl-js/blob/9bce741d3b32fde18b07084b62ea15f9bbdb85bc/packages/fcl/src/wallet-provider-spec/draft-v3.md [the API reference]: ./packages-docs/fcl/authenticate.md [Dev Wallet]: https://github.com/onflow/fcl-dev-wallet [Discovery Github Repo]: https://github.com/onflow/fcl-discovery [fcl-discovery wallet data]: https://github.com/onflow/fcl-discovery/tree/master/data/wallets --- # File: /md/build/tools/clients/fcl-js/installation.mdx # Installation This chapter explains the installation of the Flow CLient Library (FCL) JS library in your system. However, before we move to the installation, let's verify the prerequisite first. ## Prerequisite - Node.js version v12.0.0 or higher. FCL JS depends on Node.js version v12.0.0 or higher. You can check your currently-installed version with the below command: ```javascript node --version ``` If Node.js is not installed on your system, you can visit [Node.js Download](https://nodejs.org/en/download/) to download and install it. Install FCL JS with **npm** or **yarn** ```shell npm i -S @onflow/fcl ``` ```shell yarn add @onflow/fcl ``` #### Importing **ES6** ```js import * as fcl from "@onflow/fcl"; ``` **Node.js** ```js const fcl = require("@onflow/fcl"); ``` --- # File: /md/build/tools/clients/fcl-js/interaction-templates.mdx --- title: Interaction Templates --- # Interaction Templates > Interaction Templates are a concept established in FLIP-934. Read the FLIP [here](https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md) > "Interaction" in this context refers to the higher order term establised in FLIP-934 that encompases a transaction and script, things that _interact_ with the blockchain. ## Overview Interaction Templates establish a format for metadata that exists about an interaction. Interaction Templates can include: - Human readable, internationalized messages about the interaction. - The Cadence code to carry out the interaction. - Information about arguments such as internationalized human readable messages and what the arguments act upon. - Contract dependencies the Interaction engages with, pinned to a version of them and their dependency tree. Applications and Wallets can use Interaction Templates and its interaction metadata. For example, Applications and Wallets can extract the internationalized human readable messaging from an Interaction Template to display to their users prior to execution of the interaction. ## For Applications Flow Client Library (FCL) `mutate` and `query` can accept an Interaction Template. FCL `mutate` and `query` will use the Interaction Template to: - Extract the Cadence code to carry out the interaction. - Extract dependency configuration for the interaction (for example, Information about contract import addresses) Here is an example of how to use `mutate` with an Interaction Template: ```javascript import * as fcl from "@onflow/fcl" import myTransactionTemplate from "./my-transaction-template.template.json" const txId = await fcl.mutate({ template: myTransactionTemplate }) ``` An Interaction Template can also be used with `query`: ```javascript import * as fcl from "@onflow/fcl" import myScriptTemplate from "./my-script-template.template.json" const info = await fcl.query({ template: myScriptTemplate }) ``` Interaction Templates can be resolved from remote locations: ```javascript import * as fcl from "@onflow/fcl" const txId = await fcl.mutate({ template: "http://interactions.awesome-crypto-project.com/buy-nft" }) const nftInfo = await fcl.query({ template: "http://interactions.awesome-crypto-project.com/read-nft", args: (arg, t) => [arg("nft-id", t.String)] }) ``` FCL will resolve the template from the remote location before it uses it to execute its underlying transaction or script. > 💡 When apps use an Interaction Template from an external location, they have a mechanism to always retrieve the most up to date way of accomplishing an interaction. By default, FCL supports resolving Interaction Templates over http or https, but FCL can also be configured with various other ways to resolve Interaction Templates: ```javascript import * as fcl from "@onflow/fcl" await fcl.config().put("document.resolver.ipfs", async ({ url }) => { const jsonTemplate = getDocumentFromIPFS(url) // resolve interaction template from ipfs return jsonTemplate }) const txId = await fcl.mutate({ template: "ipfs://IPFSHASHGOESHERE" }) ``` ## For Wallets Wallets can use Interaction Templates to: - Display internationalized human readable information about a transaction to their users during signing. - Verify the dependencies of an Interaction Template have not changed since when the Interaction Template was created. - With Interaction Template Audits, gain confidence in the correctness and safety of an Interaction Template and it's underlying transaction. When wallets receive a transaction to sign, they can query for an Interaction Template that corresponds to it. Flow operates an "Interaction Template Discovery Service" which wallets can use to query for Interaction Templates. Anyone can run an "Interaction Template Discovery Service" and wallets can choose to query from any of them. ```javascript const cadence = cadenceFromTransactionToSign const network = "mainnet" // "mainnet" | "testnet" const cadence_base64 = btoa(cadence) const interactionTemplate = await fetch( "https://flix.flow.com/v1/templates/search", { method: "POST", headers: { "Content-Type": "application/json" } body: JSON.stringify({ cadence_base64, network }) } ) ``` > 📖 For more on the "Interaction Template Discovery Service" that Flow operates, see [here](https://github.com/onflow/flow-interaction-template-service) > ❗️ Not all transactions will have a corresponding Interaction Template. Wallets are encouraged to always support signing transactions that do not have a corresponding Interaction Template, or if they fail to discover one. When a wallet has a corresponding Interaction Template for a given transaction, they may also may wish to verify that the transaction it represents is safe to sign, and that the Interaction Template is accurate for that transaction. To do so, wallets can rely on themselves, along with external Interaction Template Auditors to gain confidence in the Interaction Template and its underlying transaction. Interaction Template Auditors are entities that audit Interaction Templates for correctness and safety. > 💡 Anyone can be an Interaction Template Auditor. Wallets can choose auditors they trust, if any. Wallets can specify auditors it trusts to FCL by configuring FCL with the address of each auditor: ```javascript import * as fcl from "@onflow/fcl" await fcl.config().put("flow.network", "mainnet") const auditorA_FlowAddress = "0xABC123DEF456" const auditorB_FlowAddress = "0xFFAA1212DEFF" await fcl.config().put("flow.auditors", [ auditorA_FlowAddress, auditorB_FlowAddress ]) ``` Wallets can check if the auditors they configured FCL with have audited a given Interaction Template: ```javascript import * as fcl from "@onflow/fcl" import myTransactionTemplate from "./my-transaction-template.template.json" const audits = await fcl.InteractionTemplateUtils .getInteractionTemplateAudits({ template: myTransactionTemplate }) /** * audits = { * "0xABC123DEF456": true, * "0xFFAA1212DEFF": false * } ** / ``` The Flow team operates these auditor accounts: | Flow Team Auditor Accounts | Address | |----------------------------|:-------------------| | TestNet | 0xf78bfc12d0a786dc | | MainNet | 0xfd100e39d50a13e6 | Since not all auditors that a wallet trusts may have audited a given Interaction Template, trusting multiple auditors can increase the chance that at least one of the trusted auditors has audited the Interaction Template. > ❗️ Auditors can revoke audits at any time, so be sure to always check an Interaction Template's audit status. Since contracts on Flow are mutable, wallets may additionally wish to verify that none of the dependency tree for the transaction an Interaction Template represents has changed since when it was created and of what it was audited against. ```javascript import * as fcl from "@onflow/fcl" import myTransactionTemplate from "./my-transaction-template.template.json" const hasDependencyTreeChanged = await fcl.InteractionTemplateUtils .verifyDependencyPinsSameAtLatestSealedBlock({ template: myTransactionTemplate }) ``` If the dependency tree has changed, wallets may choose to disregard the Interaction Template (and it's audits). After auditors that the wallet trusts sufficiently audits the Interaction Template, and it's dependency tree determined unchanged since the interaction was created and audited against, then the wallet can use the Interaction Template with greater confidence in its correctness and safety. The wallet may then decide to render human readable information about the transaction such as: - Internationalized 'title' and 'description' of the transaction - Internationalized 'title' for each of the transactions arguments alongside the arguments value The wallet may then also make the status of it's audits known to the user in their UI. This allows the user to have greater confidence in the safety of the transaction. ## Data Structure The following is an example Interaction Template that corresponds to a "Transfer FLOW" transaction: ```json { "f_type": "InteractionTemplate", "f_version": "1.0.0", "id": "290b6b6222b2a77b16db896a80ddf29ebd1fa3038c9e6625a933fa213fce51fa", "data": { "type": "transaction", "interface": "", "messages": { "title": { "i18n": { "en-US": "Transfer Tokens" } }, "description": { "i18n": { "en-US": "Transfer tokens from one account to another" } } }, "cadence": "import FungibleToken from 0xFUNGIBLETOKENADDRESS\ntransaction(amount: UFix64, to: Address) {\nlet vault: @FungibleToken.Vault\nprepare(signer: &Account) {\nself.vault <- signer\n.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n.withdraw(amount: amount)\n}\nexecute {\ngetAccount(to)\n.capabilities.get(/public/flowTokenReceiver)!\n.borrow<&{FungibleToken.Receiver}>()!\n.deposit(from: <-self.vault)\n}\n}", "dependencies": { "0xFUNGIBLETOKENADDRESS": { "FungibleToken": { "mainnet": { "address": "0xf233dcee88fe0abe", "fq_address": "A.0xf233dcee88fe0abe.FungibleToken", "contract": "FungibleToken", "pin": "83c9e3d61d3b5ebf24356a9f17b5b57b12d6d56547abc73e05f820a0ae7d9cf5", "pin_block_height": 34166296 }, "testnet": { "address": "0x9a0766d93b6608b7", "fq_address": "A.0x9a0766d93b6608b7.FungibleToken", "contract": "FungibleToken", "pin": "83c9e3d61d3b5ebf24356a9f17b5b57b12d6d56547abc73e05f820a0ae7d9cf5", "pin_block_height": 74776482 } } } }, "arguments": { "amount": { "index": 0, "type": "UFix64", "messages": { "title": { "i18n": { "en-US": "The amount of FLOW tokens to send" } } } }, "to": { "index": 1, "type": "Address", "messages": { "title": { "i18n": { "en-US": "The Flow account the tokens will go to" } } } } } } } ``` --- # File: /md/build/tools/clients/fcl-js/packages-docs.md --- sidebar_position: 1 title: Packages Docs description: Packages documentation. --- # Packages Docs A list of all packages available inside Flow Client Library (FCL) with functions and type definitions. - [@onflow/fcl](./fcl/index.md) - High-level JavaScript/TypeScript library for building web applications on the Flow blockchain. - [@onflow/sdk](./sdk/index.md) - Low-level JavaScript/TypeScript SDK for interacting with the Flow blockchain. - [Type Definitions](./types/index.md) - Type definitions for the Flow Client Library (FCL) packages. --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl.md --- title: "@onflow/fcl" description: "High-level JavaScript/TypeScript library for building web applications on the Flow blockchain." --- # @onflow/fcl ## Overview The Flow fcl library provides a set of tools for developers to build applications on the Flow blockchain. ## Installation You can install the @onflow/fcl package using npm or yarn: ```bash npm install @onflow/fcl ``` Or using yarn: ```bash yarn add @onflow/fcl ``` ### Requirements - Node.js 14.x or later ### Importing You can import the entire package: ```typescript import * as fcl from "@onflow/fcl" ``` Or import specific functions: ```typescript import { functionName } from "@onflow/fcl" ``` ## Configuration FCL has a mechanism that lets you configure various aspects of FCL. When you move from one instance of the Flow Blockchain to another (Local Emulator to Testnet to Mainnet) the only thing you should need to change for your FCL implementation is your configuration. ### Setting Configuration Values Values only need to be set once. We recommend doing this once and as early in the life cycle as possible. To set a configuration value, the `put` method on the `config` instance needs to be called, the `put` method returns the `config` instance so they can be chained. Alternatively, you can set the config by passing a JSON object directly. ```javascript import * as fcl from '@onflow/fcl'; fcl .config() // returns the config instance .put('foo', 'bar') // configures "foo" to be "bar" .put('baz', 'buz'); // configures "baz" to be "buz" // OR fcl.config({ foo: 'bar', baz: 'buz', }); ``` ### Getting Configuration Values The `config` instance has an **asynchronous** `get` method. You can also pass it a fallback value. ```javascript import * as fcl from '@onflow/fcl'; fcl.config().put('foo', 'bar').put('woot', 5).put('rawr', 7); const FALLBACK = 1; async function addStuff() { var woot = await fcl.config().get('woot', FALLBACK); // will be 5 -- set in the config before var rawr = await fcl.config().get('rawr', FALLBACK); // will be 7 -- set in the config before var hmmm = await fcl.config().get('hmmm', FALLBACK); // will be 1 -- uses fallback because this isnt in the config return woot + rawr + hmmm; } addStuff().then((d) => console.log(d)); // 13 (5 + 7 + 1) ``` ### Common Configuration Keys | Name | Example | Description | | ------------------------------------ | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `accessNode.api` **(required)** | `https://rest-testnet.onflow.org` | API URL for the Flow Blockchain Access Node you want to be communicating with. See all available access node endpoints [here](https://developers.onflow.org/http-api/). | | `app.detail.title` | `Cryptokitties` | Your applications title, can be requested by wallets and other services. Used by WalletConnect plugin & Wallet Discovery service. | | `app.detail.icon` | `https://fcl-discovery.onflow.org/images/blocto.png` | Url for your applications icon, can be requested by wallets and other services. Used by WalletConnect plugin & Wallet Discovery service. | | `app.detail.description` | `Cryptokitties is a blockchain game` | Your applications description, can be requested by wallets and other services. Used by WalletConnect plugin & Wallet Discovery service. | | `app.detail.url` | `https://cryptokitties.co` | Your applications url, can be requested by wallets and other services. Used by WalletConnect plugin & Wallet Discovery service. | | `challenge.handshake` | **DEPRECATED** | Use `discovery.wallet` instead. | | `discovery.authn.endpoint` | `https://fcl-discovery.onflow.org/api/testnet/authn` | Endpoint for alternative configurable Wallet Discovery mechanism. | | `discovery.wallet` **(required)** | `https://fcl-discovery.onflow.org/testnet/authn` | Points FCL at the Wallet or Wallet Discovery mechanism. | | `discovery.wallet.method` | `IFRAME/RPC`, `POP/RPC`, `TAB/RPC`, `HTTP/POST`, or `EXT/RPC` | Describes which service strategy a wallet should use. | | `fcl.limit` | `100` | Specifies fallback compute limit if not provided in transaction. Provided as integer. | | `flow.network` **(recommended)** | `testnet` | Used in conjunction with stored interactions and provides FCLCryptoContract address for `testnet` and `mainnet`. Possible values: `local`, `testnet`, `mainnet`. | | `walletconnect.projectId` | `YOUR_PROJECT_ID` | Your app's WalletConnect project ID. See [WalletConnect Cloud](https://cloud.walletconnect.com/sign-in) to obtain a project ID for your application. | | `walletconnect.disableNotifications` | `false` | Optional flag to disable pending WalletConnect request notifications within the application's UI. | ## Using Contracts in Scripts and Transactions ### Address Replacement Configuration keys that start with `0x` will be replaced in FCL scripts and transactions, this allows you to write your script or transaction Cadence code once and not have to change it when you point your application at a difference instance of the Flow Blockchain. ```javascript import * as fcl from '@onflow/fcl'; fcl.config().put('0xFungibleToken', '0xf233dcee88fe0abe'); async function myScript() { return fcl .send([ fcl.script` import FungibleToken from 0xFungibleToken // will be replaced with 0xf233dcee88fe0abe because of the configuration access(all) fun main() { /* Rest of the script goes here */ } `, ]) .then(fcl.decode); } async function myTransaction() { return fcl .send([ fcl.transaction` import FungibleToken from 0xFungibleToken // will be replaced with 0xf233dcee88fe0abe because of the configuration transaction { /* Rest of the transaction goes here */ } `, ]) .then(fcl.decode); } ``` #### Example ```javascript import * as fcl from '@onflow/fcl'; fcl .config() .put('flow.network', 'testnet') .put('walletconnect.projectId', 'YOUR_PROJECT_ID') .put('accessNode.api', 'https://rest-testnet.onflow.org') .put('discovery.wallet', 'https://fcl-discovery.onflow.org/testnet/authn') .put('app.detail.title', 'Test Harness') .put('app.detail.icon', 'https://i.imgur.com/r23Zhvu.png') .put('app.detail.description', 'A test harness for FCL') .put('app.detail.url', 'https://myapp.com') .put('service.OpenID.scopes', 'email email_verified name zoneinfo') .put('0xFlowToken', '0x7e60df042a9c0868'); ``` ### Using `flow.json` for Contract Imports A simpler and more flexible way to manage contract imports in scripts and transactions is by using the `config.load` method in FCL. This lets you load contract configurations from a `flow.json` file, keeping your import syntax clean and allowing FCL to pick the correct contract addresses based on the network you're using. #### 1. Define Your Contracts in `flow.json` Here’s an example of a `flow.json` file with aliases for multiple networks: ```json { "contracts": { "HelloWorld": { "source": "./cadence/contracts/HelloWorld.cdc", "aliases": { "testnet": "0x1cf0e2f2f715450", "mainnet": "0xf8d6e0586b0a20c7" } } } } ``` - **`source`**: Points to the contract file in your project. - **`aliases`**: Maps each network to the correct contract address. #### 2. Configure FCL Load the `flow.json` file and set up FCL to use it: ```javascript import { config } from '@onflow/fcl'; import flowJSON from '../flow.json'; config({ 'flow.network': 'testnet', // Choose your network, e.g., testnet or mainnet 'accessNode.api': 'https://rest-testnet.onflow.org', // Access node for the network 'discovery.wallet': `https://fcl-discovery.onflow.org/testnet/authn`, // Wallet discovery }).load({ flowJSON }); ``` With this setup, FCL will automatically use the correct contract address based on the selected network (e.g., `testnet` or `mainnet`). #### 3. Use Contract Names in Scripts and Transactions After setting up `flow.json`, you can import contracts by name in your Cadence scripts or transactions: ```cadence import "HelloWorld" access(all) fun main(): String { return HelloWorld.sayHello() } ``` FCL replaces `"HelloWorld"` with the correct address from the `flow.json` configuration. > **Note**: Don’t store private keys in your `flow.json`. Instead, keep sensitive keys in a separate, `.gitignore`-protected file. ## API Reference This section contains documentation for all of the functions and namespaces in the fcl package. - [account](./account.md) - Retrieve any account from Flow network's latest block or from a specified block... - [arg](./arg.md) - A utility builder to be used with fcl.args[...] to create FCL supported... - [args](./args.md) - A utility builder to be used with other builders to pass in arguments with a... - [atBlockHeight](./atBlockHeight.md) - A builder function that returns a partial interaction to a block at a specific... - [atBlockId](./atBlockId.md) - A builder function that returns a partial interaction to a block at a specific... - [authenticate](./authenticate.md) - Calling this method will authenticate the current user via any wallet that... - [authorization](./authorization.md) - Creates an authorization function for use in transactions. An authorization... - [authorizations](./authorizations.md) - A utility builder to set the authorizations on a transaction. Authorizations... - [authz](./authz.md) - A convenience method that produces the needed authorization details for the... - [block](./block.md) - Query the network for block by id, height or get the latest block. Block ID is... - [build](./build.md) - A builder function that creates an interaction from an array of builder... - [cadence](./cadence.md) - Creates a template function - [cdc](./cdc.md) - Creates a template function - [config](./config.md) - Sets the config - [createFlowClient](./createFlowClient.md) - Creates a Flow client instance with scoped configuration. This function... - [createSignableVoucher](./createSignableVoucher.md) - Creates a signable voucher object from an interaction for signing purposes. A... - [currentUser](./currentUser.md) - The main current user service for managing user authentication and authorization... - [decode](./decode.md) - Decodes the response from 'fcl.send()' into the appropriate JSON representation... - [display](./display.md) - Adds 0x to address if not already present - [events](./events.md) - Subscribes to Flow blockchain events in real-time. This function provides a way... - [getAccount](./getAccount.md) - A builder function that returns the interaction to get an account by address.... - [getBlock](./getBlock.md) - A builder function that returns the interaction to get the latest block. Use... - [getBlockHeader](./getBlockHeader.md) - A builder function that returns the interaction to get a block header. A block... - [getCollection](./getCollection.md) - A builder function that returns a collection containing a list of transaction... - [getEvents](./getEvents.md) - A builder function that returns the interaction to get events. Events are... - [getEventsAtBlockHeightRange](./getEventsAtBlockHeightRange.md) - A builder function that returns all instances of a particular event (by name)... - [getEventsAtBlockIds](./getEventsAtBlockIds.md) - A builder function that returns all instances of a particular event (by name)... - [getNetworkParameters](./getNetworkParameters.md) - A builder function that returns the interaction to get network parameters.... - [getNodeVersionInfo](./getNodeVersionInfo.md) - A builder function for the Get Node Version Info interaction. Creates an... - [getTransaction](./getTransaction.md) - A builder function that returns the interaction to get a transaction by id.... - [getTransactionStatus](./getTransactionStatus.md) - A builder function that returns the status of transaction. The transaction id... - [invariant](./invariant.md) - [isBad](./isBad.md) - Checks if an interaction has a failed status. - [isOk](./isOk.md) - Checks if an interaction has a successful status. - [limit](./limit.md) - A utility builder to set the compute limit on a transaction. The compute limit... - [logIn](./logIn.md) - A convenience method that calls and is equivalent to `fcl.authenticate()`. This... - [mutate](./mutate.md) - A transaction execution function that allows you to submit Cadence transactions... - [nodeVersionInfo](./nodeVersionInfo.md) - Retrieve version information from the connected Flow Access Node. This function... - [param](./param.md) - Legacy function for setting a single parameter on an interaction. - [params](./params.md) - Legacy function for setting parameters on an interaction. - [payer](./payer.md) - A builder function that adds payer account(s) to a transaction. Every... - [ping](./ping.md) - A builder function that creates a ping interaction to test connectivity to the... - [pipe](./pipe.md) - Async pipe function to compose interactions. The pipe function is the foundation... - [pluginRegistry](./pluginRegistry.md) - Global plugin registry instance for managing FCL plugins. This registry handles... - [proposer](./proposer.md) - A builder function that adds the proposer to a transaction. The proposer is... - [query](./query.md) - Allows you to submit scripts to query the blockchain. - [queryRaw](./queryRaw.md) - Allows you to submit scripts to query the blockchain and get raw response data. - [reauthenticate](./reauthenticate.md) - A convenience method that calls `fcl.unauthenticate()` and then `fcl.authenticat... - [ref](./ref.md) - A builder function that sets the reference block for a transaction. The... - [sansPrefix](./sansPrefix.md) - Removes 0x from address if present - [script](./script.md) - A builder function that creates a script interaction. Scripts allow you to write... - [send](./send.md) - Sends arbitrary scripts, transactions, and requests to Flow. This method... - [serialize](./serialize.md) - Serializes a Flow transaction or script to a JSON-formatted signable voucher... - [signUp](./signUp.md) - A convenience method that calls and is equivalent to `fcl.authenticate()`. This... - [subscribe](./subscribe.md) - Subscribe to real-time data from the Flow blockchain and automatically decode... - [subscribeEvents](./subscribeEvents.md) - Subscribe to events with the given filter and parameters. Creates a subscription... - [subscribeRaw](./subscribeRaw.md) - Subscribe to a topic without decoding the data. This function creates a raw... - [transaction](./transaction.md) - Creates a transaction monitor that provides methods for tracking and subscribing... - [tx](./tx.md) - Creates a transaction monitor that provides methods for tracking and subscribing... - [unauthenticate](./unauthenticate.md) - Logs out the current user and sets the values on the current user object to... - [validator](./validator.md) - A builder function that adds a validator to a transaction. Validators are... - [verifyUserSignatures](./verifyUserSignatures.md) - Verify a valid signature/s for an account on Flow. - [voucherIntercept](./voucherIntercept.md) - A builder function that intercepts and modifies a voucher. This function is... - [voucherToTxId](./voucherToTxId.md) - Converts a voucher object to a transaction ID. This function computes the... - [why](./why.md) - Returns the reason for an interaction failure. - [withPrefix](./withPrefix.md) - Adds 0x to address if not already present --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/account.md --- title: "account" description: "account function documentation." --- # account Retrieve any account from Flow network's latest block or from a specified block height. Account address is a unique account identifier. Be mindful about the '0x' prefix, you should use the prefix as a default representation but be careful and safely handle user inputs without the prefix. An account includes the following data: - Address: the account address. - Balance: balance of the account. - Contracts: list of contracts deployed to the account. - Keys: list of keys associated with the account. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.account(address, accountQueryOptions, opts) ``` Or import directly the specific function: ```typescript import { account } from "@onflow/fcl" account(address, accountQueryOptions, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get account from latest block height const account = await fcl.account("0x1d007d755706c469"); console.log("Address:", account.address); console.log("Balance:", account.balance); console.log("Keys:", account.keys); console.log("Contracts:", Object.keys(account.contracts)); // Get account at a specific block height const historicalAccount = await fcl.account("0x1d007d755706c469", { height: 12345 }); // Get account at a specific block ID const accountAtBlock = await fcl.account("0x1d007d755706c469", { id: "9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3" }); // Get account from sealed block const sealedAccount = await fcl.account("0x1d007d755706c469", { isSealed: true }); // Alternative using builder pattern fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockHeight(123) ]).then(fcl.decode); ``` ## Parameters ### `address` - Type: `string` - Description: Address of the account ### `accountQueryOptions` (optional) - Type: ```typescript export interface AccountQueryOptions { height?: number id?: string isSealed?: boolean } ``` ### `opts` (optional) - Type: `object` - Description: Optional parameters ## Returns [`Promise`](../types#account) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/arg.md --- title: "arg" description: "arg function documentation." --- # arg A utility builder to be used with fcl.args[...] to create FCL supported arguments for interactions. Arguments are used to pass data to Cadence scripts and transactions. The arguments must match the number and order declared in the Cadence script. This function creates an ArgumentObject that holds the value and type passed in. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.arg(value, xform) ``` Or import directly the specific function: ```typescript import { arg } from "@onflow/fcl" arg(value, xform) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl" const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg("0xba1132bc08f82fe2", t.Address), // addr: Address ], }); ``` ## Parameters ### `value` - Type: ```typescript TypeDescriptorInput ``` - Description: Any value that you are looking to pass to other builders ### `xform` - Type: `T` - Description: A type supported by Flow (FType descriptor) ## Returns ```typescript CadenceArgument ``` An ArgumentObject that holds the value and type passed in --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/args.md --- title: "args" description: "args function documentation." --- # args A utility builder to be used with other builders to pass in arguments with a value and supported type. A transaction can accept zero or more arguments that are passed into the Cadence script. The arguments on the transaction must match the number and order declared in the Cadence script. This function returns a Partial Interaction that contains the arguments and types passed in. This alone is a partial and incomplete interaction. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.args(ax) ``` Or import directly the specific function: ```typescript import { args } from "@onflow/fcl" args(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl" await fcl.mutate({ cadence: ` transaction(amount: UFix64, to: Address) { prepare(signer: AuthAccount) { // transaction logic } } `, args: (arg, t) => [ arg("10.0", t.UFix64), // Will be the first argument `amount: UFix64` arg("0xba1132bc08f82fe2", t.Address), // Will be the second argument `to: Address` ], }) ``` ## Parameters ### `ax` - Type: ```typescript CadenceArgument[] ``` - Description: An array of argument objects created with fcl.arg() ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A Partial Interaction object containing the arguments and types passed in --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/atBlockHeight.md --- title: "atBlockHeight" description: "atBlockHeight function documentation." --- # atBlockHeight A builder function that returns a partial interaction to a block at a specific height. Use with other interactions like 'fcl.getBlock()' to get a full interaction at the specified block height. Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.atBlockHeight(height) ``` Or import directly the specific function: ```typescript import { atBlockHeight } from "@onflow/fcl" atBlockHeight(height) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get block at specific height await fcl.send([fcl.getBlock(), fcl.atBlockHeight(123)]).then(fcl.decode); // Get account at specific block height await fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockHeight(12345) ]).then(fcl.decode); // Execute script at specific block height await fcl.send([ fcl.script` access(all) fun main(): UFix64 { return getCurrentBlock().height } `, fcl.atBlockHeight(100) ]).then(fcl.decode); ``` ## Parameters ### `height` - Type: `number` - Description: The height of the block to execute the interaction at ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A partial interaction to be paired with another interaction such as 'fcl.getBlock()' or 'fcl.getAccount()' --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/atBlockId.md --- title: "atBlockId" description: "atBlockId function documentation." --- # atBlockId A builder function that returns a partial interaction to a block at a specific block ID. Use with other interactions like 'fcl.getBlock()' to get a full interaction at the specified block ID. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from 'GetLatestBlock'). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.atBlockId(id) ``` Or import directly the specific function: ```typescript import { atBlockId } from "@onflow/fcl" atBlockId(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get block by ID await fcl.send([fcl.getBlock(), fcl.atBlockId("23232323232")]).then(fcl.decode); // Get account at specific block ID await fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockId("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); // Execute script at specific block await fcl.send([ fcl.script` access(all) fun main(): UFix64 { return getCurrentBlock().timestamp } `, fcl.atBlockId("a1b2c3d4e5f6") ]).then(fcl.decode); ``` ## Parameters ### `id` - Type: `string` - Description: The ID of the block to execute the interaction at ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A partial interaction to be paired with another interaction such as 'fcl.getBlock()' or 'fcl.getAccount()' --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/authenticate.md --- title: "authenticate" description: "authenticate function documentation." --- # authenticate Calling this method will authenticate the current user via any wallet that supports FCL. Once called, FCL will initiate communication with the configured `discovery.wallet` endpoint which lets the user select a wallet to authenticate with. Once the wallet provider has authenticated the user, FCL will set the values on the current user object for future use and authorization. This method can only be used in web browsers. `discovery.wallet` value must be set in the configuration before calling this method. See FCL Configuration. The default discovery endpoint will open an iframe overlay to let the user choose a supported wallet. `authenticate` can also take a service returned from discovery with `fcl.authenticate({ service })`. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.authenticate(opts) ``` Or import directly the specific function: ```typescript import { authenticate } from "@onflow/fcl" authenticate(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; fcl .config() .put('accessNode.api', 'https://rest-testnet.onflow.org') .put('discovery.wallet', 'https://fcl-discovery.onflow.org/testnet/authn'); // anywhere on the page fcl.authenticate(); ``` ## Parameters ### `opts` (optional) - Type: `{}` - Description: Authentication options #### Properties: - **`service`** - Optional service to use for authentication. A service returned from discovery can be passed here. - **`redir`** - Optional redirect flag. Defaults to false. - **`forceReauth`** - Optional force re-authentication flag. Defaults to false. ## Returns [`Promise`](../types#currentuser) Promise that resolves to the authenticated CurrentUser object or undefined --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/authorization.md --- title: "authorization" description: "authorization function documentation." --- # authorization Creates an authorization function for use in transactions. An authorization function must produce the information of the user that is going to sign and a signing function to use the information to produce a signature. Read more about [authorization functions](https://docs.onflow.org/fcl/reference/authorization-function/) and [transaction roles](https://docs.onflow.org/concepts/transaction-signing/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.authorization(addr, signingFunction, keyId, sequenceNum) ``` Or import directly the specific function: ```typescript import { authorization } from "@onflow/fcl" authorization(addr, signingFunction, keyId, sequenceNum) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { ec as EC } from "elliptic"; // Create a signing function const signingFunction = ({ message }) => { // Your signing logic here return { addr: "0x123456789abcdef0", keyId: 0, signature: "your_signature_here" }; }; // Create authorization const authz = fcl.authorization( "0x123456789abcdef0", // account address signingFunction, // signing function 0, // key ID 42 // sequence number ); // Use in transaction await fcl.mutate({ cadence: `transaction { prepare(acct: AuthAccount) {} }`, proposer: authz, payer: authz, authorizations: [authz] }); ``` ## Parameters ### `addr` - Type: `string` - Description: The address of the account that will sign the transaction ### `signingFunction` - Type: ```typescript type SigningFn = ( signable?: SignableMessage ) => SigningResult | Promise ``` - Description: A function that produces signatures for the account ### `keyId` (optional) - Type: `string | number` - Description: The index of the key to use for signing (optional) ### `sequenceNum` (optional) - Type: `number` - Description: The sequence number for the account key (optional) ## Returns ```typescript Partial ``` A partial interaction account object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/authorizations.md --- title: "authorizations" description: "authorizations function documentation." --- # authorizations A utility builder to set the authorizations on a transaction. Authorizations define the accounts that are responsible for paying the transaction fees and providing signatures for the transaction. You can have multiple authorizers in a single transaction (multi-signature transactions). Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/) and [signing transactions](https://docs.onflow.org/concepts/accounts-and-keys/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.authorizations(ax) ``` Or import directly the specific function: ```typescript import { authorizations } from "@onflow/fcl" authorizations(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Single authorizer (most common case) await fcl.mutate({ cadence: ` transaction { prepare(acct: AuthAccount) { log("Hello from: ".concat(acct.address.toString())) } } `, authorizations: [fcl.authz] // Current user authorization }); // Multiple authorizers - both accounts must approve await fcl.mutate({ cadence: ` transaction { prepare(acct1: AuthAccount, acct2: AuthAccount) { log("Transaction signed by both accounts") } } `, authorizations: [userOneAuthz, userTwoAuthz] }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { acct.save("Hello, World!", to: /storage/greeting) } } `, fcl.authorizations([fcl.authz]), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.limit(100) ]); ``` ## Parameters ### `ax` (optional) - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: An array of authorization functions that produce account authorization details ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/authz.md --- title: "authz" description: "authz function documentation." --- # authz A convenience method that produces the needed authorization details for the current user to submit transactions to Flow. It defines a signing function that connects to a user's wallet provider to produce signatures to submit transactions. You can replace this function with your own authorization function if needed. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.authz() ``` Or import directly the specific function: ```typescript import { authz } from "@onflow/fcl" authz() ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; // login somewhere before fcl.authenticate(); // once logged in authz will produce values console.log(fcl.authz); // prints {addr, signingFunction, keyId, sequenceNum} from the current authenticated user. const txId = await fcl.mutate({ cadence: ` import Profile from 0xba1132bc08f82fe2 transaction(name: String) { prepare(account: auth(BorrowValue) &Account) { account.storage.borrow<&{Profile.Owner}>(from: Profile.privatePath)!.setName(name) } } `, args: (arg, t) => [arg('myName', t.String)], proposer: fcl.authz, // optional - default is fcl.authz payer: fcl.authz, // optional - default is fcl.authz authorizations: [fcl.authz], // optional - default is [fcl.authz] }); ``` ## Returns ```typescript (account: Account) => Promise ``` An object containing the necessary details from the current user to authorize a transaction in any role. --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/block.md --- title: "block" description: "block function documentation." --- # block Query the network for block by id, height or get the latest block. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from `GetLatestBlock`). Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.block(blockQueryOptions, opts) ``` Or import directly the specific function: ```typescript import { block } from "@onflow/fcl" block(blockQueryOptions, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get latest block const latestBlock = await fcl.block(); // Get the latest finalized block const latestSealedBlock = await fcl.block({sealed: true}); // Get the latest sealed block // Get block by ID (uses builder function) await fcl.send([fcl.getBlock(), fcl.atBlockId("23232323232")]).then(fcl.decode); // Get block at height (uses builder function) await fcl.send([fcl.getBlock(), fcl.atBlockHeight(123)]).then(fcl.decode) ``` ## Parameters ### `blockQueryOptions` (optional) - Type: ```typescript export interface BlockQueryOptions { sealed?: boolean height?: number id?: string } ``` ### `opts` (optional) - Type: `object` - Description: Optional parameters ## Returns [`Promise`](../types#block) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/build.md --- title: "build" description: "build function documentation." --- # build A builder function that creates an interaction from an array of builder functions. The build function takes an array of builder functions and applies them to create a complete interaction object. This is the foundation for constructing all interactions in Flow, whether they're scripts, transactions, or queries. Each builder function modifies specific parts of the interaction object, such as adding Cadence code, arguments, authorization details, or other configuration. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.build(fns) ``` Or import directly the specific function: ```typescript import { build } from "@onflow/fcl" build(fns) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Build a script interaction const scriptInteraction = await fcl.build([ fcl.script` access(all) fun main(a: Int, b: Int): Int { return a + b } `, fcl.args([ fcl.arg(1, fcl.t.Int), fcl.arg(2, fcl.t.Int) ]) ]); // Build a transaction interaction const txInteraction = await fcl.build([ fcl.transaction` transaction(name: String) { prepare(account: AuthAccount) { log("Hello, " + name) } } `, fcl.args([fcl.arg("World", fcl.t.String)]), fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); ``` ## Parameters ### `fns` (optional) - Type: `(false | InteractionBuilderFn)[]` - Description: The functions to apply to the interaction ## Returns [`Promise`](../types#interaction) A promise of an interaction --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/cadence.md --- title: "cadence" description: "cadence function documentation." --- # cadence Creates a template function ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.cadence(head, rest) ``` Or import directly the specific function: ```typescript import { cadence } from "@onflow/fcl" cadence(head, rest) ``` ## Usage ```typescript import { template } from "@onflow/util-template" // String template const simpleTemplate = template("Hello, World!"); console.log(simpleTemplate()); // "Hello, World!" // Template literal with interpolation const name = "Alice"; const greeting = template`Hello, ${name}!`; console.log(greeting()); // "Hello, Alice!" // Cadence script template const cadenceScript = template` access(all) fun main(greeting: String): String { return greeting.concat(", from Flow!") } `; console.log(cadenceScript()); // The Cadence script as a string // Used with FCL for dynamic Cadence code import * as fcl from "@onflow/fcl"; const contractAddress = "0x123456789abcdef0"; const scriptTemplate = fcl.cadence` import MyContract from ${contractAddress} access(all) fun main(): String { return MyContract.getMessage() } `; ``` ## Parameters ### `head` - Type: ```typescript string | TemplateStringsArray | ((x?: unknown) => string) ``` - Description: - A string, template string array, or template function ### `rest` (optional) - Type: `unknown[]` - Description: - The rest of the arguments ## Returns `string` A template function --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/cdc.md --- title: "cdc" description: "cdc function documentation." --- # cdc Creates a template function ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.cdc(head, rest) ``` Or import directly the specific function: ```typescript import { cdc } from "@onflow/fcl" cdc(head, rest) ``` ## Usage ```typescript import { template } from "@onflow/util-template" // String template const simpleTemplate = template("Hello, World!"); console.log(simpleTemplate()); // "Hello, World!" // Template literal with interpolation const name = "Alice"; const greeting = template`Hello, ${name}!`; console.log(greeting()); // "Hello, Alice!" // Cadence script template const cadenceScript = template` access(all) fun main(greeting: String): String { return greeting.concat(", from Flow!") } `; console.log(cadenceScript()); // The Cadence script as a string // Used with FCL for dynamic Cadence code import * as fcl from "@onflow/fcl"; const contractAddress = "0x123456789abcdef0"; const scriptTemplate = fcl.cadence` import MyContract from ${contractAddress} access(all) fun main(): String { return MyContract.getMessage() } `; ``` ## Parameters ### `head` - Type: ```typescript string | TemplateStringsArray | ((x?: unknown) => string) ``` - Description: - A string, template string array, or template function ### `rest` (optional) - Type: `unknown[]` - Description: - The rest of the arguments ## Returns `string` A template function --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/config.md --- title: "config" description: "config function documentation." --- # config Sets the config ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.config(values) ``` Or import directly the specific function: ```typescript import { config } from "@onflow/fcl" config(values) ``` ## Parameters ### `values` (optional) - Type: ```typescript Record ``` - Description: - The values to set ## Returns ```typescript { put: typeof put; get: typeof get; all: typeof all; first: typeof first; update: typeof update; delete: typeof _delete; where: typeof where; subscribe: typeof subscribe; overload: typeof overload; load: typeof load; } ``` The config object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/createFlowClient.md --- title: "createFlowClient" description: "createFlowClient function documentation." --- # createFlowClient Creates a Flow client instance with scoped configuration. This function decouples FCL functions from the global state and constructs a new SDK client instance bound to a custom context. This allows for better modularity and supports multiple FCL instances in the same application, each with their own isolated configuration and state. Benefits of scoped configuration: - **Isolation**: Each client has its own configuration, storage, and state - **Multi-tenancy**: Connect to different Flow networks simultaneously - **Type Safety**: Configuration is validated at compile time via TypeScript - **Testing**: Easy to create isolated client instances for testing ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.createFlowClient(params) ``` Or import directly the specific function: ```typescript import { createFlowClient } from "@onflow/fcl" createFlowClient(params) ``` ## Usage ```typescript // Multiple isolated clients for different networks import { createFlowClient } from "@onflow/fcl" const mainnetClient = createFlowClient({ accessNodeUrl: "https://rest-mainnet.onflow.org", flowNetwork: "mainnet", appDetailTitle: "My App (Mainnet)", }) const testnetClient = createFlowClient({ accessNodeUrl: "https://rest-testnet.onflow.org", flowNetwork: "testnet", appDetailTitle: "My App (Testnet)", }) // Query both networks simultaneously const [mainnetBlock, testnetBlock] = await Promise.all([ mainnetClient.query({ cadence: `access(all) fun main(): UInt64 { return getCurrentBlock().height }`, }), testnetClient.query({ cadence: `access(all) fun main(): UInt64 { return getCurrentBlock().height }`, }), ]) ``` ## Parameters ### `params` - Type: ```typescript export interface FlowClientConfig { accessNodeUrl: string flowNetwork?: string flowJson?: any discoveryWallet?: string discoveryWalletMethod?: string discoveryAuthnEndpoint?: string discoveryAuthnInclude?: string[] discoveryAuthnExclude?: string[] walletconnectProjectId?: string walletconnectDisableNotifications?: boolean storage?: StorageProvider appDetailTitle?: string appDetailIcon?: string appDetailDescription?: string appDetailUrl?: string serviceOpenIdScopes?: string[] transport?: SdkTransport computeLimit?: number customResolver?: any customDecoders?: any } ``` - Description: Configuration object for the Flow client ## Returns ```typescript { send: (args?: false | InteractionBuilderFn | (false | InteractionBuilderFn)[], opts?: any) => Promise; subscribe: ({ topic, args, onData, onError }: SubscribeParams, opts?: { node?: string; transport?: SdkTransport; }) => Subscription; subscribeRaw: ({ topic, args, onData, onError }: SubscribeRawParams, opts?: { node?: string; transport?: SdkTransport; }) => { unsubscribe: () => void; }; account: (address: string, { height, id, isSealed }?: AccountQueryOptions, opts?: object) => Promise; block: ({ sealed, id, height }?: BlockQueryOptions, opts?: object) => Promise; resolve: (ix: Interaction) => Promise; decode: (response: any) => Promise; currentUser: CurrentUserServiceApi; mutate: (opts?: MutateOptions) => Promise; query: (opts?: QueryOptions) => Promise; queryRaw: (opts?: QueryOptions) => Promise; verifyUserSignatures: (message: string, compSigs: CompositeSignature[], opts?: VerifySignaturesScriptOptions) => Promise; getChainId: (opts?: GetChainIdOptions) => Promise; tx: { (transactionId: string, opts?: { pollRate?: number; txNotFoundTimeout?: number; }): { snapshot: () => Promise; subscribe: (onData: (txStatus: TransactionStatus) => void, onError?: (err: Error) => void) => () => void; onceFinalized: () => Promise; onceExecuted: () => Promise; onceSealed: () => Promise; }; isUnknown: (ix: Interaction) => boolean; isPending: (tx: TransactionStatus) => boolean; isFinalized: (tx: TransactionStatus) => boolean; isExecuted: (tx: TransactionStatus) => boolean; isSealed: (tx: TransactionStatus) => boolean; isExpired: (tx: TransactionStatus) => boolean; }; events: (filterOrType?: string | EventFilter) => { subscribe: (onData: (event: Event) => void, onError?: (error: Error) => void) => () => void; }; authenticate: (opts?: AuthenticationOptions) => Promise; unauthenticate: () => void; signUserMessage: (msg: string) => Promise; serialize: (args: (false | InteractionBuilderFn)[] | Interaction, opts?: SerializeOptions) => Promise; } ``` A Flow client object with methods for interacting with the Flow blockchain --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/createSignableVoucher.md --- title: "createSignableVoucher" description: "createSignableVoucher function documentation." --- # createSignableVoucher Creates a signable voucher object from an interaction for signing purposes. A voucher is a standardized representation of a transaction that contains all the necessary information for signing and submitting to the Flow network. This function transforms an interaction object into a voucher format. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.createSignableVoucher(ix) ``` Or import directly the specific function: ```typescript import { createSignableVoucher } from "@onflow/fcl" createSignableVoucher(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { createSignableVoucher } from "@onflow/sdk" // Build a transaction interaction const interaction = await fcl.build([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log(amount) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]), fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); // Create a voucher for signing const voucher = createSignableVoucher(interaction); console.log(voucher.cadence); // The Cadence script console.log(voucher.arguments); // The transaction arguments console.log(voucher.proposalKey); // Proposer account details console.log(voucher.authorizers); // List of authorizer addresses // The voucher can now be signed and submitted ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing transaction details ## Returns ```typescript { cadence: string; refBlock: string; computeLimit: number; arguments: any[]; proposalKey: { address: string; keyId: string | number; sequenceNum: number; } | { address?: undefined; keyId?: undefined; sequenceNum?: undefined; }; payer: string; authorizers: string[]; payloadSigs: any[]; envelopeSigs: any[]; } ``` A voucher object containing all transaction data and signatures --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/currentUser.md --- title: "currentUser" description: "currentUser function documentation." --- # currentUser The main current user service for managing user authentication and authorization in Flow applications. This service provides a complete interface for wallet connections, user sessions, transaction signing, and user data management. It handles the complexity of connecting to various FCL-compatible wallets, managing authentication state, and providing authorization functions for transaction signing. The currentUser service is configured for web platforms and uses the browser's localStorage by default for session persistence. It integrates with Flow's discovery service to enable wallet selection and supports both authentication and re-authentication flows. This service is reactive and provides subscription capabilities to monitor authentication state changes in real-time. All wallet interactions are handled through FCL's standardized protocols, ensuring compatibility with the Flow ecosystem. Returns an object with the following methods: ```typescript { authenticate, // Authenticates the user via FCL-compatible wallets unauthenticate, // Logs out the current user and clears session data authorization, // Produces authorization details for transaction signing signUserMessage, // Signs arbitrary messages with the user's wallet subscribe, // Subscribes to authentication state changes snapshot, // Returns the current user object snapshot resolveArgument // Resolves the current user as a transaction argument } ``` ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.currentUser() ``` Or import directly the specific function: ```typescript import { currentUser } from "@onflow/fcl" currentUser() ``` ## Usage ```typescript // Basic authentication flow import * as fcl from "@onflow/fcl" // Configure FCL fcl.config({ "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", "flow.network": "testnet" }) // Authenticate user const user = await fcl.currentUser.authenticate() console.log("User authenticated:", user.addr) // Check authentication status const currentUser = await fcl.currentUser.snapshot() if (currentUser.loggedIn) { console.log("User is logged in:", currentUser.addr) } // Subscribe to authentication state changes import * as fcl from "@onflow/fcl" const unsubscribe = fcl.currentUser.subscribe((user) => { if (user.loggedIn) { console.log("User logged in:", user.addr) document.getElementById("login-btn").style.display = "none" document.getElementById("logout-btn").style.display = "block" } else { console.log("User logged out") document.getElementById("login-btn").style.display = "block" document.getElementById("logout-btn").style.display = "none" } }) // Clean up subscription when component unmounts window.addEventListener("beforeunload", () => unsubscribe()) ``` ## Returns ```typescript export interface CurrentUserService extends CurrentUserServiceApi { (): CurrentUserServiceApi } ``` A CurrentUserService object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/decode.md --- title: "decode" description: "decode function documentation." --- # decode Decodes the response from 'fcl.send()' into the appropriate JSON representation of any values returned from Cadence code. The response from Flow contains encoded values that need to be decoded into JavaScript types. This function handles that conversion, including complex types like structs, arrays, and dictionaries. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.decode(response) ``` Or import directly the specific function: ```typescript import { decode } from "@onflow/fcl" decode(response) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Simple script to add 2 numbers const response = await fcl.send([ fcl.script` access(all) fun main(int1: Int, int2: Int): Int { return int1 + int2 } `, fcl.args([fcl.arg(1, fcl.t.Int), fcl.arg(2, fcl.t.Int)]) ]); const decoded = await fcl.decode(response); console.log(decoded); // 3 console.log(typeof decoded); // "number" // Complex return types const complexResponse = await fcl.send([ fcl.script` access(all) fun main(): {String: Int} { return {"foo": 1, "bar": 2} } ` ]); const complexDecoded = await fcl.decode(complexResponse); console.log(complexDecoded); // {foo: 1, bar: 2} ``` ## Parameters ### `response` - Type: `any` - Description: Should be the response returned from 'fcl.send([...])' ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/display.md --- title: "display" description: "display function documentation." --- # display Adds 0x to address if not already present ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.display(address) ``` Or import directly the specific function: ```typescript import { display } from "@onflow/fcl" display(address) ``` ## Parameters ### `address` - Type: `string` - Description: - Flow address ## Returns `string` Flow address with 0x prefix --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/events.md --- title: "events" description: "events function documentation." --- # events Subscribes to Flow blockchain events in real-time. This function provides a way to listen for specific events emitted by smart contracts on the Flow blockchain. It automatically handles fallback to legacy polling for environments that don't support WebSocket subscriptions. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.events(filterOrType) ``` Or import directly the specific function: ```typescript import { events } from "@onflow/fcl" events(filterOrType) ``` ## Usage ```typescript // Subscribe to a specific event type import * as fcl from "@onflow/fcl" const unsubscribe = fcl.events("A.0x1654653399040a61.FlowToken.TokensWithdrawn") .subscribe((event) => { console.log("Event received:", event) console.log("Event data:", event.data) console.log("Transaction ID:", event.transactionId) }) // Stop listening after 30 seconds setTimeout(() => { unsubscribe() }, 30000) // Subscribe to multiple event types with error handling const unsubscribe = fcl.events({ eventTypes: [ "A.0x1654653399040a61.FlowToken.TokensWithdrawn", "A.0x1654653399040a61.FlowToken.TokensDeposited" ] }).subscribe( (event) => { console.log("Token event:", event.type, event.data) }, (error) => { console.error("Event subscription error:", error) } ) // Subscribe to events starting from a specific block height const unsubscribe = fcl.events({ eventTypes: ["A.CONTRACT.EVENT"], startBlockHeight: 12345678 }).subscribe((event) => { console.log("Historical and new events:", event) }) ``` ## Parameters ### `filterOrType` (optional) - Type: `string` | [`EventFilter`](../types#eventfilter) - Description: Event filter object or event type string. If a string is provided, it will be treated as a single event type to subscribe to. If an EventFilter object is provided, it can contain multiple event types and other filter criteria. #### Properties: - **`eventTypes`** - Array of event type strings to subscribe to - **`startBlockId`** - Block ID to start streaming from - **`startBlockHeight`** - Block height to start streaming from ## Returns ```typescript { subscribe: (onData: (event: Event) => void, onError?: (error: Error) => void) => () => void; } ``` An object containing a subscribe method • returns.subscribe Function to start the subscription • returns.subscribe.onData Callback function called when an event is received • returns.subscribe.onError Optional callback function called when an error occurs • returns.subscribe.unsubscribe Function returned by subscribe() to stop the subscription --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getAccount.md --- title: "getAccount" description: "getAccount function documentation." --- # getAccount A builder function that returns the interaction to get an account by address. Consider using the pre-built interaction 'fcl.account(address)' if you do not need to pair with any other builders. Account address is a unique account identifier. Be mindful about the '0x' prefix, you should use the prefix as a default representation but be careful and safely handle user inputs without the prefix. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getAccount(addr) ``` Or import directly the specific function: ```typescript import { getAccount } from "@onflow/fcl" getAccount(addr) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // somewhere in an async function // fcl.account is the same as this function const getAccount = async (address) => { const account = await fcl.send([fcl.getAccount(address)]).then(fcl.decode); return account; }; ``` ## Parameters ### `addr` - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getBlock.md --- title: "getBlock" description: "getBlock function documentation." --- # getBlock A builder function that returns the interaction to get the latest block. Use with 'fcl.atBlockId()' and 'fcl.atBlockHeight()' when building the interaction to get information for older blocks. Consider using the pre-built interaction 'fcl.block(options)' if you do not need to pair with any other builders. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from 'GetLatestBlock'). Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getBlock(isSealed) ``` Or import directly the specific function: ```typescript import { getBlock } from "@onflow/fcl" getBlock(isSealed) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const latestSealedBlock = await fcl.send([ fcl.getBlock(true) // isSealed = true ]).then(fcl.decode); ``` ## Parameters ### `isSealed` (optional) - Type: `boolean` - Description: If the latest block should be sealed or not. See block states ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getBlockHeader.md --- title: "getBlockHeader" description: "getBlockHeader function documentation." --- # getBlockHeader A builder function that returns the interaction to get a block header. A block header contains metadata about a block without the full transaction details, making it more lightweight than fetching the entire block. This is useful when you only need block metadata like timestamp, height, parent hash, etc. Use with 'fcl.atBlockId()' and 'fcl.atBlockHeight()' when building the interaction to get headers for specific blocks. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getBlockHeader(isSealed) ``` Or import directly the specific function: ```typescript import { getBlockHeader } from "@onflow/fcl" getBlockHeader(isSealed) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get latest sealed block header const sealedHeader = await fcl.send([ fcl.getBlockHeader(true) ]).then(fcl.decode); console.log("Block height:", sealedHeader.height); console.log("Block timestamp:", sealedHeader.timestamp); console.log("Parent block ID:", sealedHeader.parentId); // Get header for specific block const blockHeader = await fcl.send([ fcl.getBlockHeader(), fcl.atBlockHeight(12345) ]).then(fcl.decode); // Get latest finalized block header const finalizedHeader = await fcl.send([ fcl.getBlockHeader(false) ]).then(fcl.decode); ``` ## Parameters ### `isSealed` (optional) - Type: `boolean` - Description: Block finality state, true for sealed blocks, false for finalized blocks, null for latest ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getCollection.md --- title: "getCollection" description: "getCollection function documentation." --- # getCollection A builder function that returns a collection containing a list of transaction IDs by its collection ID. A collection is a batch of transactions that have been included in a block. Each collection has a unique ID which is the SHA3-256 hash of the collection payload. Collections are used to group related transactions together for more efficient processing by the network. The collection ID provided must be from the current spork. Collections from past sporks are currently unavailable. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getCollection(id) ``` Or import directly the specific function: ```typescript import { getCollection } from "@onflow/fcl" getCollection(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get a collection and see what transactions it contains const collection = await fcl.send([ fcl.getCollection("cccdb0c67d015dc7f6444e8f62a3244ed650215ed66b90603006c70c5ef1f6e5") ]).then(fcl.decode); console.log("Collection ID:", collection.id); console.log("Transaction IDs:", collection.transactionIds); console.log("Total transactions:", collection.transactionIds.length); // Process each transaction in the collection for (const txId of collection.transactionIds) { const transaction = await fcl.send([ fcl.getTransaction(txId) ]).then(fcl.decode); console.log("Transaction:", transaction); } ``` ## Parameters ### `id` (optional) - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getEvents.md --- title: "getEvents" description: "getEvents function documentation." --- # getEvents A builder function that returns the interaction to get events. Events are emitted by Cadence code during transaction execution and provide insights into what happened during execution. This function queries for events of a specific type within a range of block heights. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getEvents(eventType, start, end) ``` Or import directly the specific function: ```typescript import { getEvents } from "@onflow/fcl" getEvents(eventType, start, end) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get FlowToken transfer events from blocks 1000 to 2000 const events = await fcl.send([ fcl.getEvents("A.1654653399040a61.FlowToken.TokensDeposited", 1000, 2000) ]).then(fcl.decode); console.log("Found events:", events.length); events.forEach(event => { console.log("Event data:", event.data); console.log("Transaction ID:", event.transactionId); }); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get (e.g., "A.1654653399040a61.FlowToken.TokensWithdrawn") ### `start` - Type: `number` - Description: The start block height to query from ### `end` - Type: `number` - Description: The end block height to query to ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getEventsAtBlockHeightRange.md --- title: "getEventsAtBlockHeightRange" description: "getEventsAtBlockHeightRange function documentation." --- # getEventsAtBlockHeightRange A builder function that returns all instances of a particular event (by name) within a height range. The block range provided must be from the current spork. The block range provided must be 250 blocks or lower per request. Event type is a string that follow a standard format: A.\{AccountAddress\}.\{ContractName\}.\{EventName\} Please read more about [events in the documentation](https://docs.onflow.org/cadence/language/events/). Block height range expresses the height of the start and end block in the chain. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getEventsAtBlockHeightRange(eventType, startHeight, endHeight) ``` Or import directly the specific function: ```typescript import { getEventsAtBlockHeightRange } from "@onflow/fcl" getEventsAtBlockHeightRange(eventType, startHeight, endHeight) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get events at block height range await fcl .send([ fcl.getEventsAtBlockHeightRange( "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", // event name 35580624, // block to start looking for events at 35580624 // block to stop looking for events at ), ]) .then(fcl.decode); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get ### `startHeight` - Type: `number` - Description: The height of the block to start looking for events (inclusive) ### `endHeight` - Type: `number` - Description: The height of the block to stop looking for events (inclusive) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getEventsAtBlockIds.md --- title: "getEventsAtBlockIds" description: "getEventsAtBlockIds function documentation." --- # getEventsAtBlockIds A builder function that returns all instances of a particular event (by name) within a set of blocks, specified by block ids. The block range provided must be from the current spork. Event type is a string that follow a standard format: A.\{AccountAddress\}.\{ContractName\}.\{EventName\} Please read more about [events in the documentation](https://docs.onflow.org/cadence/language/events/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getEventsAtBlockIds(eventType, blockIds) ``` Or import directly the specific function: ```typescript import { getEventsAtBlockIds } from "@onflow/fcl" getEventsAtBlockIds(eventType, blockIds) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const events = await fcl.send([ fcl.getEventsAtBlockIds("A.7e60df042a9c0868.FlowToken.TokensWithdrawn", [ "c4f239d49e96d1e5fbcf1f31027a6e582e8c03fcd9954177b7723fdb03d938c7", "5dbaa85922eb194a3dc463c946cc01c866f2ff2b88f3e59e21c0d8d00113273f" ]) ]).then(fcl.decode); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get ### `blockIds` - Type: `string[]` - Description: The ids of the blocks to scan for events ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getNetworkParameters.md --- title: "getNetworkParameters" description: "getNetworkParameters function documentation." --- # getNetworkParameters A builder function that returns the interaction to get network parameters. Network parameters contain important configuration information about the Flow network, including the chain ID, which is essential for signing transactions correctly. This information is crucial for ensuring transactions are submitted to the correct network. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getNetworkParameters() ``` Or import directly the specific function: ```typescript import { getNetworkParameters } from "@onflow/fcl" getNetworkParameters() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get network parameters to verify chain ID const params = await fcl.send([ fcl.getNetworkParameters() ]).then(fcl.decode); console.log("Chain ID:", params.chainId); console.log("Network:", params.name); // Use this to verify you're connected to the right network if (params.chainId === "flow-mainnet") { console.log("Connected to Flow Mainnet"); } else if (params.chainId === "flow-testnet") { console.log("Connected to Flow Testnet"); } ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getNodeVersionInfo.md --- title: "getNodeVersionInfo" description: "getNodeVersionInfo function documentation." --- # getNodeVersionInfo A builder function for the Get Node Version Info interaction. Creates an interaction to retrieve version information from the connected Flow Access Node. This includes details about the node's software version, protocol version, and spork information. Consider using the pre-built interaction 'fcl.nodeVersionInfo()' if you do not need to pair with any other builders. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getNodeVersionInfo() ``` Or import directly the specific function: ```typescript import { getNodeVersionInfo } from "@onflow/fcl" getNodeVersionInfo() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get node version information using builder const versionInfo = await fcl.send([ fcl.getNodeVersionInfo() ]).then(fcl.decode); console.log("Node version:", versionInfo.semver); console.log("Protocol version:", versionInfo.protocol_version); console.log("Spork ID:", versionInfo.spork_id); // Use with other builders if needed const interaction = await fcl.build([ fcl.getNodeVersionInfo() // other builders can be added here ]); ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getTransaction.md --- title: "getTransaction" description: "getTransaction function documentation." --- # getTransaction A builder function that returns the interaction to get a transaction by id. Transaction id is a hash of the encoded transaction payload and can be calculated before submitting the transaction to the network. Transaction status represents the state of a transaction in the blockchain. Status can change until it is finalized. The transaction id provided must be from the current spork. Consider using 'fcl.tx(id).onceExecuted()' instead of calling this method directly for real-time transaction monitoring. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getTransaction(id) ``` Or import directly the specific function: ```typescript import { getTransaction } from "@onflow/fcl" getTransaction(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const tx = await fcl.send([ fcl.getTransaction("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); ``` ## Parameters ### `id` - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/getTransactionStatus.md --- title: "getTransactionStatus" description: "getTransactionStatus function documentation." --- # getTransactionStatus A builder function that returns the status of transaction. The transaction id provided must be from the current spork. Consider using 'fcl.tx(id)' instead of calling this method directly for real-time transaction monitoring. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.getTransactionStatus(transactionId) ``` Or import directly the specific function: ```typescript import { getTransactionStatus } from "@onflow/fcl" getTransactionStatus(transactionId) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const status = await fcl.send([ fcl.getTransactionStatus("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); ``` ## Parameters ### `transactionId` - Type: `string` - Description: The id of the transaction to get the status of ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/invariant.md --- title: "invariant" description: "invariant function documentation." --- # invariant ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.invariant(args) ``` Or import directly the specific function: ```typescript import { invariant } from "@onflow/fcl" invariant(args) ``` ## Parameters ### `args` (optional) - Type: `any[]` ## Returns `any` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/isBad.md --- title: "isBad" description: "isBad function documentation." --- # isBad Checks if an interaction has a failed status. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.isBad(ix) ``` Or import directly the specific function: ```typescript import { isBad } from "@onflow/fcl" isBad(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { isBad, why } from "@onflow/sdk" const response = await fcl.send([ fcl.transaction`transaction { prepare(account: AuthAccount) {} }` ]); if (isBad(response)) { console.log("Transaction failed:", why(response)); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to check ## Returns `boolean` True if the interaction status is BAD, false otherwise --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/isOk.md --- title: "isOk" description: "isOk function documentation." --- # isOk Checks if an interaction has a successful status. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.isOk(ix) ``` Or import directly the specific function: ```typescript import { isOk } from "@onflow/fcl" isOk(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { isOk } from "@onflow/sdk" // Check if a transaction was successful const response = await fcl.send([ fcl.transaction`transaction { prepare(account: AuthAccount) {} }` ]); if (isOk(response)) { console.log("Transaction was successful"); } else { console.log("Transaction failed"); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to check ## Returns `boolean` True if the interaction status is OK, false otherwise --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/limit.md --- title: "limit" description: "limit function documentation." --- # limit A utility builder to set the compute limit on a transaction. The compute limit is the maximum amount of computation that can be performed during transaction execution. Setting an appropriate compute limit helps prevent infinite loops and ensures predictable transaction costs. Read more about [computation cost](https://docs.onflow.org/concepts/fees/#computation-cost) and [transaction fees](https://docs.onflow.org/concepts/fees/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.limit(limit) ``` Or import directly the specific function: ```typescript import { limit } from "@onflow/fcl" limit(limit) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; await fcl.mutate({ cadence: ` transaction { prepare(account: AuthAccount) { // Complex transaction logic here } } `, limit: 1000 // Set compute limit to 1000 }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { // Transaction logic } } `, fcl.limit(9999) // Set higher limit for complex operations ]); ``` ## Parameters ### `limit` - Type: `number` - Description: The maximum amount of computation for the transaction ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/logIn.md --- title: "logIn" description: "logIn function documentation." --- # logIn A convenience method that calls and is equivalent to `fcl.authenticate()`. This method can only be used in web browsers. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.logIn(opts) ``` Or import directly the specific function: ```typescript import { logIn } from "@onflow/fcl" logIn(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; fcl.config() .put('accessNode.api', 'https://rest-testnet.onflow.org') .put('discovery.wallet', 'https://fcl-discovery.onflow.org/testnet/authn'); // User clicks log in button fcl.logIn(); ``` ## Parameters ### `opts` (optional) - Type: `{}` - Description: Authentication options passed to authenticate method #### Properties: - **`service`** - Optional service to use for authentication - **`redir`** - Optional redirect flag. Defaults to false. - **`forceReauth`** - Optional force re-authentication flag. Defaults to false. ## Returns [`Promise`](../types#currentuser) Promise that resolves to the authenticated CurrentUser object or undefined --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/mutate.md --- title: 'mutate' description: 'mutate function documentation.' --- # mutate A transaction execution function that allows you to submit Cadence transactions to the Flow blockchain to mutate on-chain state. This function handles the complete transaction lifecycle including building, signing, and sending transactions to Flow. It provides a high-level interface that abstracts the complexity of transaction construction while offering flexibility for advanced use cases. The mutate function automatically handles authorization using the current authenticated user by default, but allows for custom authorization functions to be specified for different transaction roles (proposer, payer, authorizer). It supports both simple single-party transactions and complex multi-party transactions with different signatories. This function integrates with FCL's address replacement system, allowing you to use placeholder addresses in your Cadence code that are replaced with actual addresses at execution time. It also supports Interaction Templates for standardized transaction execution patterns. The mutate function accepts a configuration object with the following structure: ```typescript { cadence?: string, // The Cadence transaction code to execute (required if template not provided) args?: Function, // Function that returns an array of arguments for the transaction template?: any, // Interaction Template object or URL for standardized transactions limit?: number, // Compute units limit for the transaction execution authz?: AccountAuthorization, // Authorization function for all signatory roles (proposer, payer, authorizer) proposer?: AccountAuthorization, // Specific authorization function for the proposer role payer?: AccountAuthorization, // Specific authorization function for the payer role authorizations?: AccountAuthorization[] // Array of authorization functions for authorizer roles } ``` ## Import You can import the entire package and access the function: ```typescript import * as fcl from '@onflow/fcl'; fcl.mutate(opts); ``` Or import directly the specific function: ```typescript import { mutate } from '@onflow/fcl'; mutate(opts); ``` ## Usage ```typescript // Basic transaction submission import * as fcl from '@onflow/fcl'; // Configure FCL first fcl.config({ 'accessNode.api': 'https://rest-testnet.onflow.org', 'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', 'flow.network': 'testnet', }); // Authenticate user await fcl.authenticate(); // Submit a basic transaction const txId = await fcl.mutate({ cadence: ` transaction(message: String) { prepare(account: AuthAccount) { log("Transaction executed by: ".concat(account.address.toString())) log("Message: ".concat(message)) } } `, args: (arg, t) => [arg('Hello Flow!', t.String)], limit: 50, }); console.log('Transaction submitted:', txId); ``` ## Parameters ### `opts` - Type: `any` - Description: Transaction configuration options ## Returns ```typescript (opts?: MutateOptions) => Promise; ``` Promise that resolves to the transaction ID (txId) when the transaction is submitted --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/nodeVersionInfo.md --- title: "nodeVersionInfo" description: "nodeVersionInfo function documentation." --- # nodeVersionInfo Retrieve version information from the connected Flow Access Node. This function returns detailed information about the Flow node's version, including the protocol version, spork information, and node-specific details. This is useful for debugging, compatibility checks, and understanding the network state. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.nodeVersionInfo(opts) ``` Or import directly the specific function: ```typescript import { nodeVersionInfo } from "@onflow/fcl" nodeVersionInfo(opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get node version information const versionInfo = await fcl.nodeVersionInfo(); console.log(versionInfo); // { // semver: "v0.37.13", // commit: "12345abcd", // spork_id: "mainnet-23", // protocol_version: "2.13.10", // spork_root_block_height: "88483760", // node_root_block_height: "88483760" // } // Check compatibility const info = await fcl.nodeVersionInfo(); if (info.protocol_version.startsWith("2.13")) { console.log("Compatible with current protocol version"); } ``` ## Parameters ### `opts` (optional) - Type: `any` - Description: Optional parameters for the request ## Returns [`Promise`](../types#nodeversioninfo) A promise that resolves to a block response --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/param.md --- title: "param" description: "param function documentation." --- # param Legacy function for setting a single parameter on an interaction. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.param(params) ``` Or import directly the specific function: ```typescript import { param } from "@onflow/fcl" param(params) ``` ## Parameters ### `params` - Type: `never` - Description: The parameter to set ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/params.md --- title: "params" description: "params function documentation." --- # params Legacy function for setting parameters on an interaction. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.params(params) ``` Or import directly the specific function: ```typescript import { params } from "@onflow/fcl" params(params) ``` ## Parameters ### `params` - Type: `never` - Description: The parameters to set ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/payer.md --- title: "payer" description: "payer function documentation." --- # payer A builder function that adds payer account(s) to a transaction. Every transaction requires at least one payer. The payer is the account that pays the transaction fee for executing the transaction on the network. The payer account must have sufficient Flow tokens to cover the transaction fees. Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/#payer) and [transaction fees](https://docs.onflow.org/concepts/fees/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.payer(ax) ``` Or import directly the specific function: ```typescript import { payer } from "@onflow/fcl" payer(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using current user as payer (most common case) await fcl.mutate({ cadence: ` transaction { prepare(acct: AuthAccount) { log("Transaction fees paid by: ".concat(acct.address.toString())) } } `, payer: fcl.authz // Current user as payer }); // Using custom payer with builder pattern await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { // Transaction logic } } `, fcl.proposer(fcl.authz), // Current user as proposer fcl.authorizations([fcl.authz]), // Current user as authorizer fcl.payer(customPayerAuthz) // Custom payer pays fees ]); // Multiple payers (advanced use case) await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { // Transaction logic } } `, fcl.payer([payerAuthz1, payerAuthz2]) // Multiple payers split fees ]); ``` ## Parameters ### `ax` (optional) - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: An account address or an array of account addresses ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that takes an interaction object and returns a new interaction object with the payer(s) added --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/ping.md --- title: "ping" description: "ping function documentation." --- # ping A builder function that creates a ping interaction to test connectivity to the Flow Access Node. The ping interaction is a simple way to test if the Flow Access Node is reachable and responding. This is useful for health checks, connectivity testing, and debugging network issues. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.ping() ``` Or import directly the specific function: ```typescript import { ping } from "@onflow/fcl" ping() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Simple ping to test connectivity try { const response = await fcl.send([fcl.ping()]); console.log("Access Node is reachable"); } catch (error) { console.error("Access Node is not reachable:", error); } // Use ping for health checks const healthCheck = async () => { try { await fcl.send([fcl.ping()]); return { status: "healthy", timestamp: new Date().toISOString() }; } catch (error) { return { status: "unhealthy", error: error.message, timestamp: new Date().toISOString() }; } }; const health = await healthCheck(); console.log("Health status:", health); ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/pipe.md --- title: "pipe" description: "pipe function documentation." --- # pipe Async pipe function to compose interactions. The pipe function is the foundation for composing multiple interaction builder functions together. It sequentially applies builder functions to an interaction, allowing for complex interaction construction. Each function in the pipe receives the result of the previous function and can modify or validate the interaction. Pipe has two main forms: 1. `pipe(builderFunctions)`: Returns a builder function 2. `pipe(interaction, builderFunctions)`: Directly executes the pipe on an interaction ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.pipe(fns) ``` Or import directly the specific function: ```typescript import { pipe } from "@onflow/fcl" pipe(fns) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using pipe to create a reusable builder const myTransactionBuilder = fcl.pipe([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log(amount) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]), fcl.limit(100) ]); // Use the builder const interaction = await fcl.build([myTransactionBuilder]); // Pipe is used internally by build() and send() await fcl.send([ fcl.script`access(all) fun main(): Int { return 42 }` ]); // This uses pipe internally ``` ## Parameters ### `fns` - Type: `(false | InteractionBuilderFn)[]` - Description: Array of builder functions to apply ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` An interaction builder function when called with just functions, or a Promise<Interaction> when called with an interaction and functions --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/pluginRegistry.md --- title: "pluginRegistry" description: "pluginRegistry function documentation." --- # pluginRegistry Global plugin registry instance for managing FCL plugins. This registry handles the registration and management of various FCL plugins including service plugins that add new wallet services and strategies. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.pluginRegistry() ``` Or import directly the specific function: ```typescript import { pluginRegistry } from "@onflow/fcl" pluginRegistry() ``` ## Usage ```typescript // Add a plugin to the registry pluginRegistry.add({ name: "MyWalletPlugin", f_type: "ServicePlugin", type: "discovery-service", services: [...], serviceStrategy: { method: "CUSTOM/RPC", exec: customExecFunction } }) ``` ## Returns ```typescript Readonly<{ add: (plugins: any) => void; getPlugins: () => Map; }> ``` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/proposer.md --- title: "proposer" description: "proposer function documentation." --- # proposer A builder function that adds the proposer to a transaction. The proposer is responsible for providing the proposal key and paying the network fee for the transaction. The proposer key is used to specify the sequence number and prevent replay attacks. Every transaction requires exactly one proposer. Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/#proposer) and [signing transactions](https://docs.onflow.org/concepts/accounts-and-keys/). ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.proposer(authz) ``` Or import directly the specific function: ```typescript import { proposer } from "@onflow/fcl" proposer(authz) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using the current user as proposer await fcl.mutate({ cadence: ` transaction { prepare(account: AuthAccount) { log("Hello from proposer!") } } `, proposer: fcl.authz }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction executed") } } `, fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); ``` ## Parameters ### `authz` - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: The authorization object for the proposer ## Returns [`Interaction`](../types#interaction) A function that takes an interaction object and returns a new interaction object with the proposer added --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/query.md --- title: "query" description: "query function documentation." --- # query Allows you to submit scripts to query the blockchain. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.query(opts) ``` Or import directly the specific function: ```typescript import { query } from "@onflow/fcl" query(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg('0xba1132bc08f82fe2', t.Address), // addr: Address ], }); console.log(result); // 13 ``` ## Parameters ### `opts` (optional) - Type: ```typescript export interface QueryOptions { cadence?: string args?: ArgsFn template?: any isSealed?: boolean limit?: number } ``` - Description: Query options configuration #### Properties: - **`cadence`** - A valid cadence script (required) - **`args`** - Any arguments to the script if needed should be supplied via a function that returns an array of arguments - **`limit`** - Compute (Gas) limit for query. - **`template`** - Interaction Template for a script - **`isSealed`** - Block Finality ## Returns `Promise` A JSON representation of the response --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/queryRaw.md --- title: "queryRaw" description: "queryRaw function documentation." --- # queryRaw Allows you to submit scripts to query the blockchain and get raw response data. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.queryRaw(opts) ``` Or import directly the specific function: ```typescript import { queryRaw } from "@onflow/fcl" queryRaw(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; const result = await fcl.queryRaw({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg('0xba1132bc08f82fe2', t.Address), // addr: Address ], }); ``` ## Parameters ### `opts` (optional) - Type: ```typescript export interface QueryOptions { cadence?: string args?: ArgsFn template?: any isSealed?: boolean limit?: number } ``` - Description: Query Options and configuration #### Properties: - **`cadence`** - Cadence Script used to query Flow - **`args`** - Arguments passed to cadence script - **`template`** - Interaction Template for a script - **`isSealed`** - Block Finality - **`limit`** - Compute Limit for Query ## Returns `Promise` A promise that resolves to the raw query result --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/reauthenticate.md --- title: "reauthenticate" description: "reauthenticate function documentation." --- # reauthenticate A convenience method that calls `fcl.unauthenticate()` and then `fcl.authenticate()` for the current user. This method can only be used in web browsers. The current user must be authenticated first. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.reauthenticate(opts) ``` Or import directly the specific function: ```typescript import { reauthenticate } from "@onflow/fcl" reauthenticate(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; // first authenticate to set current user fcl.authenticate(); // ... somewhere else & sometime later fcl.reauthenticate(); // logs out user and opens up login/sign-up flow ``` ## Parameters ### `opts` (optional) - Type: `{}` - Description: Authentication options passed to authenticate method #### Properties: - **`service`** - Optional service to use for authentication - **`redir`** - Optional redirect flag. Defaults to false. - **`forceReauth`** - Optional force re-authentication flag. Defaults to false. ## Returns [`Promise`](../types#currentuser) Promise that resolves to the authenticated CurrentUser object or undefined --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/ref.md --- title: "ref" description: "ref function documentation." --- # ref A builder function that sets the reference block for a transaction. The reference block specifies an expiration window (measured in blocks) during which a transaction is considered valid by the network. A transaction will be rejected if it is submitted past its expiry block. Flow calculates transaction expiry using the reference block field. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.ref(refBlock) ``` Or import directly the specific function: ```typescript import { ref } from "@onflow/fcl" ref(refBlock) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Set specific reference block for transaction await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction with custom reference block") } } `, fcl.ref("a1b2c3d4e5f6789..."), // Custom reference block ID fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]), fcl.limit(100) ]); // Usually, you don't need to set reference block manually // as FCL will automatically set it to the latest block ``` ## Parameters ### `refBlock` - Type: `string` - Description: The reference block ID ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/sansPrefix.md --- title: "sansPrefix" description: "sansPrefix function documentation." --- # sansPrefix Removes 0x from address if present ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.sansPrefix(address) ``` Or import directly the specific function: ```typescript import { sansPrefix } from "@onflow/fcl" sansPrefix(address) ``` ## Parameters ### `address` - Type: `null` - Description: - Flow address ## Returns `null` Flow address without 0x prefix --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/script.md --- title: "script" description: "script function documentation." --- # script A builder function that creates a script interaction. Scripts allow you to write arbitrary non-mutating Cadence code on the Flow blockchain and return data. You can learn more about [Cadence here](https://cadence-lang.org/docs/language), but we are now only interested in executing the script code and getting back the data. We can execute a script using the latest state of the Flow blockchain or we can choose to execute the script at a specific time in history defined by a block height or block ID. Block ID is SHA3-256 hash of the entire block payload, but you can get that value from the block response properties. Block height expresses the height of the block in the chain. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.script(args) ``` Or import directly the specific function: ```typescript import { script } from "@onflow/fcl" script(args) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg("0xba1132bc08f82fe2", t.Address), // addr: Address ], }); console.log(result); // 13 ``` ## Parameters ### `args` (optional) - Type: ```typescript [string | TemplateStringsArray | ((x?: unknown) => string), ...unknown[]] ``` - Description: The arguments to pass to the template ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/send.md --- title: "send" description: "send function documentation." --- # send Sends arbitrary scripts, transactions, and requests to Flow. This method consumes an array of builders that are to be resolved and sent. The builders required to be included in the array depend on the interaction that is being built. WARNING: Must be used in conjunction with 'fcl.decode(response)' to get back correct keys and all values in JSON. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.send(args, opts) ``` Or import directly the specific function: ```typescript import { send } from "@onflow/fcl" send(args, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // a script only needs to resolve the arguments to the script const response = await fcl.send([fcl.script`${script}`, fcl.args(args)]); // note: response values are encoded, call await fcl.decode(response) to get JSON // a transaction requires multiple 'builders' that need to be resolved prior to being sent to the chain - such as setting the authorizations. const response = await fcl.send([ fcl.transaction` ${transaction} `, fcl.args(args), fcl.proposer(proposer), fcl.authorizations(authorizations), fcl.payer(payer), fcl.limit(9999) ]); // note: response contains several values ``` ## Parameters ### `args` (optional) - Type: ```typescript false | InteractionBuilderFn | (false | InteractionBuilderFn)[] ``` - Description: An array of builders (functions that take an interaction object and return a new interaction object) ### `opts` (optional) - Type: `any` - Description: Additional optional options for the request #### Properties: - **`node`** - Custom node endpoint to use for this request - **`resolve`** - Custom resolve function to use for processing the interaction ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/serialize.md --- title: "serialize" description: "serialize function documentation." --- # serialize Serializes a Flow transaction or script to a JSON-formatted signable voucher that can be used for offline signing or inspection. This is useful for creating signable transactions that can be signed by external wallets or hardware devices. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.serialize(args, opts) ``` Or import directly the specific function: ```typescript import { serialize } from "@onflow/fcl" serialize(args, opts) ``` ## Usage ```typescript // Serialize a simple transaction import * as fcl from "@onflow/fcl" const voucher = await fcl.serialize([ fcl.transaction` transaction(amount: UFix64, to: Address) { prepare(signer: AuthAccount) { // Transaction logic here } } `, fcl.args([ fcl.arg("10.0", fcl.t.UFix64), fcl.arg("0x01", fcl.t.Address) ]), fcl.proposer(authz), fcl.payer(authz), fcl.authorizations([authz]) ]) ``` ## Parameters ### `args` - Type: `(false` | `InteractionBuilderFn)[]` | [`Interaction`](../types#interaction) - Description: Array of interaction builder functions or a pre-built interaction object. Builder functions are typically from ### `opts` (optional) - Type: ```typescript export interface SerializeOptions { resolve?: InteractionBuilderFn } ``` - Description: Optional configuration object #### Properties: - **`resolve`** - Custom resolve function to use instead of the default ## Returns `Promise` A JSON string representation of the signable voucher that contains all the transaction details needed for signing --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/signUp.md --- title: "signUp" description: "signUp function documentation." --- # signUp A convenience method that calls and is equivalent to `fcl.authenticate()`. This method can only be used in web browsers. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.signUp(opts) ``` Or import directly the specific function: ```typescript import { signUp } from "@onflow/fcl" signUp(opts) ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; fcl.config() .put('accessNode.api', 'https://rest-testnet.onflow.org') .put('discovery.wallet', 'https://fcl-discovery.onflow.org/testnet/authn'); // User clicks sign up button fcl.signUp(); ``` ## Parameters ### `opts` (optional) - Type: `{}` - Description: Authentication options passed to authenticate method #### Properties: - **`service`** - Optional service to use for authentication - **`redir`** - Optional redirect flag. Defaults to false. - **`forceReauth`** - Optional force re-authentication flag. Defaults to false. ## Returns [`Promise`](../types#currentuser) Promise that resolves to the authenticated CurrentUser object or undefined --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/subscribe.md --- title: "subscribe" description: "subscribe function documentation." --- # subscribe Subscribe to real-time data from the Flow blockchain and automatically decode the responses. This is a utility function used for subscribing to real-time data from the WebSocket Streaming API. Data returned will be automatically decoded via the 'decode' function. Available topics include: `events`, `blocks`, `block_headers`, `block_digests`, `transaction_statuses`, `account_statuses`. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.subscribe(subscribeParams, opts) ``` Or import directly the specific function: ```typescript import { subscribe } from "@onflow/fcl" subscribe(subscribeParams, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { SubscriptionTopic } from "@onflow/sdk"; // Subscribe to events const subscription = fcl.subscribe({ topic: SubscriptionTopic.EVENTS, args: { eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"] }, onData: (events) => { console.log("Received events:", events); }, onError: (error) => { console.error("Subscription error:", error); } }); // Subscribe to blocks const blockSubscription = fcl.subscribe({ topic: SubscriptionTopic.BLOCKS, args: { blockStatus: "finalized" }, onData: (block) => { console.log("New block:", block); }, onError: (error) => { console.error("Block subscription error:", error); } }); // Later, to unsubscribe: subscription.unsubscribe(); blockSubscription.unsubscribe(); ``` ## Parameters ### `subscribeParams` - Type: ```typescript SubscribeParams ``` ### `opts` (optional) - Type: ```typescript { node?: string; transport?: SdkTransport; } ``` - Description: Additional options for the subscription #### Properties: - **`node`** - Custom node endpoint to be used for the subscription - **`transport`** - Custom transport implementation for handling the connection ## Returns [`Subscription`](../types#subscription) A subscription object that allows you to manage the subscription (e.g., to unsubscribe later) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/subscribeEvents.md --- title: "subscribeEvents" description: "subscribeEvents function documentation." --- # subscribeEvents Subscribe to events with the given filter and parameters. Creates a subscription to listen for real-time events from the Flow blockchain. This function configures the subscription parameters for filtering specific events based on type, addresses, contracts, and other criteria. Events are emitted by Cadence code during transaction execution and provide insights into what happened. Subscriptions allow you to listen for these events in real-time without polling. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.subscribeEvents(eventFilter) ``` Or import directly the specific function: ```typescript import { subscribeEvents } from "@onflow/fcl" subscribeEvents(eventFilter) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Subscribe to FlowToken transfer events const subscription = await fcl.send([ fcl.subscribeEvents({ eventTypes: [ "A.1654653399040a61.FlowToken.TokensWithdrawn", "A.1654653399040a61.FlowToken.TokensDeposited" ], startHeight: 1000000, // Start from specific block height heartbeatInterval: 3000 // 3 second heartbeat }) ]); // Subscribe to events from specific contracts const contractSubscription = await fcl.send([ fcl.subscribeEvents({ contracts: ["FlowToken", "FungibleToken"], addresses: ["0x1654653399040a61"] }) ]); // Handle the subscription data elsewhere using fcl.subscribe() ``` ## Parameters ### `eventFilter` - Type: [`EventFilter`](../types#eventfilter) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/subscribeRaw.md --- title: "subscribeRaw" description: "subscribeRaw function documentation." --- # subscribeRaw Subscribe to a topic without decoding the data. This function creates a raw subscription to Flow blockchain data streams without automatic decoding. It's useful when you need more control over data processing or want to handle raw responses directly. For most use cases, consider using the `subscribe()` function instead which provides automatic decoding. Available topics include: `events`, `blocks`, `block_headers`, `block_digests`, `transaction_statuses`, `account_statuses`. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.subscribeRaw(subscribeRawParams, opts) ``` Or import directly the specific function: ```typescript import { subscribeRaw } from "@onflow/fcl" subscribeRaw(subscribeRawParams, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { SubscriptionTopic } from "@onflow/sdk"; // Subscribe to raw event data without automatic decoding const rawSubscription = fcl.subscribeRaw({ topic: SubscriptionTopic.EVENTS, args: { eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"] }, onData: (rawData) => { console.log("Raw event data:", rawData); // Handle raw data manually - no automatic decoding }, onError: (error) => { console.error("Raw subscription error:", error); } }); // Subscribe to raw block data const blockSubscription = fcl.subscribeRaw({ topic: SubscriptionTopic.BLOCKS, args: { blockStatus: "finalized" }, onData: (rawBlock) => { console.log("Raw block data:", rawBlock); }, onError: (error) => { console.error("Error:", error); } }); // Unsubscribe when done rawSubscription.unsubscribe(); ``` ## Parameters ### `subscribeRawParams` - Type: ```typescript SubscribeRawParams ``` ### `opts` (optional) - Type: ```typescript { node?: string; transport?: SdkTransport; } ``` - Description: Additional options for the subscription #### Properties: - **`node`** - Custom node endpoint to be used for the subscription - **`transport`** - Custom transport implementation for handling the connection ## Returns ```typescript { unsubscribe: () => void; } ``` A subscription object with an unsubscribe method --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/transaction.md --- title: "transaction" description: "transaction function documentation." --- # transaction Creates a transaction monitor that provides methods for tracking and subscribing to transaction status updates on the Flow blockchain. This function returns an object with methods to get snapshots, subscribe to status changes, and wait for specific transaction states. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.transaction(transactionId, opts) ``` Or import directly the specific function: ```typescript import { transaction } from "@onflow/fcl" transaction(transactionId, opts) ``` ## Usage ```typescript // Basic transaction monitoring import * as fcl from "@onflow/fcl" const txId = await fcl.mutate({ cadence: ` transaction { execute { log("Hello, World!") } } ` }) // Get current status const status = await fcl.tx(txId).snapshot() console.log("Current status:", status.status) // Subscribe to all status changes const unsubscribe = fcl.tx(txId).subscribe((status) => { console.log("Status update:", status.status) if (status.status === fcl.transaction.isSealed) { console.log("Transaction sealed!") console.log("Events:", status.events) } }) // Clean up subscription when done setTimeout(() => unsubscribe(), 60000) // Wait for specific transaction states try { // Wait for finalization (consensus reached) const finalizedStatus = await fcl.tx(txId).onceFinalized() console.log("Transaction finalized") // Wait for execution (transaction executed) const executedStatus = await fcl.tx(txId).onceExecuted() console.log("Transaction executed") // Wait for sealing (transaction sealed in block) const sealedStatus = await fcl.tx(txId).onceSealed() console.log("Transaction sealed:", sealedStatus.events) } catch (error) { console.error("Transaction failed:", error.message) } // Handle transaction errors fcl.tx(txId).subscribe( (status) => { if (status.statusCode === 1) { console.error("Transaction error:", status.errorMessage) } }, (error) => { console.error("Subscription error:", error) } ) ``` ## Parameters ### `transactionId` - Type: `string` - Description: The 64-character hex transaction ID to monitor. Must be a valid Flow transaction hash (64 bytes represented as hex string). ### `opts` (optional) - Type: ```typescript { pollRate?: number; txNotFoundTimeout?: number; } ``` - Description: Optional configuration parameters #### Properties: - **`pollRate`** - Polling rate in milliseconds when using legacy polling fallback - **`txNotFoundTimeout`** - Timeout in milliseconds for ignoring transaction not found errors during initial transaction propagation (do not modify unless you know what you are doing) ## Returns ```typescript { snapshot: () => Promise; subscribe: (onData: (txStatus: TransactionStatus) => void, onError?: (err: Error) => void) => () => void; onceFinalized: () => Promise; onceExecuted: () => Promise; onceSealed: () => Promise; } ``` Transaction monitor object with methods for tracking transaction status --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/tx.md --- title: "tx" description: "tx function documentation." --- # tx Creates a transaction monitor that provides methods for tracking and subscribing to transaction status updates on the Flow blockchain. This function returns an object with methods to get snapshots, subscribe to status changes, and wait for specific transaction states. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.tx(transactionId, opts) ``` Or import directly the specific function: ```typescript import { tx } from "@onflow/fcl" tx(transactionId, opts) ``` ## Usage ```typescript // Basic transaction monitoring import * as fcl from "@onflow/fcl" const txId = await fcl.mutate({ cadence: ` transaction { execute { log("Hello, World!") } } ` }) // Get current status const status = await fcl.tx(txId).snapshot() console.log("Current status:", status.status) // Subscribe to all status changes const unsubscribe = fcl.tx(txId).subscribe((status) => { console.log("Status update:", status.status) if (status.status === fcl.transaction.isSealed) { console.log("Transaction sealed!") console.log("Events:", status.events) } }) // Clean up subscription when done setTimeout(() => unsubscribe(), 60000) // Wait for specific transaction states try { // Wait for finalization (consensus reached) const finalizedStatus = await fcl.tx(txId).onceFinalized() console.log("Transaction finalized") // Wait for execution (transaction executed) const executedStatus = await fcl.tx(txId).onceExecuted() console.log("Transaction executed") // Wait for sealing (transaction sealed in block) const sealedStatus = await fcl.tx(txId).onceSealed() console.log("Transaction sealed:", sealedStatus.events) } catch (error) { console.error("Transaction failed:", error.message) } // Handle transaction errors fcl.tx(txId).subscribe( (status) => { if (status.statusCode === 1) { console.error("Transaction error:", status.errorMessage) } }, (error) => { console.error("Subscription error:", error) } ) ``` ## Parameters ### `transactionId` - Type: `string` - Description: The 64-character hex transaction ID to monitor. Must be a valid Flow transaction hash (64 bytes represented as hex string). ### `opts` (optional) - Type: ```typescript { pollRate?: number; txNotFoundTimeout?: number; } ``` - Description: Optional configuration parameters #### Properties: - **`pollRate`** - Polling rate in milliseconds when using legacy polling fallback - **`txNotFoundTimeout`** - Timeout in milliseconds for ignoring transaction not found errors during initial transaction propagation (do not modify unless you know what you are doing) ## Returns ```typescript { snapshot: () => Promise; subscribe: (onData: (txStatus: TransactionStatus) => void, onError?: (err: Error) => void) => () => void; onceFinalized: () => Promise; onceExecuted: () => Promise; onceSealed: () => Promise; } ``` Transaction monitor object with methods for tracking transaction status --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/unauthenticate.md --- title: "unauthenticate" description: "unauthenticate function documentation." --- # unauthenticate Logs out the current user and sets the values on the current user object to null. This method can only be used in web browsers. The current user must be authenticated first. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.unauthenticate() ``` Or import directly the specific function: ```typescript import { unauthenticate } from "@onflow/fcl" unauthenticate() ``` ## Usage ```typescript import * as fcl from '@onflow/fcl'; fcl.config().put('accessNode.api', 'https://rest-testnet.onflow.org'); // first authenticate to set current user fcl.authenticate(); // ... somewhere else & sometime later fcl.unauthenticate(); // fcl.currentUser.loggedIn === null ``` ## Returns `void` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/validator.md --- title: "validator" description: "validator function documentation." --- # validator A builder function that adds a validator to a transaction. Validators are functions that run during transaction building to check for invalid configurations or parameters. They help catch errors early before submitting transactions to the network, preventing failed transactions and wasted compute costs. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.validator(cb) ``` Or import directly the specific function: ```typescript import { validator } from "@onflow/fcl" validator(cb) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Custom validator to ensure account has sufficient balance const validateBalance = (ix) => { if (ix.message.computeLimit > 1000) { throw new Error("Compute limit too high for this account"); } return ix; }; await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { // Transaction logic } } `, fcl.validator(validateBalance), fcl.limit(500) // This will pass validation ]); ``` ## Parameters ### `cb` - Type: `Function` - Description: The validator function that takes an interaction and returns it (or throws an error if invalid) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/verifyUserSignatures.md --- title: "verifyUserSignatures" description: "verifyUserSignatures function documentation." --- # verifyUserSignatures Verify a valid signature/s for an account on Flow. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.verifyUserSignatures() ``` Or import directly the specific function: ```typescript import { verifyUserSignatures } from "@onflow/fcl" verifyUserSignatures() ``` ## Returns ```typescript Promise | ((...args: any[]) => Promise>) ``` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/voucherIntercept.md --- title: "voucherIntercept" description: "voucherIntercept function documentation." --- # voucherIntercept A builder function that intercepts and modifies a voucher. This function is useful for debugging, logging, or making modifications to the transaction data. The voucher contains all the transaction details in their final form. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.voucherIntercept(fn) ``` Or import directly the specific function: ```typescript import { voucherIntercept } from "@onflow/fcl" voucherIntercept(fn) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Intercept voucher for logging await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction executed") } } `, fcl.voucherIntercept((voucher) => { console.log("Voucher details:", { cadence: voucher.cadence, proposalKey: voucher.proposalKey, payer: voucher.payer, authorizers: voucher.authorizers, computeLimit: voucher.computeLimit }); }), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]) ]); ``` ## Parameters ### `fn` - Type: ```typescript type VoucherInterceptFn = (voucher: Voucher) => any | Promise ``` - Description: The function to intercept and potentially modify the voucher ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/voucherToTxId.md --- title: "voucherToTxId" description: "voucherToTxId function documentation." --- # voucherToTxId Converts a voucher object to a transaction ID. This function computes the transaction ID by encoding and hashing the voucher. The transaction ID can be used to track the transaction status on the Flow network. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.voucherToTxId(voucher) ``` Or import directly the specific function: ```typescript import { voucherToTxId } from "@onflow/fcl" voucherToTxId(voucher) ``` ## Usage ```typescript import { voucherToTxId, createSignableVoucher } from "@onflow/sdk" import * as fcl from "@onflow/fcl"; // Create a voucher from an interaction const interaction = await fcl.build([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Hello, Flow!") } } `, fcl.proposer(authz), fcl.payer(authz), fcl.authorizations([authz]) ]); const voucher = createSignableVoucher(interaction); // Calculate the transaction ID const txId = voucherToTxId(voucher); console.log("Transaction ID:", txId); // Returns something like: "a1b2c3d4e5f6789..." // You can use this ID to track the transaction const txStatus = await fcl.tx(txId).onceSealed(); console.log("Transaction status:", txStatus); ``` ## Parameters ### `voucher` - Type: ```typescript export interface Voucher { cadence: string refBlock: string computeLimit: number arguments: VoucherArgument[] proposalKey: VoucherProposalKey payer: string authorizers: string[] payloadSigs: Sig[] envelopeSigs: Sig[] } ``` - Description: The voucher object to convert ## Returns `string` A transaction ID string --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/why.md --- title: "why" description: "why function documentation." --- # why Returns the reason for an interaction failure. ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.why(ix) ``` Or import directly the specific function: ```typescript import { why } from "@onflow/fcl" why(ix) ``` ## Usage ```typescript import { Bad, why, initInteraction } from "@onflow/sdk" const interaction = Bad(initInteraction(), "Network timeout"); console.log(why(interaction)); // "Network timeout" // Used with error handling if (isBad(response)) { console.error("Error occurred:", why(response)); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to get the failure reason from ## Returns `string` The reason string or undefined if no reason is set --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/fcl/withPrefix.md --- title: "withPrefix" description: "withPrefix function documentation." --- # withPrefix Adds 0x to address if not already present ## Import You can import the entire package and access the function: ```typescript import * as fcl from "@onflow/fcl" fcl.withPrefix(address) ``` Or import directly the specific function: ```typescript import { withPrefix } from "@onflow/fcl" withPrefix(address) ``` ## Parameters ### `address` - Type: `null` - Description: - Flow address ## Returns `null` Flow address with 0x prefix --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk.md --- title: '@onflow/sdk' description: 'Low-level JavaScript/TypeScript SDK for interacting with the Flow blockchain.' --- # @onflow/sdk ## Overview The Flow sdk library provides a set of tools for developers to build applications on the Flow blockchain. ## Installation You can install the @onflow/sdk package using npm or yarn: ```bash npm install @onflow/sdk ``` Or using yarn: ```bash yarn add @onflow/sdk ``` ### Requirements - Node.js 14.x or later ### Importing You can import the entire package: ```typescript import * as sdk from '@onflow/sdk'; ``` Or import specific functions: ```typescript import { functionName } from '@onflow/sdk'; ``` ## Connect By default, the library uses HTTP to communicate with the access nodes and it must be configured with the correct access node API URL. An error will be returned if the host is unreachable. Example: ```typescript import { config } from '@onflow/fcl'; config({ 'accessNode.api': 'https://rest-testnet.onflow.org', }); ``` ## Querying the Flow Network After you have established a connection with an access node, you can query the Flow network to retrieve data about blocks, accounts, events and transactions. We will explore how to retrieve each of these entities in the sections below. ## Mutate Flow Network Flow, like most blockchains, allows anybody to submit a transaction that mutates the shared global chain state. A transaction is an object that holds a payload, which describes the state mutation, and one or more authorizations that permit the transaction to mutate the state owned by specific accounts. Transaction data is composed and signed with help of the SDK. The signed payload of transaction then gets submitted to the access node API. If a transaction is invalid or the correct number of authorizing signatures are not provided, it gets rejected. ## Transactions A transaction is nothing more than a signed set of data that includes script code which are instructions on how to mutate the network state and properties that define and limit it's execution. All these properties are explained below. **Script** field is the portion of the transaction that describes the state mutation logic. On Flow, transaction logic is written in [Cadence](https://cadence-lang.org/docs). Here is an example transaction script: ```typescript transaction(greeting: string) { execute { log(greeting.concat(", World!")) } } ``` **Arguments**. A transaction can accept zero or more arguments that are passed into the Cadence script. The arguments on the transaction must match the number and order declared in the Cadence script. Sample script from above accepts a single `String` argument. **Proposal key** must be provided to act as a sequence number and prevent replay and other potential attacks. Each account key maintains a separate transaction sequence counter; the key that lends its sequence number to a transaction is called the proposal key. A proposal key contains three fields: - Account address - Key index - Sequence number A transaction is only valid if its declared sequence number matches the current on-chain sequence number for that key. The sequence number increments by one after the transaction is executed. **Payer** is the account that pays the fees for the transaction. A transaction must specify exactly one payer. The payer is only responsible for paying the network and compute unit (gas) fees; the transaction is not authorized to access resources or code stored in the payer account. **Authorizers** are accounts that authorize a transaction to read and mutate their resources. A transaction can specify zero or more authorizers, depending on how many accounts the transaction needs to access. The number of authorizers on the transaction must match the number of `&Account` parameters declared in the prepare statement of the Cadence script. Example transaction with multiple authorizers: ```typescript transaction { prepare(authorizer1: &Account, authorizer2: &Account) { } } ``` **Compute Limit** is the limit on the amount of computation a transaction requires, and it will abort if it exceeds its compute unit limit. Cadence uses metering to measure the number of operations per transaction. You can read more about it in the [Cadence documentation](https://cadence-lang.org/docs). The compute limit depends on the complexity of the transaction script. Until dedicated estimation tooling exists, it's best to use the emulator to test complex transactions and determine a safe limit. Keep in mind that Flow is **very** efficient, so transaction fees are generally low. A limit resulting in max charges of `.001` Flow is sufficient to cover even complex transactions. - Flow token transfer: 19 CU. - Single NFT Transfer: 26 CU. - EVM Token transfer 28 CU. **Reference block** specifies an expiration window (measured in blocks) during which a transaction is considered valid by the network. A transaction will be rejected if it is submitted past its expiry block. Flow calculates transaction expiry using the _reference block_ field on a transaction. A transaction expires after `600` blocks are committed on top of the reference block, which takes about 8 minutes at average Mainnet block rates. ## API Reference This section contains documentation for all of the functions and namespaces in the sdk package. - [account](./account.md) - Retrieve any account from Flow network's latest block or from a specified block... - [arg](./arg.md) - A utility builder to be used with fcl.args[...] to create FCL supported... - [args](./args.md) - A utility builder to be used with other builders to pass in arguments with a... - [atBlockHeight](./atBlockHeight.md) - A builder function that returns a partial interaction to a block at a specific... - [atBlockId](./atBlockId.md) - A builder function that returns a partial interaction to a block at a specific... - [atLatestBlock](./atLatestBlock.md) - A builder function that returns a partial interaction to query the latest block... - [authorization](./authorization.md) - Creates an authorization function for use in transactions. An authorization... - [authorizations](./authorizations.md) - A utility builder to set the authorizations on a transaction. Authorizations... - [block](./block.md) - Query the network for block by id, height or get the latest block. Block ID is... - [build](./build.md) - A builder function that creates an interaction from an array of builder... - [cadence](./cadence.md) - Creates a template function - [cdc](./cdc.md) - Creates a template function - [config](./config.md) - Sets the config - [createSdkClient](./createSdkClient.md) - Creates an SDK client with the provided options. - [createSignableVoucher](./createSignableVoucher.md) - Creates a signable voucher object from an interaction for signing purposes. A... - [decode](./decode.md) - Decodes the response from 'fcl.send()' into the appropriate JSON representation... - [destroy](./destroy.md) - Removes a property from an interaction object using a dot-notation key path. - [encodeMessageFromSignable](./encodeMessageFromSignable.md) - Encodes a message from a signable object for a specific signer address. This... - [encodeTransactionEnvelope](./encodeTransactionEnvelope.md) - Encodes a complete transaction envelope including payload and signatures. This... - [encodeTransactionPayload](./encodeTransactionPayload.md) - Encodes a transaction payload for signing. This function takes a transaction... - [encodeTxIdFromVoucher](./encodeTxIdFromVoucher.md) - Encodes a transaction ID from a voucher by computing its hash. A voucher is an... - [get](./get.md) - Gets a value from an interaction object using a dot-notation key path. - [getAccount](./getAccount.md) - A builder function that returns the interaction to get an account by address.... - [getBlock](./getBlock.md) - A builder function that returns the interaction to get the latest block. Use... - [getBlockHeader](./getBlockHeader.md) - A builder function that returns the interaction to get a block header. A block... - [getCollection](./getCollection.md) - A builder function that returns a collection containing a list of transaction... - [getEvents](./getEvents.md) - A builder function that returns the interaction to get events. Events are... - [getEventsAtBlockHeightRange](./getEventsAtBlockHeightRange.md) - A builder function that returns all instances of a particular event (by name)... - [getEventsAtBlockIds](./getEventsAtBlockIds.md) - A builder function that returns all instances of a particular event (by name)... - [getNetworkParameters](./getNetworkParameters.md) - A builder function that returns the interaction to get network parameters.... - [getNodeVersionInfo](./getNodeVersionInfo.md) - A builder function for the Get Node Version Info interaction. Creates an... - [getTransaction](./getTransaction.md) - A builder function that returns the interaction to get a transaction by id.... - [getTransactionStatus](./getTransactionStatus.md) - A builder function that returns the status of transaction. The transaction id... - [initInteraction](./initInteraction.md) - Creates a new interaction object with default values. - [interaction](./interaction.md) - Creates a new interaction object with default values. - [isBad](./isBad.md) - Checks if an interaction has a failed status. - [isOk](./isOk.md) - Checks if an interaction has a successful status. - [limit](./limit.md) - A utility builder to set the compute limit on a transaction. The compute limit... - [nodeVersionInfo](./nodeVersionInfo.md) - Retrieve version information from the connected Flow Access Node. This function... - [param](./param.md) - Legacy function for setting a single parameter on an interaction. - [params](./params.md) - Legacy function for setting parameters on an interaction. - [payer](./payer.md) - A builder function that adds payer account(s) to a transaction. Every... - [ping](./ping.md) - A builder function that creates a ping interaction to test connectivity to the... - [pipe](./pipe.md) - Async pipe function to compose interactions. The pipe function is the foundation... - [proposer](./proposer.md) - A builder function that adds the proposer to a transaction. The proposer is... - [put](./put.md) - Sets a value in an interaction object using a dot-notation key path. - [ref](./ref.md) - A builder function that sets the reference block for a transaction. The... - [resolve](./resolve.md) - Resolves an interaction by applying a series of resolvers in sequence. This is... - [resolveAccounts](./resolveAccounts.md) - Resolves account authorization functions and validates account configurations... - [resolveArguments](./resolveArguments.md) - Resolves transaction arguments by evaluating argument functions and converting... - [resolveCadence](./resolveCadence.md) - [resolveFinalNormalization](./resolveFinalNormalization.md) - Normalizes account addresses by removing the "0x" prefix from all account... - [resolveProposerSequenceNumber](./resolveProposerSequenceNumber.md) - Resolves the sequence number for the proposer account by querying the... - [resolveRefBlockId](./resolveRefBlockId.md) - Resolves the reference block ID for a transaction by querying the latest block... - [resolveSignatures](./resolveSignatures.md) - Resolves signatures for a transaction by coordinating the signing process for... - [resolveValidators](./resolveValidators.md) - Executes validator functions that have been attached to an interaction to... - [resolveVoucherIntercept](./resolveVoucherIntercept.md) - Resolves voucher intercept functions by calling them with the current voucher. - [response](./response.md) - Creates a default response object - [script](./script.md) - A builder function that creates a script interaction. Scripts allow you to write... - [send](./send.md) - Sends arbitrary scripts, transactions, and requests to Flow. This method... - [subscribe](./subscribe.md) - Subscribe to real-time data from the Flow blockchain and automatically decode... - [subscribeEvents](./subscribeEvents.md) - Subscribe to events with the given filter and parameters. Creates a subscription... - [subscribeRaw](./subscribeRaw.md) - Subscribe to a topic without decoding the data. This function creates a raw... - [TestUtils](./testUtils.md) (namespace) - Namespace containing TestUtils utilities - [TestUtils.authzDeepResolveMany](./testUtils.md#authzDeepResolveMany) - Creates a deep test authorization resolver with nested resolution for complex... - [TestUtils.authzFn](./testUtils.md#authzFn) - Creates a test authorization function for testing transactions. - [TestUtils.authzResolve](./testUtils.md#authzResolve) - Creates a test authorization resolver that can be used for testing account... - [TestUtils.authzResolveMany](./testUtils.md#authzResolveMany) - Creates a test authorization resolver that handles multiple accounts with... - [TestUtils.idof](./testUtils.md#idof) - Generates a unique identifier for an account based on its address and key ID. - [TestUtils.run](./testUtils.md#run) - Runs a set of functions on an interaction This is a utility function for testing... - [TestUtils.sig](./testUtils.md#sig) - Generates a test signature string for an account. - [transaction](./transaction.md) - A template builder to use a Cadence transaction for an interaction. FCL "mutate"... - [update](./update.md) - Updates a value in an interaction object using a transformation function. - [validator](./validator.md) - A builder function that adds a validator to a transaction. Validators are... - [voucherIntercept](./voucherIntercept.md) - A builder function that intercepts and modifies a voucher. This function is... - [voucherToTxId](./voucherToTxId.md) - Converts a voucher object to a transaction ID. This function computes the... - [why](./why.md) - Returns the reason for an interaction failure. --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/account.md --- title: "account" description: "account function documentation." --- # account Retrieve any account from Flow network's latest block or from a specified block height. Account address is a unique account identifier. Be mindful about the '0x' prefix, you should use the prefix as a default representation but be careful and safely handle user inputs without the prefix. An account includes the following data: - Address: the account address. - Balance: balance of the account. - Contracts: list of contracts deployed to the account. - Keys: list of keys associated with the account. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.account(address, accountQueryOptions, opts) ``` Or import directly the specific function: ```typescript import { account } from "@onflow/sdk" account(address, accountQueryOptions, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get account from latest block height const account = await fcl.account("0x1d007d755706c469"); console.log("Address:", account.address); console.log("Balance:", account.balance); console.log("Keys:", account.keys); console.log("Contracts:", Object.keys(account.contracts)); // Get account at a specific block height const historicalAccount = await fcl.account("0x1d007d755706c469", { height: 12345 }); // Get account at a specific block ID const accountAtBlock = await fcl.account("0x1d007d755706c469", { id: "9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3" }); // Get account from sealed block const sealedAccount = await fcl.account("0x1d007d755706c469", { isSealed: true }); // Alternative using builder pattern fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockHeight(123) ]).then(fcl.decode); ``` ## Parameters ### `address` - Type: `string` - Description: Address of the account ### `accountQueryOptions` (optional) - Type: ```typescript export interface AccountQueryOptions { height?: number id?: string isSealed?: boolean } ``` ### `opts` (optional) - Type: `object` - Description: Optional parameters ## Returns [`Promise`](../types#account) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/arg.md --- title: "arg" description: "arg function documentation." --- # arg A utility builder to be used with fcl.args[...] to create FCL supported arguments for interactions. Arguments are used to pass data to Cadence scripts and transactions. The arguments must match the number and order declared in the Cadence script. This function creates an ArgumentObject that holds the value and type passed in. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.arg(value, xform) ``` Or import directly the specific function: ```typescript import { arg } from "@onflow/sdk" arg(value, xform) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl" const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg("0xba1132bc08f82fe2", t.Address), // addr: Address ], }); ``` ## Parameters ### `value` - Type: ```typescript TypeDescriptorInput ``` - Description: Any value that you are looking to pass to other builders ### `xform` - Type: `T` - Description: A type supported by Flow (FType descriptor) ## Returns ```typescript CadenceArgument ``` An ArgumentObject that holds the value and type passed in --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/args.md --- title: "args" description: "args function documentation." --- # args A utility builder to be used with other builders to pass in arguments with a value and supported type. A transaction can accept zero or more arguments that are passed into the Cadence script. The arguments on the transaction must match the number and order declared in the Cadence script. This function returns a Partial Interaction that contains the arguments and types passed in. This alone is a partial and incomplete interaction. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.args(ax) ``` Or import directly the specific function: ```typescript import { args } from "@onflow/sdk" args(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl" await fcl.mutate({ cadence: ` transaction(amount: UFix64, to: Address) { prepare(signer: AuthAccount) { // transaction logic } } `, args: (arg, t) => [ arg("10.0", t.UFix64), // Will be the first argument `amount: UFix64` arg("0xba1132bc08f82fe2", t.Address), // Will be the second argument `to: Address` ], }) ``` ## Parameters ### `ax` - Type: ```typescript CadenceArgument[] ``` - Description: An array of argument objects created with fcl.arg() ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A Partial Interaction object containing the arguments and types passed in --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/atBlockHeight.md --- title: "atBlockHeight" description: "atBlockHeight function documentation." --- # atBlockHeight A builder function that returns a partial interaction to a block at a specific height. Use with other interactions like 'fcl.getBlock()' to get a full interaction at the specified block height. Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.atBlockHeight(height) ``` Or import directly the specific function: ```typescript import { atBlockHeight } from "@onflow/sdk" atBlockHeight(height) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get block at specific height await fcl.send([fcl.getBlock(), fcl.atBlockHeight(123)]).then(fcl.decode); // Get account at specific block height await fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockHeight(12345) ]).then(fcl.decode); // Execute script at specific block height await fcl.send([ fcl.script` access(all) fun main(): UFix64 { return getCurrentBlock().height } `, fcl.atBlockHeight(100) ]).then(fcl.decode); ``` ## Parameters ### `height` - Type: `number` - Description: The height of the block to execute the interaction at ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A partial interaction to be paired with another interaction such as 'fcl.getBlock()' or 'fcl.getAccount()' --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/atBlockId.md --- title: "atBlockId" description: "atBlockId function documentation." --- # atBlockId A builder function that returns a partial interaction to a block at a specific block ID. Use with other interactions like 'fcl.getBlock()' to get a full interaction at the specified block ID. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from 'GetLatestBlock'). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.atBlockId(id) ``` Or import directly the specific function: ```typescript import { atBlockId } from "@onflow/sdk" atBlockId(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get block by ID await fcl.send([fcl.getBlock(), fcl.atBlockId("23232323232")]).then(fcl.decode); // Get account at specific block ID await fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atBlockId("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); // Execute script at specific block await fcl.send([ fcl.script` access(all) fun main(): UFix64 { return getCurrentBlock().timestamp } `, fcl.atBlockId("a1b2c3d4e5f6") ]).then(fcl.decode); ``` ## Parameters ### `id` - Type: `string` - Description: The ID of the block to execute the interaction at ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A partial interaction to be paired with another interaction such as 'fcl.getBlock()' or 'fcl.getAccount()' --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/atLatestBlock.md --- title: "atLatestBlock" description: "atLatestBlock function documentation." --- # atLatestBlock A builder function that returns a partial interaction to query the latest block with the given finality state. Use with other interactions like 'fcl.getBlock()' to get the latest block information. Block finality determines whether you get the latest executed block or the latest sealed block. - Executed blocks (soft-finality): Latest block that has been executed but may not be final - Sealed blocks (hard-finality): Latest block that has been sealed and is considered final ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.atLatestBlock(isSealed) ``` Or import directly the specific function: ```typescript import { atLatestBlock } from "@onflow/sdk" atLatestBlock(isSealed) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get latest executed block (soft finality) await fcl.send([fcl.getBlock(), fcl.atLatestBlock()]).then(fcl.decode); // Get latest sealed block (hard finality) await fcl.send([fcl.getBlock(), fcl.atLatestBlock(true)]).then(fcl.decode); // Get account from latest sealed block await fcl.send([ fcl.getAccount("0x1d007d755706c469"), fcl.atLatestBlock(true) ]).then(fcl.decode); // Execute script against latest executed block await fcl.send([ fcl.script` access(all) fun main(): UFix64 { return getCurrentBlock().height } `, fcl.atLatestBlock() ]).then(fcl.decode); ``` ## Parameters ### `isSealed` (optional) - Type: `boolean` - Description: Block finality state, defaults to latest executed block ("soft-finality"), set to true for sealed blocks ("hard-finality") ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes a partial interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/authorization.md --- title: "authorization" description: "authorization function documentation." --- # authorization Creates an authorization function for use in transactions. An authorization function must produce the information of the user that is going to sign and a signing function to use the information to produce a signature. Read more about [authorization functions](https://docs.onflow.org/fcl/reference/authorization-function/) and [transaction roles](https://docs.onflow.org/concepts/transaction-signing/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.authorization(addr, signingFunction, keyId, sequenceNum) ``` Or import directly the specific function: ```typescript import { authorization } from "@onflow/sdk" authorization(addr, signingFunction, keyId, sequenceNum) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { ec as EC } from "elliptic"; // Create a signing function const signingFunction = ({ message }) => { // Your signing logic here return { addr: "0x123456789abcdef0", keyId: 0, signature: "your_signature_here" }; }; // Create authorization const authz = fcl.authorization( "0x123456789abcdef0", // account address signingFunction, // signing function 0, // key ID 42 // sequence number ); // Use in transaction await fcl.mutate({ cadence: `transaction { prepare(acct: AuthAccount) {} }`, proposer: authz, payer: authz, authorizations: [authz] }); ``` ## Parameters ### `addr` - Type: `string` - Description: The address of the account that will sign the transaction ### `signingFunction` - Type: ```typescript type SigningFn = ( signable?: SignableMessage ) => SigningResult | Promise ``` - Description: A function that produces signatures for the account ### `keyId` (optional) - Type: `string | number` - Description: The index of the key to use for signing (optional) ### `sequenceNum` (optional) - Type: `number` - Description: The sequence number for the account key (optional) ## Returns ```typescript Partial ``` A partial interaction account object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/authorizations.md --- title: "authorizations" description: "authorizations function documentation." --- # authorizations A utility builder to set the authorizations on a transaction. Authorizations define the accounts that are responsible for paying the transaction fees and providing signatures for the transaction. You can have multiple authorizers in a single transaction (multi-signature transactions). Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/) and [signing transactions](https://docs.onflow.org/concepts/accounts-and-keys/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.authorizations(ax) ``` Or import directly the specific function: ```typescript import { authorizations } from "@onflow/sdk" authorizations(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Single authorizer (most common case) await fcl.mutate({ cadence: ` transaction { prepare(acct: AuthAccount) { log("Hello from: ".concat(acct.address.toString())) } } `, authorizations: [fcl.authz] // Current user authorization }); // Multiple authorizers - both accounts must approve await fcl.mutate({ cadence: ` transaction { prepare(acct1: AuthAccount, acct2: AuthAccount) { log("Transaction signed by both accounts") } } `, authorizations: [userOneAuthz, userTwoAuthz] }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { acct.save("Hello, World!", to: /storage/greeting) } } `, fcl.authorizations([fcl.authz]), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.limit(100) ]); ``` ## Parameters ### `ax` (optional) - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: An array of authorization functions that produce account authorization details ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/block.md --- title: "block" description: "block function documentation." --- # block Query the network for block by id, height or get the latest block. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from `GetLatestBlock`). Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.block(blockQueryOptions, opts) ``` Or import directly the specific function: ```typescript import { block } from "@onflow/sdk" block(blockQueryOptions, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get latest block const latestBlock = await fcl.block(); // Get the latest finalized block const latestSealedBlock = await fcl.block({sealed: true}); // Get the latest sealed block // Get block by ID (uses builder function) await fcl.send([fcl.getBlock(), fcl.atBlockId("23232323232")]).then(fcl.decode); // Get block at height (uses builder function) await fcl.send([fcl.getBlock(), fcl.atBlockHeight(123)]).then(fcl.decode) ``` ## Parameters ### `blockQueryOptions` (optional) - Type: ```typescript export interface BlockQueryOptions { sealed?: boolean height?: number id?: string } ``` ### `opts` (optional) - Type: `object` - Description: Optional parameters ## Returns [`Promise`](../types#block) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/build.md --- title: "build" description: "build function documentation." --- # build A builder function that creates an interaction from an array of builder functions. The build function takes an array of builder functions and applies them to create a complete interaction object. This is the foundation for constructing all interactions in Flow, whether they're scripts, transactions, or queries. Each builder function modifies specific parts of the interaction object, such as adding Cadence code, arguments, authorization details, or other configuration. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.build(fns) ``` Or import directly the specific function: ```typescript import { build } from "@onflow/sdk" build(fns) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Build a script interaction const scriptInteraction = await fcl.build([ fcl.script` access(all) fun main(a: Int, b: Int): Int { return a + b } `, fcl.args([ fcl.arg(1, fcl.t.Int), fcl.arg(2, fcl.t.Int) ]) ]); // Build a transaction interaction const txInteraction = await fcl.build([ fcl.transaction` transaction(name: String) { prepare(account: AuthAccount) { log("Hello, " + name) } } `, fcl.args([fcl.arg("World", fcl.t.String)]), fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); ``` ## Parameters ### `fns` (optional) - Type: `(false | InteractionBuilderFn)[]` - Description: The functions to apply to the interaction ## Returns [`Promise`](../types#interaction) A promise of an interaction --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/cadence.md --- title: "cadence" description: "cadence function documentation." --- # cadence Creates a template function ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.cadence(head, rest) ``` Or import directly the specific function: ```typescript import { cadence } from "@onflow/sdk" cadence(head, rest) ``` ## Usage ```typescript import { template } from "@onflow/util-template" // String template const simpleTemplate = template("Hello, World!"); console.log(simpleTemplate()); // "Hello, World!" // Template literal with interpolation const name = "Alice"; const greeting = template`Hello, ${name}!`; console.log(greeting()); // "Hello, Alice!" // Cadence script template const cadenceScript = template` access(all) fun main(greeting: String): String { return greeting.concat(", from Flow!") } `; console.log(cadenceScript()); // The Cadence script as a string // Used with FCL for dynamic Cadence code import * as fcl from "@onflow/fcl"; const contractAddress = "0x123456789abcdef0"; const scriptTemplate = fcl.cadence` import MyContract from ${contractAddress} access(all) fun main(): String { return MyContract.getMessage() } `; ``` ## Parameters ### `head` - Type: ```typescript string | TemplateStringsArray | ((x?: unknown) => string) ``` - Description: - A string, template string array, or template function ### `rest` (optional) - Type: `unknown[]` - Description: - The rest of the arguments ## Returns `string` A template function --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/cdc.md --- title: "cdc" description: "cdc function documentation." --- # cdc Creates a template function ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.cdc(head, rest) ``` Or import directly the specific function: ```typescript import { cdc } from "@onflow/sdk" cdc(head, rest) ``` ## Usage ```typescript import { template } from "@onflow/util-template" // String template const simpleTemplate = template("Hello, World!"); console.log(simpleTemplate()); // "Hello, World!" // Template literal with interpolation const name = "Alice"; const greeting = template`Hello, ${name}!`; console.log(greeting()); // "Hello, Alice!" // Cadence script template const cadenceScript = template` access(all) fun main(greeting: String): String { return greeting.concat(", from Flow!") } `; console.log(cadenceScript()); // The Cadence script as a string // Used with FCL for dynamic Cadence code import * as fcl from "@onflow/fcl"; const contractAddress = "0x123456789abcdef0"; const scriptTemplate = fcl.cadence` import MyContract from ${contractAddress} access(all) fun main(): String { return MyContract.getMessage() } `; ``` ## Parameters ### `head` - Type: ```typescript string | TemplateStringsArray | ((x?: unknown) => string) ``` - Description: - A string, template string array, or template function ### `rest` (optional) - Type: `unknown[]` - Description: - The rest of the arguments ## Returns `string` A template function --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/config.md --- title: "config" description: "config function documentation." --- # config Sets the config ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.config(values) ``` Or import directly the specific function: ```typescript import { config } from "@onflow/sdk" config(values) ``` ## Parameters ### `values` (optional) - Type: ```typescript Record ``` - Description: - The values to set ## Returns ```typescript { put: typeof put; get: typeof get; all: typeof all; first: typeof first; update: typeof update; delete: typeof _delete; where: typeof where; subscribe: typeof subscribe; overload: typeof overload; load: typeof load; } ``` The config object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/createSdkClient.md --- title: "createSdkClient" description: "createSdkClient function documentation." --- # createSdkClient Creates an SDK client with the provided options. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.createSdkClient(options) ``` Or import directly the specific function: ```typescript import { createSdkClient } from "@onflow/sdk" createSdkClient(options) ``` ## Usage ```typescript const client = createSdkClient({ accessNodeUrl: "https://rest-mainnet.onflow.org", transport: myTransport, computeLimit: 1000, }) client.send([myScript, myTransaction]) .then(client.decode) .catch(error => console.error("Error sending request:", error)) ``` ## Parameters ### `options` - Type: ```typescript export interface SdkClientOptions { accessNodeUrl: string transport: SdkTransport computeLimit: number contracts?: { [contractName: string]: string } customResolver?: (args: any) => Promise customDecoders?: {[key: string]: (data: any) => any} } ``` - Description: - Configuration options for the SDK client. ## Returns ```typescript { send: (args?: false | InteractionBuilderFn | (false | InteractionBuilderFn)[], opts?: any) => Promise; subscribe: ({ topic, args, onData, onError }: SubscribeParams, opts?: { node?: string; transport?: SdkTransport; }) => Subscription; subscribeRaw: ({ topic, args, onData, onError }: SubscribeRawParams, opts?: { node?: string; transport?: SdkTransport; }) => { unsubscribe: () => void; }; account: (address: string, { height, id, isSealed }?: AccountQueryOptions, opts?: object) => Promise; block: ({ sealed, id, height }?: BlockQueryOptions, opts?: object) => Promise; resolve: (ix: Interaction) => Promise; decode: (response: any) => Promise; } ``` A client object with methods to interact with the Flow blockchain. --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/createSignableVoucher.md --- title: "createSignableVoucher" description: "createSignableVoucher function documentation." --- # createSignableVoucher Creates a signable voucher object from an interaction for signing purposes. A voucher is a standardized representation of a transaction that contains all the necessary information for signing and submitting to the Flow network. This function transforms an interaction object into a voucher format. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.createSignableVoucher(ix) ``` Or import directly the specific function: ```typescript import { createSignableVoucher } from "@onflow/sdk" createSignableVoucher(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { createSignableVoucher } from "@onflow/sdk" // Build a transaction interaction const interaction = await fcl.build([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log(amount) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]), fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); // Create a voucher for signing const voucher = createSignableVoucher(interaction); console.log(voucher.cadence); // The Cadence script console.log(voucher.arguments); // The transaction arguments console.log(voucher.proposalKey); // Proposer account details console.log(voucher.authorizers); // List of authorizer addresses // The voucher can now be signed and submitted ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing transaction details ## Returns ```typescript { cadence: string; refBlock: string; computeLimit: number; arguments: any[]; proposalKey: { address: string; keyId: string | number; sequenceNum: number; } | { address?: undefined; keyId?: undefined; sequenceNum?: undefined; }; payer: string; authorizers: string[]; payloadSigs: any[]; envelopeSigs: any[]; } ``` A voucher object containing all transaction data and signatures --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/decode.md --- title: "decode" description: "decode function documentation." --- # decode Decodes the response from 'fcl.send()' into the appropriate JSON representation of any values returned from Cadence code. The response from Flow contains encoded values that need to be decoded into JavaScript types. This function handles that conversion, including complex types like structs, arrays, and dictionaries. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.decode(response) ``` Or import directly the specific function: ```typescript import { decode } from "@onflow/sdk" decode(response) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Simple script to add 2 numbers const response = await fcl.send([ fcl.script` access(all) fun main(int1: Int, int2: Int): Int { return int1 + int2 } `, fcl.args([fcl.arg(1, fcl.t.Int), fcl.arg(2, fcl.t.Int)]) ]); const decoded = await fcl.decode(response); console.log(decoded); // 3 console.log(typeof decoded); // "number" // Complex return types const complexResponse = await fcl.send([ fcl.script` access(all) fun main(): {String: Int} { return {"foo": 1, "bar": 2} } ` ]); const complexDecoded = await fcl.decode(complexResponse); console.log(complexDecoded); // {foo: 1, bar: 2} ``` ## Parameters ### `response` - Type: `any` - Description: Should be the response returned from 'fcl.send([...])' ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/destroy.md --- title: "destroy" description: "destroy function documentation." --- # destroy Removes a property from an interaction object using a dot-notation key path. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.destroy(key) ``` Or import directly the specific function: ```typescript import { destroy } from "@onflow/sdk" destroy(key) ``` ## Usage ```typescript import { destroy, put, get, initInteraction } from "@onflow/sdk" const interaction = initInteraction(); // Set some values put("user.name", "Alice")(interaction); put("user.email", "alice@example.com")(interaction); put("user.temp", "temporary data")(interaction); console.log(get(interaction, "user.temp")); // "temporary data" // Remove temporary data destroy("user.temp")(interaction); console.log(get(interaction, "user.temp")); // undefined console.log(get(interaction, "user.name")); // "Alice" (still exists) ``` ## Parameters ### `key` - Type: `string` - Description: The dot-notation key path to remove ## Returns [`Interaction`](../types#interaction) A function that takes an interaction and removes the property --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/encodeMessageFromSignable.md --- title: "encodeMessageFromSignable" description: "encodeMessageFromSignable function documentation." --- # encodeMessageFromSignable Encodes a message from a signable object for a specific signer address. This function determines whether the signer should sign the transaction payload or envelope based on their role in the transaction (authorizer, proposer, or payer), then encodes the appropriate message for signing. Payload signers include authorizers and proposers (but not payers) Envelope signers include only payers The encoded message is what gets signed by the account's private key to create the transaction signature. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.encodeMessageFromSignable(signable, signerAddress) ``` Or import directly the specific function: ```typescript import { encodeMessageFromSignable } from "@onflow/sdk" encodeMessageFromSignable(signable, signerAddress) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // This function is typically used internally by authorization functions // when implementing custom wallet connectors or signing flows const signable = { voucher: { cadence: "transaction { prepare(acct: AuthAccount) {} }", authorizers: ["0x01"], proposalKey: { address: "0x01", keyId: 0, sequenceNum: 42 }, payer: "0x02", refBlock: "a1b2c3", computeLimit: 100, arguments: [], payloadSigs: [] } }; // For an authorizer (payload signer) const authorizerMessage = fcl.encodeMessageFromSignable(signable, "0x01"); console.log("Authorizer signs:", authorizerMessage); // For a payer (envelope signer) const payerMessage = fcl.encodeMessageFromSignable(signable, "0x02"); console.log("Payer signs:", payerMessage); ``` ## Parameters ### `signable` - Type: ```typescript export interface Signable { message: string addr?: string keyId?: number signature?: string roles: Record voucher: Voucher [key: string]: any } ``` - Description: The signable object containing transaction data and voucher ### `signerAddress` - Type: `string` - Description: The address of the signer to encode the message for ## Returns `string` An encoded message string suitable for signing with the account's private key --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/encodeTransactionEnvelope.md --- title: "encodeTransactionEnvelope" description: "encodeTransactionEnvelope function documentation." --- # encodeTransactionEnvelope Encodes a complete transaction envelope including payload and signatures. This function encodes the full transaction including both the payload and all signatures. This is the final step before submitting a transaction to the Flow network. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.encodeTransactionEnvelope(tx) ``` Or import directly the specific function: ```typescript import { encodeTransactionEnvelope } from "@onflow/sdk" encodeTransactionEnvelope(tx) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { encodeTransactionEnvelope } from "@onflow/sdk" // Assuming you have a fully built and signed transaction const signedTransaction = await fcl.build([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Hello, Flow!") } } `, fcl.proposer(authz), fcl.payer(authz), fcl.authorizations([authz]), fcl.limit(100) ]); // Add signatures to the transaction (this is usually done automatically) // signedTransaction.payloadSigs = [...]; // signedTransaction.envelopeSigs = [...]; // Encode the complete transaction envelope const encodedEnvelope = encodeTransactionEnvelope(signedTransaction); console.log("Encoded envelope:", encodedEnvelope); // Returns a hex string ready for network submission ``` ## Parameters ### `tx` - Type: [`Transaction`](../types#transaction) - Description: The transaction object to encode ## Returns `string` A hex-encoded string representing the complete transaction envelope --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/encodeTransactionPayload.md --- title: "encodeTransactionPayload" description: "encodeTransactionPayload function documentation." --- # encodeTransactionPayload Encodes a transaction payload for signing. This function takes a transaction object and encodes it into a format suitable for signing. The encoded payload contains all the transaction details except for the signatures. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.encodeTransactionPayload(tx) ``` Or import directly the specific function: ```typescript import { encodeTransactionPayload } from "@onflow/sdk" encodeTransactionPayload(tx) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { encodeTransactionPayload } from "@onflow/sdk" // Build a transaction const transaction = await fcl.build([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log("Transferring: ".concat(amount.toString())) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]), fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); // Encode the transaction payload for signing const encodedPayload = encodeTransactionPayload(transaction); console.log("Encoded payload:", encodedPayload); // Returns a hex string like "f90145b90140..." ``` ## Parameters ### `tx` - Type: [`Transaction`](../types#transaction) - Description: The transaction object to encode ## Returns `string` A hex-encoded string representing the transaction payload --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/encodeTxIdFromVoucher.md --- title: "encodeTxIdFromVoucher" description: "encodeTxIdFromVoucher function documentation." --- # encodeTxIdFromVoucher Encodes a transaction ID from a voucher by computing its hash. A voucher is an intermediary object that contains transaction details before final encoding. This function computes the transaction ID that would result from submitting the transaction. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.encodeTxIdFromVoucher(voucher) ``` Or import directly the specific function: ```typescript import { encodeTxIdFromVoucher } from "@onflow/sdk" encodeTxIdFromVoucher(voucher) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { encodeTxIdFromVoucher } from "@onflow/sdk" // Create a voucher (usually done internally by FCL) const voucher = { cadence: ` transaction { prepare(account: AuthAccount) { log("Hello") } } `, arguments: [], refBlock: "abc123...", computeLimit: 100, proposalKey: { address: "0x123456789abcdef0", keyId: 0, sequenceNum: 42 }, payer: "0x123456789abcdef0", authorizers: ["0x123456789abcdef0"], payloadSigs: [], envelopeSigs: [] }; // Calculate the transaction ID const txId = encodeTxIdFromVoucher(voucher); console.log("Transaction ID:", txId); // Returns a transaction ID that can be used to track the transaction ``` ## Parameters ### `voucher` - Type: ```typescript export interface Voucher { cadence: string refBlock: string computeLimit: number arguments: VoucherArgument[] proposalKey: VoucherProposalKey payer: string authorizers: string[] payloadSigs: Sig[] envelopeSigs: Sig[] } ``` - Description: The voucher object containing transaction details ## Returns `string` A hex-encoded string representing the transaction ID --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/get.md --- title: "get" description: "get function documentation." --- # get Gets a value from an interaction object using a dot-notation key path. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.get(ix, key, fallback) ``` Or import directly the specific function: ```typescript import { get } from "@onflow/sdk" get(ix, key, fallback) ``` ## Usage ```typescript import { get, put, initInteraction } from "@onflow/sdk" const interaction = initInteraction(); // Set a value first put("user.name", "Alice")(interaction); // Get the value const userName = get(interaction, "user.name"); // "Alice" const userAge = get(interaction, "user.age", 25); // 25 (fallback) // Get nested values put("config.network.url", "https://access.mainnet.onflow.org")(interaction); const networkUrl = get(interaction, "config.network.url"); ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object ### `key` - Type: `string` - Description: The dot-notation key path (e.g., "message.arguments") ### `fallback` (optional) - Type: `any` - Description: The fallback value if the key is not found ## Returns `any` The value at the key path or the fallback value --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getAccount.md --- title: "getAccount" description: "getAccount function documentation." --- # getAccount A builder function that returns the interaction to get an account by address. Consider using the pre-built interaction 'fcl.account(address)' if you do not need to pair with any other builders. Account address is a unique account identifier. Be mindful about the '0x' prefix, you should use the prefix as a default representation but be careful and safely handle user inputs without the prefix. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getAccount(addr) ``` Or import directly the specific function: ```typescript import { getAccount } from "@onflow/sdk" getAccount(addr) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // somewhere in an async function // fcl.account is the same as this function const getAccount = async (address) => { const account = await fcl.send([fcl.getAccount(address)]).then(fcl.decode); return account; }; ``` ## Parameters ### `addr` - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getBlock.md --- title: "getBlock" description: "getBlock function documentation." --- # getBlock A builder function that returns the interaction to get the latest block. Use with 'fcl.atBlockId()' and 'fcl.atBlockHeight()' when building the interaction to get information for older blocks. Consider using the pre-built interaction 'fcl.block(options)' if you do not need to pair with any other builders. Block ID is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from 'GetLatestBlock'). Block height expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getBlock(isSealed) ``` Or import directly the specific function: ```typescript import { getBlock } from "@onflow/sdk" getBlock(isSealed) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const latestSealedBlock = await fcl.send([ fcl.getBlock(true) // isSealed = true ]).then(fcl.decode); ``` ## Parameters ### `isSealed` (optional) - Type: `boolean` - Description: If the latest block should be sealed or not. See block states ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getBlockHeader.md --- title: "getBlockHeader" description: "getBlockHeader function documentation." --- # getBlockHeader A builder function that returns the interaction to get a block header. A block header contains metadata about a block without the full transaction details, making it more lightweight than fetching the entire block. This is useful when you only need block metadata like timestamp, height, parent hash, etc. Use with 'fcl.atBlockId()' and 'fcl.atBlockHeight()' when building the interaction to get headers for specific blocks. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getBlockHeader(isSealed) ``` Or import directly the specific function: ```typescript import { getBlockHeader } from "@onflow/sdk" getBlockHeader(isSealed) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get latest sealed block header const sealedHeader = await fcl.send([ fcl.getBlockHeader(true) ]).then(fcl.decode); console.log("Block height:", sealedHeader.height); console.log("Block timestamp:", sealedHeader.timestamp); console.log("Parent block ID:", sealedHeader.parentId); // Get header for specific block const blockHeader = await fcl.send([ fcl.getBlockHeader(), fcl.atBlockHeight(12345) ]).then(fcl.decode); // Get latest finalized block header const finalizedHeader = await fcl.send([ fcl.getBlockHeader(false) ]).then(fcl.decode); ``` ## Parameters ### `isSealed` (optional) - Type: `boolean` - Description: Block finality state, true for sealed blocks, false for finalized blocks, null for latest ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getCollection.md --- title: "getCollection" description: "getCollection function documentation." --- # getCollection A builder function that returns a collection containing a list of transaction IDs by its collection ID. A collection is a batch of transactions that have been included in a block. Each collection has a unique ID which is the SHA3-256 hash of the collection payload. Collections are used to group related transactions together for more efficient processing by the network. The collection ID provided must be from the current spork. Collections from past sporks are currently unavailable. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getCollection(id) ``` Or import directly the specific function: ```typescript import { getCollection } from "@onflow/sdk" getCollection(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get a collection and see what transactions it contains const collection = await fcl.send([ fcl.getCollection("cccdb0c67d015dc7f6444e8f62a3244ed650215ed66b90603006c70c5ef1f6e5") ]).then(fcl.decode); console.log("Collection ID:", collection.id); console.log("Transaction IDs:", collection.transactionIds); console.log("Total transactions:", collection.transactionIds.length); // Process each transaction in the collection for (const txId of collection.transactionIds) { const transaction = await fcl.send([ fcl.getTransaction(txId) ]).then(fcl.decode); console.log("Transaction:", transaction); } ``` ## Parameters ### `id` (optional) - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getEvents.md --- title: "getEvents" description: "getEvents function documentation." --- # getEvents A builder function that returns the interaction to get events. Events are emitted by Cadence code during transaction execution and provide insights into what happened during execution. This function queries for events of a specific type within a range of block heights. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getEvents(eventType, start, end) ``` Or import directly the specific function: ```typescript import { getEvents } from "@onflow/sdk" getEvents(eventType, start, end) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get FlowToken transfer events from blocks 1000 to 2000 const events = await fcl.send([ fcl.getEvents("A.1654653399040a61.FlowToken.TokensDeposited", 1000, 2000) ]).then(fcl.decode); console.log("Found events:", events.length); events.forEach(event => { console.log("Event data:", event.data); console.log("Transaction ID:", event.transactionId); }); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get (e.g., "A.1654653399040a61.FlowToken.TokensWithdrawn") ### `start` - Type: `number` - Description: The start block height to query from ### `end` - Type: `number` - Description: The end block height to query to ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getEventsAtBlockHeightRange.md --- title: "getEventsAtBlockHeightRange" description: "getEventsAtBlockHeightRange function documentation." --- # getEventsAtBlockHeightRange A builder function that returns all instances of a particular event (by name) within a height range. The block range provided must be from the current spork. The block range provided must be 250 blocks or lower per request. Event type is a string that follow a standard format: A.\{AccountAddress\}.\{ContractName\}.\{EventName\} Please read more about [events in the documentation](https://docs.onflow.org/cadence/language/events/). Block height range expresses the height of the start and end block in the chain. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getEventsAtBlockHeightRange(eventType, startHeight, endHeight) ``` Or import directly the specific function: ```typescript import { getEventsAtBlockHeightRange } from "@onflow/sdk" getEventsAtBlockHeightRange(eventType, startHeight, endHeight) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get events at block height range await fcl .send([ fcl.getEventsAtBlockHeightRange( "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", // event name 35580624, // block to start looking for events at 35580624 // block to stop looking for events at ), ]) .then(fcl.decode); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get ### `startHeight` - Type: `number` - Description: The height of the block to start looking for events (inclusive) ### `endHeight` - Type: `number` - Description: The height of the block to stop looking for events (inclusive) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getEventsAtBlockIds.md --- title: "getEventsAtBlockIds" description: "getEventsAtBlockIds function documentation." --- # getEventsAtBlockIds A builder function that returns all instances of a particular event (by name) within a set of blocks, specified by block ids. The block range provided must be from the current spork. Event type is a string that follow a standard format: A.\{AccountAddress\}.\{ContractName\}.\{EventName\} Please read more about [events in the documentation](https://docs.onflow.org/cadence/language/events/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getEventsAtBlockIds(eventType, blockIds) ``` Or import directly the specific function: ```typescript import { getEventsAtBlockIds } from "@onflow/sdk" getEventsAtBlockIds(eventType, blockIds) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const events = await fcl.send([ fcl.getEventsAtBlockIds("A.7e60df042a9c0868.FlowToken.TokensWithdrawn", [ "c4f239d49e96d1e5fbcf1f31027a6e582e8c03fcd9954177b7723fdb03d938c7", "5dbaa85922eb194a3dc463c946cc01c866f2ff2b88f3e59e21c0d8d00113273f" ]) ]).then(fcl.decode); ``` ## Parameters ### `eventType` - Type: `string` - Description: The type of event to get ### `blockIds` - Type: `string[]` - Description: The ids of the blocks to scan for events ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getNetworkParameters.md --- title: "getNetworkParameters" description: "getNetworkParameters function documentation." --- # getNetworkParameters A builder function that returns the interaction to get network parameters. Network parameters contain important configuration information about the Flow network, including the chain ID, which is essential for signing transactions correctly. This information is crucial for ensuring transactions are submitted to the correct network. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getNetworkParameters() ``` Or import directly the specific function: ```typescript import { getNetworkParameters } from "@onflow/sdk" getNetworkParameters() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get network parameters to verify chain ID const params = await fcl.send([ fcl.getNetworkParameters() ]).then(fcl.decode); console.log("Chain ID:", params.chainId); console.log("Network:", params.name); // Use this to verify you're connected to the right network if (params.chainId === "flow-mainnet") { console.log("Connected to Flow Mainnet"); } else if (params.chainId === "flow-testnet") { console.log("Connected to Flow Testnet"); } ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getNodeVersionInfo.md --- title: "getNodeVersionInfo" description: "getNodeVersionInfo function documentation." --- # getNodeVersionInfo A builder function for the Get Node Version Info interaction. Creates an interaction to retrieve version information from the connected Flow Access Node. This includes details about the node's software version, protocol version, and spork information. Consider using the pre-built interaction 'fcl.nodeVersionInfo()' if you do not need to pair with any other builders. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getNodeVersionInfo() ``` Or import directly the specific function: ```typescript import { getNodeVersionInfo } from "@onflow/sdk" getNodeVersionInfo() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get node version information using builder const versionInfo = await fcl.send([ fcl.getNodeVersionInfo() ]).then(fcl.decode); console.log("Node version:", versionInfo.semver); console.log("Protocol version:", versionInfo.protocol_version); console.log("Spork ID:", versionInfo.spork_id); // Use with other builders if needed const interaction = await fcl.build([ fcl.getNodeVersionInfo() // other builders can be added here ]); ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getTransaction.md --- title: "getTransaction" description: "getTransaction function documentation." --- # getTransaction A builder function that returns the interaction to get a transaction by id. Transaction id is a hash of the encoded transaction payload and can be calculated before submitting the transaction to the network. Transaction status represents the state of a transaction in the blockchain. Status can change until it is finalized. The transaction id provided must be from the current spork. Consider using 'fcl.tx(id).onceExecuted()' instead of calling this method directly for real-time transaction monitoring. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getTransaction(id) ``` Or import directly the specific function: ```typescript import { getTransaction } from "@onflow/sdk" getTransaction(id) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const tx = await fcl.send([ fcl.getTransaction("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); ``` ## Parameters ### `id` - Type: `string` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/getTransactionStatus.md --- title: "getTransactionStatus" description: "getTransactionStatus function documentation." --- # getTransactionStatus A builder function that returns the status of transaction. The transaction id provided must be from the current spork. Consider using 'fcl.tx(id)' instead of calling this method directly for real-time transaction monitoring. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.getTransactionStatus(transactionId) ``` Or import directly the specific function: ```typescript import { getTransactionStatus } from "@onflow/sdk" getTransactionStatus(transactionId) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const status = await fcl.send([ fcl.getTransactionStatus("9dda5f281897389b99f103a1c6b180eec9dac870de846449a302103ce38453f3") ]).then(fcl.decode); ``` ## Parameters ### `transactionId` - Type: `string` - Description: The id of the transaction to get the status of ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/initInteraction.md --- title: "initInteraction" description: "initInteraction function documentation." --- # initInteraction Creates a new interaction object with default values. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.initInteraction() ``` Or import directly the specific function: ```typescript import { initInteraction } from "@onflow/sdk" initInteraction() ``` ## Returns [`Interaction`](../types#interaction) A new interaction object initialized with default values --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/interaction.md --- title: "interaction" description: "interaction function documentation." --- # interaction Creates a new interaction object with default values. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.interaction() ``` Or import directly the specific function: ```typescript import { interaction } from "@onflow/sdk" interaction() ``` ## Returns [`Interaction`](../types#interaction) A new interaction object initialized with default values --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/isBad.md --- title: "isBad" description: "isBad function documentation." --- # isBad Checks if an interaction has a failed status. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.isBad(ix) ``` Or import directly the specific function: ```typescript import { isBad } from "@onflow/sdk" isBad(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { isBad, why } from "@onflow/sdk" const response = await fcl.send([ fcl.transaction`transaction { prepare(account: AuthAccount) {} }` ]); if (isBad(response)) { console.log("Transaction failed:", why(response)); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to check ## Returns `boolean` True if the interaction status is BAD, false otherwise --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/isOk.md --- title: "isOk" description: "isOk function documentation." --- # isOk Checks if an interaction has a successful status. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.isOk(ix) ``` Or import directly the specific function: ```typescript import { isOk } from "@onflow/sdk" isOk(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { isOk } from "@onflow/sdk" // Check if a transaction was successful const response = await fcl.send([ fcl.transaction`transaction { prepare(account: AuthAccount) {} }` ]); if (isOk(response)) { console.log("Transaction was successful"); } else { console.log("Transaction failed"); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to check ## Returns `boolean` True if the interaction status is OK, false otherwise --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/limit.md --- title: "limit" description: "limit function documentation." --- # limit A utility builder to set the compute limit on a transaction. The compute limit is the maximum amount of computation that can be performed during transaction execution. Setting an appropriate compute limit helps prevent infinite loops and ensures predictable transaction costs. Read more about [computation cost](https://docs.onflow.org/concepts/fees/#computation-cost) and [transaction fees](https://docs.onflow.org/concepts/fees/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.limit(limit) ``` Or import directly the specific function: ```typescript import { limit } from "@onflow/sdk" limit(limit) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; await fcl.mutate({ cadence: ` transaction { prepare(account: AuthAccount) { // Complex transaction logic here } } `, limit: 1000 // Set compute limit to 1000 }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { // Transaction logic } } `, fcl.limit(9999) // Set higher limit for complex operations ]); ``` ## Parameters ### `limit` - Type: `number` - Description: The maximum amount of computation for the transaction ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/nodeVersionInfo.md --- title: "nodeVersionInfo" description: "nodeVersionInfo function documentation." --- # nodeVersionInfo Retrieve version information from the connected Flow Access Node. This function returns detailed information about the Flow node's version, including the protocol version, spork information, and node-specific details. This is useful for debugging, compatibility checks, and understanding the network state. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.nodeVersionInfo(opts) ``` Or import directly the specific function: ```typescript import { nodeVersionInfo } from "@onflow/sdk" nodeVersionInfo(opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Get node version information const versionInfo = await fcl.nodeVersionInfo(); console.log(versionInfo); // { // semver: "v0.37.13", // commit: "12345abcd", // spork_id: "mainnet-23", // protocol_version: "2.13.10", // spork_root_block_height: "88483760", // node_root_block_height: "88483760" // } // Check compatibility const info = await fcl.nodeVersionInfo(); if (info.protocol_version.startsWith("2.13")) { console.log("Compatible with current protocol version"); } ``` ## Parameters ### `opts` (optional) - Type: `any` - Description: Optional parameters for the request ## Returns [`Promise`](../types#nodeversioninfo) A promise that resolves to a block response --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/param.md --- title: "param" description: "param function documentation." --- # param Legacy function for setting a single parameter on an interaction. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.param(params) ``` Or import directly the specific function: ```typescript import { param } from "@onflow/sdk" param(params) ``` ## Parameters ### `params` - Type: `never` - Description: The parameter to set ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/params.md --- title: "params" description: "params function documentation." --- # params Legacy function for setting parameters on an interaction. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.params(params) ``` Or import directly the specific function: ```typescript import { params } from "@onflow/sdk" params(params) ``` ## Parameters ### `params` - Type: `never` - Description: The parameters to set ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/payer.md --- title: "payer" description: "payer function documentation." --- # payer A builder function that adds payer account(s) to a transaction. Every transaction requires at least one payer. The payer is the account that pays the transaction fee for executing the transaction on the network. The payer account must have sufficient Flow tokens to cover the transaction fees. Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/#payer) and [transaction fees](https://docs.onflow.org/concepts/fees/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.payer(ax) ``` Or import directly the specific function: ```typescript import { payer } from "@onflow/sdk" payer(ax) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using current user as payer (most common case) await fcl.mutate({ cadence: ` transaction { prepare(acct: AuthAccount) { log("Transaction fees paid by: ".concat(acct.address.toString())) } } `, payer: fcl.authz // Current user as payer }); // Using custom payer with builder pattern await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { // Transaction logic } } `, fcl.proposer(fcl.authz), // Current user as proposer fcl.authorizations([fcl.authz]), // Current user as authorizer fcl.payer(customPayerAuthz) // Custom payer pays fees ]); // Multiple payers (advanced use case) await fcl.send([ fcl.transaction` transaction { prepare(acct: AuthAccount) { // Transaction logic } } `, fcl.payer([payerAuthz1, payerAuthz2]) // Multiple payers split fees ]); ``` ## Parameters ### `ax` (optional) - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: An account address or an array of account addresses ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that takes an interaction object and returns a new interaction object with the payer(s) added --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/ping.md --- title: "ping" description: "ping function documentation." --- # ping A builder function that creates a ping interaction to test connectivity to the Flow Access Node. The ping interaction is a simple way to test if the Flow Access Node is reachable and responding. This is useful for health checks, connectivity testing, and debugging network issues. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.ping() ``` Or import directly the specific function: ```typescript import { ping } from "@onflow/sdk" ping() ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Simple ping to test connectivity try { const response = await fcl.send([fcl.ping()]); console.log("Access Node is reachable"); } catch (error) { console.error("Access Node is not reachable:", error); } // Use ping for health checks const healthCheck = async () => { try { await fcl.send([fcl.ping()]); return { status: "healthy", timestamp: new Date().toISOString() }; } catch (error) { return { status: "unhealthy", error: error.message, timestamp: new Date().toISOString() }; } }; const health = await healthCheck(); console.log("Health status:", health); ``` ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/pipe.md --- title: "pipe" description: "pipe function documentation." --- # pipe Async pipe function to compose interactions. The pipe function is the foundation for composing multiple interaction builder functions together. It sequentially applies builder functions to an interaction, allowing for complex interaction construction. Each function in the pipe receives the result of the previous function and can modify or validate the interaction. Pipe has two main forms: 1. `pipe(builderFunctions)`: Returns a builder function 2. `pipe(interaction, builderFunctions)`: Directly executes the pipe on an interaction ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.pipe(fns) ``` Or import directly the specific function: ```typescript import { pipe } from "@onflow/sdk" pipe(fns) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using pipe to create a reusable builder const myTransactionBuilder = fcl.pipe([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log(amount) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]), fcl.limit(100) ]); // Use the builder const interaction = await fcl.build([myTransactionBuilder]); // Pipe is used internally by build() and send() await fcl.send([ fcl.script`access(all) fun main(): Int { return 42 }` ]); // This uses pipe internally ``` ## Parameters ### `fns` - Type: `(false | InteractionBuilderFn)[]` - Description: Array of builder functions to apply ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` An interaction builder function when called with just functions, or a Promise<Interaction> when called with an interaction and functions --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/proposer.md --- title: "proposer" description: "proposer function documentation." --- # proposer A builder function that adds the proposer to a transaction. The proposer is responsible for providing the proposal key and paying the network fee for the transaction. The proposer key is used to specify the sequence number and prevent replay attacks. Every transaction requires exactly one proposer. Read more about [transaction roles](https://docs.onflow.org/concepts/transaction-signing/#proposer) and [signing transactions](https://docs.onflow.org/concepts/accounts-and-keys/). ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.proposer(authz) ``` Or import directly the specific function: ```typescript import { proposer } from "@onflow/sdk" proposer(authz) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Using the current user as proposer await fcl.mutate({ cadence: ` transaction { prepare(account: AuthAccount) { log("Hello from proposer!") } } `, proposer: fcl.authz }); // Using builder pattern await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction executed") } } `, fcl.proposer(proposerAuthz), fcl.payer(payerAuthz), fcl.authorizations([authorizerAuthz]), fcl.limit(100) ]); ``` ## Parameters ### `authz` - Type: ```typescript export type AccountAuthorization = | (AuthorizationFn & Partial) | Partial ``` - Description: The authorization object for the proposer ## Returns [`Interaction`](../types#interaction) A function that takes an interaction object and returns a new interaction object with the proposer added --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/put.md --- title: "put" description: "put function documentation." --- # put Sets a value in an interaction object using a dot-notation key path. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.put(key, value) ``` Or import directly the specific function: ```typescript import { put } from "@onflow/sdk" put(key, value) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { put } from "@onflow/sdk" // Using put in a custom builder function const setCustomData = (data) => put("custom.data", data); await fcl.send([ fcl.script`access(all) fun main(): String { return "Hello" }`, setCustomData({ userId: 123, timestamp: Date.now() }) ]); // Direct usage const interaction = initInteraction(); put("network.endpoint", "https://access.mainnet.onflow.org")(interaction); ``` ## Parameters ### `key` - Type: `string` - Description: The dot-notation key path (e.g., "message.arguments") ### `value` - Type: `any` - Description: The value to set ## Returns [`Interaction`](../types#interaction) A function that takes an interaction and sets the value --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/ref.md --- title: "ref" description: "ref function documentation." --- # ref A builder function that sets the reference block for a transaction. The reference block specifies an expiration window (measured in blocks) during which a transaction is considered valid by the network. A transaction will be rejected if it is submitted past its expiry block. Flow calculates transaction expiry using the reference block field. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.ref(refBlock) ``` Or import directly the specific function: ```typescript import { ref } from "@onflow/sdk" ref(refBlock) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Set specific reference block for transaction await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction with custom reference block") } } `, fcl.ref("a1b2c3d4e5f6789..."), // Custom reference block ID fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]), fcl.limit(100) ]); // Usually, you don't need to set reference block manually // as FCL will automatically set it to the latest block ``` ## Parameters ### `refBlock` - Type: `string` - Description: The reference block ID ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolve.md --- title: "resolve" description: "resolve function documentation." --- # resolve Resolves an interaction by applying a series of resolvers in sequence. This is the main resolver function that takes a built interaction and prepares it for submission to the Flow blockchain by applying all necessary resolvers. The resolve function uses a pipeline approach, applying each resolver in sequence to transform the interaction from its initial built state to a fully resolved state ready for transmission to the Flow Access API. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolve(context) ``` Or import directly the specific function: ```typescript import { resolve } from "@onflow/sdk" resolve(context) ``` ## Usage ```typescript import { resolve, build, script } from "@onflow/sdk" const interaction = await build([ script` access(all) fun main(): String { return "Hello, World!" } ` ]) const resolved = await resolve(interaction) ``` ## Parameters ### `context` - Type: ```typescript export interface SdkContext { get accessNodeUrl(): string get transport(): SdkTransport get computeLimit(): number get customResolver(): ((args: any) => Promise) | undefined get customDecoders(): {[key: string]: (data: any) => any} get contracts(): { [contractName: string]: string } get debug(): {[key: string]: any} get legacyContractIdentifiers(): Record } ``` ## Returns [`Promise`](../types#interaction) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveAccounts.md --- title: "resolveAccounts" description: "resolveAccounts function documentation." --- # resolveAccounts Resolves account authorization functions and validates account configurations for transactions. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveAccounts(ix, opts) ``` Or import directly the specific function: ```typescript import { resolveAccounts } from "@onflow/sdk" resolveAccounts(ix, opts) ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing accounts to resolve ### `opts` (optional) - Type: ```typescript Record ``` - Description: Configuration options for resolution ## Returns [`Promise`](../types#interaction) The interaction with resolved accounts --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveArguments.md --- title: "resolveArguments" description: "resolveArguments function documentation." --- # resolveArguments Resolves transaction arguments by evaluating argument functions and converting them to appropriate types. This function processes all arguments in a transaction or script interaction, calling their transform functions to convert JavaScript values into Cadence-compatible argument formats that can be sent to the Flow network. The resolution process includes: - Calling argument resolver functions if present - Applying type transformations using the xform field - Handling recursive argument resolution up to a depth limit ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveArguments(ix) ``` Or import directly the specific function: ```typescript import { resolveArguments } from "@onflow/sdk" resolveArguments(ix) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Arguments are automatically resolved during send() await fcl.send([ fcl.script` access(all) fun main(amount: UFix64, recipient: Address): String { return "Sending ".concat(amount.toString()).concat(" to ").concat(recipient.toString()) } `, fcl.args([ fcl.arg("100.0", fcl.t.UFix64), // Will be resolved to Cadence UFix64 fcl.arg("0x01", fcl.t.Address) // Will be resolved to Cadence Address ]) ]).then(fcl.decode); // The resolveArguments function handles the conversion automatically ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing arguments to resolve ## Returns [`Promise`](../types#interaction) The interaction with resolved arguments ready for network transmission --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveCadence.md --- title: "resolveCadence" description: "resolveCadence function documentation." --- # resolveCadence ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveCadence(context) ``` Or import directly the specific function: ```typescript import { resolveCadence } from "@onflow/sdk" resolveCadence(context) ``` ## Parameters ### `context` - Type: ```typescript export interface SdkContext { get accessNodeUrl(): string get transport(): SdkTransport get computeLimit(): number get customResolver(): ((args: any) => Promise) | undefined get customDecoders(): {[key: string]: (data: any) => any} get contracts(): { [contractName: string]: string } get debug(): {[key: string]: any} get legacyContractIdentifiers(): Record } ``` ## Returns [`Promise`](../types#interaction) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveFinalNormalization.md --- title: "resolveFinalNormalization" description: "resolveFinalNormalization function documentation." --- # resolveFinalNormalization Normalizes account addresses by removing the "0x" prefix from all account addresses in the interaction. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveFinalNormalization(ix) ``` Or import directly the specific function: ```typescript import { resolveFinalNormalization } from "@onflow/sdk" resolveFinalNormalization(ix) ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object to normalize ## Returns [`Promise`](../types#interaction) The interaction with normalized account addresses --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveProposerSequenceNumber.md --- title: "resolveProposerSequenceNumber" description: "resolveProposerSequenceNumber function documentation." --- # resolveProposerSequenceNumber Resolves the sequence number for the proposer account by querying the blockchain. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveProposerSequenceNumber(nodeConfig) ``` Or import directly the specific function: ```typescript import { resolveProposerSequenceNumber } from "@onflow/sdk" resolveProposerSequenceNumber(nodeConfig) ``` ## Parameters ### `nodeConfig` - Type: ```typescript interface NodeConfig { node: string } ``` ## Returns [`Promise`](../types#interaction) A function that resolves the proposer sequence number for an interaction --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveRefBlockId.md --- title: "resolveRefBlockId" description: "resolveRefBlockId function documentation." --- # resolveRefBlockId Resolves the reference block ID for a transaction by querying the latest block from the network. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveRefBlockId(opts) ``` Or import directly the specific function: ```typescript import { resolveRefBlockId } from "@onflow/sdk" resolveRefBlockId(opts) ``` ## Parameters ### `opts` (optional) - Type: ```typescript { [key: string]: any; } ``` - Description: Optional configuration parameters ## Returns [`Promise`](../types#interaction) A function that resolves the reference block ID for an interaction --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveSignatures.md --- title: "resolveSignatures" description: "resolveSignatures function documentation." --- # resolveSignatures Resolves signatures for a transaction by coordinating the signing process for inside and outside signers. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveSignatures(ix) ``` Or import directly the specific function: ```typescript import { resolveSignatures } from "@onflow/sdk" resolveSignatures(ix) ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing transaction details ## Returns [`Promise`](../types#interaction) The interaction object with resolved signatures --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveValidators.md --- title: "resolveValidators" description: "resolveValidators function documentation." --- # resolveValidators Executes validator functions that have been attached to an interaction to perform validation checks. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveValidators(ix) ``` Or import directly the specific function: ```typescript import { resolveValidators } from "@onflow/sdk" resolveValidators(ix) ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object containing validators to execute ## Returns [`Promise`](../types#interaction) The interaction after running all validators --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/resolveVoucherIntercept.md --- title: "resolveVoucherIntercept" description: "resolveVoucherIntercept function documentation." --- # resolveVoucherIntercept Resolves voucher intercept functions by calling them with the current voucher. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.resolveVoucherIntercept(ix) ``` Or import directly the specific function: ```typescript import { resolveVoucherIntercept } from "@onflow/sdk" resolveVoucherIntercept(ix) ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction object to resolve voucher intercepts for ## Returns [`Promise`](../types#interaction) The interaction after voucher intercept processing --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/response.md --- title: "response" description: "response function documentation." --- # response Creates a default response object ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.response() ``` Or import directly the specific function: ```typescript import { response } from "@onflow/sdk" response() ``` ## Usage ```typescript import { response } from "@onflow/sdk" // Create a default response object const defaultResponse = response(); console.log(defaultResponse.transaction); // null console.log(defaultResponse.account); // null console.log(defaultResponse.block); // null // Typically used internally by the SDK to initialize responses // You'll rarely need to use this directly in application code ``` ## Returns ```typescript { tag: any; transaction: any; transactionStatus: any; transactionId: any; encodedData: any; events: any; event: any; accountStatusEvent: any; account: any; block: any; blockHeader: any; blockDigest: any; latestBlock: any; collection: any; networkParameters: any; streamConnection: any; heartbeat: any; nodeVersionInfo: any; } ``` A default response object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/script.md --- title: "script" description: "script function documentation." --- # script A builder function that creates a script interaction. Scripts allow you to write arbitrary non-mutating Cadence code on the Flow blockchain and return data. You can learn more about [Cadence here](https://cadence-lang.org/docs/language), but we are now only interested in executing the script code and getting back the data. We can execute a script using the latest state of the Flow blockchain or we can choose to execute the script at a specific time in history defined by a block height or block ID. Block ID is SHA3-256 hash of the entire block payload, but you can get that value from the block response properties. Block height expresses the height of the block in the chain. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.script(args) ``` Or import directly the specific function: ```typescript import { script } from "@onflow/sdk" script(args) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; const result = await fcl.query({ cadence: ` access(all) fun main(a: Int, b: Int, addr: Address): Int { log(addr) return a + b } `, args: (arg, t) => [ arg(7, t.Int), // a: Int arg(6, t.Int), // b: Int arg("0xba1132bc08f82fe2", t.Address), // addr: Address ], }); console.log(result); // 13 ``` ## Parameters ### `args` (optional) - Type: ```typescript [string | TemplateStringsArray | ((x?: unknown) => string), ...unknown[]] ``` - Description: The arguments to pass to the template ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/send.md --- title: "send" description: "send function documentation." --- # send Sends arbitrary scripts, transactions, and requests to Flow. This method consumes an array of builders that are to be resolved and sent. The builders required to be included in the array depend on the interaction that is being built. WARNING: Must be used in conjunction with 'fcl.decode(response)' to get back correct keys and all values in JSON. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.send(args, opts) ``` Or import directly the specific function: ```typescript import { send } from "@onflow/sdk" send(args, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // a script only needs to resolve the arguments to the script const response = await fcl.send([fcl.script`${script}`, fcl.args(args)]); // note: response values are encoded, call await fcl.decode(response) to get JSON // a transaction requires multiple 'builders' that need to be resolved prior to being sent to the chain - such as setting the authorizations. const response = await fcl.send([ fcl.transaction` ${transaction} `, fcl.args(args), fcl.proposer(proposer), fcl.authorizations(authorizations), fcl.payer(payer), fcl.limit(9999) ]); // note: response contains several values ``` ## Parameters ### `args` (optional) - Type: ```typescript false | InteractionBuilderFn | (false | InteractionBuilderFn)[] ``` - Description: An array of builders (functions that take an interaction object and return a new interaction object) ### `opts` (optional) - Type: `any` - Description: Additional optional options for the request #### Properties: - **`node`** - Custom node endpoint to use for this request - **`resolve`** - Custom resolve function to use for processing the interaction ## Returns `Promise` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/subscribe.md --- title: "subscribe" description: "subscribe function documentation." --- # subscribe Subscribe to real-time data from the Flow blockchain and automatically decode the responses. This is a utility function used for subscribing to real-time data from the WebSocket Streaming API. Data returned will be automatically decoded via the 'decode' function. Available topics include: `events`, `blocks`, `block_headers`, `block_digests`, `transaction_statuses`, `account_statuses`. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.subscribe(subscribeParams, opts) ``` Or import directly the specific function: ```typescript import { subscribe } from "@onflow/sdk" subscribe(subscribeParams, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { SubscriptionTopic } from "@onflow/sdk"; // Subscribe to events const subscription = fcl.subscribe({ topic: SubscriptionTopic.EVENTS, args: { eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"] }, onData: (events) => { console.log("Received events:", events); }, onError: (error) => { console.error("Subscription error:", error); } }); // Subscribe to blocks const blockSubscription = fcl.subscribe({ topic: SubscriptionTopic.BLOCKS, args: { blockStatus: "finalized" }, onData: (block) => { console.log("New block:", block); }, onError: (error) => { console.error("Block subscription error:", error); } }); // Later, to unsubscribe: subscription.unsubscribe(); blockSubscription.unsubscribe(); ``` ## Parameters ### `subscribeParams` - Type: `SubscribeParams` #### Properties: - **`topic`** - Type: `T` - The topic to subscribe to. - **`args`** - Type: `SubscriptionArgs` - The arguments for the subscription. - **`onData`** - Type: `(data: SubscriptionData) => void` - The callback to call when data is received. - **`onError`** - Type: `(error: Error) => void` - The callback to call when a fatal error occurs. ### `opts` (optional) - Type: ```typescript { node?: string; transport?: SdkTransport; } ``` - Description: Additional options for the subscription #### Properties: - **`node`** - Custom node endpoint to be used for the subscription - **`transport`** - Custom transport implementation for handling the connection ## Returns [`Subscription`](../types#subscription) A subscription object that allows you to manage the subscription (e.g., to unsubscribe later) --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/subscribeEvents.md --- title: "subscribeEvents" description: "subscribeEvents function documentation." --- # subscribeEvents Subscribe to events with the given filter and parameters. Creates a subscription to listen for real-time events from the Flow blockchain. This function configures the subscription parameters for filtering specific events based on type, addresses, contracts, and other criteria. Events are emitted by Cadence code during transaction execution and provide insights into what happened. Subscriptions allow you to listen for these events in real-time without polling. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.subscribeEvents(eventFilter) ``` Or import directly the specific function: ```typescript import { subscribeEvents } from "@onflow/sdk" subscribeEvents(eventFilter) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Subscribe to FlowToken transfer events const subscription = await fcl.send([ fcl.subscribeEvents({ eventTypes: [ "A.1654653399040a61.FlowToken.TokensWithdrawn", "A.1654653399040a61.FlowToken.TokensDeposited" ], startHeight: 1000000, // Start from specific block height heartbeatInterval: 3000 // 3 second heartbeat }) ]); // Subscribe to events from specific contracts const contractSubscription = await fcl.send([ fcl.subscribeEvents({ contracts: ["FlowToken", "FungibleToken"], addresses: ["0x1654653399040a61"] }) ]); // Handle the subscription data elsewhere using fcl.subscribe() ``` ## Parameters ### `eventFilter` - Type: [`EventFilter`](../types#eventfilter) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/subscribeRaw.md --- title: "subscribeRaw" description: "subscribeRaw function documentation." --- # subscribeRaw Subscribe to a topic without decoding the data. This function creates a raw subscription to Flow blockchain data streams without automatic decoding. It's useful when you need more control over data processing or want to handle raw responses directly. For most use cases, consider using the `subscribe()` function instead which provides automatic decoding. Available topics include: `events`, `blocks`, `block_headers`, `block_digests`, `transaction_statuses`, `account_statuses`. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.subscribeRaw(subscribeRawParams, opts) ``` Or import directly the specific function: ```typescript import { subscribeRaw } from "@onflow/sdk" subscribeRaw(subscribeRawParams, opts) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; import { SubscriptionTopic } from "@onflow/sdk"; // Subscribe to raw event data without automatic decoding const rawSubscription = fcl.subscribeRaw({ topic: SubscriptionTopic.EVENTS, args: { eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"] }, onData: (rawData) => { console.log("Raw event data:", rawData); // Handle raw data manually - no automatic decoding }, onError: (error) => { console.error("Raw subscription error:", error); } }); // Subscribe to raw block data const blockSubscription = fcl.subscribeRaw({ topic: SubscriptionTopic.BLOCKS, args: { blockStatus: "finalized" }, onData: (rawBlock) => { console.log("Raw block data:", rawBlock); }, onError: (error) => { console.error("Error:", error); } }); // Unsubscribe when done rawSubscription.unsubscribe(); ``` ## Parameters ### `subscribeRawParams` - Type: `SubscribeRawParams` #### Properties: - **`topic`** - Type: `T` - The topic to subscribe to. - **`args`** - Type: `SubscriptionArgs` - The arguments for the subscription. - **`onData`** - Type: `(data: RawSubscriptionData) => void` - The callback to call when data is received. - **`onError`** - Type: `(error: Error) => void` - The callback to call when a fatal error occurs. ### `opts` (optional) - Type: ```typescript { node?: string; transport?: SdkTransport; } ``` - Description: Additional options for the subscription #### Properties: - **`node`** - Custom node endpoint to be used for the subscription - **`transport`** - Custom transport implementation for handling the connection ## Returns ```typescript { unsubscribe: () => void; } ``` A subscription object with an unsubscribe method --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/testUtils.md --- title: "TestUtils" description: "Namespace containing TestUtils utilities" --- # TestUtils ## Overview Namespace containing TestUtils utilities ## Functions ### authzDeepResolveMany Creates a deep test authorization resolver with nested resolution for complex testing scenarios. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.authzDeepResolveMany(opts, depth) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.authzDeepResolveMany(opts, depth) ``` #### Parameters ##### `opts` (optional) - Type: ```typescript interface IAuthzResolveMany { tempId?: string authorizations: any[] proposer?: any payer?: any } ``` - Description: Configuration including authorizations array and optional proposer/payer ##### `depth` (optional) - Type: `number` - Description: The depth of nesting for the resolver (default: 1) #### Returns [`InteractionAccount`](../types#interactionaccount) ### authzFn Creates a test authorization function for testing transactions. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.authzFn(opts) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.authzFn(opts) ``` #### Parameters ##### `opts` (optional) - Type: ```typescript interface IAuthzOpts { signingFunction?: (signable: any) => any } ``` - Description: Optional configuration including custom signing function #### Returns `Partial` ### authzResolve Creates a test authorization resolver that can be used for testing account resolution. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.authzResolve(opts) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.authzResolve(opts) ``` #### Parameters ##### `opts` (optional) - Type: ```typescript interface IAuthzResolveOpts { tempId?: string } ``` - Description: Optional configuration including temporary ID #### Returns ```typescript Partial; kind: InteractionResolverKind.ACCOUNT; addr: string; keyId: string | number; sequenceNum: number; signature: string; extensionData?: string; signingFunction: any; role: { proposer: boolean; authorizer: boolean; payer: boolean; param?: boolean; }; authorization: any; } ``` ### authzResolveMany Creates a test authorization resolver that handles multiple accounts with different roles. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.authzResolveMany(opts) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.authzResolveMany(opts) ``` #### Parameters ##### `opts` (optional) - Type: ```typescript interface IAuthzResolveMany { tempId?: string authorizations: any[] proposer?: any payer?: any } ``` - Description: Configuration including authorizations array and optional proposer/payer #### Returns [`InteractionAccount`](../types#interactionaccount) ### idof Generates a unique identifier for an account based on its address and key ID. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.idof(acct) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.idof(acct) ``` #### Parameters ##### `acct` - Type: [`InteractionAccount`](../types#interactionaccount) - Description: The account object #### Returns `string` ### run Runs a set of functions on an interaction This is a utility function for testing that builds and resolves an interaction with the provided builder functions. It automatically adds a reference block and then resolves the interaction for testing purposes. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.run(fns) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.run(fns) ``` #### Usage ```typescript import { run } from "@onflow/sdk" import * as fcl from "@onflow/fcl"; // Test a simple script interaction const result = await run([ fcl.script` access(all) fun main(): Int { return 42 } ` ]); console.log(result.cadence); // The Cadence script console.log(result.tag); // "SCRIPT" // Test a transaction with arguments const txResult = await run([ fcl.transaction` transaction(amount: UFix64) { prepare(account: AuthAccount) { log(amount) } } `, fcl.args([fcl.arg("10.0", fcl.t.UFix64)]) ]); console.log(txResult.message.arguments); // The resolved arguments ``` #### Parameters ##### `fns` (optional) - Type: ```typescript ((ix: Interaction) => Interaction | Promise)[] ``` - Description: An array of functions to run on the interaction #### Returns [`Promise`](../types#interaction) ### sig Generates a test signature string for an account. #### Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.TestUtils.sig(opts) ``` Or import the namespace directly: ```typescript import { TestUtils } from "@onflow/sdk" TestUtils.sig(opts) ``` #### Parameters ##### `opts` - Type: `Partial` - Description: Partial account object containing address and keyId #### Returns `string` --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/transaction.md --- title: "transaction" description: "transaction function documentation." --- # transaction A template builder to use a Cadence transaction for an interaction. FCL "mutate" does the work of building, signing, and sending a transaction behind the scenes. Flow supports great flexibility when it comes to transaction signing, we can define multiple authorizers (multi-sig transactions) and have different payer account than proposer. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.transaction(args) ``` Or import directly the specific function: ```typescript import { transaction } from "@onflow/sdk" transaction(args) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl" // Basic transaction usage await fcl.mutate({ cadence: ` transaction(a: Int) { prepare(acct: &Account) { log(acct) log(a) } } `, args: (arg, t) => [ arg(6, t.Int) ], limit: 50 }) // Single party, single signature // Proposer, payer and authorizer are the same account await fcl.mutate({ cadence: ` transaction { prepare(acct: &Account) {} } `, authz: currentUser, // Optional. Will default to currentUser if not provided. limit: 50, }) // Multiple parties // Proposer and authorizer are the same account, but different payer await fcl.mutate({ cadence: ` transaction { prepare(acct: &Account) {} } `, proposer: authzFn, payer: authzTwoFn, authorizations: [authzFn], limit: 50, }) ``` ## Parameters ### `args` (optional) - Type: ```typescript [string | TemplateStringsArray, ...any[]] ``` - Description: The arguments to pass to the template ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/update.md --- title: "update" description: "update function documentation." --- # update Updates a value in an interaction object using a transformation function. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.update(key, fn) ``` Or import directly the specific function: ```typescript import { update } from "@onflow/sdk" update(key, fn) ``` ## Usage ```typescript import { update, put, initInteraction } from "@onflow/sdk" const interaction = initInteraction(); // Set initial value put("counter", 0)(interaction); // Increment counter const increment = update("counter", (current) => (current || 0) + 1); increment(interaction); // counter becomes 1 increment(interaction); // counter becomes 2 // Update array put("tags", ["flow", "blockchain"])(interaction); const addTag = update("tags", (tags) => [...(tags || []), "web3"]); addTag(interaction); // tags becomes ["flow", "blockchain", "web3"] ``` ## Parameters ### `key` - Type: `string` - Description: The dot-notation key path to update ### `fn` (optional) - Type: ```typescript (v: T | T[], ...args: any[]) => T | T[] ``` - Description: The transformation function to apply to the existing value ## Returns [`Interaction`](../types#interaction) A function that takes an interaction and updates the value --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/validator.md --- title: "validator" description: "validator function documentation." --- # validator A builder function that adds a validator to a transaction. Validators are functions that run during transaction building to check for invalid configurations or parameters. They help catch errors early before submitting transactions to the network, preventing failed transactions and wasted compute costs. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.validator(cb) ``` Or import directly the specific function: ```typescript import { validator } from "@onflow/sdk" validator(cb) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Custom validator to ensure account has sufficient balance const validateBalance = (ix) => { if (ix.message.computeLimit > 1000) { throw new Error("Compute limit too high for this account"); } return ix; }; await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { // Transaction logic } } `, fcl.validator(validateBalance), fcl.limit(500) // This will pass validation ]); ``` ## Parameters ### `cb` - Type: `Function` - Description: The validator function that takes an interaction and returns it (or throws an error if invalid) ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/voucherIntercept.md --- title: "voucherIntercept" description: "voucherIntercept function documentation." --- # voucherIntercept A builder function that intercepts and modifies a voucher. This function is useful for debugging, logging, or making modifications to the transaction data. The voucher contains all the transaction details in their final form. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.voucherIntercept(fn) ``` Or import directly the specific function: ```typescript import { voucherIntercept } from "@onflow/sdk" voucherIntercept(fn) ``` ## Usage ```typescript import * as fcl from "@onflow/fcl"; // Intercept voucher for logging await fcl.send([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Transaction executed") } } `, fcl.voucherIntercept((voucher) => { console.log("Voucher details:", { cadence: voucher.cadence, proposalKey: voucher.proposalKey, payer: voucher.payer, authorizers: voucher.authorizers, computeLimit: voucher.computeLimit }); }), fcl.proposer(fcl.authz), fcl.payer(fcl.authz), fcl.authorizations([fcl.authz]) ]); ``` ## Parameters ### `fn` - Type: ```typescript type VoucherInterceptFn = (voucher: Voucher) => any | Promise ``` - Description: The function to intercept and potentially modify the voucher ## Returns ```typescript export type InteractionBuilderFn = ( ix: Interaction ) => Interaction | Promise ``` A function that processes an interaction object --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/voucherToTxId.md --- title: "voucherToTxId" description: "voucherToTxId function documentation." --- # voucherToTxId Converts a voucher object to a transaction ID. This function computes the transaction ID by encoding and hashing the voucher. The transaction ID can be used to track the transaction status on the Flow network. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.voucherToTxId(voucher) ``` Or import directly the specific function: ```typescript import { voucherToTxId } from "@onflow/sdk" voucherToTxId(voucher) ``` ## Usage ```typescript import { voucherToTxId, createSignableVoucher } from "@onflow/sdk" import * as fcl from "@onflow/fcl"; // Create a voucher from an interaction const interaction = await fcl.build([ fcl.transaction` transaction { prepare(account: AuthAccount) { log("Hello, Flow!") } } `, fcl.proposer(authz), fcl.payer(authz), fcl.authorizations([authz]) ]); const voucher = createSignableVoucher(interaction); // Calculate the transaction ID const txId = voucherToTxId(voucher); console.log("Transaction ID:", txId); // Returns something like: "a1b2c3d4e5f6789..." // You can use this ID to track the transaction const txStatus = await fcl.tx(txId).onceSealed(); console.log("Transaction status:", txStatus); ``` ## Parameters ### `voucher` - Type: ```typescript export interface Voucher { cadence: string refBlock: string computeLimit: number arguments: VoucherArgument[] proposalKey: VoucherProposalKey payer: string authorizers: string[] payloadSigs: Sig[] envelopeSigs: Sig[] } ``` - Description: The voucher object to convert ## Returns `string` A transaction ID string --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/sdk/why.md --- title: "why" description: "why function documentation." --- # why Returns the reason for an interaction failure. ## Import You can import the entire package and access the function: ```typescript import * as sdk from "@onflow/sdk" sdk.why(ix) ``` Or import directly the specific function: ```typescript import { why } from "@onflow/sdk" why(ix) ``` ## Usage ```typescript import { Bad, why, initInteraction } from "@onflow/sdk" const interaction = Bad(initInteraction(), "Network timeout"); console.log(why(interaction)); // "Network timeout" // Used with error handling if (isBad(response)) { console.error("Error occurred:", why(response)); } ``` ## Parameters ### `ix` - Type: [`Interaction`](../types#interaction) - Description: The interaction to get the failure reason from ## Returns `string` The reason string or undefined if no reason is set --- --- # File: /md/build/tools/clients/fcl-js/packages-docs/types.md --- title: Type Definitions description: Type definitions for the Flow Client Library (FCL) packages. --- # Type Definitions Documentation for core types used throughout the Flow Client Library (FCL). ## Interfaces ### Account ```typescript import { type Account } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ----------- | ------------------------ | ------------------------------------------------------- | | `address` | `string` | The address of the account | | `balance` | `number` | The FLOW balance of the account in 10^8 | | `code` | `number` | The code of any Cadence contracts stored in the account | | `contracts` | `Record` | Any contracts deployed to this account | | `keys` | `AccountKey[]` | The keys associated with the account | ### AccountKey ```typescript import { type AccountKey } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------- | -------------------- | --------------------------------------------------- | | `index` | `number` | The index of the key in the account | | `publicKey` | `string` | The public key of the account key | | `signAlgo` | `SignatureAlgorithm` | The signature algorithm used by the key | | `signAlgoString` | `string` | The signature algorithm used by the key as a string | | `hashAlgo` | `HashAlgorithm` | The hashing algorithm used by the key | | `hashAlgoString` | `string` | The hashing algorithm used by the key as a string | | `sequenceNumber` | `number` | The sequence number of the key | | `weight` | `number` | The weight of the key | | `revoked` | `boolean` | Whether or not the key has been revoked | ### AccountStatusEvent ```typescript import { type AccountStatusEvent } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------- | -------- | -------------------------------------------------------------- | | `accountAddress` | `string` | The address of the account which the event is associated with. | ### Block ```typescript import { type Block } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------------- | ----------------------- | --------------------------------------------------------- | | `id` | `string` | The id of the block | | `parentId` | `string` | The id of the parent block | | `height` | `number` | The height of the block | | `timestamp` | `string` | Time related fields | | `parentVoterSignature` | `string` | The parent voter signature of the block | | `collectionGuarantees` | `CollectionGuarantee[]` | Contains the ids of collections included in the block | | `blockSeals` | `BlockSeal[]` | The details of which nodes executed and sealed the blocks | ### BlockDigest ```typescript import { type BlockDigest } from '@onflow/fcl'; ``` BlockDigest holds lightweight block information which includes only block id, block height and block timestamp. **Properties:** | Name | Type | Description | | ----------- | -------- | ----------------------- | | `id` | `string` | The id of the block | | `height` | `number` | The height of the block | | `timestamp` | `string` | Timestamp of the block | ### BlockHeader ```typescript import { type BlockHeader } from '@onflow/fcl'; ``` Header contains all meta-data for a block, as well as a hash representing the combined payload of the entire block. It is what consensus nodes agree on after validating the contents against the payload hash. **Properties:** | Name | Type | Description | | ---------------------- | -------- | --------------------------------------- | | `id` | `string` | The id of the block | | `parentId` | `string` | The id of the parent block | | `height` | `number` | The height of the block | | `timestamp` | `string` | The timestamp of the block | | `parentVoterSignature` | `string` | The parent voter signature of the block | ### BlockHeartbeat ```typescript import { type BlockHeartbeat } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ------------- | -------- | -------------------------- | | `blockId` | `string` | The ID of the block | | `blockHeight` | `number` | The height of the block | | `timestamp` | `string` | The timestamp of the block | ### BlockSeal ```typescript import { type BlockSeal } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | -------------------- | -------- | ------------------------------------- | | `blockId` | `string` | The id of the block | | `executionReceiptId` | `string` | The execution receipt id of the block | ### CollectionGuarantee ```typescript import { type CollectionGuarantee } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | -------------- | ---------- | --------------------------- | | `collectionId` | `string` | The id of the block | | `signerIds` | `string[]` | The signer ids of the block | ### CompositeSignature ```typescript import { type CompositeSignature } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | --------------- | -------- | -------------------------------------------------------------------------- | | `f_type` | `string` | A type identifier used internally by FCL | | `f_vsn` | `string` | FCL protocol version | | `addr` | `string` | Flow Address (sans prefix) | | `keyId` | `number` | Key ID | | `signature` | `string` | Signature as a hex string | | `extensionData` | `string` | Optional signature extension data for alternative schemes (e.g., WebAuthn) | ### CurrentUser ```typescript import { type CurrentUser } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ----------- | ----------- | -------------------------------------------------------------------------------------------- | | `addr` | `string` | The public address of the current user | | `cid` | `string` | A wallet specified content identifier for user metadata | | `expiresAt` | `number` | A wallet specified time-frame for a valid session | | `f_type` | `string` | A type identifier used internally by FCL | | `f_vsn` | `string` | FCL protocol version | | `loggedIn` | `boolean` | Whether or not the current user is logged in | | `services` | `Service[]` | A list of trusted services that express ways of interacting with the current user's identity | ### Event ```typescript import { type Event } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ------------------ | -------- | ------------------------------------------------------------------------------------------------- | | `blockId` | `string` | ID of the block that contains the event. | | `blockHeight` | `number` | Height of the block that contains the event. | | `blockTimestamp` | `string` | The timestamp of when the block was sealed in a DateString format. eg. '2021-06-25T13:42:04.227Z' | | `type` | `string` | A string containing the event name. | | `transactionId` | `string` | Can be used to query transaction information, eg. via a Flow block explorer. | | `transactionIndex` | `number` | Used to prevent replay attacks. | | `eventIndex` | `number` | Used to prevent replay attacks. | | `data` | `any` | The data emitted from the event. | ### EventFilter ```typescript import { type EventFilter } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ------------------- | ---------- | ------------------------------------------------------------------- | | `eventTypes` | `string[]` | The event types to listen for | | `addresses` | `string[]` | The addresses to listen for | | `contracts` | `string[]` | The contracts to listen for | | `startBlockId` | `string` | The block ID to start listening for events | | `startHeight` | `number` | The block height to start listening for events | | `heartbeatInterval` | `number` | The interval in milliseconds to send a heartbeat to the Access Node | ### Interaction ```typescript import { type Interaction } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | `tag` | `InteractionTag` | The type of interaction | | `assigns` | `Record` | Assigned values for the interaction | | `status` | `InteractionStatus` | The status of the interaction | | `reason` | `string` | Reason for the current status | | `accounts` | `Record` | Accounts involved in the interaction | | `params` | `Record` | Parameters for the interaction | | `arguments` | `Record` | Arguments for the interaction | | `message` | `{ cadence: string; refBlock: string; computeLimit: number; proposer: string; payer: string; authorizations: string[]; params: Record[]; arguments: string[]; }` | Message details for the interaction | | `proposer` | `string` | The proposer of the transaction | | `authorizations` | `string[]` | The authorizations for the transaction | | `payer` | `string[]` | The payer(s) of the transaction | | `events` | `{ eventType: string; start: string \| number; end: string \| number; blockIds: string[]; }` | Event-related information | | `transaction` | `{ id: string; }` | Transaction-related information | | `block` | `{ id: string; height: string \| number; isSealed: boolean; }` | Block-related information | | `account` | `{ addr: string; }` | Account-related information | | `collection` | `{ id: string; }` | Collection-related information | | `subscribeEvents` | `{ eventTypes: string[]; addresses: string[]; contracts: string[]; startBlockId: string; startHeight: number; heartbeatInterval: number; }` | Event subscription information | ### InteractionAccount ```typescript import { type InteractionAccount } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ----------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | | `kind` | `InteractionResolverKind.ACCOUNT` | The kind of interaction resolver | | `tempId` | `string` | Temporary identifier for the account | | `addr` | `string` | The address of the account | | `keyId` | `string \| number` | The key ID used for signing | | `sequenceNum` | `number` | The sequence number for the account key | | `signature` | `string` | The signature for the account | | `extensionData` | `string` | Optional extension data for alternative signature schemes (e.g., WebAuthn) | | `signingFunction` | `any` | Function used for signing | | `resolve` | `any` | Resolver function for the account | | `role` | `{ proposer: boolean; authorizer: boolean; payer: boolean; param?: boolean; }` | Role of the account in the transaction | | `authorization` | `any` | Authorization details for the account | ### Key ```typescript import { type Key } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------- | -------- | ------------------------------------------------------------------------- | | `sequenceNumber` | `number` | Sequence number of key used by the proposer of this transaction | | `keyId` | `number` | The ID of the key in the account used by the proposer of this transaction | | `address` | `string` | The address of the proposer of this transaction | ### NodeVersionInfo ```typescript import { type NodeVersionInfo } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------------- | -------- | ---------------------------------------- | | `semver` | `string` | The semver version of the node. | | `commit` | `string` | The commit hash of the node. | | `sporkId` | `string` | The spork id of the node. | | `protocolVersion` | `number` | The protocol version of the node. | | `sporkRootBlockHeight` | `number` | The spork root block height of the node. | | `nodeRootBlockHeight` | `number` | The node root block height of the node. | ### Provider ```typescript import { type Provider } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ------------------ | --------- | ---------------------------------------------------------------------------- | | `address` | `string` | The blockchain address of the Wallet provider. | | `name` | `string` | The name of the Wallet provider. | | `icon` | `string` | The icon of the Wallet provider (may be a URL or a data URI). | | `description` | `string` | A brief description of the Wallet provider. | | `color` | `string` | The preferred color to represent the Wallet provider (e.g., for UI styling). | | `supportEmail` | `string` | The support email address of the Wallet provider. | | `website` | `string` | The website URL of the Wallet provider. | | `is_installed` | `boolean` | Indicates whether the Wallet provider is installed (if applicable). | | `requires_install` | `boolean` | Indicates whether the Wallet provider requires installation (if applicable). | | `install_link` | `string` | The install link for the Wallet provider. | ### Service ```typescript import { type Service } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------- | ------------------------ | ---------------------------------------- | | `f_type` | `string` | A type identifier used internally by FCL | | `f_vsn` | `string` | FCL protocol version | | `type` | `string` | Service type | | `method` | `string` | Service method | | `uid` | `string` | Service uid | | `endpoint` | `string` | Service endpoint | | `provider` | `Provider` | Service provider object | | `params` | `Record` | Service parameters | | `data` | `Record` | Service data | | `headers` | `Record` | Service headers | ### Signature ```typescript import { type Signature } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ---------------- | -------- | ------------------------------------------------------------ | | `sequenceNumber` | `string` | Sequence number of the key used to perform this signature. | | `keyId` | `number` | ID of the key in the account used to perform this signature. | | `signature` | `string` | The signature represented as a hex string. | ### StreamConnection ```typescript import { type StreamConnection } from '@onflow/fcl'; ``` ### Transaction ```typescript import { type Transaction } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | -------------------- | ------------- | -------------------------------------------------------------------------- | | `script` | `string` | The Cadence code used to execute this transaction. | | `args` | `string[]` | The JSON-CDC encoded arguments passed in to the transaction. | | `referenceBlockId` | `string` | The reference block id for this transaction. | | `computeLimit` | `number` | The compute unit limit for the transaction. | | `proposalKey` | `Key` | The key used by the proposer of this transaction. | | `sequenceNumber` | `string` | Sequence number of the key used by the proposer of this transaction. | | `keyId` | `number` | The ID of the key in the account used by the proposer of this transaction. | | `address` | `string` | The address of the proposer of this transaction. | | `payer` | `string` | Address of the payer of the transaction. | | `proposer` | `string` | Address of the proposer of this transaction. | | `authorizers` | `string[]` | Array of addresses of authorizers of this transaction. | | `payloadSignatures` | `Signature[]` | The payload signatures for the transaction. | | `envelopeSignatures` | `Signature[]` | The envelope signatures for the transaction. | ### TransactionStatus ```typescript import { type TransactionStatus } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | -------------- | ---------------------------- | ------------------------------------------------------------------------------ | | `blockId` | `string` | The ID of the Block the transaction is included in. | | `status` | `TransactionExecutionStatus` | The execution status of the transaction | | `statusString` | `string` | The status as as descriptive text (e.g. "FINALIZED"). | | `statusCode` | `0 \| 1` | The result of the transaction, if executed (i.e. 0 for success, 1 for failure) | | `errorMessage` | `string` | The error message of the transaction. | | `events` | `Event[]` | The events for this result. | ## Types ### EventStream ```typescript import { type EventStream } from '@onflow/fcl'; ``` **Properties:** | Name | Type | Description | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `on` | `{ (channel: C, listener: (data: { events: Event[]; heartbeat: BlockHeartbeat; }[C]) => void): EventStream; (event: "close", listener: () => void): EventStream; (event: "error", listener: (err: any) => void): EventStream; }` | | | `off` | `{ (event: C, listener: (data: { events: Event[]; heartbeat: BlockHeartbeat; }[C]) => void): EventStream; (event: "close", listener: () => void): EventStream; (event: "error", listener: (err: any) => void): EventStream; }` | | | `close` | `() => void` | | ### RawSubscriptionData ```typescript import { type RawSubscriptionData } from '@onflow/fcl'; ``` Raw data returned by a subscription, which will vary depending on the topic and is not decoded ### SdkTransport ```typescript import { type SdkTransport } from '@onflow/fcl'; ``` Transport interface for the Flow SDK that provides methods for sending interactions and subscribing to data **Properties:** | Name | Type | Description | | ----------- | ------------- | ---------------------------------------------------------------- | | `send` | `SendFn` | Function to send an interaction to the Flow blockchain | | `subscribe` | `SubscribeFn` | Function to subscribe to real-time data from the Flow blockchain | ### Subscription ```typescript import { type Subscription } from '@onflow/fcl'; ``` A subscription object that allows managing the subscription lifecycle **Properties:** | Name | Type | Description | | ------------- | ------------ | --------------------------------------------- | | `unsubscribe` | `() => void` | Function to unsubscribe from the subscription | ### SubscriptionArgs ```typescript import { type SubscriptionArgs } from '@onflow/fcl'; ``` Arguments for a subscription, which will vary depending on the topic ### SubscriptionData ```typescript import { type SubscriptionData } from '@onflow/fcl'; ``` The data returned by a subscription, which will vary depending on the topic ## Enums ### FvmErrorCode ```typescript import { FvmErrorCode } from '@onflow/fcl'; ``` Error codes defined by the Flow Virtual Machine (FVM) for various types of errors that can occur during transaction execution **Members:** | Name | Value | | ------------------------------------------------- | ----- | | `UNKNOWN_ERROR` | -1 | | `TX_VALIDATION_ERROR` | 1000 | | `INVALID_TX_BYTE_SIZE_ERROR` | 1001 | | `INVALID_REFERENCE_BLOCK_ERROR` | 1002 | | `EXPIRED_TRANSACTION_ERROR` | 1003 | | `INVALID_SCRIPT_ERROR` | 1004 | | `INVALID_GAS_LIMIT_ERROR` | 1005 | | `INVALID_PROPOSAL_SIGNATURE_ERROR` | 1006 | | `INVALID_PROPOSAL_SEQ_NUMBER_ERROR` | 1007 | | `INVALID_PAYLOAD_SIGNATURE_ERROR` | 1008 | | `INVALID_ENVELOPE_SIGNATURE_ERROR` | 1009 | | `FVM_INTERNAL_ERROR` | 1050 | | `VALUE_ERROR` | 1051 | | `INVALID_ARGUMENT_ERROR` | 1052 | | `INVALID_ADDRESS_ERROR` | 1053 | | `INVALID_LOCATION_ERROR` | 1054 | | `ACCOUNT_AUTHORIZATION_ERROR` | 1055 | | `OPERATION_AUTHORIZATION_ERROR` | 1056 | | `OPERATION_NOT_SUPPORTED_ERROR` | 1057 | | `BLOCK_HEIGHT_OUT_OF_RANGE_ERROR` | 1058 | | `EXECUTION_ERROR` | 1100 | | `CADENCE_RUNTIME_ERROR` | 1101 | | `ENCODING_UNSUPPORTED_VALUE` | 1102 | | `STORAGE_CAPACITY_EXCEEDED` | 1103 | | `GAS_LIMIT_EXCEEDED_ERROR` | 1104 | | `EVENT_LIMIT_EXCEEDED_ERROR` | 1105 | | `LEDGER_INTERACTION_LIMIT_EXCEEDED_ERROR` | 1106 | | `STATE_KEY_SIZE_LIMIT_ERROR` | 1107 | | `STATE_VALUE_SIZE_LIMIT_ERROR` | 1108 | | `TRANSACTION_FEE_DEDUCTION_FAILED_ERROR` | 1109 | | `COMPUTATION_LIMIT_EXCEEDED_ERROR` | 1110 | | `MEMORY_LIMIT_EXCEEDED_ERROR` | 1111 | | `COULD_NOT_DECODE_EXECUTION_PARAMETER_FROM_STATE` | 1112 | | `SCRIPT_EXECUTION_TIMED_OUT_ERROR` | 1113 | | `SCRIPT_EXECUTION_CANCELLED_ERROR` | 1114 | | `EVENT_ENCODING_ERROR` | 1115 | | `INVALID_INTERNAL_STATE_ACCESS_ERROR` | 1116 | | `INSUFFICIENT_PAYER_BALANCE` | 1118 | | `ACCOUNT_ERROR` | 1200 | | `ACCOUNT_NOT_FOUND_ERROR` | 1201 | | `ACCOUNT_PUBLIC_KEY_NOT_FOUND_ERROR` | 1202 | | `ACCOUNT_ALREADY_EXISTS_ERROR` | 1203 | | `FROZEN_ACCOUNT_ERROR` | 1204 | | `ACCOUNT_STORAGE_NOT_INITIALIZED_ERROR` | 1205 | | `ACCOUNT_PUBLIC_KEY_LIMIT_ERROR` | 1206 | | `CONTRACT_ERROR` | 1250 | | `CONTRACT_NOT_FOUND_ERROR` | 1251 | | `CONTRACT_NAMES_NOT_FOUND_ERROR` | 1252 | | `EVM_EXECUTION_ERROR` | 1300 | ### HashAlgorithm ```typescript import { HashAlgorithm } from '@onflow/fcl'; ``` **Members:** | Name | Value | | ----------------------- | ----- | | `SHA2_256` | 1 | | `SHA2_384` | 2 | | `SHA3_256` | 3 | | `SHA3_384` | 4 | | `KMAC128_BLS_BLS12_381` | 5 | ### InteractionResolverKind ```typescript import { InteractionResolverKind } from '@onflow/fcl'; ``` Represents different kinds of interaction resolvers **Members:** | Name | Value | | ---------- | ---------- | | `ARGUMENT` | "ARGUMENT" | | `ACCOUNT` | "ACCOUNT" | ### InteractionStatus ```typescript import { InteractionStatus } from '@onflow/fcl'; ``` Status of an interaction with the Flow blockchain **Members:** | Name | Value | | ----- | ----- | | `BAD` | "BAD" | | `OK` | "OK" | ### InteractionTag ```typescript import { InteractionTag } from '@onflow/fcl'; ``` Represents different types of interactions with the Flow blockchain **Members:** | Name | Value | | ------------------------ | ------------------------ | | `UNKNOWN` | "UNKNOWN" | | `SCRIPT` | "SCRIPT" | | `TRANSACTION` | "TRANSACTION" | | `GET_TRANSACTION_STATUS` | "GET_TRANSACTION_STATUS" | | `GET_ACCOUNT` | "GET_ACCOUNT" | | `GET_EVENTS` | "GET_EVENTS" | | `PING` | "PING" | | `GET_TRANSACTION` | "GET_TRANSACTION" | | `GET_BLOCK` | "GET_BLOCK" | | `GET_BLOCK_HEADER` | "GET_BLOCK_HEADER" | | `GET_COLLECTION` | "GET_COLLECTION" | | `GET_NETWORK_PARAMETERS` | "GET_NETWORK_PARAMETERS" | | `SUBSCRIBE_EVENTS` | "SUBSCRIBE_EVENTS" | | `GET_NODE_VERSION_INFO` | "GET_NODE_VERSION_INFO" | ### SignatureAlgorithm ```typescript import { SignatureAlgorithm } from '@onflow/fcl'; ``` **Members:** | Name | Value | | ----------------- | ----- | | `ECDSA_P256` | 1 | | `ECDSA_secp256k1` | 2 | | `BLS_BLS12_381` | 3 | ### SubscriptionTopic ```typescript import { SubscriptionTopic } from '@onflow/fcl'; ``` Represents different topics that can be subscribed to for real-time data from the Flow blockchain **Members:** | Name | Value | | ---------------------- | ---------------------- | | `BLOCKS` | "blocks" | | `BLOCK_HEADERS` | "block_headers" | | `BLOCK_DIGESTS` | "block_digests" | | `ACCOUNT_STATUSES` | "account_statuses" | | `TRANSACTION_STATUSES` | "transaction_statuses" | | `EVENTS` | "events" | ### TransactionExecutionStatus ```typescript import { TransactionExecutionStatus } from '@onflow/fcl'; ``` The execution status of the transaction. **Members:** | Name | Value | | ----------- | ----- | | `UNKNOWN` | 0 | | `PENDING` | 1 | | `FINALIZED` | 2 | | `EXECUTED` | 3 | | `SEALED` | 4 | | `EXPIRED` | 5 | ### TransactionRole ```typescript import { TransactionRole } from '@onflow/fcl'; ``` Represents different roles in a transaction **Members:** | Name | Value | | ------------ | ------------ | | `AUTHORIZER` | "authorizer" | | `PAYER` | "payer" | | `PROPOSER` | "proposer" | --- --- # File: /md/build/tools/clients/fcl-js/proving-authentication.mdx --- title: Proving Ownership of a Flow Account --- ## Proving Ownership of a Flow Account A common desire that application developers have is to be able to prove that a user controls an onchain account. Proving ownership of an onchain account is a way to authenticate a user with an application backend. Fortunately, Flow Client Library (FCL) provides a way to achieve this. During user authentication, some FCL compatible wallets will choose to support the FCL `account-proof` service. If a wallet chooses to support this service, and the user approves the signing of message data, they will return `account-proof` data and a signature(s) that can be used to prove a user controls an onchain account. We'll walk through how you, an application developer, can use the `account-proof` service to authenticate a user. > Are you an FCL Wallet Developer? Check out the wallet provider specific docs > [here](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/wallet-provider-spec/provable-authn.md) ### Authenticate a user with `account-proof` In order to authenticate your users via a wallet provider's account-proof service, your application needs to configure FCL by setting `fcl.accountProof.resolver` and providing two pieces of information. The `fcl.accountProof.resolver` is an async resolver function used by FCL to retrieve account proof data from your application server. It can be set in your application configuration under the `fcl.accountProof.resolver` key. The resolved data should include a random `nonce`. This data will be sent to the wallet for signing by the user. If the user approves and authentication is successfull, a signature is returned to the client in the data field of an `account-proof` service. **Random Nonce** Your application must provide a **minimum 32-byte random nonce** as a hex string. If for any reason your application backend does not want to request an `account-proof` during authentication, it should send a response of `null`. If FCL receives a `null` response from the `accountProof.resolver` it will continue the authentication process with the wallet but will not request an account-proof and no signature will be returned. > In the case of a network or server error FCL will cancel the authentication process and return a rejected promise. ```js import {config} from "@onflow/fcl" type AccountProofData { // e.g. "75f8587e5bd5f9dcc9909d0dae1f0ac5814458b2ae129620502cb936fde7120a" - minimum 32-byte random nonce as hex string nonce: string; } type AccountProofDataResolver = () => Promise; config({ "fcl.accountProof.resolver": accountProofDataResolver }) ``` Here is the suggested order of operations of how your application might use the `account-proof` service: - A user would like to authenticate via your application client with FCL. The process is triggered by a call to `fcl.authenticate()`. If `fcl.accountProof.resolver` is configured, FCL will attempt to retrieve the account proof data (`nonce`) and trigger your server to start a new account proof authentication process. - Your application server generates a **minimum 32-byte random nonce** with a local source of entropy and sends it to the client. The server saves the challenge for future look-ups. - If FCL successfully retrieves the `account-proof` data, it continues the authentication process over a secure channel with the wallet. FCL includes the `appIdentifier` and `nonce` as part of the `FCL:VIEW:READY:RESPONSE` or HTTP POST request body. The `appIdentifier` is implicitly generated by FCL and corresponds to the application's [RFC 6454](https://datatracker.ietf.org/doc/html/rfc6454) origin. If the resolver function call fails to retrieve the nonce, FCL will cancel the authentication process. - If the wallet supports account proofs and the user approves authentication with the wallet, the wallet will return the `account-proof` service with its response. The data within the `account-proof` service will look like this: ```js { f_type: "Service", // Its a service! f_vsn: "1.0.0", // Follows the v1.0.0 spec for the service type: "account-proof", // The type of service it is method: "DATA", // Its data! uid: "awesome-wallet#account-proof", // A unique identifier for the service data: { f_type: "account-proof", f_vsn: "2.0.0" // The user's address (8 bytes, i.e 16 hex characters) address: "0xf8d6e0586b0a20c7", // Nonce signed by the current account-proof (minimum 32 bytes in total, i.e 64 hex characters) nonce: "75f8587e5bd5f9dcc9909d0dae1f0ac5814458b2ae129620502cb936fde7120a", signatures: [CompositeSignature], } } ``` - Your application client initiates a secure channel with your application server to relay the `account-proof` data and authenticate the user with your server. Subsequent exchanges between the client and server will happen over this channel. - Your application server receives the `account-proof` data structure, and can then begin the verification process. - The server checks if the Flow address corresponds to an existing application account and determines whether it needs to sign in a returning user or create a new account. It is up to your application to decide how to manage the two cases. - The server looks the challenge up. If the nonce is not found or the nonce has expired, reject the authentication request, otherwise continue. - The server determines whether the `CompositeSignature` in the `account-proof` data structure contains valid signatures for the nonce and onchain accounts (more details in the section below on how this is done). - If the verification is successful, delete the `nonce` or mark it as expired, the application account defined by the onchain address is successfully logged in. Otherwise the authentication fails and the `nonce` is not deleted. **Verification** Your application can verify the signature against the data from `account-proof` data with FCL's provided utility: ```js import { AppUtils } from "@onflow/fcl" const accountProofData = { accountProof.address, // address of the user authenticating accountProof.nonce, // nonce accountProof.signatures // signatures } const isValid = await AppUtils.verifyAccountProof( origin, accountProofData ) ``` ## Implementation considerations: - The authentication assumes the Flow address is user's application account identifier. If a current user doesn't have a Flow address in their profile, or if they decide to authenticate with a Flow address different than the one saved in their profile, the system won't find the user's account and the process considers a new user creating an account. It is useful for your application to consider other authentication methods that allow a current user to update the Flow address in their profile so they are able to use FCL authentication. - In the `account-proof` flow as this document describes, the backend doesn't know the user's account address at the moment it generates a nonce. This results in the nonces not being tied to particular Flow addresses. The backend should enforce an expiry window for each nonce to avoid the pool of valid nonces from growing indefinitely. We encourage your application to implement further mitigations against malicious attempts and maintain a scalable authentication process. - FCL `account-proof` provides functionality to prove a user is in control of a Flow address. All other aspects of authentication, authorization and session management are up to the application. There are many resources available to set up secure user authentication systems. Application developers should carefully consider what's best for their use case and follow industry best practices. - It is important to use a secure source of entropy to generate the random nonces. The source should look at previously generated nonces to make sure that future nonces aren't predictible. Moreover, backend should use its own local source and not rely on a publicly available source. Use a nonce of at least 32-bytes to try and prevent a nonce collision. - The origin / `appIdentifier` is a tuple ⟨scheme, host, port⟩ computed per RFC 6454 (that is, the value returned by window.location.origin in conforming user agents). Wallets will embed this origin into the RLP-encoded payload which is cryptographically signed. The resulting signature serves as an attestation that the authentication request originated from the specified application origin (which is known through verification with supporting Browser APIs) - A successful FCL authentication proves the user fully controls a Flow account. This means the user controls one or many account keys with weights that add up to the full account weight. The authentication would fail if the user doesn't control keys that add up to a full weight. --- # File: /md/build/tools/clients/fcl-js/scripts.md # Scripts Scripts let you run non-permanent Cadence scripts on the Flow blockchain. They can return data. They always need to contain a `access(all) fun main()` function as an entry point to the script. `fcl.query` is a function that sends Cadence scripts to the chain and receives back decoded responses. The `cadence` key inside the object sent to the `query` function is a [JavaScript Tagged Template Literal] that we can pass Cadence code into. ### Send your first script The following example demonstrates how to send a script to the Flow blockchain. This script adds two numbers and returns the result. ```javascript import * as fcl from "@onflow/fcl" const response = await fcl.query({ cadence: ` access(all) fun main(): Int { return 1 + 2 } ` }) console.log(response) // 3 ``` ### A more complex script [Resources] and [Structs] are complex data types that are fairly common place in Cadence. In the following code snippet, our script defines a struct called `Point`, it then returns a list of them. The closest thing to a Structure in JavaScript is an object. In this case when we decode this response, we would expect to get back an array of objects, where the objects have an `x` and `y` value. ```javascript import * as fcl from "@onflow/fcl" const response = await fcl.query({ cadence: ` access(all) struct Point { access(all) var x: Int access(all) var y: Int init(x: Int, y: Int) { self.x = x self.y = y } } access(all) fun main(): [Point] { return [Point(x: 1, y: 1), Point(x: 2, y: 2)] } ` }) console.log(response) // [{x:1, y:1}, {x:2, y:2}] ``` ### Transform data with custom decoders In our app, we probably have a way to represent these Cadence values internally. In the above example it might be a `Point` class. FCL allows us to provide custom decoders that we can use to transform the data we receive from the Flow blockchain at the edge, before anything else in our dApp gets a chance to look at it. To add these custom decoders, we [configure FCL]. This lets us set it once when our dApp starts up and use our normalized data through out the rest of our dapp. In the below example, we will use the concept of a `Point` again, but this time, we will add a custom decoder, that allows `fcl.decode` to transform it into a custom JavaScript `Point` class. ```javascript import * as fcl from "@onflow/fcl" class Point { constructor({ x, y }) { this.x = x this.y = y } } fcl.config() .put("decoder.Point", point => new Point(point)) const response = await fcl.query({ cadence: ` access(all) struct Point { access(all) var x: Int access(all) var y: Int init(x: Int, y: Int) { self.x = x self.y = y } } access(all) fun main(): [Point] { return [Point(x: 1, y: 1), Point(x: 2, y: 2)] } ` }) console.log(response) // [Point{x:1, y:1}, Point{x:2, y:2}] ``` To learn more about `query`, check out the [API documentation]. [JavaScript Tagged Template Literal]: https://styled-components.com/docs/advanced#tagged-template-literals [Resources]: https://cadence-lang.org/docs/language/resources [Structs]: https://cadence-lang.org/docs/language/composite-types#structures [configure FCL]: ./configure-fcl.md [API documentation]: ./packages-docs/fcl/query.md --- # File: /md/build/tools/clients/fcl-js/transactions.md # Transactions Transactions let you send Cadence code to the Flow blockchain that permanently alters its state. We assume you have read the [Scripts Documentation]: ./scripts.md before this, as transactions are sort of scripts with more required things. While `query` is used to send scripts to the chain, `mutate` is used to build and send transactions. Just like [scripts], `fcl.mutate` is a [JavaScript Tagged Template Literal] that we can pass Cadence code into. Unlike scripts, they require a little more information, things like a proposer, authorizations and a payer, which may be a little confusing and overwhelming. ## Send your first transaction There is a lot to unpack in the following code snippet. It sends a transaction to the Flow blockchain. For the transaction, the current user authorizes it as both the `proposer` and the `payer`. Something that is unique to Flow is the one who pays for the transaction doesn't always need to be the one who performs the transaction. Proposers and Payers are special kinds of authorizations that are always required for a transaction. - The `proposer` acts similar to the `nonce` in Ethereum transactions, and helps prevent repeat attacks. - The `payer` is who will be paying for the transaction. If these are not set, Flow Client Library (FCL) defaults to the current user for all roles. `fcl.mutate` will return a `transactionId`. We can pass the response directly to `fcl.tx` and then use the `onceExecuted` method which resolves a promise when a transaction result is available. ```javascript import * as fcl from '@onflow/fcl'; const transactionId = await fcl.mutate({ cadence: ` transaction { execute { log("Hello from execute") } } `, proposer: fcl.currentUser, payer: fcl.currentUser, limit: 50, }); const transaction = await fcl.tx(transactionId).onceExecuted(); console.log(transaction); // The transactions status and events after being executed ``` ## Authorize a transaction The below code snippet is the same as the above one, except for one extremely important difference. Our Cadence code this time has a prepare statement, and we use the `fcl.currentUser` when constructing our transaction. The `prepare` statement's arguments directly map to the order of the authorizations in the `authorizations` array. Four authorizations means four `&Account`s as arguments passed to `prepare`. In this case though there is only one, and it is the `currentUser`. These authorizations are important as you can only access or modify an account's storage if you have that account's authorization. ```javascript import * as fcl from '@onflow/fcl'; const transactionId = await fcl.mutate({ cadence: ` transaction { prepare(acct: &Account) { log("Hello from prepare") } execute { log("Hello from execute") } } `, proposer: fcl.currentUser, payer: fcl.currentUser, authorizations: [fcl.currentUser], limit: 50, }); const transaction = await fcl.tx(transactionId).onceExecuted(); console.log(transaction); // The transactions status and events after being executed ``` To learn more about `mutate`, check out the [API documentation]. ## Query transaction results When you query transaction results (for example, via HTTP/REST endpoints like `GET /v1/transaction_results/{id}`), you can provide either: - A **transaction ID** (256-bit hash as hex string). - A **scheduled transaction ID** (UInt64 as decimal string). The returned result always includes `transaction_id` as the underlying native transaction ID. For scheduled transactions, this will be the system transaction ID that executed the scheduled callback. Learn more about [Scheduled Transactions]. ## Transaction finality As of **FCL v1.15.0**, it is now recommended to use use `onceExecuted` in most cases, which leads to a 2.5x reduction in latency when you wait for a transaction result. For example, the following code snippet should be updated from: ```ts import * as fcl from '@onflow/fcl'; const result = await fcl.tx(txId).onceSealed(); ``` to: ```ts import * as fcl from '@onflow/fcl'; const result = await fcl.tx(txId).onceExecuted(); ``` Developers who manually subscribe to transaction statuses should update their listeners to treat "executed" as the final status (see the [release notes]). For example, the following code snippet should be updated from: ```ts import * as fcl from '@onflow/fcl'; import { TransactionExecutionStatus } from '@onflow/typedefs'; fcl.tx(txId).subscribe((txStatus) => { if (txStatus.status === TransactionExecutionStatus.SEALED) { console.log('Transaction executed!'); } }); ``` ```ts import * as fcl from '@onflow/fcl'; import { TransactionExecutionStatus } from '@onflow/typedefs'; fcl.tx(txId).subscribe((txStatus) => { if ( // SEALED status is no longer necessary txStatus.status === TransactionExecutionStatus.EXECUTED ) { console.log('Transaction executed!'); } }); ``` The "executed" status corresponds to soft finality, which indicates that the transaction has been included in a block and a transaction status is available, backed by a cryptographic proof. Only in rare cases should a developer need to wait for "sealed" status in their applications and you can learn more about the different transaction statuses on Flow [here]. See the following video for demonstration of how to update your code to wait for "executed" status: [Scripts Documentation]: ./scripts.md [scripts]: ./scripts.md [JavaScript Tagged Template Literal]: https://styled-components.com/docs/advanced#tagged-template-literals [API documentation]: ./packages-docs/fcl/mutate.md [Scheduled Transactions]: /docs/build/cadence/advanced-concepts/scheduled-transactions.md [release notes]: https://github.com/onflow/fcl-js/releases/tag/%40onflow%2Ffcl%401.15.0 [here]: ../../../cadence/basics/transactions.md#transaction-status --- # File: /md/build/tools/clients/fcl-js/user-signatures.md --- title: Signing and Verifying Arbitrary Data --- ## Signing and Verifying Arbitrary Data Cryptographic signatures are a key part of the blockchain. They prove ownership of an address without exposing its private key. While primarily used to sign transactions, you can also use cryptographic signatures to sign arbitrary messages. FCL has a feature that lets you send arbitrary data to a configured wallet or service. The user may approve signing it with their private keys. ## Verify user signatures What makes message signatures more interesting is that we can use Flow blockchain to verify the signatures. Cadence has a built-in function `publicKey.verify` that will verify a signature against a Flow account given the account address. FCL includes a utility function, `AppUtils.verifyUserSignatures`, that verifies one or more signatures against an account's public key on the Flow blockchain. You can use both in tandem to prove a user is in control of a private key or keys. This allows cryptographically-secure login flow with a message-signing-based authentication mechanism with a user’s public address as their identifier. --- ## `currentUser.signUserMessage()` A method that allows the user to personally sign data via FCL Compatible Wallets or Services. :::info > **Requires authentication/configuration with an authorized signing service.** ::: ### Arguments | Name | Type | Description | | --------- | ------ | --------------------------------- | | `message` | string | A hexadecimal string to be signed | #### Returns | Type | Description | | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Array` | An Array of [CompositeSignatures]: {`addr`, `keyId`, `signature`} | #### Usage ```javascript import * as fcl from "@onflow/fcl" const signMessage = async () => { const MSG = Buffer.from("FOO").toString("hex") try { return await fcl.currentUser.signUserMessage(MSG) } catch (error) { console.log(error) } } ``` --- ## `AppUtils.verifyUserSignatures` :::info ⚠️ `fcl.config.flow.network` or options override is required to use this API. See [FCL Configuration](./configure-fcl.md). A method to verify that a user's private keys signed a message, which allows applications to cryptographically verify Flow account ownership. This is typically used with the response from `currentUser.signUserMessage`. ::: ### Arguments | Name | Type | Description | | --------------------- | --------------------- | --------------------------------- | | `message` | string **(required)** | A hexadecimal string | | `compositeSignatures` | Array **(required)** | An Array of `CompositeSignatures` | | `opts` | Object **(optional)** | `opts.fclCryptoContract` can be provided to override FCLCryptoContract address for local development | #### Returns | Type | Description | | ------- | ---------------------------- | | Boolean | `true` if verified or `false` | #### Usage ```javascript /** * Verify a valid signature/s for an account on Flow. * * @param {string} msg - A message string in hexadecimal format * @param {Array} compSigs - An array of Composite Signatures * @param {string} compSigs[].addr - The account address * @param {number} compSigs[].keyId - The account keyId * @param {string} compSigs[].signature - The signature to verify * @param {Object} [opts={}] - Options object * @param {string} opts.fclCryptoContract - An optional override of Flow account address where the FCLCrypto contract is deployed * @return {bool} * * @example * * const isValid = await fcl.AppUtils.verifyUserSignatures( * Buffer.from('FOO').toString("hex"), * [{f_type: "CompositeSignature", f_vsn: "1.0.0", addr: "0x123", keyId: 0, signature: "abc123"}], * {fclCryptoContract} * ) */ ``` #### Examples Use cases include cryptographic login, message validation, verifiable credentials, and others. --- [FCL Configuration]: ./configure-fcl.md [CompositeSignatures]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/wallet-provider-spec/draft-v2.md#compositesignature --- # File: /md/build/tools/clients/flow-go-sdk.md --- title: Flow Go SDK ---
    ## Overview This reference documents all the methods available in the SDK, and explains in detail how these methods work. SDKs are open source, and you can use them according to the licence. The library client specifications can be found here: [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client) ## Getting Started ### Installing The recommended way to install Go Flow SDK is by using Go modules. If you already initialized your Go project, you can run the following command in your terminal: ```sh go get github.com/onflow/flow-go-sdk ``` It's usually good practice to pin your dependencies to a specific version. Refer to the [SDK releases](https://github.com/onflow/flow-go-sdk/tags) page to identify the latest version. ### Importing the Library After the library has been installed you can import it. ```go import "github.com/onflow/flow-go-sdk" ``` ## Connect [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#New) The Go SDK library uses HTTP or gRPC APIs to communicate with the access nodes and it must be configured with correct access node API URL. The library provides default factories for connecting to Flow AN APIs and you can easily switch between HTTP or gRPC if you use the provided client interface. You can check more examples for creating clients in the examples: **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/http_grpc_clients/main.go)** Basic Example: ```go // common client interface var flowClient client.Client // initialize an http emulator client flowClient, err := http.NewClient(http.EmulatorHost) // initialize a gRPC emulator client flowClient, err = grpc.NewClient(grpc.EmulatorHost) ``` You can also initialize an HTTP client or gRPC client directly which will offer you access to network specific options, but be aware you won't be able to easily switch between those since they don't implement a common interface. This is only advisable if the implementation needs the access to those advanced options. Advanced Example: ```go // initialize http specific client httpClient, err := http.NewHTTPClient(http.EMULATOR_URL) // initialize grpc specific client grpcClient, err := grpc.NewGRPCClient( grpc.EMULATOR_URL, grpcOpts.WithTransportCredentials(insecure.NewCredentials()), ) ``` ## Querying the Flow Network After you have established a connection with an access node, you can query the Flow network to retrieve data about blocks, accounts, events and transactions. We will explore how to retrieve each of these entities in the sections below. ### Get Blocks [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.GetBlockByHeight) Query the network for block by id, height or get the latest block. 📖 **Block ID** is SHA3-256 hash of the entire block payload. This hash is stored as an ID field on any block response object (ie. response from `GetLatestBlock`). 📖 **Block height** expresses the height of the block on the chain. The latest block height increases by one for every valid block produced. #### Examples This example depicts ways to get the latest block as well as any other block by height or ID: **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/get_blocks/main.go)** ```go func demo() { ctx := context.Background() flowClient := examples.NewFlowClient() // get the latest sealed block isSealed := true latestBlock, err := flowClient.GetLatestBlock(ctx, isSealed) printBlock(latestBlock, err) // get the block by ID blockID := latestBlock.ID.String() blockByID, err := flowClient.GetBlockByID(ctx, flow.HexToID(blockID)) printBlock(blockByID, err) // get block by height blockByHeight, err := flowClient.GetBlockByHeight(ctx, 0) printBlock(blockByHeight, err) } func printBlock(block *flow.Block, err error) { examples.Handle(err) fmt.Printf("\nID: %s\n", block.ID) fmt.Printf("height: %d\n", block.Height) fmt.Printf("timestamp: %s\n\n", block.Timestamp) } ``` Result output: ```bash ID: 835dc83939141097aa4297aa6cf69fc600863e3b5f9241a0d7feac1868adfa4f height: 10 timestamp: 2021-10-06 15:06:07.105382 +0000 UTC ID: 835dc83939141097aa4297aa6cf69fc600863e3b5f9241a0d7feac1868adfa4f height: 10 timestamp: 2021-10-06 15:06:07.105382 +0000 UTC ID: 7bc42fe85d32ca513769a74f97f7e1a7bad6c9407f0d934c2aa645ef9cf613c7 height: 0 timestamp: 2018-12-19 22:32:30.000000042 +0000 UTC ``` ### Get Account [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.GetAccount) Retrieve any account from Flow network's latest block or from a specified block height. The `GetAccount` method is actually an alias for the get account at latest block method. 📖 **Account address** is a unique account identifier. Be mindful about the `0x` prefix, you should use the prefix as a default representation but be careful and safely handle user inputs without the prefix. An account includes the following data: - Address: the account address. - Balance: balance of the account. - Contracts: list of contracts deployed to the account. - Keys: list of keys associated with the account. #### Examples Example depicts ways to get an account at the latest block and at a specific block height: **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/get_accounts/main.go)** ```go func demo() { ctx := context.Background() flowClient := examples.NewFlowClient() // get account from the latest block address := flow.HexToAddress("f8d6e0586b0a20c7") account, err := flowClient.GetAccount(ctx, address) printAccount(account, err) // get account from the block by height 0 account, err = flowClient.GetAccountAtBlockHeight(ctx, address, 0) printAccount(account, err) } func printAccount(account *flow.Account, err error) { examples.Handle(err) fmt.Printf("\nAddress: %s", account.Address.String()) fmt.Printf("\nBalance: %d", account.Balance) fmt.Printf("\nContracts: %d", len(account.Contracts)) fmt.Printf("\nKeys: %d\n", len(account.Keys)) } ``` Result output: ```bash Address: f8d6e0586b0a20c7 Balance: 999999999999600000 Contracts: 2 Keys: 1 Address: f8d6e0586b0a20c7 Balance: 999999999999600000 Contracts: 2 Keys: 1 ``` ### Get Transactions [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.GetTransaction) Retrieve transactions from the network by providing a transaction ID. After a transaction has been submitted, you can also get the transaction result to check the status. 📖 **Transaction ID** is a hash of the encoded transaction payload and can be calculated before submitting the transaction to the network. ⚠️ The transaction ID provided must be from the current spork. 📖 **Transaction status** represents the state of transaction in the blockchain. Status can change until it is sealed. | Status | Final | Description | | --------- | ----- | ------------------------------------------------------------------------ | | UNKNOWN | ❌ | The transaction has not yet been seen by the network | | PENDING | ❌ | The transaction has not yet been included in a block | | FINALIZED | ❌ | The transaction has been included in a block | | EXECUTED | ❌ | The transaction has been executed but the result has not yet been sealed | | SEALED | ✅ | The transaction has been executed and the result is sealed in a block | | EXPIRED | ✅ | The transaction reference block is outdated before being executed | **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/get_transactions/main.go)** ```go func demo(txID flow.Identifier) { ctx := context.Background() flowClient := examples.NewFlowClient() tx, err := flowClient.GetTransaction(ctx, txID) printTransaction(tx, err) txr, err := flowClient.GetTransactionResult(ctx, txID) printTransactionResult(txr, err) } func printTransaction(tx *flow.Transaction, err error) { examples.Handle(err) fmt.Printf("\nID: %s", tx.ID().String()) fmt.Printf("\nPayer: %s", tx.Payer.String()) fmt.Printf("\nProposer: %s", tx.ProposalKey.Address.String()) fmt.Printf("\nAuthorizers: %s", tx.Authorizers) } func printTransactionResult(txr *flow.TransactionResult, err error) { examples.Handle(err) fmt.Printf("\nStatus: %s", txr.Status.String()) fmt.Printf("\nError: %v", txr.Error) } ``` Example output: ```bash ID: fb1272c57cdad79acf2fcf37576d82bf760e3008de66aa32a900c8cd16174e1c Payer: f8d6e0586b0a20c7 Proposer: f8d6e0586b0a20c7 Authorizers: [] Status: SEALED Error: ``` ### Get Events [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.GetEventsForBlockIDs) Retrieve events by a given type in a specified block height range or through a list of block IDs. 📖 **Event type** is a string that follow a standard format: ``` A.{contract address}.{contract name}.{event name} ``` Please read more about [events in the documentation](../../../cadence/core-contracts/03-flow-token.md). The exception to this standard are core events, and you should read more about them in [this document](https://cadence-lang.org/docs/language/core-events). 📖 **Block height range** expresses the height of the start and end block in the chain. #### Examples Example depicts ways to get events within block range or by block IDs: **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/get_events/main.go)** ```go func demo(deployedContract *flow.Account, runScriptTx *flow.Transaction) { ctx := context.Background() flowClient := examples.NewFlowClient() // Query for account creation events by type result, err := flowClient.GetEventsForHeightRange(ctx, "flow.AccountCreated", 0, 30) printEvents(result, err) // Query for our custom event by type customType := fmt.Sprintf("AC.%s.EventDemo.EventDemo.Add", deployedContract.Address.Hex()) result, err = flowClient.GetEventsForHeightRange(ctx, customType, 0, 10) printEvents(result, err) // Get events directly from transaction result txResult, err := flowClient.GetTransactionResult(ctx, runScriptTx.ID()) examples.Handle(err) printEvent(txResult.Events) } func printEvents(result []client.BlockEvents, err error) { examples.Handle(err) for _, block := range result { printEvent(block.Events) } } func printEvent(events []flow.Event) { for _, event := range events { fmt.Printf("\n\nType: %s", event.Type) fmt.Printf("\nValues: %v", event.Value) fmt.Printf("\nTransaction ID: %s", event.TransactionID) } } ``` Example output: ```bash Type: flow.AccountCreated Values: flow.AccountCreated(address: 0xfd43f9148d4b725d) Transaction ID: ba9d53c8dcb0f9c2f854f93da8467a22d053eab0c540bde0b9ca2f7ad95eb78e Type: flow.AccountCreated Values: flow.AccountCreated(address: 0xeb179c27144f783c) Transaction ID: 8ab7bfef3de1cf8b2ffb36559446100bf4129a9aa88d6bc59f72a467acf0c801 ... Type: A.eb179c27144f783c.EventDemo.Add Values: A.eb179c27144f783c.EventDemo.Add(x: 2, y: 3, sum: 5) Transaction ID: f3a2e33687ad23b0e02644ebbdcd74a7cd8ea7214065410a8007811d0bcbd353 ``` ### Get Collections [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.GetCollection) Retrieve a batch of transactions that have been included in the same block, known as **_collections_**. Collections are used to improve consensus throughput by increasing the number of transactions per block and they act as a link between a block and a transaction. 📖 **Collection ID** is SHA3-256 hash of the collection payload. Example retrieving a collection: ```go func demo(exampleCollectionID flow.Identifier) { ctx := context.Background() flowClient := examples.NewFlowClient() // get collection by ID collection, err := flowClient.GetCollection(ctx, exampleCollectionID) printCollection(collection, err) } func printCollection(collection *flow.Collection, err error) { examples.Handle(err) fmt.Printf("\nID: %s", collection.ID().String()) fmt.Printf("\nTransactions: %s", collection.TransactionIDs) } ``` Example output: ```bash ID: 3d7b8037381f2497d83f2f9e09422c036aae2a59d01a7693fb6003b4d0bc3595 Transactions: [cf1184e3de4bd9a7232ca3d0b9dd2cfbf96c97888298b81a05c086451fa52ec1] ``` ### Execute Scripts [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.ExecuteScriptAtLatestBlock) Scripts allow you to write arbitrary non-mutating Cadence code on the Flow blockchain and return data. You can learn more about [Cadence and scripts here](https://cadence-lang.org/docs/language), but we are now only interested in executing the script code and getting back the data. We can execute a script using the latest state of the Flow blockchain or we can choose to execute the script at a specific time in history defined by a block height or block ID. 📖 **Block ID** is SHA3-256 hash of the entire block payload, but you can get that value from the block response properties. 📖 **Block height** expresses the height of the block in the chain. **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/execute_script/main.go)** ```go func demo() { ctx := context.Background() flowClient := examples.NewFlowClient() script := []byte(` access(all) fun main(a: Int): Int { return a + 10 } `) args := []cadence.Value{ cadence.NewInt(5) } value, err := flowClient.ExecuteScriptAtLatestBlock(ctx, script, args) examples.Handle(err) fmt.Printf("\nValue: %s", value.String()) complexScript := []byte(` access(all) struct User { access(all) var balance: UFix64 access(all) var address: Address access(all) var name: String init(name: String, address: Address, balance: UFix64) { self.name = name self.address = address self.balance = balance } } access(all) fun main(name: String): User { return User( name: name, address: 0x1, balance: 10.0 ) } `) args = []cadence.Value{ cadence.NewString("Dete") } value, err = flowClient.ExecuteScriptAtLatestBlock(ctx, complexScript, args) printComplexScript(value, err) } type User struct { balance uint64 address flow.Address name string } func printComplexScript(value cadence.Value, err error) { examples.Handle(err) fmt.Printf("\nString value: %s", value.String()) s := value.(cadence.Struct) u := User{ balance: s.Fields[0].ToGoValue().(uint64), address: s.Fields[1].ToGoValue().([flow.AddressLength]byte), name: s.Fields[2].ToGoValue().(string), } fmt.Printf("\nName: %s", u.name) fmt.Printf("\nAddress: %s", u.address.String()) fmt.Printf("\nBalance: %d", u.balance) } ``` Example output: ```bash Value: 15 String value: s.34a17571e1505cf6770e6ef16ca387e345e9d54d71909f23a7ec0d671cd2faf5.User(balance: 10.00000000, address: 0x1, name: "Dete") Name: Dete Address: 0000000000000001 Balance: 1000000000 ``` ## Mutate Flow Network Flow, like most blockchains, allows anybody to submit a transaction that mutates the shared global chain state. A transaction is an object that holds a payload, which describes the state mutation, and one or more authorizations that permit the transaction to mutate the state owned by specific accounts. Transaction data is composed and signed with help of the SDK. The signed payload of transaction then gets submitted to the access node API. If a transaction is invalid or the correct number of authorizing signatures are not provided, it gets rejected. Executing a transaction requires couple of steps: - [Building transaction](#build-the-transaction). - [Signing transaction](#sign-transactions). - [Sending transaction](#send-transactions). ## Transactions A transaction is nothing more than a signed set of data that includes script code which are instructions on how to mutate the network state and properties that define and limit it's execution. All these properties are explained bellow. 📖 **Script** field is the portion of the transaction that describes the state mutation logic. On Flow, transaction logic is written in [Cadence](https://cadence-lang.org/docs). Here is an example transaction script: ``` transaction(greeting: String) { execute { log(greeting.concat(", World!")) } } ``` 📖 **Arguments**. A transaction can accept zero or more arguments that are passed into the Cadence script. The arguments on the transaction must match the number and order declared in the Cadence script. Sample script from above accepts a single `String` argument. 📖 **[Proposal key](../../../cadence/basics/transactions.md#proposal-key)** must be provided to act as a sequence number and prevent reply and other potential attacks. Each account key maintains a separate transaction sequence counter; the key that lends its sequence number to a transaction is called the proposal key. A proposal key contains three fields: - Account address - Key index - Sequence number A transaction is only valid if its declared sequence number matches the current onchain sequence number for that key. The sequence number increments by one after the transaction is executed. 📖 **[Payer](../../../cadence/basics/transactions.md#signer-roles)** is the account that pays the fees for the transaction. A transaction must specify exactly one payer. The payer is only responsible for paying the network and compute unit fees; the transaction is not authorized to access resources or code stored in the payer account. 📖 **[Authorizers](../../../cadence/basics/transactions.md#signer-roles)** are accounts that authorize a transaction to read and mutate their resources. A transaction can specify zero or more authorizers, depending on how many accounts the transaction needs to access. The number of authorizers on the transaction must match the number of &Account parameters declared in the prepare statement of the Cadence script. Example transaction with multiple authorizers: ``` transaction { prepare(authorizer1: &Account, authorizer2: &Account) { } } ``` #### Gas Limit **Compute Limit** is the limit on the amount of computation a transaction requires, and it will abort if it exceeds its compute unit (gas) limit. Cadence uses metering to measure the number of operations per transaction. You can read more about it in the [Cadence documentation](https://cadence-lang.org/docs). The compute limit depends on the complexity of the transaction script. Until dedicated estimation tooling exists, it's best to use the emulator to test complex transactions and determine a safe limit. Keep in mind that Flow is **very** efficient, so transaction fees are generally low. A limit resulting in max charges of `.001` Flow is sufficient to cover even complex transactions. - Flow token transfer: 19 CU. - Single NFT Transfer: 26 CU. - EVM Token transfer 28 CU. #### Reference Block 📖 **Reference block** specifies an expiration window (measured in blocks) during which a transaction is considered valid by the network. A transaction will be rejected if it is submitted past its expiry block. Flow calculates transaction expiry using the _reference block_ field on a transaction. A transaction expires after `600` blocks are committed on top of the reference block, which takes about 10 minutes at average Mainnet block rates. ### Build Transactions [](https://pkg.go.dev/github.com/onflow/flow-go-sdk#Transaction) Building a transaction involves setting the required properties explained above and producing a transaction object. Here we define a simple transaction script that will be used to execute on the network and serve as a good learning example. ``` transaction(greeting: String) { let guest: Address prepare(authorizer: &Account) { self.guest = authorizer.address } execute { log(greeting.concat(",").concat(self.guest.toString())) } } ``` **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/transaction_signing/single_party/main.go)** ```go import ( "context" "os" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" ) func main() { greeting, err := os.ReadFile("Greeting2.cdc") if err != nil { panic("failed to load Cadence script") } proposerAddress := flow.HexToAddress("9a0766d93b6608b7") proposerKeyIndex := 3 payerAddress := flow.HexToAddress("631e88ae7f1d7c20") authorizerAddress := flow.HexToAddress("7aad92e5a0715d21") var accessAPIHost string // Establish a connection with an access node flowClient := examples.NewFlowClient() // Get the latest sealed block to use as a reference block latestBlock, err := flowClient.GetLatestBlockHeader(context.Background(), true) if err != nil { panic("failed to fetch latest block") } // Get the latest account info for this address proposerAccount, err := flowClient.GetAccountAtLatestBlock(context.Background(), proposerAddress) if err != nil { panic("failed to fetch proposer account") } // Get the latest sequence number for this key sequenceNumber := proposerAccount.Keys[proposerKeyIndex].SequenceNumber tx := flow.NewTransaction(). SetScript(greeting). SetComputeLimit(100). SetReferenceBlockID(latestBlock.ID). SetProposalKey(proposerAddress, proposerKeyIndex, sequenceNumber). SetPayer(payerAddress). AddAuthorizer(authorizerAddress) // Add arguments last hello := cadence.NewString("Hello") err = tx.AddArgument(hello) if err != nil { panic("invalid argument") } } ``` After you have successfully [built a transaction](#build-the-transaction) the next step in the process is to sign it. ### Sign Transactions [](https://pkg.go.dev/github.com/onflow/flow-go-sdk#Transaction.SignEnvelope) Flow introduces new concepts that allow for more flexibility when creating and signing transactions. Before trying the examples below, we recommend that you read through the [transaction signature documentation](../../../cadence/basics/transactions.md. After you have successfully [built a transaction](#build-the-transaction) the next step in the process is to sign it. Flow transactions have envelope and payload signatures, and you should learn about each in the [signature documentation](../../../cadence/basics/transactions.md). Quick example of building a transaction: ```go import ( "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" ) var ( myAddress flow.Address myAccountKey flow.AccountKey myPrivateKey crypto.PrivateKey ) tx := flow.NewTransaction(). SetScript([]byte("transaction { execute { log(\"Hello, World!\") } }")). SetComputeLimit(100). SetProposalKey(myAddress, myAccountKey.Index, myAccountKey.SequenceNumber). SetPayer(myAddress) ``` Transaction signing is done through the `crypto.Signer` interface. The simplest (and least secure) implementation of `crypto.Signer` is `crypto.InMemorySigner`. Signatures can be generated more securely using keys stored in a hardware device such as an [HSM](https://en.wikipedia.org/wiki/Hardware_security_module). The `crypto.Signer` interface is intended to be flexible enough to support a variety of signer implementations and is not limited to in-memory implementations. Simple signature example: ```go // construct a signer from your private key and configured hash algorithm mySigner, err := crypto.NewInMemorySigner(myPrivateKey, myAccountKey.HashAlgo) if err != nil { panic("failed to create a signer") } err = tx.SignEnvelope(myAddress, myAccountKey.Index, mySigner) if err != nil { panic("failed to sign transaction") } ``` Flow supports great flexibility when it comes to transaction signing, we can define multiple authorizers (multi-sig transactions) and have different payer account than proposer. We will explore advanced signing scenarios bellow. ### [Single party, single signature](../../../cadence/basics/transactions.md#single-party-single-signature) - Proposer, payer and authorizer are the same account (`0x01`). - Only the envelope must be signed. - Proposal key must have full signing weight. | Account | Key ID | Weight | | ------- | ------ | ------ | | `0x01` | 1 | 1000 | **[](https://github.com/onflow/flow-go-sdk/tree/master/examples#single-party-single-signature)** ```go account1, _ := c.GetAccount(ctx, flow.HexToAddress("01")) key1 := account1.Keys[0] // create signer from securely-stored private key key1Signer := getSignerForKey1() referenceBlock, _ := flow.GetLatestBlock(ctx, true) tx := flow.NewTransaction(). SetScript([]byte(` transaction { prepare(signer: &Account) { log(signer.address) } } `)). SetComputeLimit(100). SetProposalKey(account1.Address, key1.Index, key1.SequenceNumber). SetReferenceBlockID(referenceBlock.ID). SetPayer(account1.Address). AddAuthorizer(account1.Address) // account 1 signs the envelope with key 1 err := tx.SignEnvelope(account1.Address, key1.Index, key1Signer) ``` ### [Single party, multiple signatures](../../../cadence/basics/transactions.md#single-party-multiple-signatures) - Proposer, payer and authorizer are the same account (`0x01`). - Only the envelope must be signed. - Each key has weight 500, so two signatures are required. | Account | Key ID | Weight | | ------- | ------ | ------ | | `0x01` | 1 | 500 | | `0x01` | 2 | 500 | **[](https://github.com/onflow/flow-go-sdk/tree/master/examples#single-party-multiple-signatures)** ```go account1, _ := c.GetAccount(ctx, flow.HexToAddress("01")) key1 := account1.Keys[0] key2 := account1.Keys[1] // create signers from securely-stored private keys key1Signer := getSignerForKey1() key2Signer := getSignerForKey2() referenceBlock, _ := flow.GetLatestBlock(ctx, true) tx := flow.NewTransaction(). SetScript([]byte(` transaction { prepare(signer: &Account) { log(signer.address) } } `)). SetComputeLimit(100). SetProposalKey(account1.Address, key1.Index, key1.SequenceNumber). SetReferenceBlockID(referenceBlock.ID). SetPayer(account1.Address). AddAuthorizer(account1.Address) // account 1 signs the envelope with key 1 err := tx.SignEnvelope(account1.Address, key1.Index, key1Signer) // account 1 signs the envelope with key 2 err = tx.SignEnvelope(account1.Address, key2.Index, key2Signer) ``` ### [Multiple parties](../../../cadence/basics/transactions.md#multiple-parties) - Proposer and authorizer are the same account (`0x01`). - Payer is a separate account (`0x02`). - Account `0x01` signs the payload. - Account `0x02` signs the envelope. - Account `0x02` must sign last since it is the payer. | Account | Key ID | Weight | | ------- | ------ | ------ | | `0x01` | 1 | 1000 | | `0x02` | 3 | 1000 | **[](https://github.com/onflow/flow-go-sdk/tree/master/examples#multiple-parties)** ```go account1, _ := c.GetAccount(ctx, flow.HexToAddress("01")) account2, _ := c.GetAccount(ctx, flow.HexToAddress("02")) key1 := account1.Keys[0] key3 := account2.Keys[0] // create signers from securely-stored private keys key1Signer := getSignerForKey1() key3Signer := getSignerForKey3() referenceBlock, _ := flow.GetLatestBlock(ctx, true) tx := flow.NewTransaction(). SetScript([]byte(` transaction { prepare(signer: &Account) { log(signer.address) } } `)). SetComputeLimit(100). SetProposalKey(account1.Address, key1.Index, key1.SequenceNumber). SetReferenceBlockID(referenceBlock.ID). SetPayer(account2.Address). AddAuthorizer(account1.Address) // account 1 signs the payload with key 1 err := tx.SignPayload(account1.Address, key1.Index, key1Signer) // account 2 signs the envelope with key 3 // note: payer always signs last err = tx.SignEnvelope(account2.Address, key3.Index, key3Signer) ``` ### [Multiple parties, two authorizers](../../../cadence/basics/transactions.md#multiple-parties) - Proposer and authorizer are the same account (`0x01`). - Payer is a separate account (`0x02`). - Account `0x01` signs the payload. - Account `0x02` signs the envelope. - Account `0x02` must sign last since it is the payer. - Account `0x02` is also an authorizer to show how to include two `&Account` objects into an transaction | Account | Key ID | Weight | | ------- | ------ | ------ | | `0x01` | 1 | 1000 | | `0x02` | 3 | 1000 | **[](https://github.com/onflow/flow-go-sdk/tree/master/examples#multiple-parties-two-authorizers)** ```go account1, _ := c.GetAccount(ctx, flow.HexToAddress("01")) account2, _ := c.GetAccount(ctx, flow.HexToAddress("02")) key1 := account1.Keys[0] key3 := account2.Keys[0] // create signers from securely-stored private keys key1Signer := getSignerForKey1() key3Signer := getSignerForKey3() referenceBlock, _ := flow.GetLatestBlock(ctx, true) tx := flow.NewTransaction(). SetScript([]byte(` transaction { prepare(signer1: &Account, signer2: &Account) { log(signer.address) log(signer2.address) } } `)). SetComputeLimit(100). SetProposalKey(account1.Address, key1.Index, key1.SequenceNumber). SetReferenceBlockID(referenceBlock.ID). SetPayer(account2.Address). AddAuthorizer(account1.Address). AddAuthorizer(account2.Address) // account 1 signs the payload with key 1 err := tx.SignPayload(account1.Address, key1.Index, key1Signer) // account 2 signs the envelope with key 3 // note: payer always signs last err = tx.SignEnvelope(account2.Address, key3.Index, key3Signer) ``` ### [Multiple parties, multiple signatures](../../../cadence/basics/transactions.md#multiple-parties) - Proposer and authorizer are the same account (`0x01`). - Payer is a separate account (`0x02`). - Account `0x01` signs the payload. - Account `0x02` signs the envelope. - Account `0x02` must sign last since it is the payer. - Both accounts must sign twice (once with each of their keys). | Account | Key ID | Weight | | ------- | ------ | ------ | | `0x01` | 1 | 500 | | `0x01` | 2 | 500 | | `0x02` | 3 | 500 | | `0x02` | 4 | 500 | **[](https://github.com/onflow/flow-go-sdk/tree/master/examples#multiple-parties-multiple-signatures)** ```go account1, _ := c.GetAccount(ctx, flow.HexToAddress("01")) account2, _ := c.GetAccount(ctx, flow.HexToAddress("02")) key1 := account1.Keys[0] key2 := account1.Keys[1] key3 := account2.Keys[0] key4 := account2.Keys[1] // create signers from securely-stored private keys key1Signer := getSignerForKey1() key2Signer := getSignerForKey1() key3Signer := getSignerForKey3() key4Signer := getSignerForKey4() referenceBlock, _ := flow.GetLatestBlock(ctx, true) tx := flow.NewTransaction(). SetScript([]byte(` transaction { prepare(signer: &Account) { log(signer.address) } } `)). SetComputeLimit(100). SetProposalKey(account1.Address, key1.Index, key1.SequenceNumber). SetReferenceBlockID(referenceBlock.ID). SetPayer(account2.Address). AddAuthorizer(account1.Address) // account 1 signs the payload with key 1 err := tx.SignPayload(account1.Address, key1.Index, key1Signer) // account 1 signs the payload with key 2 err = tx.SignPayload(account1.Address, key2.Index, key2Signer) // account 2 signs the envelope with key 3 // note: payer always signs last err = tx.SignEnvelope(account2.Address, key3.Index, key3Signer) // account 2 signs the envelope with key 4 // note: payer always signs last err = tx.SignEnvelope(account2.Address, key4.Index, key4Signer) ``` ### Send Transactions [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.SendTransaction) After a transaction has been [built](#build-the-transaction) and [signed](#sign-transactions), it can be sent to the Flow blockchain where it will be executed. If sending was successful you can then [retrieve the transaction result](#get-transactions). **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/send_transactions/main.go)** ```go func demo(tx *flow.Transaction) { ctx := context.Background() flowClient := examples.NewFlowClient() err := flowClient.SendTransaction(ctx, *tx) if err != nil { fmt.Println("error sending transaction", err) } } ``` ### Create Accounts [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/templates#CreateAccount) On Flow, account creation happens inside a transaction. Because the network allows for a many-to-many relationship between public keys and accounts, it's not possible to derive a new account address from a public key offline. The Flow VM uses a deterministic address generation algorithm to assign account addresses on chain. You can find more details about address generation in the [accounts & keys documentation](../../../cadence/basics/accounts.md). #### Public Key Flow uses ECDSA key pairs to control access to user accounts. Each key pair can be used in combination with the SHA2-256 or SHA3-256 hashing algorithms. ⚠️ You'll need to authorize at least one public key to control your new account. Flow represents ECDSA public keys in raw form without additional metadata. Each key is a single byte slice containing a concatenation of its X and Y components in big-endian byte form. A Flow account can contain zero (not possible to control) or more public keys, referred to as account keys. Read more about [accounts in the documentation](../../../cadence/basics/accounts.md). An account key contains the following data: - Raw public key (described above) - Signature algorithm - Hash algorithm - Weight (integer between 0-1000) Account creation happens inside a transaction, which means that somebody must pay to submit that transaction to the network. We'll call this person the account creator. Make sure you have read [sending a transaction section](#send-transactions) first. ```go var ( creatorAddress flow.Address creatorAccountKey *flow.AccountKey creatorSigner crypto.Signer ) var accessAPIHost string // Establish a connection with an access node flowClient := examples.NewFlowClient() // Use the templates package to create a new account creation transaction tx := templates.CreateAccount([]*flow.AccountKey{accountKey}, nil, creatorAddress) // Set the transaction payer and proposal key tx.SetPayer(creatorAddress) tx.SetProposalKey( creatorAddress, creatorAccountKey.Index, creatorAccountKey.SequenceNumber, ) // Get the latest finalized block to use as a reference block // It is recommended to use the latest finalized block as the reference block, as // it is the most recent block guaranteed to be included by the network. Using the // latest sealed block is not recommended, as sealing occurs after finalization and // could cause the transaction to fall outside of the expiration window. latestBlock, err := flowClient.GetLatestBlockHeader(context.Background(), false) if err != nil { panic("failed to fetch latest block") } tx.SetReferenceBlockID(latestBlock.ID) // Sign and submit the transaction err = tx.SignEnvelope(creatorAddress, creatorAccountKey.Index, creatorSigner) if err != nil { panic("failed to sign transaction envelope") } err = flowClient.SendTransaction(context.Background(), *tx) if err != nil { panic("failed to send transaction to network") } ``` After the account creation transaction has been submitted you can retrieve the new account address by [getting the transaction result](#get-transactions). The new account address will be emitted in a system-level `flow.AccountCreated` event. ```go result, err := flowClient.GetTransactionResult(ctx, tx.ID()) if err != nil { panic("failed to get transaction result") } var newAddress flow.Address if result.Status != flow.TransactionStatusSealed { panic("address not known until transaction is sealed") } for _, event := range result.Events { if event.Type == flow.EventAccountCreated { newAddress = flow.AccountCreatedEvent(event).Address() break } } ``` ### Generate Keys [](../../../cadence/basics/accounts.md#signature-and-hash-algorithms) Flow uses [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) signatures to control access to user accounts. Each key pair can be used in combination with the `SHA2-256` or `SHA3-256` hashing algorithms. Here's how to generate an ECDSA private key for the P-256 (secp256r1) curve. ```go import "github.com/onflow/flow-go-sdk/crypto" // deterministic seed phrase // note: this is only an example, please use a secure random generator for the key seed seed := []byte("elephant ears space cowboy octopus rodeo potato cannon pineapple") privateKey, err := crypto.GeneratePrivateKey(crypto.ECDSA_P256, seed) // the private key can then be encoded as bytes (i.e. for storage) encPrivateKey := privateKey.Encode() // the private key has an accompanying public key publicKey := privateKey.PublicKey() ``` The example above uses an ECDSA key pair on the P-256 (secp256r1) elliptic curve. Flow also supports the secp256k1 curve used by Bitcoin and Ethereum. Read more about [supported algorithms here](../../../cadence/basics/accounts.md#signature-and-hash-algorithms). ### Transferring Flow This is an example of how to construct a FLOW token transfer transaction with the Flow Go SDK. ## Cadence Script The following Cadence script will transfer FLOW tokens from a sender to a recipient. _Note: this transaction is only compatible with Flow Mainnet._ ```cadence // This transaction is a template for a transaction that // could be used by anyone to send tokens to another account // that has been set up to receive tokens. // // The withdraw amount and the account from getAccount // would be the parameters to the transaction import "FungibleToken" import "FlowToken" transaction(amount: UFix64, to: Address) { // The Vault resource that holds the tokens that are being transferred let sentVault: @{FungibleToken.Vault} prepare(signer: auth(BorrowValue) &Account) { // Get a reference to the signer's stored vault let vaultRef = signer.storage.borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") // Withdraw tokens from the signer's stored vault self.sentVault <- vaultRef.withdraw(amount: amount) } execute { // Get a reference to the recipient's Receiver let receiverRef = getAccount(to) .capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Could not borrow receiver reference to the recipient's Vault") // Deposit the withdrawn tokens in the recipient's receiver receiverRef.deposit(from: <-self.sentVault) } } ``` ## Build the Transaction ```go import ( "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" ) // Replace with script above const transferScript string = TOKEN_TRANSFER_CADENCE_SCRIPT var ( senderAddress flow.Address senderAccountKey flow.AccountKey senderPrivateKey crypto.PrivateKey ) func main() { tx := flow.NewTransaction(). SetScript([]byte(transferScript)). SetComputeLimit(100). SetPayer(senderAddress). SetAuthorizer(senderAddress). SetProposalKey(senderAddress, senderAccountKey.Index, senderAccountKey.SequenceNumber) amount, err := cadence.NewUFix64("123.4") if err != nil { panic(err) } recipient := cadence.NewAddress(flow.HexToAddress("0xabc...")) err = tx.AddArgument(amount) if err != nil { panic(err) } err = tx.AddArgument(recipient) if err != nil { panic(err) } } ``` ## Stream Events [](https://pkg.go.dev/github.com/onflow/flow-go-sdk/client#Client.SubscribeEventsByBlockID) Subscribe to and stream events from an Access node using the SDK subscription primitives. The canonical example demonstrates a complete runnable implementation that connects to an access node, subscribes from a reference block, and processes an event stream. **[](https://github.com/onflow/flow-go-sdk/blob/master/examples/stream_events/main.go)** ```go // Minimal walkthrough (based on examples/stream_events/main.go) ctx := context.Background() // 1) Create a client (SubscribeEvents only supported by Flow Go SDK gRPC client) flowClient, err := grpc.NewClient(grpc.TestnetHost) // handle err // 2) Obtain a reference block/header to start the subscription from header, err := flowClient.GetLatestBlockHeader(ctx, true) // handle err // 3) Subscribe for events by block ID (returns data channel, error channel, init error) dataCh, errCh, initErr := flowClient.SubscribeEventsByBlockID(ctx, header.ID, flow.EventFilter{}) if initErr != nil { // handle init error } // 4) Process the stream (select loop) for { select { case <-ctx.Done(): // graceful shutdown return case data, ok := <-dataCh: if !ok { // subscription closed; reconnect / exponential back-off return } // data contains block-level payload with Events for _, ev := range data.Events { fmt.Printf("Type: %s\n", ev.Type) fmt.Printf("Values: %v\n", ev.Value) fmt.Printf("Transaction ID: %s\n", ev.TransactionID) } case err := <-errCh: if err != nil { // handle streaming error (log, reconnect / exponential back-off) } } } ``` Notes & best practices - Use `flow.EventFilter` to subscribe only to event types / contracts you need to reduce bandwidth. - Persist the last processed block ID/height to resume after restarts; resume subscriptions from a checkpoint. - Treat empty payloads/heartbeats as liveness; implement reconnect and exponential back-off on termination. - For historical backfills use `GetEventsForBlockIDs` / `GetEventsForBlockRange` instead of live streaming. --- # File: /md/build/tools/clients/flow-go-sdk/flowkit.md --- title: Flow Project Configuration description: Learn how to work with Flow project state using the Flowkit Go library sidebar_position: 3 keywords: - Flowkit - Go SDK - Flow - Project State - Tutorial - Configuration - flow.json --- # Flowkit Go Tutorial: Working with Flow Project State ## Introduction **Flowkit** is a Go package for interacting with the Flow blockchain in the context of `flow.json` configuration files. It provides APIs for managing Flow projects, including: - Loading and managing project configuration (`flow.json`) - Resolving import statements in Cadence contracts, scripts, and transactions - Deploying contracts to different networks (emulator, testnet, mainnet) - Managing accounts, networks, and deployments - Executing scripts and building transactions with proper import resolution Flowkit is the core package used by the Flow CLI and can be integrated into any Go application that needs to interact with Flow projects. ## Installation ### Prerequisites - Go 1.25.0 or higher - A Flow project with a `flow.json` configuration file ### Install the Package Add Flowkit to your Go module: ```bash go get github.com/onflow/flowkit/v2 ``` This will install Flowkit v2 and all its dependencies. ### Import in Your Code ```go import ( "github.com/onflow/flowkit/v2" "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/project" "github.com/spf13/afero" ) ``` ## Loading Project State The first step when working with Flowkit is loading your project's state from `flow.json`. ### Basic Usage ```go package main import ( "log" "github.com/onflow/flowkit/v2" "github.com/spf13/afero" ) func main() { // Load flow.json from the current directory state, err := flowkit.Load([]string{"flow.json"}, afero.Afero{Fs: afero.NewOsFs()}) if err != nil { log.Fatalf("Failed to load project state: %v", err) } // Now you can work with the project state log.Println("Project state loaded successfully!") } ``` ### Creating a New Project State If you need to create a new project from scratch: ```go // Initialize an empty state state, err := flowkit.Init(afero.Afero{Fs: afero.NewOsFs()}) if err != nil { log.Fatalf("Failed to initialize state: %v", err) } ``` ### Accessing State Components The `State` object provides access to all project configuration: ```go // Get all contracts contracts := state.Contracts() // Get all networks networks := state.Networks() // Get all accounts accounts := state.Accounts() // Get all deployments deployments := state.Deployments() // Get the underlying config config := state.Config() ``` ## Working with Contracts Contracts are Cadence smart contracts defined in your project. ### Getting Contract Information ```go // Get a contract by name contract, err := state.Contracts().ByName("MyContract") if err != nil { log.Fatalf("Contract not found: %v", err) } log.Printf("Contract: %s\n", contract.Name) log.Printf("Location: %s\n", contract.Location) // Get all contract names for _, c := range *state.Contracts() { log.Printf("Available contract: %s\n", c.Name) } ``` ### Getting Deployment Contracts for a Network When deploying or executing code, you often need contracts with their target addresses: ```go import "github.com/onflow/flowkit/v2/config" // Get all contracts configured for deployment on testnet contracts, err := state.DeploymentContractsByNetwork(config.TestnetNetwork) if err != nil { log.Fatalf("Failed to get deployment contracts: %v", err) } // Each contract includes deployment information for _, contract := range contracts { log.Printf("Contract: %s\n", contract.Name) log.Printf(" Location: %s\n", contract.Location()) log.Printf(" Target Account: %s\n", contract.AccountAddress) log.Printf(" Account Name: %s\n", contract.AccountName) log.Printf(" Code Size: %d bytes\n", len(contract.Code())) } ``` ## Working with Networks Flowkit supports multiple networks including emulator, testnet, and mainnet. ### Available Networks ```go import "github.com/onflow/flowkit/v2/config" // Predefined networks emulator := config.EmulatorNetwork // Local emulator testnet := config.TestnetNetwork // Flow testnet mainnet := config.MainnetNetwork // Flow mainnet log.Printf("Emulator: %s\n", emulator.Host) log.Printf("Testnet: %s\n", testnet.Host) log.Printf("Mainnet: %s\n", mainnet.Host) ``` ### Getting Networks from State ```go // Get all networks defined in flow.json networks := state.Networks() // Get a specific network by name testnet, err := networks.ByName("testnet") if err != nil { log.Fatalf("Network not found: %v", err) } log.Printf("Network: %s\n", testnet.Name) log.Printf("Host: %s\n", testnet.Host) ``` ### Adding or Updating Networks ```go import "github.com/onflow/flowkit/v2/config" // Add a custom network networks := state.Networks() networks.AddOrUpdate(config.Network{ Name: "custom-network", Host: "localhost:3570", }) // Save the updated configuration err := state.SaveDefault() if err != nil { log.Fatalf("Failed to save state: %v", err) } ``` ### Getting Network-Specific Aliases Network aliases map contract names/locations to their deployed addresses on specific networks: ```go import "github.com/onflow/flowkit/v2/config" // Get aliases for testnet aliases := state.AliasesForNetwork(config.TestnetNetwork) // aliases is a map[string]string of location/name -> address for location, address := range aliases { log.Printf("%s deployed at %s on testnet\n", location, address) } ``` ## Resolving Imports with ImportReplacer The `ImportReplacer` resolves import statements in Cadence contracts, scripts, and transactions by replacing relative file paths and contract names with their deployed blockchain addresses. ### Basic Usage When you have a Cadence program with imports like `import "Kibble"`, you need to resolve these to blockchain addresses: ```go import "github.com/onflow/flowkit/v2/project" import "github.com/onflow/flowkit/v2/config" // Get contracts for your target network contracts, err := state.DeploymentContractsByNetwork(config.TestnetNetwork) if err != nil { log.Fatal(err) } // Create an import replacer with your project's contracts importReplacer := project.NewImportReplacer(contracts, nil) // Parse your Cadence program code := []byte(` import "Kibble" import "FungibleToken" transaction { prepare(signer: &Account) { // ... } } `) program, err := project.NewProgram(code, nil, "") if err != nil { log.Fatalf("Failed to parse program: %v", err) } // Replace imports with deployed addresses resolvedProgram, err := importReplacer.Replace(program) if err != nil { log.Fatalf("Failed to resolve imports: %v", err) } // The resolved program now has addresses instead of file paths log.Printf("Resolved code:\n%s", string(resolvedProgram.Code())) ``` ### Integration with Project State The most common pattern is to use network-specific aliases from your project state: ```go // Load project state and get network-specific contracts and aliases state, err := flowkit.Load([]string{"flow.json"}, afero.Afero{Fs: afero.NewOsFs()}) if err != nil { log.Fatal(err) } // Choose your target network network := config.TestnetNetwork // Get contracts for this network contracts, err := state.DeploymentContractsByNetwork(network) if err != nil { log.Fatal(err) } // Use network-specific aliases for address mapping importReplacer := project.NewImportReplacer( contracts, state.AliasesForNetwork(network), ) // Parse and resolve your program program, err := project.NewProgram(scriptCode, nil, "script.cdc") if err != nil { log.Fatalf("Failed to parse program: %v", err) } resolvedProgram, err := importReplacer.Replace(program) if err != nil { log.Fatalf("Failed to resolve imports: %v", err) } // Use the resolved program for execution log.Printf("Ready to execute:\n%s", string(resolvedProgram.Code())) ``` ## Working with Accounts Accounts represent Flow blockchain accounts used for signing transactions and deploying contracts. ### Getting Account Information ```go import "github.com/onflow/flow-go-sdk" accounts := state.Accounts() // Get account by name account, err := accounts.ByName("emulator-account") if err != nil { log.Fatalf("Account not found: %v", err) } log.Printf("Account: %s\n", account.Name) log.Printf("Address: %s\n", account.Address) // Get all account names names := accounts.Names() for _, name := range names { log.Printf("Available account: %s\n", name) } // Get account by address addr := flow.HexToAddress("0xf8d6e0586b0a20c7") account, err = accounts.ByAddress(addr) if err != nil { log.Fatalf("Account not found by address: %v", err) } ``` ### Getting the Emulator Service Account ```go // Get the emulator's default service account serviceAccount, err := state.EmulatorServiceAccount() if err != nil { log.Fatalf("Failed to get service account: %v", err) } log.Printf("Service account address: %s\n", serviceAccount.Address) ``` ## Working with Deployments Deployments define which contracts should be deployed to which accounts on specific networks. ### Getting Deployment Information ```go deployments := state.Deployments() // Get all deployments for a network testnetDeployments := deployments.ByNetwork("testnet") for _, deployment := range testnetDeployments { log.Printf("Account: %s\n", deployment.Account) log.Printf("Network: %s\n", deployment.Network) log.Printf("Contracts:\n") for _, contract := range deployment.Contracts { log.Printf(" - %s\n", contract.Name) } } // Get deployment for specific account and network deployment := deployments.ByAccountAndNetwork("my-account", "testnet") if deployment != nil { log.Printf("Found deployment: %d contracts\n", len(deployment.Contracts)) } ``` ## Complete Example Here's a complete example that ties everything together: ```go package main import ( "log" "github.com/onflow/flowkit/v2" "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/project" "github.com/spf13/afero" ) func main() { // 1. Load project state state, err := flowkit.Load([]string{"flow.json"}, afero.Afero{Fs: afero.NewOsFs()}) if err != nil { log.Fatalf("Failed to load state: %v", err) } // 2. Choose target network network := config.TestnetNetwork log.Printf("Using network: %s\n", network.Name) // 3. Get deployment contracts for the network contracts, err := state.DeploymentContractsByNetwork(network) if err != nil { log.Fatalf("Failed to get contracts: %v", err) } log.Printf("Found %d contracts for deployment\n", len(contracts)) for _, contract := range contracts { log.Printf(" - %s -> %s\n", contract.Name, contract.AccountAddress) } // 4. Get network aliases aliases := state.AliasesForNetwork(network) log.Printf("Network has %d aliases\n", len(aliases)) // 5. Create import replacer importReplacer := project.NewImportReplacer(contracts, aliases) // 6. Resolve imports in a script scriptCode := []byte(` import "Kibble" import "FungibleToken" access(all) fun main(): String { return "Hello, Flow!" } `) program, err := project.NewProgram(scriptCode, nil, "script.cdc") if err != nil { log.Fatalf("Failed to parse program: %v", err) } resolvedProgram, err := importReplacer.Replace(program) if err != nil { log.Fatalf("Failed to resolve imports: %v", err) } log.Printf("Resolved script:\n%s\n", string(resolvedProgram.Code())) // 7. Get account for signing account, err := state.Accounts().ByName("testnet-account") if err != nil { log.Fatalf("Failed to get account: %v", err) } log.Printf("Using account: %s (%s)\n", account.Name, account.Address) log.Println("Setup complete! Ready to interact with Flow.") } ``` ## Conclusion Flowkit provides a powerful and flexible API for managing Flow projects in Go. By understanding how to work with project state, contracts, networks, and import resolution, you can build robust applications that interact with the Flow blockchain. The import replacer is particularly critical for ensuring your Cadence code works correctly across different networks by automatically resolving contract imports to their deployed addresses. --- # File: /md/build/tools/clients/flow-go-sdk/migration-v0.25.0.md # Migration Guide v0.25.0 The Go SDK version 0.25.0 introduced breaking changes in the API and package naming. Changes were required to make the implementation of the new HTTP access node API available. We will list all the changes and provide examples on how to migrate. - **Renamed package: client -> access:** the `client` package was renamed to `access` which now includes both `grpc` package containing previously only gRPC implementation and also `http` package containing the new HTTP API implementation. - **Removed package: convert:** the `convert` package was removed and all its functions were moved to each of the corresponding `grpc` or `http` packages. The methods were also changed to not be exported, so you can no longer use them outside the `convert` package. - **New clients:** new clients were added each implementing the functions from the client interface and exposing a factory for creating them. - **New Client Interface**: new client interface was created which is now network agnostic, meaning it doesn't any more expose additional options in the API that were used to pass gRPC specific options. You can still pass those options but you must use the network specific client as shown in the example bellow. The interface also changed some functions: - `GetCollectionByID` renamed to `GetCollection` - `Close() error` was added ### Migration #### Creating a Client Creating a client for communicating with the access node has changed since it's now possible to pick and choose between HTTP and gRPC communication protocols. *Previous versions:* ```go // initialize a gRPC emulator client flowClient, err := client.New("127.0.0.1:3569", grpc.WithInsecure()) ``` *Version 0.25.0*: ```go // common client interface var flowClient access.Client // initialize an http emulator client flowClient, err := http.NewClient(http.EmulatorHost) // initialize a gPRC emulator client flowClient, err = grpc.NewClient(grpc.EmulatorHost) ``` #### Using the gRPC Client with Options Using the client is in most cases the same except for the advance case of passing additional options to the gRPC client which is no longer possible in the base client, you must use a network specific client as shown in the advanced example: *Previous versions:* ```go // initialize a gRPC emulator client flowClient, err := client.New("127.0.0.1:3569", grpc.WithInsecure()) latestBlock, err := flowClient.GetLatestBlock(ctx, true, MaxCallSendMsgSize(100)) ``` *Version 0.25.0:* ```go // initialize a grpc network specific client flowClient, err := NewBaseClient( grpc.EmulatorHost, grpc.WithTransportCredentials(insecure.NewCredentials()), ) latestBlock, err := flowClient.GetLatestBlock(ctx, true, MaxCallSendMsgSize(100)) ``` --- # File: /md/build/tools/emulator.md --- title: Flow Emulator description: Local Flow network for development and testing sidebar_position: 3 keywords: - Flow Emulator - local development - testing - flow emulator --fork - fork mode - emulator flags - mainnet fork - testnet fork - fork-height - fork-host - gRPC server - REST API - snapshots - persistent storage - block time - code coverage - debugging - service account - Flow CLI - local blockchain - E2E testing - off-chain mocking --- The Flow Emulator is a lightweight tool that emulates the behavior of the real Flow network for local development and testing. ## Installation The emulator is included with the [Flow CLI]. Follow the [installation guide] to get started. ## Quick Start First, create a `flow.json` configuration file: ```bash flow init --config-only ``` Then start the Flow Emulator in fork mode (defaults to mainnet when value omitted): ```bash flow emulator --fork ``` You'll see output similar to: ```bash INFO[0000] ⚙️ Using service account 0xf8d6e0586b0a20c7 serviceAddress=f8d6e0586b0a20c7 ... INFO[0000] 🌱 Starting Flow Emulator INFO[0000] 🛠 GRPC server started on 127.0.0.1:3569 INFO[0000] 📡 HTTP server started on 127.0.0.1:8080 ``` This starts a local Flow network with: - gRPC server on port `3569` - REST API on `http://localhost:8888` - Admin API on port `8080` ## Available commands - `snapshot`: Create/Load/List emulator snapshots. See: [Create Emulator Snapshot] ## Key flags - **Networking** - `--host `: Host to listen on for gRPC, REST, and Admin (default: all interfaces) - `--port, -p `: gRPC port (default `3569`) - `--rest-port `: REST API port (default `8888`) - `--admin-port `: Admin API port (default `8080`) - `--debugger-port `: Debug Adapter Protocol port (default `2345`) - `--grpc-debug`: Turn on gRPC server reflection - `--rest-debug`: Turn on REST API debug output - **State and Persistence** - `--persist`: Turn on persistent storage (default disabled) - `--dbpath `: Directory for on-disk state (default `./flowdb`) - `--sqlite-url `: Use SQLite storage backend - `--redis-url `: Use Redis storage backend - `--checkpoint-dir `: Load state from checkpoint directory - `--state-hash `: Load state from checkpoint state hash - **Forking** - `--fork `: Start the emulator in fork mode using a network from `flow.json`. If provided without a value, defaults to `mainnet`. - `--fork-host `: Access node to query when you fork Mainnet or Testnet - `--fork-height `: Starting block height when you fork - **Cadence and VM** - `--block-time, -b `: Time between sealed blocks (for exxample, `1s`, `300ms`) - `--coverage-reporting`: Turn on code coverage reporting - `--computation-reporting`: Turn on computation reporting - `--legacy-upgrade`: Turn on legacy contract upgrade behavior - `--scheduled-transactions`: Turn on scheduled transactions (default true) - `--script-compute-limit `: Compute unit limit for scripts (default `100000`) - `--transaction-max-compute-limit `: Max transaction compute unit limit (default `9999`) - `--transaction-expiry `: Transaction expiry in blocks (default `10`) - `--skip-tx-validation`: Skip tx signature and sequence number checks - `--simple-addresses`: Use sequential addresses starting with `0x01` - `--storage-limit`: Enforce account storage limit (default true) - `--storage-per-flow `: MB of storage per 1 FLOW token - `--token-supply `: Initial FLOW token supply (default `1000000000.0`) - `--transaction-fees`: Turn on transaction fees - `--setup-evm`: Deploy EVM contracts (default true) - `--setup-vm-bridge`: Deploy VM Bridge contracts (default true) - **Service Account and Identity** - `--chain-id `: Address generation chain (default `emulator`) - `--service-priv-key ` / `--service-pub-key `: Service account keys - `--service-sig-algo `: Service key signature algo (default `ECDSA_P256`) - `--service-hash-algo `: Service key hash algo (default `SHA3_256`) - `--min-account-balance `: Minimum account balance or account creation cost - `--contracts`: Deploy common contracts on start - `--contract-removal`: Allow contract removal for development (default true) - `--init`: Initialize a new account profile - **Logging and Output** - `--verbose, -v`: Verbose logging - `--log-format `: Logging output format (default `text`) - **Snapshots** - `--snapshot`: Enable snapshots in the emulator ## Examples ```bash # Verbose logs flow emulator --verbose # Custom ports flow emulator --port 9000 --rest-port 9001 --admin-port 9002 # Custom block time (1 second between blocks) flow emulator --block-time 1s # Persist state on disk flow emulator --persist --dbpath ./flowdb # Fork from Mainnet using flow.json flow emulator --fork # Fork from Testnet using flow.json and pin to a height flow emulator --fork testnet --fork-height 12345678 # Fork from Testnet at a specific height flow emulator --fork-host access.devnet.nodes.onflow.org:9000 --fork-height 12345678 # Disable fees and use simple addresses for local testing flow emulator --transaction-fees=false --simple-addresses # Enable code coverage reporting flow emulator --coverage-reporting # Change the gRPC and REST API ports flow emulator --port 9000 --rest-port 9001 # For a complete list of available flags, run: flow emulator --help ``` For the complete and current list of flags, run: ```bash flow emulator --help ``` ## Debugging and Testing - **Code Coverage**: Add `--coverage-reporting` flag and visit `http://localhost:8080/emulator/codeCoverage` - **Computation Profiling**: Add `--computation-profiling` and/or `--computation-reporting` flags to analyze computational costs and identify performance bottlenecks in your Cadence code. See the [Cadence Computation Profiling guide] for detailed instructions. - **Debugging**: Use `#debugger()` pragma in Cadence code for breakpoints - **Fork mode note**: When you use `flow emulator --fork`, only Flow chain state is available. External oracles/APIs and cross-chain reads are not live; mock these or run local stub services for E2E. ### Fork Mode Tutorial For a complete guide on using the emulator in fork mode with dapps, E2E tests, and account impersonation, see: [Interactive Testing with Forked Emulator]. ## Snapshots The Flow CLI provides a command to create emulator snapshots, which are points in blockchain history you can later jump to and reset the state to that moment. This can be useful to test where you establish a beginning state, run tests and after revert back to the initial state. ### Quick snapshot workflow ```bash # 1) Start the emulator with snapshots enabled (in a separate terminal) flow emulator --snapshot # 2) Create a snapshot at the current state flow emulator snapshot create baseline # 3) Make changes, run tests, etc. # 4) Reset the emulator back to the snapshot flow emulator snapshot load baseline ``` ### Create a new snapshot Create a new emulator snapshot at the current block with a name of `myInitialState`. ```shell flow emulator snapshot create myInitialState ``` ### Load a current snapshot To jump to a previously created snapshot we use the load command in combination with the name. ```shell flow emulator snapshot load myInitialState ``` ### List all snapshots To list all the snapshots we previously created and can load to run: ```shell flow emulator list ``` ## Additional resources To learn more about how to use the Emulator, have a look at the [public GitHub repository]. [Flow CLI]: ../flow-cli/index.md [installation guide]: ../flow-cli/install.md [Create Emulator Snapshot]: ../flow-cli/utils/snapshot-save.md [public GitHub repository]: https://github.com/onflow/flow-emulator [Interactive Testing with Forked Emulator]: ../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md [Cadence Computation Profiling guide]: ../../cadence/advanced-concepts/computation-profiling.md --- # File: /md/build/tools/error-codes.md --- title: Error Codes sidebar_position: 7 --- # Error Codes List of error codes returned from transactions and scripts that fail. The error code has an accompanied error message that usually gives more clarification. This list is meant to give more information and helpful hints. [Code file] ### 1006 **ErrCodeInvalidProposalSignatureError** Example: `...` ### 1007 **ErrCodeInvalidProposalSeqNumberError** Example: `[Error Code: 1007] invalid proposal key: public key 0 on account xxx has sequence number xxx, but given xxx` ### 1008 **ErrCodeInvalidPayloadSignatureError** Example: `[Error Code: 1008] invalid payload signature: public key 0 on account xxx does not have a valid signature: signature is not valid` ### 1009 **ErrCodeInvalidEnvelopeSignatureError** Example: `[Error Code: 1009] invalid envelope key: public key 1 on account xxx does not have a valid signature: signature is not valid` ### 1051 **ErrCodeValueError** Example: `[Error Code: 1051] invalid value (xxx): invalid encoded public key value: rlp: expected input list for flow.runtimeAccountPublicKeyWrapper...` ### 1052 **ErrCodeInvalidArgumentError** Example: `[Error Code: 1052] transaction arguments are invalid: (argument is not json decodable: failed to decode value: runtime error: slice bounds out of range [:2] with length 0)` ### 1053 **ErrCodeInvalidAddressError** Example: `...` ### 1054 **ErrCodeInvalidLocationError** Example: `[Error Code: 1054] location (../contracts/FungibleToken.cdc) is not a valid location: expecting an AddressLocation, but other location types are passed ../contracts/FungibleToken.cdc` ### 1055 **ErrCodeAccountAuthorizationError** Example: `[Error Code: 1055] authorization failed for account e85d442d61a611d8: payer account does not have sufficient signatures (1 < 1000)` ### 1056 **ErrCodeOperationAuthorizationError** Example: `[Error Code: 1056] (RemoveContract) is not authorized: removing contracts requires authorization from specific accounts goroutine 5688834491 [running]:` ### 1057 **ErrCodeOperationNotSupportedError** Example: `...` ### 1101 **ErrCodeCadenceRunTimeError** Example: `[Error Code: 1101] cadence runtime error Execution failed: error: pre-condition failed: Amount withdrawn must be less than or equal than the balance of the Vault` ### 1103 **ErrCodeStorageCapacityExceeded** Example: `[Error Code: 1103] The account with address (xxx) uses 96559611 bytes of storage which is over its capacity (96554500 bytes). Capacity can be increased by adding FLOW tokens to the account.` For more information refer to [Fees] ### 1105 **ErrCodeEventLimitExceededError** Example: `[Error Code: 1105] total event byte size (256200) exceeds limit (256000)` ### 1106 **ErrCodeLedgerInteractionLimitExceededError** Example: `[Error Code: 1106] max interaction with storage has exceeded the limit (used: 20276498 bytes, limit 20000000 bytes)` ### 1107 **ErrCodeStateKeySizeLimitError** Example: `...` ### 1108 **ErrCodeStateValueSizeLimitError** Example: `...` ### 1109 **ErrCodeTransactionFeeDeductionFailedError** Example: `[Error Code: 1109] failed to deduct 0 transaction fees from 14af75b8c487333c: Execution failed: f919ee77447b7497.FlowFees:97:24` ### 1110 **ErrCodeComputationLimitExceededError** Example: `[Error Code: 1110] computation exceeds limit (100)` ### 1111 **ErrCodeMemoryLimitExceededError** Example: `...` ### 1112 **ErrCodeCouldNotDecodeExecutionParameterFromState** Example: `...` ### 1113 **ErrCodeScriptExecutionTimedOutError** Example: `...` ### 1114 **ErrCodeScriptExecutionCancelledError** Example: `...` ### 1115 **ErrCodeEventEncodingError** Example: `...` ### 1116 **ErrCodeInvalidInternalStateAccessError** Example: `...` ### 1118 **ErrCodeInsufficientPayerBalance** Example: ` [Error Code: 1118] payer ... has insufficient balance to attempt transaction execution (required balance: 0.00100000)` ### 1201 **ErrCodeAccountNotFoundError** Example: `[Error Code: 1201] account not found for address xxx` ### 1202 **ErrCodeAccountPublicKeyNotFoundError** Example: `[Error Code: 1202] account public key not found for address xxx and key index 3` ### 1203 **ErrCodeAccountAlreadyExistsError** Example: `...` ### 1204 **ErrCodeFrozenAccountError** Example: `...` ### 1206 **ErrCodeAccountPublicKeyLimitError** Example: `...` ### 1251 **ErrCodeContractNotFoundError** Example: `...` ### 2000 **FailureCodeUnknownFailure** Example: `...` ### 2001 **FailureCodeEncodingFailure** Example: `...` ### 2002 **FailureCodeLedgerFailure** Example: `...` ### 2003 **FailureCodeStateMergeFailure** Example: `...` ### 2004 **FailureCodeBlockFinderFailure** Example: `...` ### 2006 **FailureCodeParseRestrictedModeInvalidAccessFailure** Example: `...` ### 2007 **FailureCodePayerBalanceCheckFailure** Example: `...` [Code file]: https://github.com/onflow/flow-go/blob/master/fvm/errors/codes.go [Fees]: ../cadence/basics/fees.md#maximum-available-balance --- # File: /md/build/tools/flow-cli.md --- title: Flow CLI sidebar_label: Flow CLI sidebar_position: 3 --- The **Flow Command Line Interface (CLI)** is a powerful tool that allows developers to seamlessly interact with the Flow blockchain across various environments, such as testnet, mainnet, and local development with the Flow Emulator. Designed for ease of use, the Flow CLI simplifies common blockchain tasks such as account and contract dependency management, sending transactions, querying chain state, smart contract deployment, and much more. With Flow CLI, developers can: - **Initialize Projects**: Quickly set up new Flow projects using the `flow init` command, which creates the necessary files and directories, sets up your project configuration, and installs any core contract dependencies. - **Manage Contract Dependencies**: Use the [Dependency Manager] to install and manage smart contract dependencies effortlessly, which simplifies the integration of external contracts into your project. - **Manage Accounts**: Create and manage Flow accounts, configure keys, and handle account-related operations. - **Send Transactions**: Build, sign, and submit transactions to the Flow network, which allows for contract interaction and fund transfers. - **Query Chain State**: Retrieve data from the Flow blockchain, such as account balances, event logs, and the status of specific transactions. - **Deploy Smart Contracts**: Easily deploy and update Cadence smart contracts on any Flow environment (emulator, testnet, or mainnet). - **Use the Emulator:** Set up a local Flow blockchain instance with the Flow emulator to test and debug smart contracts in a development environment before you deploy them on the network. - **Interact with the [Flow Access API]**: Automate complex workflows with configuration files and command-line scripting, which allows for greater flexibility in continuous integration (CI) or custom development tools. - **Access Flow’s Tooling Ecosystem**: Integrate Flow CLI with other developer tools like the [Cadence Extension for VSCode] to enhance your development experience. The Flow CLI is essential for developers who want to build, test, and maintain decentralized applications on the Flow blockchain efficiently. It offers a feature-rich, user-friendly interface for both beginners and experienced blockchain developers. ## Installation Follow [these steps] to install the Flow CLI on macOS, Linux, and Windows. ## Create your first project To get started, run: ```zsh flow init ``` The `flow init` command gets you up and running with a new project setup in one command. Choose from scaffolds for scheduled transactions, DeFi actions, stablecoins, and more, or start with a basic Cadence project. To learn more about Flow CLI commands and how to use them, see the [Commands documentation]. [Dependency Manager]: dependency-manager.md [Flow Access API]: /http-api [Cadence Extension for VSCode]: https://marketplace.visualstudio.com/items?itemName=onflow.cadence [these steps]: ../flow-cli/install.md [Commands documentation]: ./commands.md --- # File: /md/build/tools/flow-cli/_template.md --- title: -title- sidebar_label: description: -description- --- \{short description\} ```shell {command} ``` \{optional warning\} ## Example Usage ```shell {usage example with response} ``` ## Arguments ### \{Argument 1\} - Name: `\{argument\}` - Valid Input: `\{input\}` \{argument general description\} ## Arguments ### Address - Name: `address` - Valid Input: Flow account address Flow [account address](../../../cadence/basics/accounts.md) (prefixed with `0x` or not). ## Flags ### \{Option 1\} - Flag: `\{flag value\}` - Valid inputs: \{input description\} \{flag general description\} ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used to sign the transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the commands. ### Network Key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when executing the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: valid filename Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see while command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: valid filename Specify a filename for the configuration files, you can provide multiple configuration files by using `-f` flag multiple times. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/accounts/account-add-contract.md --- title: Deploy a Contract sidebar_position: 3 --- Deploy a new contract to a Flow account with the Flow CLI. ```shell flow accounts add-contract [ ...] [flags] ``` ⚠️ Deprecation notice: we will deprecate the name argument in adding contract command soon. ```shell flow accounts add-contract [ ...] [flags] ``` ## Example usage ```shell > flow accounts add-contract ./FungibleToken.cdc Contract 'FungibleToken' deployed to the account 0xf8d6e0586b0a20c7 Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 1 Contract: 'FungibleToken' ``` **Testnet example** ``` > flow accounts add-contract ./FungibleToken.cdc --signer alice --network testnet Contract 'FungibleToken' deployed to the account 0xf8d6e0586b0a20c7 Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 1 Contract: 'FungibleToken' ``` *Make sure alice account is defined in flow.json* ## Arguments ### Name - Name: `name` - Valid inputs: any string value. Name of the contract as it is defined in the contract source code. ⚠️ Deprecation notice: use filename argument only, no need to use name argument. ### Filename - Name: `filename` - Valid inputs: a path in the current filesystem. Path to the file containing the contract source code. ### Arguments - Name: `argument` - Valid inputs: valid [cadence values] that match argument type in transaction code. Input arguments values that match corresponding types in the source code and passed in the same order. Example: ```shell > flow accounts add-contract ./contract.cdc Hello 2 ``` Transaction code: ``` access(all) contract HelloWorld { init(a:String, b:Int) { } } ``` ## Flags ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used to sign the transaction. ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow accounts add-contract ./tx.cdc '[{"type": "String", "value": "Hello"}]'` Arguments passed to the Cadence transaction in Cadence JSON format. Cadence JSON format contains `type` and `value` keys and is [documented here]. ### Include fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`). - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [cadence values]: https://cadencelang.dev/docs/1.0/json-cadence-spec [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec --- # File: /md/build/tools/flow-cli/accounts/account-fund.md --- title: Funding a Testnet Account description: How to fund a Testnet Flow account from the command line sidebar_position: 7 --- :::info The [Flow Testnet Faucet] allows users to create accounts and receive 1,000 Testnet FLOW tokens for testing and development purposes. You can also fund a current Testnet account without the need to create one through the site, or through the CLI. ::: Fund a valid Testnet Flow Account with the Flow CLI. ```shell flow accounts fund [address|name] ``` ## Example usage ### Fund by address ``` > flow accounts fund 8e94eaa81771313a Opening the faucet to fund 0x8e94eaa81771313a on your native browser. If there is an issue, please use this link instead: https://testnet-faucet.onflow.org/fund-account?address=8e94eaa81771313a ``` ### Fund by account name ``` > flow accounts fund testnet-account Opening the faucet to fund 0x8e94eaa81771313a on your native browser. If there is an issue, please use this link instead: https://testnet-faucet.onflow.org/fund-account?address=8e94eaa81771313a ``` ### Interactive prompt ``` > flow accounts fund ? Select account to fund: (Use arrow keys) ❯ testnet-account (0x8e94eaa81771313a) emulator-account (0x0ae53cb6e3f42a79) ``` ## Arguments ### Address or account name (optional) - Name: `address|name` - Valid Input: Flow Testnet account address or account name from `flow.json` You can provide: - A Flow [account address](../../../cadence/basics/accounts.md) (prefixed with `0x` or not) - An account name configured in your `flow.json` - No argument to get an interactive prompt for account selection [Flow Testnet Faucet]: https://testnet-faucet.onflow.org/ [account address]: ../../../cadence/basics/accounts.md --- # File: /md/build/tools/flow-cli/accounts/account-remove-contract.md --- title: Remove a Contract sidebar_position: 5 --- _This feature is only found in the Emulator. You **cannot** remove a contract on Testnet or Mainnet._ Remove a contract deployed to a Flow account with the Flow CLI. ```shell flow accounts remove-contract ``` ## Example usage ```shell > flow accounts remove-contract FungibleToken Contract 'FungibleToken' removed from account '0xf8d6e0586b0a20c7' Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 0 ``` **Testnet example** ``` > flow accounts remove-contract FungibleToken --signer alice --network testnet Contract 'FungibleToken' removed from account '0xf8d6e0586b0a20c7' Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 0 ``` *Make sure alice account is defined in flow.json* ## Arguments ### Name - Name: `name` - Valid inputs: any string value. Name of the contract as it is defined in the contract source code. ## Flags ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`). Specify the name of the account that will be used to sign the transaction. ### Include fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when executing the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/accounts/account-staking-info.md --- title: Account Staking Info description: How to get staking info sidebar_position: 6 --- Retrieve staking information for the account on the Flow network with Flow CLI. ```shell flow accounts staking-info
    ``` ## Example usage ```shell > accounts staking-info 535b975637fb6bee --host access.testnet.nodes.onflow.org:9000 Account Staking Info: ID: "ca00101101010100001011010101010101010101010101011010101010101010" Initial Weight: 100 Networking Address: "ca00101101010100001011010101010101010101010101011010101010101010" Networking Key: "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010" Role: 1 Staking Key: "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010" Tokens Committed: 0.00000000 Tokens To Unstake: 0.00000000 Tokens Rewarded: 82627.77000000 Tokens Staked: 250000.00000000 Tokens Unstaked: 0.00000000 Tokens Unstaking: 0.00000000 Node Total Stake (including delegators): 250000.00000000 Account Delegation Info: ID: 7 Tokens Committed: 0.00000000 Tokens To Unstake: 0.00000000 Tokens Rewarded: 30397.81936000 Tokens Staked: 100000.00000000 Tokens Unstaked: 0.00000000 Tokens Unstaking: 0.00000000 ``` ## Arguments ### Address - Name: `address` - Valid Input: Flow account address. Flow [account address] (prefixed with `0x` or not). ## Flags ### Include fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [account address]: ../../../cadence/basics/accounts.md --- # File: /md/build/tools/flow-cli/accounts/account-update-contract.md --- title: Update a Contract sidebar_position: 4 --- Update a contract deployed to a Flow account with the Flow CLI. ```shell flow accounts update-contract [ ...] [flags] ``` ⚠️ Deprecation notice: We will depreate the name argument in update contract command soon. ```shell flow accounts update-contract [ ...] [flags] ``` ## Example usage ```shell > flow accounts update-contract ./FungibleToken.cdc Contract 'FungibleToken' updated on account '0xf8d6e0586b0a20c7' Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 1 Contract: 'FungibleToken' ``` **Testnet example** ``` > flow accounts update-contract ./FungibleToken.cdc --signer alice --network testnet Contract 'FungibleToken' updated on account '0xf8d6e0586b0a20c7' Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 1 Contract: 'FungibleToken' ``` *Make sure alice account is defined in flow.json* ## Arguments ### Name - Name: `name` - Valid inputs: Any string value Name of the contract as it is defined in the contract source code. ⚠️ Deprecation notice: use filename argument only, no need to use name argument. ### Filename - Name: `filename` - Valid inputs: Any filename and path valid on the system. Filename of the file that contains contract source code. ### Arguments - Name: `argument` - Valid inputs: valid [cadence values] that match thr argument type in transaction code. Input arguments values that match corresponding types in the source code and passed in the same order. Example: ```shell > flow accounts update-contract ./contract.cdc Hello 2 ``` Transaction code: ``` access(all) contract HelloWorld { init(a:String, b:Int) { } } ``` ## Flags ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used to sign the transaction. ### Show Diff - Flag: `--show-diff` - Valid inputs: `true`, `false` Shows a diff to approve before you update between deployed contract and new contract updates. ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow accounts update-contract ./tx.cdc '[{"type": "String", "value": "Hello"}]'` Arguments passed to the Cadence transaction in Cadence JSON format. Cadence JSON format contains `type` and `value` keys and is [documented here]. ### Include Fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [cadence values]: https://cadencelang.dev/docs/1.0/json-cadence-spec [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec --- # File: /md/build/tools/flow-cli/accounts/create-accounts.md --- title: Create an Account description: How to create a Flow account from the command line sidebar_position: 2 --- The Flow CLI provides a command to submit an account creation transaction to any Flow Access API. There are two options how to create an account: - Use the interactive mode which guides you through the process and creates the account for you. - Use the manual process which requires a current account on the network you chose. ## Interactive Mode When you create the account in interactive mode, the system prompts you for an account name and network selection. After you enter the required information, the system creates the account for you and saves it to `flow.json`. If account creation is done on testnet or mainnet the account key is saved to a separate key file, which will also be put in `.gitignore`. You can [read more about key security here]. 💡 _The account creation process can take up to a minute, so please be patient._ ```shell flow accounts create Enter an account name: mike ✔ Testnet 🎉 New account created with address 0x77e6ae4c8c2f1dd6 and name mike on Testnet network. Here’s a summary of all the actions that were taken: - Added the new account to flow.json. - Saved the private key to mike.pkey. - Added mike.pkey to .gitignore. ``` ## Manual mode Manual mode requires you to have a current account on the network, which you will have to provide as a signer. You must add that account to `flow.json` for the command to work. You also have to generate a key pair, we suggest you use the `flow keys generate` command, [which you can read more about here]. ```shell # Create an account on Flow Testnet > flow accounts create \ --key a69c6986e846ba6d0....1397f5904cd319c3e01e96375d5777f1a47010 \ --signer my-testnet-account Address 0x01cf0e2f2f715450 Balance 10000000 Keys 1 Key 0 Public Key a69c6986e846ba6d0....1397f5904cd319c3e01e96375d5777f1a47010 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Contracts Deployed: 0 ``` In the above example, the `flow.json` file would look something like this: ```json { "accounts": { "my-testnet-account": { "address": "a2c4941b5f3c7151", "key": "12c5dfde...bb2e542f1af710bd1d40b2" } } } ``` ## Flags ### Public key - Flag: `--key` - Valid inputs: a hex-encoded public key in raw form. Specify the public key that the system adds to the new account upon creation. ### Key weight - Flag: `--key-weight` - Valid inputs: number between 0 and 1000 - Default: 1000 Specify the weight of the public key being added to the new account. When you use this flag, you must specify a `--key-weight` flag for each public `--key` flag provided. ### Public key signature algorithm - Flag: `--sig-algo` - Valid inputs: `"ECDSA_P256", "ECDSA_secp256k1"` - Default: `"ECDSA_P256"` Specify the ECDSA signature algorithm for the provided public key. This option can only be used together with the `--key` flag. Flow supports the secp256k1 and P-256 curves. ### Public key hash algorithm - Flag: `--hash-algo` - Valid inputs: `"SHA2_256", "SHA3_256"` - Default: `"SHA3_256"` Specify the hash algorithm that will be paired with the public key upon account creation. ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in `flow.json`. Specify the name of the account that will be used to sign the transaction and pay the account creation fee. ### Contract - Flag: `--contract` - Valid inputs: String with format `name:filename`, where `name` is the name of the contract as it is defined in the contract source code, and `filename` is the filename of the contract source code. Specify one or more contracts to be deployed during account creation. ### Include fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [read more about key security here]: ../flow.json/security.md [which you can read more about here]: ../keys/generate-keys.md --- # File: /md/build/tools/flow-cli/accounts/get-accounts.md --- title: Get an Account description: How to get a Flow account from the command line sidebar_position: 1 --- The Flow CLI provides a command to fetch any account by its address from the Flow network. ```shell flow accounts get
    ``` ## Example usage ```shell flow accounts get 0xf8d6e0586b0a20c7 ``` ### Example response ```shell Address 0xf8d6e0586b0a20c7 Balance 99999999999.70000000 Keys 1 Key 0 Public Key 640a5a359bf3536d15192f18d872d57c98a96cb871b92b70cecb0739c2d5c37b4be12548d3526933c2cda9b0b9c69412f45ffb6b85b6840d8569d969fe84e5b7 Weight 1000 Signature Algorithm ECDSA_P256 Hash Algorithm SHA3_256 Revoked false Sequence Number 6 Index 0 Contracts Deployed: 2 Contract: 'FlowServiceAccount' Contract: 'FlowStorageFees' ``` ## Arguments ### Address - Name: `address` - Valid Input: Flow account address Flow [account address] (prefixed with `0x` or not). ## Flags ### Include fields - Flag: `--include` - Valid inputs: `contracts` Specify fields to include in the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [account address]: ../../../cadence/basics/accounts.md --- # File: /md/build/tools/flow-cli/commands.md --- title: Commands Overview description: Essential Flow CLI commands for project development sidebar_position: 2 --- Flow CLI provides a set of powerful commands that simplify your development workflow. These "super commands" handle complex tasks automatically, which lets you focus on writing your smart contracts while the CLI manages the rest. ## Project lifecycle ### 1. Initialize a project Start a new Flow project with `flow init`: ```bash flow init my-project ``` This creates: - `flow.json` - Project configuration - `cadence/` directory structure - Example contracts, scripts, and tests - Emulator account setup **Options:** ```bash # Configuration only (no project structure) flow init --config-only # Global configuration flow init --global # Custom service account flow init --service-private-key ``` 📖 **[Learn more about project initialization]** ### 2. Generate project files Create new files with the `flow generate` command: ```bash # Generate a new contract flow generate contract MyToken # Generate a new script flow generate script GetBalance # Generate a new transaction flow generate transaction TransferTokens # Generate a new test flow generate test MyToken ``` **Generated structure:** ``` cadence/ ├── contracts/ │ └── MyToken.cdc ├── scripts/ │ └── GetBalance.cdc ├── transactions/ │ └── TransferTokens.cdc └── tests/ └── MyToken_test.cdc ``` 📖 **[Learn more about generating Cadence boilerplate]** ### 3. Run Tests Test your contracts with `flow test`: ```bash # Run all tests flow test # Run specific test file flow test cadence/tests/MyToken_test.cdc # Run with coverage flow test --coverage # Run with verbose output flow test --verbose ``` 📖 **[Learn more about testing]** ### 4. Deploy contracts Deploy your contracts with `flow project deploy`: ```bash # Deploy to emulator flow project deploy # Deploy to testnet flow project deploy --network=testnet # Deploy to mainnet flow project deploy --network=mainnet # Update existing contracts flow project deploy --update ``` 📖 **[Learn more about project deployment]** ## Configuration management ### Add configuration items Use `flow config add` to manage your project configuration: ```bash # Add an account flow config add account --name my-account --address 0x123 --private-key abc123 # Add a contract flow config add contract --name MyToken --filename ./cadence/contracts/MyToken.cdc # Add a deployment flow config add deployment --network testnet --account my-account --contract MyToken ``` ### Remove configuration items ```bash # Remove an account flow config remove account my-account # Remove a contract flow config remove contract MyToken # Remove a deployment flow config remove deployment testnet my-account MyToken ``` 📖 **[Learn more about configuration management]** ## Account management ### List accounts ```bash # List all configured accounts with status flow accounts list ``` ### Create accounts ```bash # Interactive account creation flow accounts create # Create with specific network flow accounts create --network testnet # Create with custom key flow accounts create --key ``` ### Fund accounts ```bash # Interactive funding prompt flow accounts fund # Fund by account name from flow.json flow accounts fund testnet-account # Fund by address flow accounts fund 0x8e94eaa81771313a ``` ### Manage account keys ```bash # Generate new key pair flow keys generate # Decode a key flow keys decode # Derive public key from private key flow keys derive ``` 📖 **[Learn more about account management]** ## Contract interactions ### Execute scripts ```bash # Run a script flow scripts execute cadence/scripts/GetBalance.cdc # Run with arguments flow scripts execute cadence/scripts/GetBalance.cdc --arg 0x123 # Run on specific network flow scripts execute cadence/scripts/GetBalance.cdc --network testnet ``` ### Send transactions ```bash # Send a transaction flow transactions send cadence/transactions/TransferTokens.cdc # Send with arguments flow transactions send cadence/transactions/TransferTokens.cdc --arg 0x123 --arg 100 # Send with specific signer flow transactions send cadence/transactions/TransferTokens.cdc --signer my-account ``` ### Get system transactions ```bash # Get system transaction from latest block flow transactions get-system latest # Get specific system transaction by ID flow transactions get-system latest 07a8...b433 # Get system transaction from specific block height flow transactions get-system 12345 ``` ### Profile Transaction Performance ```bash # Profile a mainnet transaction flow transactions profile 07a8...b433 --network mainnet # Profile with custom output location flow transactions profile 0xabc123 --network testnet --output my-profile.pb.gz # Analyze profile with pprof go tool pprof -http=:8080 profile-07a8b433.pb.gz ``` 📖 **[Learn more about scripts](./scripts/execute-scripts.md)** | **[Learn more about transactions](./transactions/send-transactions.md)** | **[Learn more about transaction profiling](./transactions/profile-transactions.md)** ## Dependency management ### Install dependencies ```bash # Install a contract dependency flow dependencies install testnet://8a4dce54554b225d.NumberFormatter # Install from mainnet flow dependencies install mainnet://f233dcee88fe0abe.FungibleToken # Install with specific account flow dependencies install testnet://8a4dce54554b225d.NumberFormatter --account my-account ``` ### Manage dependencies ```bash # List installed dependencies flow dependencies list # Discover available contracts flow dependencies discover # Install a contract dependency flow dependencies install testnet://8a4dce54554b225d.NumberFormatter ``` 📖 **[Learn more about dependency management]** ## Scheduled transactions ### Setup manager resource ```bash # Initialize Manager resource for scheduled transactions flow schedule setup --network testnet --signer my-account ``` ### List scheduled transactions ```bash # List all scheduled transactions for an account flow schedule list my-account --network testnet ``` ### Get transaction details ```bash # Get details for a specific scheduled transaction flow schedule get 123 --network testnet ``` ### Cancel scheduled transaction ```bash # Cancel a scheduled transaction and receive refund flow schedule cancel 123 --network testnet --signer my-account ``` 📖 **[Learn more about scheduled transactions]** ## Development workflow ### Local development 1. **Start the emulator:** ```bash flow emulator start ``` 2. **Deploy contracts:** ```bash flow project deploy ``` 3. **Run tests:** ```bash flow test ``` 4. **Execute scripts:** ```bash flow scripts execute cadence/scripts/GetBalance.cdc ``` 5. **Send transactions:** ```bash flow transactions send cadence/transactions/TransferTokens.cdc ``` ### Testnet deployment 1. **Configure testnet account:** ```bash flow config add account --name testnet-account --address 0x123 --private-key abc123 ``` 2. **Deploy to testnet:** ```bash flow project deploy --network=testnet ``` 3. **Test on testnet:** ```bash flow scripts execute cadence/scripts/GetBalance.cdc --network=testnet ``` ## Import schema Use simplified imports in your Cadence code: ```cadence // Instead of complex import paths import FungibleToken from 0x9a0766d93b6608b7 // Use simple contract names import "FungibleToken" ``` The CLI automatically resolves imports based on your `flow.json` configuration. ## Best Practices ### 1. Use configuration commands Rather than manually edit `flow.json`, use CLI commands: ```bash # ✅ Good flow config add account --name my-account --address 0x123 # ❌ Avoid # Manually editing flow.json ``` ### 2. Test locally first Always test on emulator before you deploy: ```bash # 1. Start emulator flow emulator start # 2. Deploy locally flow project deploy # 3. Run tests flow test # 4. Deploy to testnet flow project deploy --network=testnet ``` ### 3. Use descriptive names Choose clear names for accounts and contracts: ```bash # ✅ Good flow config add account --name testnet-deployer flow generate contract MyNFT # ❌ Avoid flow config add account --name acc1 flow generate contract c1 ``` ### 4. Secure your keys Use secure key management: ```bash # Use file-based keys flow config add account --name my-account --key-file ./keys/my-account.key # Use environment variables FLOW_PRIVATE_KEY=abc123 flow project deploy ``` 📖 **[Learn more about security best practices]** ## Related Documentation - **[Configuration Management]** - Learn how to manage your `flow.json` file - **[Project Deployment]** - Deploy contracts to different networks - **[Account Management]** - Create and manage Flow accounts - **[Testing]** - Write and run tests for your contracts - **[Security]** - Secure your private keys and configuration [Learn more about project initialization]: ./flow.json/initialize-configuration.md [Learn more about generating Cadence boilerplate]: ./generate.md [Learn more about testing]: ./tests.md [Learn more about project deployment]: ./deployment/deploy-project-contracts.md [Learn more about configuration management]: ./flow.json/manage-configuration.md [Learn more about account management]: ./accounts/create-accounts.md [Learn more about scripts]: ./scripts/execute-scripts.md [Learn more about transactions]: ./transactions/send-transactions.md [Learn more about dependency management]: ./dependency-manager.md [Learn more about scheduled transactions]: ./scheduled-transactions.md [Learn more about security best practices]: ./flow.json/security.md [Configuration Management]: ./flow.json/manage-configuration.md [Project Deployment]: ./deployment/deploy-project-contracts.md [Account Management]: ./accounts/create-accounts.md [Testing]: ./tests.md [Security]: ./flow.json/security.md --- # File: /md/build/tools/flow-cli/data-collection.md --- title: Data Collection description: Data collected from Flow CLI usage sidebar_position: 17 --- Flow CLI tracks flow command usage count with Mixpanel. Data collection is turned on by default. To opt out of our data collection, users can run `flow settings metrics disable`. To opt back in, users can run `flow settings metrics enable`. ## Why do we collect data about flow CLI usage? When we collect aggregate command count, it allow us to prioritise features and fixes based on how users use flow CLI. ## What data do we collect? We only collect the number of times a command is executed. We don't keep track of the values of arguments, flags used and the values of the flags used. We also don't associate any commands to any particular user. The only property that we collect from our users are their opt in / out data collection preferences. T he analytics user ID is specific to Mixpanel and does not permit Flow CLI maintainers to, for exxample, track you across websites you visit. For more information about the data collected, see Mixpanel's data collection page in the `Ingestion API` section of https://help.mixpanel.com/hc/en-us/articles/115004613766-Default-Properties-Collected-by-Mixpanel. Although Mixpanel's page above mentions that geolocation properties are recorded by default, we have turned off geolocation data reporting to Mixpanel. --- # File: /md/build/tools/flow-cli/dependency-manager.md --- title: Dependency Manager sidebar_label: Dependency Manager description: Dependency Manager for the Flow Blockchain. sidebar_position: 11 --- The Dependency Manager in the Flow CLI streamlines the development process when you use contracts from outside your project. It eliminates the manual tasks of copying, pasting, and updating contracts that you use or build upon, such as core contracts or any other ecosystem contracts. For example, if you wanted to build a new application with the `FlowToken` contract, you would traditionally need to locate the contract on the network, copy it into your local project, and add it to your `flow.json` file. You would repeat this process for each import (dependency) it relies on, like the `NonFungibleToken` contract. The Dependency Manager simplifies this process with a few straightforward commands. ## `install` The `install` command allows you to install dependencies and all their sub-dependencies with ease. You can use it to install specific dependencies or to install all dependencies listed in your `flow.json`. ### Install specific dependencies If you know the address and name of the contract you want to install (which can often be found via the [Contract Browser]), you can use the following syntax: ```bash flow dependencies install testnet://7e60df042a9c0868.FlowToken ``` In this command, the string `testnet://7e60df042a9c0868.FlowToken` used as the `source` in the `flow.json` is broken down as: - **Network:** `testnet` - **Address:** `7e60df042a9c0868` - **Contract Name:** `FlowToken` This specifies the remote source of the contract on the network that will be used as the source of truth. ### Install core contracts with simplified syntax For core contracts (and [DeFiActions]), you can use a simplified syntax that defaults to the Flow Mainnet: ```bash flow dependencies install FlowToken ``` This command is functionally equivalent to: ```bash flow dependencies install mainnet://1654653399040a61.FlowToken ``` ### Install multiple dependencies You can also install multiple dependencies at once. For example: ```bash flow dependencies install testnet://7e60df042a9c0868.FlowToken NonFungibleToken ``` This command installs both the `FlowToken` contract from Testnet and the `NonFungibleToken` contract from Mainnet. ### Install all dependencies from an address Sometimes you may want to install all the contracts that exist at a particular address, rather than specify each contract name individually. To do this, omit the contract name in the dependency source. For example: ```bash flow dependencies install testnet://7e60df042a9c0868 ``` This tells the Dependency Manager to fetch every contract deployed at the `7e60df042a9c0868` address on `testnet` and store them in your `imports` folder. You can later import these contracts in your code or use them in your deployments as needed. ### Install dependencies from `flow.json` If you run the `install` command but don't specify any dependencies, it will install all the dependencies listed in your `flow.json` file and ensure they are up to date: ```bash flow dependencies install ``` This command checks all the dependencies specified in your `flow.json`, installs them, and updates them if there have been changes on the network. ### Example `flow.json` entry After installation, your `flow.json` might include an entry like: ```json { "dependencies": { "FlowToken": { "source": "testnet://7e60df042a9c0868.FlowToken", "aliases": { "emulator": "0ae53cb6e3f42a79" } } } } ``` ### Other things to note - After installation, a local folder named `imports` will be created. We recommend that you add this folder to your `.gitignore`, as it stores your dependencies locally. - If the contracts change on the network, the Dependency Manager will prompt you to update the local dependencies in your `imports` folder. The hash saved in the dependency object is used for this check, so don't remove it. - Dependencies function just like local contracts. You can add them to [`deployments` in your `flow.json`](./deployment/deploy-project-contracts.md) and run `flow project deploy`. You can also import them in your scripts, transactions, and contracts (for example, `import "FlowToken"`). - Core contract aliases are automatically added for you across all networks. ## `discover` The `discover` command helps you interactively find and install core contracts for your project. Core contracts are standard smart contracts maintained by the Flow Foundation and are commonly used across the Flow ecosystem (learn more about core contracts [here]). To use the `discover` command, run: ```bash flow dependencies discover ``` You'll be presented with a list of available core contracts to install: ```shell Select any core contracts you would like to install or skip to continue. Use arrow keys to navigate, space to select, enter to confirm or skip, q to quit: > [ ] FlowEpoch [ ] FlowIDTableStaking [ ] FlowClusterQC [ ] FlowDKG [ ] FlowServiceAccount [ ] NodeVersionBeacon [ ] RandomBeaconHistory [ ] FlowStorageFees [ ] FlowFees [ ] FungibleTokenSwitchboard [ ] EVM [ ] Crypto ``` After selecting the contracts, press `enter` to confirm. The selected contracts will be added to your `flow.json` file and will be accessible in your project. ## `list` The `list` command displays all the dependencies currently installed in your project. This is useful to review what contracts your project depends on and their sources. To list your installed dependencies, run: ```bash flow dependencies list ``` This command will show you all the dependencies from your `flow.json` file along with their source information, which helps you keep track of what external contracts your project uses. [Contract Browser]: https://contractbrowser.com/ [DeFiActions]: https://github.com/onflow/FlowActions/tree/main?tab=readme-ov-file#deployments [here]: ../../cadence/core-contracts/index.md --- # File: /md/build/tools/flow-cli/deployment/deploy-project-contracts.md --- title: Deploy a Project description: How to deploy Flow project contracts with the CLI sidebar_position: 3 --- ```shell flow project deploy ``` This command automatically deploys your project's contracts based on the configuration defined in your `flow.json` file. :::info Use Flow CLI commands to configure your project rather than manually edit `flow.json`. Before you use this command, read about how to [configure project contracts and deployment targets] with CLI commands. ::: ## Example usage ```shell > flow project deploy --network=testnet Deploying 2 contracts for accounts: my-testnet-account NonFungibleToken -> 0x8910590293346ec4 KittyItems -> 0x8910590293346ec4 ✨ All contracts deployed successfully ``` :::info The `flow.json` configuration shown below is created automatically when you use CLI commands. You should use `flow config add contract` and `flow config add deployment` to configure your project rather than manually edit the file. See [Add Project Contracts] for details. ::: Your `flow.json` file might look something like this: ```json { ... "contracts": { "NonFungibleToken": "./cadence/contracts/NonFungibleToken.cdc", "KittyItems": "./cadence/contracts/KittyItems.cdc" }, "deployments": { "testnet": { "my-testnet-account": ["KittyItems", "NonFungibleToken"] } }, ... } ``` Here's a sketch of the contract source files: ```cadence NonFungibleToken.cdc access(all) contract NonFungibleToken { // ... } ``` ```cadence KittyItems.cdc import "NonFungibleToken" access(all) contract KittyItems { // ... } ``` ## Initialization arguments To deploy contracts that take initialization arguments, you must add those arguments to the deployment configuration. :::info For basic deployments, use `flow config add deployment` to configure your contracts. Initialization arguments are an advanced feature that may require you to manually edit `flow.json` after the basic deployment is configured with CLI commands. ::: You can specify each deployment as an object that contains `name` and `args` keys that specify arguments to be used during the deployment. Example: ```json { "deployments": { "testnet": { "my-testnet-account": [ "NonFungibleToken", { "name": "Foo", "args": [ { "type": "String", "value": "Hello World" }, { "type": "UInt32", "value": "10" } ] } ] } } } ``` :::danger ⚠️ **Never** put raw private keys in `flow.json`. Always use `.pkey` files for key storage. Before you proceed, we recommend that you read the [Flow CLI security guidelines] to learn about the best practices for private key storage. ## Dependency resolution The `deploy` command attempts to resolve the import statements in all contracts being deployed. After the dependencies are found, the CLI will deploy the contracts in a deterministic order such that no contract is deployed until all of its dependencies are deployed. The command will return an error if no such ordering exists due to one or more cyclic dependencies. In the example above, `NonFungibleToken` will always be deployed before `KittyItems` since `KittyItems` imports `NonFungibleToken`. ## Address replacement After it resolves all dependencies, the `deploy` command rewrites each contract so that its dependencies are imported from their _target addresses_ rather than their source file location. The rewritten versions are then deployed to their respective targets, which leaves the original contract files unchanged. ### Contracts that import from other contracts In the example above, the `KittyItems` contract would be rewritten like this: ```cadence KittyItems.cdc import NonFungibleToken from 0xf8d6e0586b0a20c7 access(all) contract KittyItems { // ... } ``` ### Contracts that import from dependencies When your contracts import from the `dependencies` section, the deploy command uses the network-specific aliases defined in those dependencies. **Example `flow.json` with dependencies:** ```json { "contracts": { "ExampleConnectors": { "source": "cadence/contracts/ExampleConnectors.cdc", "aliases": { "testing": "0000000000000007" } } }, "dependencies": { "FlowToken": { "source": "mainnet://1654653399040a61.FlowToken", "hash": "cefb25fd19d9fc80ce02896267eb6157a6b0df7b1935caa8641421fe34c0e67a", "aliases": { "emulator": "0ae53cb6e3f42a79", "mainnet": "1654653399040a61", "testnet": "7e60df042a9c0868" } }, "FungibleToken": { "source": "mainnet://f233dcee88fe0abe.FungibleToken", "hash": "23c1159cf99b2b039b6b868d782d57ae39b8d784045d81597f100a4782f0285b", "aliases": { "emulator": "ee82856bf20e2aa6", "mainnet": "f233dcee88fe0abe", "testnet": "9a0766d93b6608b7" } } }, "deployments": { "testnet": { "testnet-account": ["ExampleConnectors"] } } } ``` **Original contract source:** ```cadence ExampleConnectors.cdc import "FungibleToken" import "FlowToken" access(all) contract ExampleConnectors { // ... } ``` **Rewritten for testnet deployment:** ```cadence ExampleConnectors.cdc import FungibleToken from 0x9a0766d93b6608b7 import FlowToken from 0x7e60df042a9c0868 access(all) contract ExampleConnectors { // ... } ``` **Rewritten for mainnet deployment:** ```cadence ExampleConnectors.cdc import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 access(all) contract ExampleConnectors { // ... } ``` The deploy command automatically uses the addresses from the `dependencies` section's aliases for the target network. Notice how the addresses change based on the network—testnet uses `0x9a0766d93b6608b7` for `FungibleToken`, while mainnet uses `0xf233dcee88fe0abe`. Contracts in the `dependencies` section are not deployed—they're assumed to already exist on the network at the addresses specified in their aliases. ## Merge multiple configuration files You can use the `-f` flag multiple times to merge several configuration files. If there is an overlap in any of the fields in the configuration between two or more configuration files, the value of the overlapped field in the configuration that results will come from the configuration file that is on the further right order in the list of configuration files specified in the `-f` flag. :::danger **Never** put raw private keys in `flow.json`. Always use `.pkey` files for key storage. ::: :::info Use `flow config add account` to create accounts in your main `flow.json` file. The merging feature is useful to separate sensitive account information into a separate file that you can exclude from version control. ::: **Example usage:** ```bash flow project deploy -f flow.json -f private.json ``` **Example configuration files:** ```json flow.json { "accounts": { "admin-account": { "address": "f8d6e0586b0a20c7", "key": { "type": "file", "location": "admin-account.pkey" } }, "test-account": { "address": "f8d6e0586b0a20c8", "key": { "type": "file", "location": "test-account.pkey" } } } } ``` ```json private.json { "accounts": { "admin-account": { "address": "f1d6e0586b0a20c7", "key": { "type": "file", "location": "admin-account-private.pkey" } } } } ``` When you use multiple configuration files with overlapping fields, the rightmost file takes precedence. In this example, the merged configuration that results will be: ```json { "accounts": { "admin-account": { "address": "f1d6e0586b0a20c7", "key": { "type": "file", "location": "admin-account-private.pkey" } }, "test-account": { "address": "f8d6e0586b0a20c8", "key": { "type": "file", "location": "test-account.pkey" } } } } ``` **Security best practice:** Ensure `.pkey` files are added to `.gitignore` to prevent accidentally committing private keys to version control. ## Flags ### Allow updates - Flag: `--update` - Valid inputs: `true`, `false` - Default: `false` Indicate whether to overwrite and upgrade current contracts. The system will only overwrite contracts that are different from current contracts. ### Show update diff - Flag: `--show-diff` - Valid inputs: `true`, `false` - Default: `false` Shows a diff to approve before an update between deployed contract and new contract updates. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when executing the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [configure project contracts and deployment targets]: ./project-contracts.md [Add Project Contracts]: ./project-contracts.md [Flow CLI security guidelines]: ../flow.json/security.md --- # File: /md/build/tools/flow-cli/deployment/project-contracts.md --- title: Add Project Contracts description: How to define and configure Cadence contracts for Flow projects using Flow CLI commands sidebar_position: 2 --- ## Generate a Contract Create a new contract file with the Flow CLI: ```bash flow generate contract Foo ``` This command creates `cadence/contracts/Foo.cdc` with a basic contract template and automatically adds it to your `flow.json` configuration. ## Add a contract to configuration If you have a contract file, add it to your project configuration with the CLI: ```bash flow config add contract ``` Follow the interactive prompts: 1. **Contract name**: Enter the contract name (for exxample, `Foo`) 2. **Contract filename**: Enter the path to your contract file (for example, `./cadence/contracts/Foo.cdc`) 3. **Add aliases**: Optionally add network aliases for dependencies You can also use flags to specify all details at once: ```bash flow config add contract \ --name Foo \ --filename ./cadence/contracts/Foo.cdc ``` **What gets added to `flow.json`:** ```json { "contracts": { "Foo": "./cadence/contracts/Foo.cdc" } } ``` ## Configure contract deployment targets After a contract is added to your configuration, configure deployment targets with the CLI: ```bash flow config add deployment ``` Follow the interactive prompts: 1. **Network**: Select the network (for example, `testnet`, `mainnet`, `emulator`) 2. **Account**: Select the account to deploy to (for example, `my-testnet-account`) 3. **Contract**: Select the contract to deploy (for example, `Foo`) 4. **Deploy more contracts**: Choose `yes` to add additional contracts to the same deployment You can also use flags to specify all details: ```bash flow config add deployment \ --network testnet \ --account my-testnet-account \ --contract Foo ``` **What gets added to `flow.json`:** ```json { "deployments": { "testnet": { "my-testnet-account": ["Foo"] } } } ``` ## Add multiple contracts to a deployment To deploy multiple contracts to the same account, run the deployment configuration command multiple times or use the interactive prompt to add more contracts: ```bash flow config add deployment --network testnet --account my-testnet-account --contract Bar ``` This adds `Bar` to the existing deployment: ```json { "deployments": { "testnet": { "my-testnet-account": ["Foo", "Bar"] } } } ``` ## Remove contracts and deployments Remove contracts or deployments using the CLI: ```bash # Remove a contract from configuration flow config remove contract Foo # Remove a contract from a specific deployment flow config remove deployment testnet my-testnet-account Foo ``` ## Best Practices - **Use CLI commands**: Always use `flow config add` and `flow config remove` rather than manually edit `flow.json` - **Generate contracts**: Use `flow generate contract` to create new contracts with proper structure - **Verify configuration**: Use `flow accounts list` and check your `flow.json` to verify your configuration - **Network-specific deployments**: Configure separate deployments for each network (emulator, testnet, mainnet) For more information, see [Manage Configuration] and [Production Deployment]. [Manage Configuration]: ../flow.json/manage-configuration.md [Production Deployment]: ../../../../blockchain-development-tutorials/cadence/getting-started/production-deployment.md --- # File: /md/build/tools/flow-cli/flix.md --- title: Flow Interaction Templates (FLIX) sidebar_label: Flow Interaction Templates (FLIX) description: Flow Interaction Templates (FLIX) via the CLI sidebar_position: 15 --- FLIX helps developers reuse current Cadence transactions and scripts to easily integrate with current Cadence smart contracts. Get more information about [Flow Interaction Templates] (FLIX). ## Introduction The Flow CLI provides a `flix` command with a few sub commands `execute` and `package`. [(FLIX)] are a standard for Cadence scripts and transaction distribution, and metadata in a way that is consumable by tooling and wallets. Auditors in the ecosystem can audit FLIX for correctness and safety. ```shell >flow flix execute, generate, package Usage: flow flix [command] Available Commands: execute execute FLIX template with a given id, name, local filename, or url generate generate FLIX json template given local Cadence filename package package file for FLIX template fcl-js is default ``` ### Execute The Flow CLI provides a `flix` command to `execute` FLIX. The Cadence that's executed in the FLIX can be a transaction or script. ```shell flow flix execute [ ...] [flags] ``` :::warning A FLIX template might only support testnet or mainnet. Generally, emulator is not supported. This can be the case if the FLIX template relies on contract dependencies. ::: Queries can be a FLIX `id`, `name`, `url` or `path` to a local FLIX file. ### Execute use ```shell # Execute a FLIX transaction by name on Testnet flow flix execute transfer-flow 5.0 "0x123" --network testnet --signer "testnet-account" ``` ```shell # Execute a FLIX script by id on Testnet flow flix execute bd10ab0bf472e6b58ecc0398e9b3d1bd58a4205f14a7099c52c0640d9589295f --network testnet ``` ```shell # Execute a local FLIX script by path on Testnet flow flix execute ./multiply.template.json 2 3 --network testnet ``` The Flow CLI provides a `flix` command to `package` up generated plain and simple JavaScript. This JavaScript uses FCL (Flow Client Library) to call the cadence the FLIX is based on. :::info Currently, `flix package` command only supports generating FCL-specific JavaScript and TypeScirpt. There are plans to support other languages like golang. ::: ```shell flow flix package [flags] ``` ### Generate Generate FLIX json file. This command will take in a Cadence file and produce a FLIX json file. There are two ways to provide metadata to populate the FLIX json structure. - Use `--pre-fill` flag to pass in a pre populated FLIX json structure - Use `--exclude-networks` flag to specify excluded networks when you generate a FLIX template. For example, `--exclude-networks testnet,mainnet` :::warning When you generate a FLIX template, make sure all contract dependencies have been deployed to the supported networks. Add any aliases to your flow.json that you need to populate dependencies. Verify all dependencies were populated after you generate the FLIX template. ::: ### Generate use ```shell # Generate FLIX json file using cadence transaction or script, this example is not using a prefilled json file so will not have associated message metadata flow flix generate cadence/transactions/update-helloworld.cdc --save cadence/templates/update-helloworld.template.json ``` Example of Cadence simple, no metadata associated: ```cadence import "HelloWorld" access(all) fun main(): String { return HelloWorld.greeting } ``` ### Cadence doc pragma: It's recommended to use pragma to set the metadata for the script or transaction. View more information about [Cadence Doc Pragma FLIP]. A pragma is short for "pragmatic information", it's special instructions to convey information to a processor in this case the utility that generates FLIX. ```cadence import "HelloWorld" #interaction ( version: "1.1.0", title: "Update Greeting", description: "Update the greeting on the HelloWorld contract", language: "en-US", ) transaction(greeting: String) { prepare(acct: &Account) { log(acct.address) } execute { HelloWorld.updateGreeting(newGreeting: greeting) } } ``` :::info Cadence v0.42.7 supports additional Cadence pragma functionality that FlIX utility can use to generate FLIX. It will support parameters "title" and "description" ::: The json metadata that results is extracted from Cadence Doc Pragma. ```json { "f_type": "InteractionTemplate", "f_version": "1.1.0", "id": "", "data": { "type": "transaction", "interface": "", "messages": [ { "key": "title", "i18n": [ { "tag": "en-US", "translation": "Update Greeting" } ] }, { "key": "description", "i18n": [ { "tag": "en-US", "translation": "Update the greeting on the HelloWorld contract" } ] } ], "cadence": {}, "dependencies": [], "parameters": [ { "label": "greeting", "index": 0, "type": "String", "messages": [] } ] } } ``` Example of when you use a prefilled FLIX json file. No need to use Cadence pragma when you use a prefilled FLIX json file. This method separates FLIX specific information from the transaction or script Cadence. Use the `flow flix generate` command: ```shell flow flix generate cadence/scripts/read-helloworld.cdc --pre-fill cadence/templates/read-helloworld.prefill.json --save cadence/templates/read-helloworld.template.json ``` With a pre-filled FLIX template, the cadence can be simple but no metadata accompanies it. ```cadence import "HelloWorld" access(all) fun main(): String { return HelloWorld.greeting } ``` Example of json prefill file with message metadata: ```json { "f_type": "InteractionTemplate", "f_version": "1.1.0", "id": "", "data": { "type": "script", "interface": "", "messages": [ { "key": "title", "i18n": [ { "tag": "en-US", "translation": "Get Greeting" } ] }, { "key": "description", "i18n": [ { "tag": "en-US", "translation": "Call HelloWorld contract to get greeting" } ] } ] } } ``` The FLIX json file that results after generation: ```json { "f_type": "InteractionTemplate", "f_version": "1.1.0", "id": "fd9abd34f51741401473eb1cf676b105fed28b50b86220a1619e50d4f80b0be1", "data": { "type": "script", "interface": "", "messages": [ { "key": "title", "i18n": [ { "tag": "en-US", "translation": "Get Greeting" } ] }, { "key": "description", "i18n": [ { "tag": "en-US", "translation": "Call HelloWorld contract to get greeting" } ] } ], "cadence": { "body": "import \"HelloWorld\"\naccess(all) fun main(): String {\n return HelloWorld.greeting\n}\n", "network_pins": [ { "network": "testnet", "pin_self": "41c4c25562d467c534dc92baba92e0c9ab207628731ee4eb4e883425abda692c" } ] }, "dependencies": [ { "contracts": [ { "contract": "HelloWorld", "networks": [ { "network": "testnet", "address": "0xe15193734357cf5c", "dependency_pin_block_height": 137864533, "dependency_pin": { "pin": "aad46badcab3caaeb4f0435625f43e15bb4c15b1d55c74a89e6f04850c745858", "pin_self": "a06b3cd29330a3c22df3ac2383653e89c249c5e773fd4bbee73c45ea10294b97", "pin_contract_name": "HelloWorld", "pin_contract_address": "0xe15193734357cf5c", "imports": [] } } ] } ] } ], "parameters": null } } ``` ### Package Queries can be a FLIX `url` or `path` to a local FLIX file. This command leverages [FCL] which will execute FLIX cadence code. Package files can be generated in JavaScript or TypeScript. :::warning Currently, package doesn't support `id`, `name` flix query. ::: ### Package use ```shell # Generate packaged code that leverages FCL to call the Cadence transaction code, `--save` flag will save the output to a specific file flow flix package transfer-flow --save ./package/transfer-flow.js ``` ```shell # Generate package code for a FLIX script using id, since there is no saving file, the result will display in terminal flow flix package bd10ab0bf472e6b58ecc0398e9b3d1bd58a4205f14a7099c52c0640d9589295f ``` ```shell # Generate package code using local template file to save in a local file flow flix package ./multiply.template.json --save ./multiply.js ``` ```shell # Generate package code using local template file to save in a local typescript file flow flix package ./multiply.template.json --lang ts --save ./multiply.ts ``` ### Example package output ```shell flow flix package https://flix.flow.com/v1/templates\?name\=transfer-flow ``` ```javascript /** This binding file was auto generated based on FLIX template v1.0.0. Changes to this file might get overwritten. Note fcl version 1.3.0 or higher is required to use templates. **/ import * as fcl from '@onflow/fcl'; const flixTemplate = 'https://flix.flow.com/v1/templates?name=transfer-flow'; /** * Transfer tokens from one account to another * @param {Object} Parameters - parameters for the cadence * @param {string} Parameters.amount - The amount of FLOW tokens to send: UFix64 * @param {string} Parameters.to - The Flow account the tokens will go to: Address * @returns {Promise} - returns a promise which resolves to the transaction id */ export async function transferTokens({ amount, to }) { const transactionId = await fcl.mutate({ template: flixTemplate, args: (arg, t) => [arg(amount, t.UFix64), arg(to, t.Address)], }); return transactionId; } ``` ```shell # Generate TypeScript version of package file flow flix package https://flix.flow.com/v1/templates?name=transfer-flow --lang ts ``` ```typescript /** This binding file was auto generated based on FLIX template v1.1.0. Changes to this file might get overwritten. Note fcl version 1.9.0 or higher is required to use templates. **/ import * as fcl from '@onflow/fcl'; const flixTemplate = 'https://flix.flow.com/v1/templates?name=transfer-flow'; interface TransferTokensParams { amount: string; // The amount of FLOW tokens to send to: string; // The Flow account the tokens will go to } /** * transferTokens: Transfer tokens from one account to another * @param string amount - The amount of FLOW tokens to send * @param string to - The Flow account the tokens will go to * @returns {Promise} - Returns a promise that resolves to the transaction ID */ export async function transferTokens({ amount, to, }: TransferTokensParams): Promise { const transactionId = await fcl.mutate({ template: flixTemplate, args: (arg, t) => [arg(amount, t.UFix64), arg(to, t.Address)], }); return transactionId; } ``` :::warning Notice that fcl v1.9.0 is needed to use FLIX v1.1 templates ::: ## Resources To find out more about FLIX, see the [read the FLIP]. For a list of all templates, check out the [FLIX template repository]. To generate a FLIX, see the [FLIX CLI readme]. ## Arguments - Name: `argument` - Valid input: valid [FLIX] Input argument value that match types which correspond in the source code and passed in the same order. To pass a `nil` value to optional arguments, you can execute the flow FLIX execute script like this: `flow flix execute template.json nil`. ## Flags ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow flix execute template.script.json '[{"type": "String", "value": "Hello World"}]'` Arguments passed to the Cadence script in the Cadence JSON format. Cadence JSON format contains `type` and `value` keys and is [documented here]. ## Pre Fill - Flag: `--pre-fill` - Valid inputs: a json file in the FLIX json structure [FLIX json format] ## Block Height - Flag: `--block-height` - Valid inputs: a block height number ## Block ID - Flag: `--block-id` - Valid inputs: a block ID ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used to sign the transaction. ### Proposer - Flag: `--proposer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used as the proposer in the transaction. ### Payer - Flag: `--payer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used as the payer in the transaction. ### Authorizer - Flag: `--authorizer` - Valid inputs: the name of a single or multiple comma-separated accounts defined in the configuration (`flow.json`) Specify the name of the account(s) that will be used as authorizer(s) in the transaction. If you want to provide multiple authorizers, use commas to separate them (for example, `alice,bob`) ### Compute limit - Flag: `--compute-limit` - Valid inputs: an integer greater than zero. - Default: `1000` Specify the compute unit (gas) limit for this transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want to save the result. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [Flow Interaction Templates]: ../../cadence/advanced-concepts/flix.md [(FLIX)]: https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md [Cadence Doc Pragma FLIP]: https://github.com/onflow/flips/blob/main/application/20230406-interaction-template-cadence-doc.md [FCL]: ../clients/fcl-js/ [read the FLIP]: https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md [FLIX template repository]: https://github.com/onflow/flow-interaction-template-service/tree/master/templates [FLIX CLI readme]: https://github.com/onflow/flow-interaction-template-tools/tree/master/cli [FLIX]: https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec [FLIX json format]: https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md --- # File: /md/build/tools/flow-cli/flow.json/configuration.md --- title: Configuration description: Understanding and configuring your Flow project with flow.json sidebar_position: 2 --- The `flow.json` file is the central configuration file for your Flow project. It tells the Flow CLI how to interact with networks, manage accounts, deploy contracts, and organize your project structure. ## Quick Start When you run `flow init`, a basic `flow.json` file is created for you: ```json { "networks": { "emulator": "127.0.0.1:3569", "mainnet": "access.mainnet.nodes.onflow.org:9000", "testnet": "access.devnet.nodes.onflow.org:9000" }, "accounts": { "emulator-account": { "address": "f8d6e0586b0a20c7", "key": "ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee" } }, "deployments": {}, "contracts": {} } ``` This gives you everything you need to get started with local development. As your project grows, you'll add more configuration to support different networks and deployment targets. ## Configuration Sections ### Networks The `networks` section defines which Flow networks your project can connect to. ```json "networks": { "emulator": "127.0.0.1:3569", "mainnet": "access.mainnet.nodes.onflow.org:9000", "testnet": "access.devnet.nodes.onflow.org:9000" } ``` **Common Networks:** - `emulator`: Your local development environment - `testnet`: Flow's test network for development and testing - `mainnet`: Flow's production network **Secure Connections:** For enhanced security, you can specify network keys: ```json "networks": { "testnetSecure": { "host": "access-001.devnet30.nodes.onflow.org:9001", "key": "ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d" } } ``` **Fork Networks:** Fork networks allow you to test against a local emulator that mirrors mainnet or testnet state. When you run `flow emulator --fork mainnet`, the CLI automatically creates a `mainnet-fork` network configuration that inherits contract aliases from the parent network: ```json "networks": { "mainnet-fork": { "host": "127.0.0.1:3569", "fork": "mainnet" } } ``` The `fork` property tells the CLI to inherit all contract aliases from the specified network (e.g., `mainnet`), so you don't need to manually duplicate aliases for forked networks. Learn more: [Fork Testing Overview](../fork-testing.md) ### Accounts The `accounts` section defines the accounts you can use for transactions and deployments. #### Simple account format ```json "accounts": { "my-account": { "address": "f8d6e0586b0a20c7", "key": "ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee" } } ``` #### Advanced account format For more control over key management: ```json "accounts": { "my-account": { "address": "f8d6e0586b0a20c7", "key": { "type": "hex", "index": 0, "signatureAlgorithm": "ECDSA_P256", "hashAlgorithm": "SHA3_256", "privateKey": "ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee" } } } ``` **Key Types:** - `hex`: Standard hex-encoded private key - `file`: Read key from a separate file - `bip44`: Derive from mnemonic phrase - `google-kms`: Use Google Cloud KMS **File-Based Keys:** For better security, you can store private keys in separate files: ```json "accounts": { "admin-account": { "address": "f8d6e0586b0a20c7", "key": { "type": "file", "location": "./keys/admin.key" } } } ``` The key file should contain only the hex-encoded private key (for example, `ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee`). **Special Address Values:** - `"service"`: Use the default service account (emulator only) ### Contracts The `contracts` section maps contract names to their source files. #### Simple contract format ```json "contracts": { "MyContract": "./cadence/contracts/MyContract.cdc", "AnotherContract": "./cadence/contracts/AnotherContract.cdc" } ``` #### Advanced contract format with aliases Use aliases when contracts are already deployed on specific networks: ```json "contracts": { "FungibleToken": { "source": "./cadence/contracts/FungibleToken.cdc", "aliases": { "testnet": "9a0766d93b6608b7", "mainnet": "f233dcee88fe0abe" } } } ``` **When to Use Aliases:** - For core contracts already deployed on mainnet/testnet - To avoid redeploying dependencies - To use the official versions of common contracts #### Cadence import aliasing When you deploy the same contract to multiple addresses with different names, use the `canonical` field to reference the original contract. This allows you to import multiple instances of the same contract with different identifiers. ```json "contracts": { "FUSD": { "source": "./contracts/FUSD.cdc", "aliases": { "testnet": "0x9a0766d93b6608b7" } }, "FUSD1": { "source": "./contracts/FUSD.cdc", "aliases": { "testnet": "0xe223d8a629e49c68" }, "canonical": "FUSD" } } ``` Flow CLI automatically transforms imports for aliased contracts: ```cadence import "FUSD" import "FUSD1" ``` Becomes: ```cadence import FUSD from 0x9a0766d93b6608b7 import FUSD as FUSD1 from 0xe223d8a629e49c68 ``` ### Deployments The `deployments` section defines which contracts get deployed to which accounts on which networks. ```json "deployments": { "emulator": { "emulator-account": ["MyContract", "AnotherContract"] }, "testnet": { "my-testnet-account": ["MyContract"] } } ``` **Format:** `"NETWORK": { "ACCOUNT": ["CONTRACT1", "CONTRACT2"] }` **Important Notes:** - Don't deploy contracts that have aliases defined for that network - Contracts are deployed in dependency order automatically - You can deploy the same contract to multiple accounts (but not in the same deploy command) ### Emulators Customize emulator settings (optional): ```json "emulators": { "custom-emulator": { "port": 3600, "serviceAccount": "emulator-account" } } ``` ## Complete example Here's a complete `flow.json` for a project with multiple contracts and networks: ```json { "networks": { "emulator": "127.0.0.1:3569", "testnet": "access.devnet.nodes.onflow.org:9000", "mainnet": "access.mainnet.nodes.onflow.org:9000" }, "accounts": { "emulator-account": { "address": "f8d6e0586b0a20c7", "key": "ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee" }, "testnet-account": { "address": "3ae53cb6e3f42a79", "key": "12332967fd2bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111" } }, "contracts": { "FungibleToken": { "source": "./cadence/contracts/FungibleToken.cdc", "aliases": { "testnet": "9a0766d93b6608b7", "mainnet": "f233dcee88fe0abe" } }, "MyToken": "./cadence/contracts/MyToken.cdc", "MyNFT": "./cadence/contracts/MyNFT.cdc" }, "deployments": { "emulator": { "emulator-account": ["FungibleToken", "MyToken", "MyNFT"] }, "testnet": { "testnet-account": ["MyToken", "MyNFT"] } } } ``` ## Manage configuration Rather than edit `flow.json` manually, use the CLI commands: ```bash # Add an account flow config add account # Add a contract flow config add contract # Add a deployment flow config add deployment # Remove configuration flow config remove account my-account ``` ## Best Practices 1. **Use CLI commands** when possible instead of manual editing. 2. **Keep private keys secure** - consider file-based keys for production. 3. **Use aliases** for core contracts to avoid redeployment. 4. **Test on emulator first** before you deploy to testnet. 5. **Use different accounts** for different networks. 6. **Backup your configuration** before you make major changes. ## Related Commands - [`flow init`] - Initialize a new project - [`flow config add`] - Add configuration items - [`flow project deploy`] - Deploy contracts - [`flow accounts create`] - Create new accounts [`flow init`]: ./initialize-configuration.md [`flow config add`]: ./manage-configuration.md [`flow project deploy`]: ../deployment/deploy-project-contracts.md [`flow accounts create`]: ../accounts/create-accounts.md --- # File: /md/build/tools/flow-cli/flow.json/initialize-configuration.md --- title: Initialize Configuration description: How to initialize Flow configuration using CLI sidebar_position: 1 --- The `flow init` command creates a new Flow project with a basic `flow.json` configuration file. This is the first step to set up any Flow project. ## Basic usage ```shell flow init ``` This command will: - Create a new `flow.json` configuration file. - Set up default networks (emulator, testnet, mainnet). - Create an emulator service account. - Generate a basic project structure with `cadence/` directories. - Give you options for project scaffolding. ## Project structure After you run `flow init`, you'll have: ``` my-project/ ├── flow.json ├── emulator-account.pkey └── cadence/ ├── contracts/ ├── scripts/ ├── transactions/ └── tests/ ``` ## Configuration only If you only want to generate the `flow.json` file, but not create the full project structure, use the `--config-only` flag: ```shell flow init --config-only ``` This is useful when: - You already have a project structure. - You want to add Flow configuration to a current project. - You want to set up configuration for a specific environment. ## Global configuration You can create a global `flow.json` file that applies to all Flow projects on your system: ```shell flow init --global ``` **Global configuration locations:** - **macOS/Linux:** `~/flow.json` - **Windows:** `C:\Users\$USER\flow.json` **Priority order:** 1. Local `flow.json` (highest priority) 2. Global `flow.json` (lowest priority) Local configuration files will override global settings for properties that overlap. ## Error handling If a `flow.json` file already exists, you'll see this error: ```shell ❌ Command Error: configuration already exists at: flow.json ``` **Solutions:** - Delete the current `flow.json` file first. - Initialize in a different directory. - Use `--config-only` to create a new config in a different location. ## Flags ### Configuration only ```shell flow init --config-only ``` Creates only the `flow.json` file without project structure. ### Global flags The following global flags are also available: ```shell # Log level flow init --log debug # Output format flow init --output json # Approve prompts automatically flow init --yes ``` **Available log levels:** `debug`, `info`, `error`, `none` ## Next Steps After you initialize the configuration: 1. **Review the generated `flow.json`** - Understand the default setup 2. **Add your contracts** - Use `flow config add contract` 3. **Create accounts** - Use `flow accounts create` or `flow config add account` 4. **Configure deployments** - Use `flow config add deployment` 5. **Start developing** - Run `flow emulator start` ## Related Commands - [`flow config add`] - Add configuration items - [`flow accounts create`] - Create new accounts - [`flow project deploy`] - Deploy contracts [`flow config add`]: ./manage-configuration.md [`flow project deploy`]: ../deployment/deploy-project-contracts.md [`flow accounts create`]: ../accounts/create-accounts.md --- # File: /md/build/tools/flow-cli/flow.json/manage-configuration.md --- title: Manage Configuration description: How to configure the Flow CLI using config commands sidebar_position: 3 --- Rather than manually edit `flow.json`, use the Flow CLI's `config` commands to add, remove, and manage your project configuration. These commands provide validation and ensure your configuration is properly formatted. ## Basic commands ```shell # Add configuration items flow config add # Remove configuration items flow config remove ``` ## Adding configuration ### Add an account ```shell flow config add account ``` You can use flags to specify account details: ```shell flow config add account \ --name my-testnet-account \ --address f8d6e0586b0a20c7 \ --private-key ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee \ --sig-algo ECDSA_P256 \ --hash-algo SHA3_256 \ --key-index 0 ``` **Available flags:** - `--name`: Account name - `--address`: Account address - `--private-key`: Private key - `--sig-algo`: Signature algorithm (default: ECDSA_P256) - `--hash-algo`: Hash algorithm (default: SHA3_256) - `--key-index`: Key index (default: 0) **What gets added to `flow.json`:** ```json "accounts": { "my-testnet-account": { "address": "f8d6e0586b0a20c7", "key": "ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee" } } ``` ### Add a contract ```shell flow config add contract ``` You can use flags to specify contract details: ```shell flow config add contract \ --name MyToken \ --filename ./cadence/contracts/MyToken.cdc \ --testnet-alias 9a0766d93b6608b7 \ --mainnet-alias f233dcee88fe0abe ``` **Available flags:** - `--name`: Contract name - `--filename`: Path to contract source file - `--testnet-alias`: Address for testnet alias - `--mainnet-alias`: Address for mainnet alias - `--emulator-alias`: Address for emulator alias **What gets added to `flow.json`:** ```json "contracts": { "MyToken": { "source": "./cadence/contracts/MyToken.cdc", "aliases": { "testnet": "9a0766d93b6608b7", "mainnet": "f233dcee88fe0abe" } } } ``` ### Add a network ```shell flow config add network ``` You can use flags to specify network details: ```shell flow config add network \ --name custom-testnet \ --host access-001.devnet30.nodes.onflow.org:9001 \ --network-key ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d ``` **Available flags:** - `--name`: Network name - `--host`: Flow Access API host address - `--network-key`: Network key for secure connections **What gets added to `flow.json`:** ```json "networks": { "custom-testnet": { "host": "access-001.devnet30.nodes.onflow.org:9001", "key": "ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d" } } ``` ### Add a deployment ```shell flow config add deployment ``` You can use flags to specify deployment details: ```shell flow config add deployment \ --network testnet \ --account my-testnet-account \ --contract MyToken ``` **Available flags:** - `--network`: Network name for deployment - `--account`: Account name for deployment - `--contract`: Contract name(s) to deploy (can specify multiple) **What gets added to `flow.json`:** ```json "deployments": { "testnet": { "my-testnet-account": ["MyToken"] } } ``` ## Remove configuration ### Remove an account ```shell flow config remove account my-testnet-account ``` ### Remove a contract ```shell flow config remove contract MyToken ``` ### Remove a network ```shell flow config remove network custom-testnet ``` ### Remove a deployment ```shell flow config remove deployment my-testnet-account testnet ``` :::info This removes all deployments for the specified account on the specified network. :: ## Configuration file management ### Use custom configuration files ```shell # Use a specific configuration file flow config add account --config-path ./config/flow.json # Use multiple configuration files (merged in order) flow config add account -f flow.json -f private.json ``` ### Configuration file priority When you use multiple configuration files with `-f` flag: 1. Files are merged from left to right. 2. Later files override earlier ones when properties overlap. 3. Non-overlapping properties are combined. **Example:** ```shell flow config add account -f flow.json -f private.json ``` If both files have an `admin-account`, the one from `private.json` will be used. ### Security best practices For better security, consider separate configuration files for sensitive data: **Main configuration file (`flow.json`):** ```json { "accounts": { "my-testnet-account": { "address": "3ae53cb6e3f42a79", "key": { "type": "file", "location": "./my-testnet-account.key" } } } } ``` **Private key file (`my-testnet-account.key`):** ``` 334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111 ``` **Private configuration file (`private.json`):** ```json { "accounts": { "my-testnet-account": { "address": "3ae53cb6e3f42a79", "key": "334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111" } } } ``` :::warning Always add private files to `.gitignore` to prevent committing sensitive data to source control. ::: ## Validation The `config add` command validates all inputs: - **Account addresses** must be valid Flow addresses (16-character hex). - **Private keys** must be valid hex-encoded keys. - **Contract sources** must point to current `.cdc` files. - **Network hosts** must be valid host:port combinations. - **Deployments** must reference current accounts and contracts. ## Best Practices 1. **Use CLI commands** instead of manual edits when possible. 2. **Validate your configuration** by running `flow config add` commands. 3. **Use descriptive names** for accounts and contracts. 4. **Keep sensitive data separate** with multiple config files. 5. **Test deployments** on emulator before adding to testnet and mainnet. ## Common use cases ### Set up a new project ```shell # Initialize project flow init # Add your contracts flow config add contract --name MyToken --filename ./cadence/contracts/MyToken.cdc flow config add contract --name MyNFT --filename ./cadence/contracts/MyNFT.cdc # Create accounts for different networks flow config add account --name emulator-account --address f8d6e0586b0a20c7 --private-key ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee flow config add account --name testnet-account --address 3ae53cb6e3f42a79 --private-key 12332967fd2bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111 # Configure deployments flow config add deployment --network emulator --account emulator-account --contract MyToken --contract MyNFT flow config add deployment --network testnet --account testnet-account --contract MyToken --contract MyNFT ``` ### Add to current project ```shell # Add new contract flow config add contract --name NewContract --filename ./cadence/contracts/NewContract.cdc # Add deployment for new contract flow config add deployment --network testnet --account testnet-account --contract NewContract ``` ### Manage multiple environments ```shell # Use separate config files for different environments flow config add account --name admin-account --address f8d6e0586b0a20c7 --private-key ae1b44c0f5e8f6992ef2348898a35e50a8b0b9684000da8b1dade1b3bcd6ebee -f flow.json flow config add account --name admin-account --address f1d6e0586b0a20c7 --private-key 3335dfdeb0ff03a7a73ef39788563b62c89adea67bbb21ab95e5f710bd1d40b7 -f private.json ``` ## Related commands - [`flow init`] - Initialize a new project - [`flow project deploy`] - Deploy contracts - [`flow accounts create`] - Create new accounts [`flow init`]: ./initialize-configuration.md [`flow project deploy`]: ../deployment/deploy-project-contracts.md [`flow accounts create`]: ../accounts/create-accounts.md --- # File: /md/build/tools/flow-cli/flow.json/security.md --- title: Security description: How to securely use Flow CLI and protect your private keys sidebar_position: 4 --- To manage accounts and private keys requires careful attention to security. This guide covers best practices to keep your Flow accounts and private keys secure when you use the Flow Command Line Interface (CLI). ## Security Overview :::danger ⚠️ **Never** commit private keys to source control. Always use secure methods to store and manage your private keys. ::: The Flow CLI provides several secure options to manage private account data: 1. **File-based keys** - Store keys in separate files. 2. **Environment variables** - Use system environment variables. 3. **Private configuration files** - Separate sensitive config from main config. 4. **Multiple config files** - Merge secure and public configurations. ## File-based keys Store private keys in separate files that are excluded from source control. ### Setup 1. **Create a key file** (for example, `my-account.key`): ```bash # Only the hex-encoded private key 334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111 ``` 2. **Add to `.gitignore`**: ```bash # Private key files *.key *.pkey private.json .env ``` 3. **Configure in `flow.json`**: ```json { "accounts": { "my-testnet-account": { "address": "3ae53cb6e3f42a79", "key": { "type": "file", "location": "./my-account.key" } } } } ``` ### Benefits - ✅ Keys are never stored in configuration files - ✅ Easy to manage multiple keys - ✅ Clear separation of concerns - ✅ Works with all Flow CLI commands ## Environment variables Use environment variables for sensitive data like private keys and addresses. ### Setup 1. **Set environment variables**: ```bash export FLOW_PRIVATE_KEY="334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111" export FLOW_ACCOUNT_ADDRESS="3ae53cb6e3f42a79" ``` 2. **Reference in `flow.json`**: ```json { "accounts": { "my-testnet-account": { "address": "$FLOW_ACCOUNT_ADDRESS", "key": "$FLOW_PRIVATE_KEY" } } } ``` 3. **Use with CLI commands**: ```bash FLOW_PRIVATE_KEY="your-key" flow project deploy ``` ### Benefits - ✅ Keys never stored in files. - ✅ Easy to manage different environments. - ✅ Works with CI or CD systems. - ✅ Can be rotated easily. ## Private configuration files Create separate configuration files for sensitive data and merge them when needed. ### Setup 1. **Main configuration** (`flow.json`): ```json { "networks": { "testnet": "access.devnet.nodes.onflow.org:9000" }, "contracts": { "MyContract": "./cadence/contracts/MyContract.cdc" } } ``` 2. **Private configuration** (`private.json`): ```json { "accounts": { "my-testnet-account": { "address": "3ae53cb6e3f42a79", "key": "334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111" } } } ``` 3. **Add to `.gitignore`**: ```bash private.json secrets.json *.private.json ``` 4. **Use with CLI commands**: ```bash flow project deploy -f flow.json -f private.json ``` ### Benefits - ✅ Clear separation of public and private data. - ✅ Easy to manage multiple environments. - ✅ Can be shared safely (without private files). - ✅ Works with all CLI commands. ## Environment Files (.env) Use `.env` files for local development with automatic loading by the CLI. ### Setup 1. **Create `.env` file**: ```bash # .env FLOW_PRIVATE_KEY=334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111 FLOW_ACCOUNT_ADDRESS=3ae53cb6e3f42a79 FLOW_NETWORK=testnet ``` 2. **Reference in `flow.json`**: ```json { "accounts": { "my-testnet-account": { "address": "$FLOW_ACCOUNT_ADDRESS", "key": "$FLOW_PRIVATE_KEY" } }, "networks": { "testnet": "access.devnet.nodes.onflow.org:9000" } } ``` 3. **Add to `.gitignore`**: ```bash .env .env.local .env.*.local ``` ### Benefits - ✅ Automatic loading by CLI. - ✅ Easy local development. - ✅ Can have different files for different environments. - ✅ Standard practice for many tools. ## Multiple configuration files Merge multiple configuration files for complex setups. ### Priority order When you use multiple files, they are merged in order: 1. **Left to right** - Files specified first have lowest priority. 2. **Later files override** - Properties in later files take precedence. 3. **Non-overlapping properties** - Are combined from all files. ### Example ```bash flow project deploy -f flow.json -f private.json -f local.json ``` **Result**: `local.json` overrides `private.json`, which overrides `flow.json` ### Use cases - **Development**: `flow.json` + `dev-private.json` - **Staging**: `flow.json` + `staging-private.json` - **Production**: `flow.json` + `prod-private.json` ## Security Best Practices ### 1. Never Commit Private Keys ```bash # Always add these to .gitignore *.key *.pkey private.json secrets.json .env .env.local *.private.json ``` ### 2. Use different keys for different environments - **Development**: Use testnet keys. - **Staging**: Use separate testnet keys. - **Production**: Use mainnet keys with highest security. ### 3. Rotate keys regularly - Generate new keys periodically. - Update configuration files. - Test with new keys before you switch. ### 4. Limit key permissions - Use keys with minimal required permissions. - Consider using different keys for different operations. - Monitor key usage. ### 5. Secure key storage - Use hardware security modules (HSMs) for production. - Consider cloud key management services. - Encrypt key files when possible. ## Common Security Mistakes ### ❌ Don't Do This ```json // flow.json - NEVER do this { "accounts": { "my-account": { "address": "3ae53cb6e3f42a79", "key": "334232967f52bd75234ae9037dd4694c1f00baad63a10c35172bf65fbb8ad1111" } } } ``` ### ✅ Do This Instead ```json // flow.json - Safe to commit { "accounts": { "my-account": { "address": "3ae53cb6e3f42a79", "key": { "type": "file", "location": "./my-account.key" } } } } ``` :::info Extracting Existing Inline Keys If you already have inline keys in your `flow.json`, you can easily extract them to separate files: ```bash flow config extract-key --all ``` This command creates `.pkey` files with restricted permissions, updates your `flow.json` to reference them, and adds the key files to `.gitignore`. ::: ## Troubleshooting ### Environment variables won't load Check that your environment variables are set: ```bash echo $FLOW_PRIVATE_KEY ``` ### Key file not found Verify the key file path in your configuration: ```bash ls -la ./my-account.key ``` ### Multiple config files not merging Check the order of your `-f` flags: ```bash # Correct order (left to right, later overrides earlier) flow config add account -f flow.json -f private.json ``` ## Related commands - [`flow config add`] - Add configuration items securely - [`flow project deploy`] - Deploy with secure configuration - [`flow accounts create`] - Create accounts securely [`flow config add`]: ./manage-configuration.md [`flow project deploy`]: ../deployment/deploy-project-contracts.md [`flow accounts create`]: ../accounts/create-accounts.md --- # File: /md/build/tools/flow-cli/fork-testing.md --- title: Fork Testing description: Test your Flow applications against production state using mainnet or testnet forks sidebar_position: 15 --- # Fork Testing Fork testing allows you to run tests and development environments against a **local copy of mainnet or testnet state**. This gives you access to real contracts, accounts, and data without deploying to live networks or affecting production state. ## What is Fork Testing? Fork testing creates a local Flow network that mirrors the state of a real network (mainnet or testnet). Your code runs locally, but can read from and interact with production contract implementations, real account balances, and actual on-chain data. **Key Benefits:** - ✅ **Test against real production contracts** - No need to mock complex dependencies - ✅ **Access real account state** - Test with actual balances, NFTs, and storage - ✅ **Reproduce production issues** - Debug problems at specific block heights - ✅ **Test contract upgrades safely** - Verify changes work with real mainnet state - ✅ **Safe testing environment** - All changes stay local, never affect the real network - ✅ **Fast iteration** - No deployment costs or wait times Fork testing is an essential part of a comprehensive testing strategy. It complements unit tests and integration tests by letting you validate your contracts against real-world state and dependencies. Learn more about building a complete testing approach in the [Testing Strategy guide](../../../build/cadence/smart-contracts/testing-strategy.md). ## Two Fork Testing Modes The Flow CLI provides two different fork testing modes for different use cases: ### 1. Emulator Fork Mode (`flow emulator --fork`) **Best for:** - Frontend and app development - E2E testing (Cypress, Playwright) - Manual testing and exploration - Wallet integration testing - Bot and indexer development **How it works:** Starts a full emulator with REST and gRPC APIs that you can connect to with FCL, dev wallet, or any Flow SDK. ```bash flow emulator --fork mainnet ``` **Learn more:** [Interactive Testing with Forked Emulator](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md) ### 2. Test Framework Fork Mode (`flow test` + `#test_fork`) **Best for:** - Cadence integration tests - Contract testing against real dependencies - Testing contract logic with real mainnet state **How it works:** Runs your `*_test.cdc` files against a forked network using the [Cadence Testing Framework](../../../build/cadence/smart-contracts/testing.md). Add the `#test_fork` pragma to your test file, then run: ```bash flow test ``` **Learn more:** [Fork Testing with Cadence](../../../blockchain-development-tutorials/cadence/fork-testing/index.md) ## Quick Comparison | Feature | `flow emulator --fork` | `flow test` + `#test_fork` | | --------------- | --------------------------------------- | -------------------------- | | **Use for** | App E2E, manual testing, debugging | Cadence integration tests | | **Connects to** | Frontend, wallets, bots, E2E tools | Cadence Testing Framework | | **Run with** | FCL, Cypress, Playwright, manual clicks | `flow test` command | | **Best for** | User flows, UI testing, exploration | Contract logic validation | | **Examples** | React app, wallet flows, E2E suites | `*_test.cdc` files | ## Common Use Cases ### DeFi Protocol Testing Test your DeFi contracts against real mainnet state - real DEX liquidity, real oracle prices, real token supplies. ### Contract Upgrade Testing Deploy your upgraded contract to a fork and verify it works with real mainnet state before deploying to production. ### Bug Reproduction Fork to the exact block height where a bug occurred and debug with the actual state that caused the issue. ### Integration Testing Test how your contracts interact with production versions of core contracts (FungibleToken, NFT standards, etc). ## Getting Started ### Prerequisites - [Flow CLI](./install.md) v2.12.0 or later - Basic understanding of Flow development ### Quick Start: Emulator Fork ```bash # 1. Initialize a Flow project flow init # 2. Install dependencies (e.g., FlowToken) flow dependencies install FlowToken FungibleToken # 3. Start the forked emulator flow emulator --fork mainnet # 4. In another terminal, run scripts/transactions flow scripts execute myScript.cdc --network mainnet-fork ``` **Next steps:** Follow the [complete emulator fork tutorial](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md) ### Quick Start: Cadence Test Fork Add the fork pragma to your test file: ```cadence #test_fork(network: "mainnet", height: nil) import Test access(all) fun testExample() { // Your test code here } ``` Then run the test: ```bash flow test tests/MyContract_test.cdc ``` **Next steps:** Follow the [complete Cadence fork testing tutorial](../../../blockchain-development-tutorials/cadence/fork-testing/index.md) ## Key Features ### Pin to Block Heights Fork to specific block heights for reproducible testing: ```bash # Emulator fork with block height flow emulator --fork mainnet --fork-height ``` ```cadence // Test with block height - add to your test file #test_fork(network: "mainnet", height: ) ``` ```bash # Then run the test flow test test_file.cdc ``` Replace `` with the specific block number you want to test against. Note that block heights are only available within the current spork. ### Account Impersonation Fork mode disables signature verification, allowing you to execute transactions as any mainnet account for testing. ### Dependency Mocking Override specific mainnet contracts with your own versions while keeping all other contracts unchanged - perfect for testing contract upgrades. ### Automatic Configuration Fork networks are automatically configured when you run fork commands. Contract aliases from the parent network (mainnet/testnet) are automatically inherited. Learn more: [flow.json Configuration - Fork Networks](./flow.json/configuration.md#networks) ## Best Practices 1. **Pin block heights in CI/CD** - Ensures reproducible test results 2. **Test on testnet first** - Avoid mainnet rate limits during development 3. **Use the right mode** - Emulator fork for apps, test fork for Cadence contracts 4. **Mock external services** - Fork only mirrors Flow state, not external APIs 5. **Document your fork heights** - Keep track of which blocks work for testing ## Network Requirements Fork testing requires network access to Flow's public access nodes: - **Mainnet:** `access.mainnet.nodes.onflow.org:9000` - **Testnet:** `access.devnet.nodes.onflow.org:9000` Data is fetched on-demand and cached locally for performance. ## Limitations - **Spork boundaries:** Historical data is only available within the current spork - **Off-chain services:** Oracles, IPFS, and cross-chain bridges must be mocked - **Network latency:** First access to accounts/contracts requires network fetch Learn more: [Network Upgrade (Spork) Process](../../../protocol/node-ops/node-operation/network-upgrade.md) ## Tutorials - [Interactive Testing with Forked Emulator](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md) - Complete guide to `flow emulator --fork` - [Fork Testing with Cadence](../../../blockchain-development-tutorials/cadence/fork-testing/index.md) - Complete guide to `flow test` with `#test_fork` ## Related Documentation - [Flow Emulator](../emulator/index.md) - Learn more about the Flow emulator - [Cadence Testing Framework](../../../build/cadence/smart-contracts/testing.md) - Write and run Cadence tests - [flow.json Configuration](./flow.json/configuration.md) - Configure fork networks - [Testing Strategy](../../../build/cadence/smart-contracts/testing-strategy.md) - Overall testing approach - [Dependency Manager](./dependency-manager.md) - Install and manage contract dependencies ## Need Help? - Review the [complete tutorials](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md) for step-by-step guidance - Check the [troubleshooting sections](../../../blockchain-development-tutorials/cadence/emulator-fork-testing/index.md#troubleshooting) in the tutorials - Ask questions in the [Flow Discord](https://discord.gg/flow) --- # File: /md/build/tools/flow-cli/generate.md --- title: Generating Cadence Boilerplate sidebar_label: Generating Cadence Boilerplate description: Generate template files for common Cadence code using the Flow CLI sidebar_position: 12 --- The `flow generate` command provides a convenient way to create boilerplate template files for common Cadence code components. TO streamline the development process, this command automatically generates properly structured files with the correct syntax and organization. ## Overview ```bash flow generate [command] ``` **Aliases:** `generate`, `g` The generate command supports four main subcommands to create different types of Cadence files: - **contract** - Generate Cadence smart contract templates. - **script** - Generate Cadence script templates. - **test** - Generate Cadence test templates. - **transaction** - Generate Cadence transaction templates. ## Generate contract Creates a new Cadence smart contract with a basic template structure. ### Usage ```bash flow generate contract [flags] ``` ### Example ```bash flow generate contract HelloWorld ``` This command creates a file `cadence/contracts/HelloWorld.cdc` with the following content: ```cadence access(all) contract HelloWorld { init() {} } ``` :::info When you generate a contract, a test file that corresponds to that contracts is also created automatically (unless `--skip-tests` is used). For example, when you generate `HelloWorld`, the contract will also create `cadence/tests/HelloWorld.test.cdc`. ::: ### Flags - `--dir string` - Directory to generate files in (defaults to `cadence/contracts/`). - `--skip-tests` - Skip test file generation. - `-h, --help` - Help for contract command. ## Generate transaction Creates a new Cadence transaction with a basic template structure. ### Use ```bash flow generate transaction [flags] ``` ### Example ```bash flow generate transaction TransferTokens ``` This command creates a file `cadence/transactions/TransferTokens.cdc` with the following content: ```cadence transaction() { prepare() {} execute {} } ``` ### Flags - `--dir string` - Directory to generate files in (defaults to `cadence/transactions/`). - `--skip-tests` - Skip test file generation. - `-h, --help` - Help for transaction command. ## Generate script Creates a new Cadence script with a basic template structure. ### Use ```bash flow generate script [flags] ``` ### Example ```bash flow generate script GetBalance ``` This command creates a file `cadence/scripts/GetBalance.cdc` with the following content: ```cadence access(all) fun main() {} ``` ### Flags - `--dir string` - Directory to generate files in (defaults to `cadence/scripts/`). - `--skip-tests` - Skip test file generation. - `-h, --help` - Help for script command. ## Generate test Creates a new Cadence test file with a basic template structure. ### Use ```bash flow generate test [flags] ``` ### Example ```bash flow generate test MyToken ``` This command creates a file `cadence/tests/MyToken.test.cdc` with a basic test structure. After you generate a test, you can run it using `flow test`. For more information about how to write and run Cadence tests, see the [Cadence Tests documentation]. ### Flags - `--dir string` - Directory to generate files in (defaults to `cadence/tests/`). - `--skip-tests` - Skip test file generation. - `-h, --help` - Help for test command. ## Custom directory use All generate commands support the `--dir` flag to specify a custom directory for the generated files. This is useful when your project requires a different organizational structure than the default. ### Examples ```bash # Generate contract in a custom directory flow generate contract MyToken --dir=src/contracts # Generate transaction in a custom directory flow generate transaction Transfer --dir=src/transactions # Generate script in a custom directory flow generate script GetData --dir=src/scripts # Generate test in a custom directory flow generate test MyToken --dir=src/tests ``` ## Project structure When you use the default directories, the generate command creates the following structure: ``` cadence/ ├── contracts/ │ └── MyToken.cdc ├── scripts/ │ └── GetBalance.cdc ├── transactions/ │ └── TransferTokens.cdc └── tests/ └── MyToken.test.cdc ``` The generate command is an essential tool to accelerate Flow development with standardized, well-structured boilerplate code for all common Cadence components. [Cadence Tests documentation]: ./tests.md --- # File: /md/build/tools/flow-cli/get-flow-data/get-blocks.md --- title: Get Block description: How to get a block from the command line sidebar_position: 1 --- The Flow Command Line Interface (CLI) provides a command to fetch any block from the Flow network. ```shell flow blocks get ``` ## Example Usage ```shell flow blocks get 12884163 --host access.mainnet.nodes.onflow.org:9000 --include transactions ``` ### Example response ```shell Block ID 2fb7571a6ccf02f3ac42f27c14ce0a4cb119060e4fbd7af36fd51894465e7002 Prent ID 1c5a6267ba9512e141e4e90630cb326cecfbf6113818487449efeb37fc98ca18 Timestamp 2021-03-19 17:46:15.973305066 +0000 UTC Height 12884163 Status Sealed Total Seals 2 Total Collections 8 Collection 0: 3e694588e789a72489667a36dd73104dea4579bcd400959d47aedccd7f930eeb Transaction 0: acc2ae1ff6deb2f4d7663d24af6ab1baf797ec264fd76a745a30792f6882093b Transaction 1: ae8bfbc85ce994899a3f942072bfd3455823b1f7652106ac102d161c17fcb55c Transaction 2: 70c4d39d34e654173c5c2746e7bb3a6cdf1f5e6963538d62bad2156fc02ea1b2 Transaction 3: 2466237b5eafb469c01e2e5f929a05866de459df3bd768cde748e068c81c57bf Collection 1: e93f2bd988d66288c7e1ad991dec227c6c74b8039a430e43896ad94cf8feccce Transaction 0: 4d790300722b646e7ed3e2c52675430d7ccf2efd1d93f106b53bc348df601af6 Collection 2: c7d93b80ae55809b1328c686f6a8332e8e15083ab32f8b3105c4d910646f54bf Transaction 0: 95c4efbb30f86029574d6acd7df04afe6108f6fd610d823dfd398c80cfa5e842 Collection 3: 1a4f563b48aaa38f3a7e867c89422e0bd84887de125e8f48ba147f4ee58ddf0d Transaction 0: fbcc99326336d4dbb4cbc01a3b9b85cfcdcdc071b3d0e01ee88ecd144444600b Collection 4: 01000c7773cc3c22cba6d8917a2486dc7a1a1842dd7fb7c0e87e63c22bb14abe Transaction 0: a75097639b434044de0122d3a28620e093f277fa715001e80a035568e118c59f Collection 5: 6f2b08f9673545a2e61e954feb8d55d2a3ef2b3cef7a8d2f8de527bc42d92c28 Transaction 0: 8ea63d397bd07a25db3f06fb9785dbf09bc652159f68a84c55ea2be606ada1e9 Collection 6: 13b5c48252930824a8c6e846470763582cacdacb772c1e9c584adefced6724b2 Transaction 0: 8ba57a92311367189a89a59bcb3c32192387fefca9bde493e087bc0d479186a8 Transaction 1: 8ab1d99702ccf31b6f4b3acd2580dddd440f08bc07acab4884337c0c593a8f69 Collection 7: bf90fdd2761b8f37565af60fc38165dd09edf0671fdd35b37f718a7eb45e804f Transaction 0: b92a14c0802183719efed00363d31076d7e50f41a6207781cf34d39c822bbacb ``` ## Arguments ### Query - Name: `` - Valid Input: Block ID, `latest` or block height Specify the block to retrieve by block ID or block height. ## Arguments ### Address - Name: `address` - Valid Input: Flow account address Flow [account address] (prefixed with `0x` or not). ## Flags ### Events - Flag: `--events` - Valid inputs: Valid event name List events of this type for the block. ### Include - Flag: `--include` - Valid inputs: `transactions` Include additional values in the response. ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account to use to sign the transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API to use to execute the command. This flag overrides any host defined by the `--network` flag. ### Network Key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API to use to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [account address]: ../../../cadence/basics/accounts.md --- # File: /md/build/tools/flow-cli/get-flow-data/get-collections.md --- title: Get Collection description: How to get a collection from the command line sidebar_position: 3 --- The Flow CLI provides a command to fetch any collection from the Flow network. ```shell flow collections get ``` ## Example usage ```shell flow collections get 3e694588e789a72489667a36dd73104dea4579bcd400959d47aedccd7f930eeb \ --host access.mainnet.nodes.onflow.org:9000 ``` ### Example response ```shell Collection ID 3e694588e789a72489667a36dd73104dea4579bcd400959d47aedccd7f930eeb: acc2ae1ff6deb2f4d7663d24af6ab1baf797ec264fd76a745a30792f6882093b ae8bfbc85ce994899a3f942072bfd3455823b1f7652106ac102d161c17fcb55c 70c4d39d34e654173c5c2746e7bb3a6cdf1f5e6963538d62bad2156fc02ea1b2 2466237b5eafb469c01e2e5f929a05866de459df3bd768cde748e068c81c57bf ``` ## Arguments ### Collection ID - Name: `collection_id` - Valid Input: SHA3-256 hash of the collection contents ## Arguments ## Flags ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API to use to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format. Specify the network public key of the Access API to use to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/get-flow-data/get-events.md --- title: Get Events description: How to get an event from the command line sidebar_position: 2 --- Use the event command to fetch a single or multiple events in a specific range of blocks. You can provide start and end block height range, but also specify number of the latest blocks to use to search for specified event. Events are fetched concurrently via multiple workers, which optionally you can also control by specifying the flags. ```shell flow events get ``` ## Example usage Get the event by name `A.0b2a3299cc857e29.TopShot.Deposit` from the last 20 blocks on mainnet. ```shell > flow events get A.0b2a3299cc857e29.TopShot.Deposit --last 20 --network mainnet Events Block #12913388: Index 2 Type A.0b2a3299cc857e29.TopShot.Deposit Tx ID 0a1e6cdc4eeda0e23402193d7ad5ba01a175df4c08f48fa7ac8d53e811c5357c Values id (UInt64) 3102159 to ({}?) 24214cf0faa7844d Index 2 Type A.0b2a3299cc857e29.TopShot.Deposit Tx ID 1fa5e64dcdc8ed5dad87ba58207ee4c058feb38fa271fff659ab992dc2ec2645 Values id (UInt64) 5178448 to ({}?) 26c96b6c2c31e419 Index 9 Type A.0b2a3299cc857e29.TopShot.Deposit Tx ID 262ab3996bdf98f5f15804c12b4e5d4e89c0fa9b71d57be4d7c6e8288c507c4a Values id (UInt64) 1530408 to ({}?) 2da5c6d1a541971b ... ``` Get two events `A.1654653399040a61.FlowToken.TokensDeposited` and `A.1654653399040a61.FlowToken.TokensWithdrawn` in the block height range on mainnet. ```shell > flow events get \ A.1654653399040a61.FlowToken.TokensDeposited \ A.1654653399040a61.FlowToken.TokensWithdrawn \ --start 11559500 --end 11559600 --network mainnet Events Block #17015045: Index 0 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 6dcf60d54036acb52b2e01e69890ce34c3146849998d64364200e4b21e9ac7f1 Values - amount (UFix64): 0.00100000 - from (Address?): 0x9e06eebf494e2d78 Index 1 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 6dcf60d54036acb52b2e01e69890ce34c3146849998d64364200e4b21e9ac7f1 Values - amount (UFix64): 0.00100000 - from (Never?): nil Events Block #17015047: Index 0 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 24979a3c0203f514f7f5822cc8ae7046e24f25d4a775bef697a654898fb7673e Values - amount (UFix64): 0.00100000 - from (Address?): 0x18eb4ee6b3c026d2 Index 1 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 24979a3c0203f514f7f5822cc8ae7046e24f25d4a775bef697a654898fb7673e Values - amount (UFix64): 0.00100000 - from (Never?): nil ``` ## Arguments ### Event name - Name: `event_name` - Valid Input: String Fully-qualified identifier for the events. You can provide multiple event names separated by a space. ## Flags ### Start - Flag: `--start` - Valid inputs: valid block height Specify the start block height used alongside the end flag. This will define the lower boundary of the block range. ### End - Flag: `--end` - Valid inputs: valid block height Specify the end block height used alongside the start flag. This will define the upper boundary of the block range. ### Last - Flag: `--last` - Valid inputs: number - Default: `10` Specify the number of blocks relative to the last block. Ignored if the start flag is set. Used as a default if no flags are provided. ### Batch - Flag: `--batch` - Valid inputs: number - Default: `25` Number of blocks each worker will fetch. ### Workers - Flag: `--workers` - Valid inputs: number - Default: `10` Number of workers to use when you fetch events concurrently. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API to use to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format. Specify the network public key of the Access API to use to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/get-flow-data/get-status.md --- title: Network Status description: How to get access node status from the command line sidebar_position: 4 --- The Flow CLI provides a command to get network status of specified Flow Access Node. `flow status` ## Example usage ```shell > flow status --network testnet Status: 🟢 ONLINE Network: testnet Access Node: access.devnet.nodes.onflow.org:9000 ``` ## Flags ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`). Specify which network you want the command to use for execution. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API to use to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format. Specify the network public key of the Access API to use to create a secure GRPC client when you execute the command. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--conf` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/install.md --- title: Install Instructions description: How to install the Flow command-line interface (CLI) sidebar_position: 1 --- You can install the Flow CLI on macOS, Windows (7 or greater) and most Linux systems. ## macOS ### Homebrew ```sh brew install flow-cli ``` ### From a pre-built binary _This installation method only works on x86-64._ This script downloads and installs the appropriate binary for your system: ```sh sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" ``` To update, simply re-run the installation command above. It is currently not possible to install earlier versions of the Flow CLI with Homebrew. ## Linux ### From a pre-built binary _This installation method only works on x86-64._ This script downloads and installs the appropriate binary for your system: ```sh sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" ``` To update, simply re-run the installation command above. ### Install a specific version To install a specific version of Flow CLI, the version tag can be appended to the installation command. For example, to install version v2.0.0: ```sh sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v2.0.0 ``` ## Windows ### From a pre-built binary _This installation method only works on Windows 10, 8.1, or 7 (SP1, with [PowerShell 3.0]), on x86-64._ 1. Open PowerShell ([Instructions]) 2. In PowerShell, run: ```powershell iex "& { $(irm 'https://raw.githubusercontent.com/onflow/flow-cli/master/install.ps1') }" ``` To update, simply re-run the installation command above. # Upgrade the Flow CLI ## macOS ### Homebrew ```sh brew upgrade flow-cli ``` ### From a pre-built binary _This update method only works on x86-64._ This script downloads and updates the appropriate binary for your system: ```sh sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" ``` ## Linux ### From a pre-built binary _This update method only works on x86-64._ This script downloads and updates the appropriate binary for your system: ```sh sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" ``` ## Windows ### From a pre-built binary _This update method only works on Windows 10, 8.1, or 7 (SP1, with [PowerShell 3.0]), on x86-64._ 1. Open PowerShell ([Instructions]) 2. In PowerShell, run: ```powershell iex "& { $(irm 'https://raw.githubusercontent.com/onflow/flow-cli/master/install.ps1') }" ``` ## Uninstall Flow CLI To remove the flow CLI, you can run the following command if it was previously installed from a pre-built binary. - macOS: `rm /usr/local/bin/flow` - Linux: `rm ~/.local/bin/flow` - Windows: `rm ~/Users/{user}/AppData/Flow/flow.exe` If you installed it with Hombrew, you can remove it with: `brew uninstall flow-cli`. ## Next steps Now that you have the Flow CLI installed, you can: - **[Get started with Flow CLI commands]** - Learn the essential commands for project development. - **[Initialize a new project]** - Create your first Flow project. - **[Configure your project]** - Set up your `flow.json` configuration file. [PowerShell 3.0]: https://www.microsoft.com/en-ca/download/details.aspx?id=34595 [Instructions]: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-windows-powershell?view=powershell-7#finding-powershell-in-windows-10-81-80-and-7 [Get started with Flow CLI commands]: ./commands.md [Initialize a new project]: ./flow.json/initialize-configuration.md [Configure your project]: ./flow.json/configuration.md --- # File: /md/build/tools/flow-cli/keys/decode-keys.md --- title: Decode Public Keys description: How to decode Flow public keys from the command line sidebar_position: 2 --- The Flow CLI provides a command to decode encoded public account keys. ```shell flow keys decode ``` ## Example usage ### Decode RLP encoded public key ```shell > flow keys decode rlp f847b84084d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db2402038203e8 Public Key 84d716c1...bcc9ecb59568c996d342db24 Signature algorithm ECDSA_P256 Hash algorithm SHA3_256 Weight 1000 Revoked false ``` ### Decode PEM encoded public key from file ```shell > flow keys decode pem --from-file key.pem Public Key d479b3c...c4615360039a6660a366a95f Signature algorithm ECDSA_P256 Hash algorithm UNKNOWN Revoked false ``` ## Arguments ### Encoding - Valid inputs: `rlp`, `pem` First argument specifies a valid encoding of the public key provided. ### Optional: public key - Name: `encoded public key` - Valid inputs: valid encoded key content Optional second argument provides content of the encoded public key. If this argument is omitted, the `--from-file` must be used instead. ## Flags ### From file - Flag: `--from-file` - Valid inputs: valid filepath Provide file with the encoded public key. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/keys/derive-keys.md --- title: Derive Public Key description: How to derive Flow public key from a private key from the command line sidebar_position: 3 --- The Flow CLI provides a command to derive Public Key from a Private Key. ```shell flow keys derive ``` ## Example usage ### Derive Public Key from a Private Key ```shell > flow keys derive c778170793026a9a7a3815dabed68ded445bde7f40a8c66889908197412be89f ``` ### Example response ```shell > flow keys generate 🔴️ Store Private Key safely and don't share with anyone! Private Key c778170793026a9a7a3815dabed68ded445bde7f40a8c66889908197412be89f Public Key 584245c57e5316d6606c53b1ce46dae29f5c9bd26e9e8...aaa5091b2eebcb2ac71c75cf70842878878a2d650f7 ``` ## Arguments ### Private Key - Name: `private key` - Valid inputs: valid private key content ## Flags ### Signature algorithm - Flag: `--sig-algo` - Valid inputs: `"ECDSA_P256", "ECDSA_secp256k1"` Specify the ECDSA signature algorithm for the key pair. Flow supports the secp256k1 and P-256 curves. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved. --- # File: /md/build/tools/flow-cli/keys/generate-keys.md --- title: Generate Keys description: How to generate key pair from the command line sidebar_position: 1 --- The Flow CLI provides a command to generate ECDSA key pairs that can be [attached to new or existing Flow accounts]. ```shell flow keys generate ``` :::danger ⚠️ Store private key safely and don't share with anyone! ::: ## Example usage ```shell flow keys generate ``` ### Example response ```shell > flow keys generate 🔴️ Store Private Key safely and don't share with anyone! Private Key c778170793026a9a7a3815dabed68ded445bde7f40a8c66889908197412be89f Public Key 584245c57e5316d6606c53b1ce46dae29f5c9bd26e9e8...aaa5091b2eebcb2ac71c75cf70842878878a2d650f7 ``` ## Flags ### Seed - Flag: `--seed` - Valid inputs: any string with length >= 32 Specify a UTF-8 seed string to use to generate the key pair. Key generation is deterministic, so the same seed will always result in the same key. If no seed is specified, a random 32-byte seed will generate the key pair. :::danger ⚠️ It's dangerous to use seed with production keys if seed wasn't generated with safe random generators. ### Signature algorithm - Flag: `--sig-algo` - Valid inputs: `"ECDSA_P256", "ECDSA_secp256k1"` Specify the ECDSA signature algorithm for the key pair. Flow supports the secp256k1 and P-256 curves. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [attached to new or existing Flow accounts]: ../../../cadence/basics/accounts.md --- # File: /md/build/tools/flow-cli/lint.md --- title: Cadence Linter description: A static-analysis tool for finding potential issues in Cadence code sidebar_position: 14 --- The Cadence Linter is a static-analysis tool you can use to find potential issues in Cadence code. It is available in the Flow CLI and is designed to help developers write better code by identifying common mistakes and potential issues before they become problems. The linter will also check your code for any syntax or semantic errors, and provide suggestions for how to fix them. ```shell flow cadence lint [files] ``` ## Example use ```shell flow cadence lint **/*.cdc ``` ## Example output ```shell test.cdc:27:6: semantic-error: cannot find variable in this scope: `abc` test.cdc:35:6: removal-hint: unnecessary force operator 2 problems (1 error, 1 warning) ``` :::info The Cadence Linter is also available in the [Cadence VSCode extension], which provides real-time feedback as you write your code. ::: [Cadence VSCode extension]: ../vscode-extension/index.md --- # File: /md/build/tools/flow-cli/scheduled-transactions.md --- title: Scheduled Transactions description: Manage scheduled transactions on Flow with the CLI sidebar_position: 14 --- The Flow CLI provides commands to manage scheduled transactions. These commands allow you to set up a Manager resource, list scheduled transactions, get transaction details, and cancel transactions. ## What are Scheduled Transactions? Scheduled transactions allow smart contracts to schedule autonomous execution in the future without external triggers. This allows for use cases like recurring payments, automated arbitrage, and time-based contract logic. The scheduled transactions system uses priorities (High, Medium, Low) with different execution guarantees and fee multipliers to ensure predictable performance while enabling novel autonomous blockchain patterns. 📖 **[Learn more about scheduled transactions]** ## Prerequisites Before you use the scheduled transactions commands, you must initialize a Manager resource in your account storage. The Manager resource is provided by the **FlowTransactionSchedulerUtils** core contract and provides a convenient way to group, schedule, cancel, and query scheduled transactions through a single resource. ## Why use the Manager? While it's possible to schedule transactions directly, **using the Manager resource is essential for proper tooling integration**. The Manager provides a standardized interface that allows CLI commands, block explorers, and other developer tools to discover and interact with your scheduled transactions. **Key benefits of using the Manager:** - **Tooling Integration**: CLI commands and other tools can automatically discover and manage your scheduled transactions. - **Centralized Management**: All your scheduled transactions are organized in one place for easy tracking. - **Enhanced Querying**: Query transactions by handler type, timestamp, or status through standardized interfaces. - **Metadata Access**: Tools can resolve handler views and metadata to provide richer information about your scheduled transactions. Without the Manager, your scheduled transactions exist but cannot be easily discovered or managed through tooling, requiring manual tracking and interaction. ## Commands ### Setup Manager resource Initialize a Manager resource in your account storage to manage scheduled transactions. ```shell flow schedule setup ``` This command creates and stores a Manager resource at the standard storage path, allowing you to manage scheduled transactions for your account. #### Example use ```shell flow schedule setup --network testnet --signer my-account ``` #### Flags - `--signer` - The account that will own the Manager resource - `--network` / `-n` - Network to execute on (emulator, testnet, mainnet) - `--host` - Access API hostname - `--config-path` / `-f` - Path to flow.json configuration file --- ### List scheduled transactions List all scheduled transactions for a given account that has a Manager resource. ```shell flow schedule list ``` #### Arguments **Account** - Name: `account` - Valid inputs: Flow account address (with or without `0x` prefix) or account name from flow.json The account address or name that has scheduled transactions to list. #### Example use ```shell flow schedule list 0x01cf0e2f2f715450 --network testnet ``` #### Flags - `--network` / `-n` - Network to query (emulator, testnet, mainnet) - `--host` - Access API hostname - `--output` / `-o` - Output format (`json`, `inline`) - `--filter` / `-x` - Filter output by property name - `--save` / `-s` - Save output to file - `--config-path` / `-f` - Path to flow.json configuration file --- ### Get transaction details Get detailed information about a specific scheduled transaction by its ID. ```shell flow schedule get ``` #### Arguments **Transaction ID** - Name: `transaction-id` - Valid inputs: Unsigned integer (UInt64) The unique identifier of the scheduled transaction to retrieve. #### Example use ```shell flow schedule get 123 --network testnet ``` #### Flags - `--network` / `-n` - Network to query (emulator, testnet, mainnet) - `--host` - Access API hostname - `--output` / `-o` - Output format (`json`, `inline`) - `--filter` / `-x` - Filter output by property name - `--save` / `-s` - Save output to file - `--config-path` / `-f` - Path to flow.json configuration file --- ### Cancel scheduled transaction Cancel a scheduled transaction and receive a partial fee refund. ```shell flow schedule cancel ``` When you cancel a scheduled transaction, a portion of the fees paid will be refunded based on the configured refund multiplier. The transaction must be in a scheduled state (not already executed or canceled). #### Arguments **Transaction ID** - Name: `transaction-id` - Valid inputs: Unsigned integer (UInt64) The unique identifier of the scheduled transaction to cancel. #### Example use ```shell flow schedule cancel 123 --network testnet --signer my-account ``` #### Flags - `--signer` - Account that owns the Manager resource containing the transaction - `--network` / `-n` - Network to execute on (emulator, testnet, mainnet) - `--host` - Access API hostname - `--output` / `-o` - Output format (`json`, `inline`) - `--config-path` / `-f` - Path to flow.json configuration file --- ## Common flags These flags are available across all scheduled transactions commands: ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the commands. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when executing the command. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property Specify any property name from the result you want to return as the only value. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [Learn more about scheduled transactions]: ../../../blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md --- # File: /md/build/tools/flow-cli/scripts/execute-scripts.md --- title: Execute a Script description: How to execute a Cadence script on Flow from the command line sidebar_position: 6 --- The Flow CLI provides a command to execute a Cadence script on the Flow execution state with any Flow Access API. ```shell flow scripts execute [ ...] [flags] ``` ## Example usage ```shell # Execute a script on Flow Testnet > flow scripts execute script.cdc "Hello" "World" "Hello World" ``` Script source code: ``` access(all) fun main(greeting: String, who: String): String { return greeting.concat(" ").concat(who) } ``` ## Arguments ### Filename - Name: `filename` - Valid inputs: a path in the current filesystem. The first argument is a path to a Cadence file that contains the script to be executed. ### Arguments - Name: `argument` - Valid inputs: valid [cadence values] that matches argument type in script code. Input arguments values matching corresponding types in the source code and passed in the same order. To pass a `nil` value to optional arguments, you can execute the flow script like this: `flow scripts execute script.cdc nil`. ## Flags ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow scripts execute script.cdc '[{"type": "String", "value": "Hello World"}]'` Arguments passed to the Cadence script in the Cadence JSON format. Cadence JSON format contains `type` and `value` keys and is [documented here]. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API to use to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API to use to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec [cadence values]: https://cadencelang.dev/docs/1.0/json-cadence-spec --- # File: /md/build/tools/flow-cli/tests.md --- title: Running Cadence Tests sidebar_label: Running Cadence Tests description: How to run Cadence tests from the CLI sidebar_position: 11 keywords: - flow test - Cadence tests - Flow CLI - test command - test flags - code coverage - fork testing - flow test --fork - fork-height - fork-host - mainnet fork - testnet fork - test discovery - test aliases - testing configuration - test selection - test by name - random testing - coverage reporting - test automation - integration testing - spork boundaries --- The Flow CLI provides a straightforward command to execute Cadence tests, which allows developers to validate their scripts and smart contracts effectively. To run all tests in your project, simply use: ```shell flow test ``` The `flow test` command automatically discovers and runs all test scripts in your project that end with `_test.cdc`. :::info The `test` command requires a properly initialized configuration. If you haven’t set up your Flow project yet, refer to the [flow init] guide for assistance. ::: ## Prerequisites Before you run your tests, ensure that your contracts are properly configured in your `flow.json` file, particularly any necessary testing aliases. ### Set up testing aliases in contracts If your tests involve contract deployment or contract interaction, you need to add your contracts to the `contracts` section in the `flow.json` configuration file. Specifically, include the contract name, source location, and an address alias for the `testing` environment. Example `flow.json` configuration: ```json { "contracts": { "Counter": { "source": "cadence/contracts/Counter.cdc", "aliases": { "testing": "0x0000000000000007" } } }, "networks": { // ... your network configurations }, "accounts": { // ... your account configurations }, "deployments": { // ... your deployment configurations } } ``` For the `testing` alias, you can use one of the following addresses: - `0x0000000000000005` - `0x0000000000000006` - `0x0000000000000007` - `0x0000000000000008` - `0x0000000000000009` - `0x000000000000000A` - `0x000000000000000B` - `0x000000000000000C` - `0x000000000000000D` - `0x000000000000000E` :::info For more information on how to set up contracts and aliases, refer to the [Flow CLI Configuration] documentation. ::: ## Example use This example assumes you have a test script named `test_script_test.cdc` in your project directory, which verifies the functionality of a Cadence script executed in the testing environment: ```cadence // test_script_test.cdc import Test access(all) fun testSumOfTwo() { let scriptResult = Test.executeScript( "access(all) fun main(a: Int, b: Int): Int { return a + b }", [2, 3] ) Test.expect(scriptResult, Test.beSucceeded()) let sum = scriptResult.returnValue! as! Int Test.assertEqual(5, sum) } ``` This script defines a single test case, `testSumOfTwo`, which checks if a Cadence script that adds two integers `(a + b)` works as expected. The test passes if the result matches the expected value of `5`. You can run all tests in your project with the CLI: ```shell $ flow test ``` The Flow CLI will discover all test scripts that end with `_test.cdc` and execute them. The results will be displayed in the terminal: ```shell Test results: - PASS: test_script_test.cdc > testSumOfTwo ``` To learn more about how to write tests in Cadence, visit the [Cadence Testing Framework] documentation. --- ### Run specific tests and files To run specific test scripts or directories, by provide their paths: ```shell flow test path/to/your/test_script_test.cdc path/to/another_test.cdc tests/subsuite/ ``` This executes only the tests contained in the specified files and directories. --- ## Flags The `flow test` command supports several flags that provide additional functionality to manage test execution and coverage reports. ### **Coverage report** - **Flag:** `--cover` - **Default:** `false` The `--cover` flag calculates the coverage of the code being tested, which helps you identify untested parts of your scripts and contracts. ```shell $ flow test --cover ``` Sample output: ```shell Test results: - PASS: test_script_test.cdc > testSumOfTwo Coverage: 96.5% of statements ``` --- ### Coverage report output file - **Flag:** `--coverprofile` - **Valid Inputs:** A valid filename with extension `.json` or `.lcov` - **Default:** `"coverage.json"` Use the `--coverprofile` flag to specify the output file for the coverage report. Example: ```shell $ flow test --cover --coverprofile="coverage.lcov" ``` The generated coverage file can then be inspected: ```shell $ cat coverage.lcov ``` ### Coverage code type - **Flag:** `--covercode` - **Valid Inputs:** `"all"` (default) or `"contracts"` - **Default:** `"all"` The `--covercode` flag lets you limit the coverage report to specific types of code. Set the value to `"contracts"` to exclude scripts and transactions from the coverage analysis. ```shell $ flow test --cover --covercode="contracts" ``` Sample output when no contracts are present: ```shell Test results: - PASS: test_script_test.cdc > testSumOfTwo There are no statements to cover ``` > **Note:** In this example, the coverage report is empty because the `--covercode` flag is set to `"contracts"`, and the test script only contains scripts, not contracts. ### Random execution of test cases - **Flag:** `--random` - **Default:** `false` Use the `--random` flag to execute test cases in a random order. This can help identify issues that may arise due to test dependencies or the order in which tests are run. ```shell flow test --random ``` ### Seed for random execution - **Flag:** `--seed` - **Default:** `0` Use the `--seed` flag to specify a seed value for the random execution order of test cases. This allows you to reproduce a specific random order when you use the same seed value, which is helpful to debug flaky tests. ```shell flow test --seed=12345 ``` :::info If both `--random` and `--seed` are provided, the `--random` flag will be ignored, and the seed value from `--seed` will be used for randomization. ::: --- ### Run specific test by name - **Flag:** `--name` - **Default:** `""` (empty string) Use the `--name` flag to run only tests that match the given name. This is useful when you want to execute a specific test function within your test scripts. ```shell flow test --name=testSumOfTwo ``` This command will run only the test function named `testSumOfTwo` across all test scripts that contain it. To dive deeper into testing the functionality of your Cadence scripts and contracts, explore the [Cadence Testing Framework] documentation. --- ### Fork testing flags Run tests against forked mainnet or testnet state. For a step-by-step tutorial, see: [Fork Testing with Cadence]. For background and best practices, see the guide: [Testing Strategy on Flow]. #### Configure fork tests **Recommended**: Use the `#test_fork` pragma in your test file: ```cadence #test_fork(network: "mainnet", height: nil) import Test access(all) fun testAgainstMainnet() { // Test runs against mainnet state } ``` Then run with: ```shell flow test ``` The pragma configures fork testing directly in your test files, which makes the tests self-documenting. You can also use CLI flags (documented below) to override or configure fork tests and not modify test files. #### --fork - Type: `string` - Default: `""` (empty). If provided without a value, defaults to `mainnet`. Fork tests from a network defined in `flow.json`. The CLI resolves the GRPC access host and chain ID from the selected network configuration. ```shell flow test --fork # Uses mainnet by default flow test --fork testnet # Uses testnet flow test --fork mynet # Uses a custom network defined in flow.json ``` Requirements: - The network must exist in `flow.json`. - The network must have a valid `host` configured. #### --fork-host - Type: `string` - Default: `""` Directly specify a GRPC access node host. This bypasses the `flow.json` network lookup. ```shell flow test --fork-host access.mainnet.nodes.onflow.org:9000 ``` See public access node URLs in [Flow Networks]. #### --fork-height - Type: `uint64` - Default: `0` Pin the fork to a specific block height for historical state testing. Only blocks from the current spork (since the most recent network upgrade) are available via public access nodes; earlier blocks are not accessible via public access nodes. ```shell flow test --fork mainnet --fork-height 85432100 ``` :::note Historical data beyond spork boundaries is not available via standard access nodes. See the [Network Upgrade (Spork) Process]. ::: [flow init]: flow.json/initialize-configuration.md [Flow Networks]: ../../../protocol/flow-networks/index.md [Cadence Testing Framework]: ../../cadence/smart-contracts/testing.md [Network Upgrade (Spork) Process]: ../../../protocol/node-ops/node-operation/network-upgrade.md [Flow CLI Configuration]: flow.json/initialize-configuration.md [Cadence Testing Framework]: https://cadence-lang.org/docs/testing-framework [Fork Testing with Cadence]: ../../../blockchain-development-tutorials/cadence/fork-testing/index.md [Testing Strategy on Flow]: ../../cadence/smart-contracts/testing-strategy.md --- # File: /md/build/tools/flow-cli/transactions/build-transactions.md --- title: Build a Transaction description: How to build a Flow transaction from the command line sidebar_position: 3 --- The Flow CLI provides a command to build a transactions with options to specify authorizer accounts, payer account and proposer account. The `build` command doesn't produce any signatures. Instead, use it with the `sign` and `send-signed` commands. Use this functionality in the following order: 1. Use this command (`build`) to build the transaction. 2. Use the `sign` command to sign with each account specified in the build process. 3. Use the `send-signed` command to submit the signed transaction to the Flow network. ```shell flow transactions build [ ...] [flags] ``` ## Example use ```shell > flow transactions build ./transaction.cdc "Meow" \ --authorizer alice \ --proposer bob \ --payer charlie \ --filter payload --save built.rlp ID e8c0a69952fbe50a66703985e220307c8d44b8fa36c76cbca03f8c43d0167847 Payer e03daebed8ca0615 Authorizers [f3fcd2c1a78f5eee] Proposal Key: Address 179b6b1cb6755e31 Index 0 Sequence 1 No Payload Signatures No Envelope Signatures Arguments (1): - Argument 0: {"type":"String","value":"Meow"} Code transaction(greeting: String) { let guest: Address prepare(authorizer: &Account) { self.guest = authorizer.address } execute { log(greeting.concat(",").concat(self.guest.toString())) } } Payload: f9013df90138b8d17472616e...73616374696f6e286eeec0c0 ``` JSON arguments from a file example: ```shell > flow transactions build tx1.cdc --args-json "$(cat args.json)" ``` ## Arguments ### Code filename - Name: `filename` - Valid inputs: Any filename and path valid on the system. The first argument is a path to a Cadence file that contains the transaction to be executed. ### Arguments - Name: `argument` - Valid inputs: valid [cadence values] that match argument type in transaction code. Input arguments values that match the types that correspond in the source code and passed in the same order. To pass complex argument values, see the [send transaction] document. ## Flags ### Payer - Flag: `--payer` - Valid Inputs: Flow address or account name from configuration. - Default: service account Specify account address that will pay for the transaction. Read more about payers [here]. ### Proposer - Flag: `--proposer` - Valid inputs: Flow address or account name from configuration. - Default: service account Specify a name of the account that is proposing the transaction. Account must be defined in flow configuration. ### Proposer Key Index - Flag: `--proposer-key-index` - Valid inputs: number of existing key index - Default: 0 Specify key index for the proposer account. ### Authorizer - Flag: `--authorizer` - Valid Inputs: Flow address or account name from configuration. - Default: service account Additional authorizer addresses to add to the transaction. Read more about authorizers [here]. ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow transactions build ./tx.cdc '[{"type": "String", "value": "Hello World"}]'` Arguments passed to the Cadence transaction in Cadence JSON format. Cadence JSON format contains `type` and `value` keys and is [documented here]. ### Gas limit - Flag: `--compute-limit` - Valid inputs: an integer greater than zero. - Default: `1000` Specify the compute unit (gas) limit for this transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the commands. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload`, `signatures` Specify fields to include in the result output. Applies only to the text output. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: valid filename Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see while command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: valid filename Specify a filename for the configuration files. To provide multiple configuration files, pass the `-f` flag multiple times. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [cadence values]: https://cadencelang.dev/docs/1.0/json-cadence-spec [send transaction]: ./send-transactions.md#example-usage [here]: ../../../cadence/basics/transactions.md [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec --- # File: /md/build/tools/flow-cli/transactions/complex-transactions.md --- title: Build a Complex Transaction description: How to build and send a complex Flow transaction from the command line sidebar_position: 4 --- **Simple Transactions** To send a transaction with the Flow CLI use the [send command documented here]. **Complex Transactions** If you would like to build more complex transactions the Flow CLI provides commands to build, sign and send transactions that allow you to specify different authorizers, signers and proposers. To send a complex transactions includes three steps: 1. [build a transaction] 2. [sign the built transaction] 3. [send signed transaction] Read more about each command flags and arguments in the above links. ## Examples We will describe common examples for complex transactions. All examples use an [example configuration]. ### Single payer, proposer and authorizer The simplest Flow transaction declares a single account as the proposer, payer and authorizer. Build the transaction: ```shell > flow transactions build tx.cdc --proposer alice --payer alice --authorizer alice --filter payload --save tx1 ``` Sign the transaction: ```shell > flow transactions sign tx1 --signer alice --filter payload --save tx2 ``` Submit the signed transaction: ```shell > flow transactions send-signed tx2 ``` Transaction content (`tx.cdc`): ``` transaction { prepare(signer: &Account) {} execute { ... } } ``` ### Single payer and proposer, multiple authorizers A transaction that declares same payer and proposer but multiple authorizers each required to sign the transaction. The order of signing is important, and [the payer must sign last]. Build the transaction: ```shell > flow transactions build tx.cdc --proposer alice --payer alice --authorizer bob --authorizer charlie --filter payload --save tx1 ``` Sign the transaction with authorizers: ```shell > flow transactions sign tx1 --signer bob --filter payload --save tx2 ``` ```shell > flow transactions sign tx2 --signer charlie --filter payload --save tx3 ``` Sign the transaction with payer: ```shell > flow transactions sign tx3 --signer alice --filter payload --save tx4 ``` Submit the signed transaction: ```shell > flow transactions send-signed tx4 ``` Transaction content (`tx.cdc`): ``` transaction { prepare(bob: &Account, charlie: &Account) {} execute { ... } } ``` ### Different payer, proposer and authorizer A transaction that declares different payer, proposer and authorizer each signing separately. The order of signing is important, and [the payer must sign last]. Build the transaction: ```shell > flow transactions build tx.cdc --proposer alice --payer bob --authorizer charlie --filter payload --save tx1 ``` Sign the transaction with proposer: ```shell > flow transactions sign tx1 --signer alice --filter payload --save tx2 ``` Sign the transaction with authorizer: ```shell > flow transactions sign tx2 --signer charlie --filter payload --save tx3 ``` Sign the transaction with payer: ```shell > flow transactions sign tx3 --signer bob --filter payload --save tx4 ``` Submit the signed transaction: ```shell > flow transactions send-signed tx4 ``` Transaction content (`tx.cdc`): ``` transaction { prepare(charlie: &Account) {} execute { ... } } ``` ### Single payer, proposer and authorizer but multiple keys A transaction that declares same payer, proposer and authorizer but the signer account has two keys with half weight, required to sign with both. Build the transaction: ```shell > flow transactions build tx.cdc --proposer dylan1 --payer dylan1 --authorizer dylan1 --filter payload --save tx1 ``` Sign the transaction with the first key: ```shell > flow transactions sign tx1 --signer dylan1 --filter payload --save tx2 ``` Sign the transaction with the second key: ```shell > flow transactions sign tx2 --signer dylan2 --filter payload --save tx3 ``` Submit the signed transaction: ```shell > flow transactions send-signed tx3 ``` Transaction content (`tx.cdc`): ``` transaction { prepare(signer: &Account) {} execute { ... } } ``` ### Configuration This is an example configuration that uses mock values: ```json { ... "accounts": { "alice": { "address": "0x1", "key": "111...111" }, "bob": { "address": "0x2", "key": "222...222" }, "charlie": { "address": "0x3", "key": "333...333" }, "dylan1": { "address": "0x4", "key": "444...444" }, "dylan2": { "address": "0x4", "key": "555...555" } } ... } ``` [send command documented here]: ./send-transactions.md [build a transaction]: ./build-transactions.md [sign the built transaction]: ./sign-transaction.md [send signed transaction]: ./send-signed-transactions.md [example configuration]: ./complex-transactions.md#configuration [the payer must sign last]: ../../../cadence/basics/transactions.md#payer-signs-last --- # File: /md/build/tools/flow-cli/transactions/decode-transactions.md --- title: Build a Complex Transaction description: How to decode a Flow transaction from the command line sidebar_position: 7 --- The Flow CLI provides a command to decode a transaction from RLP in a file. It uses same transaction format as get command ```shell flow transactions decode ``` ## Example Usage ```shell > flow transactions decode ./rlp-file.rlp ID c1a52308fb906358d4a33c1f1d5fc458d3cfea0d570a51a9dea915b90d678346 Payer 83de1a7075f190a1 Authorizers [83de1a7075f190a1] Proposal Key: Address 83de1a7075f190a1 Index 1 Sequence 1 No Payload Signatures Envelope Signature 0: 83de1a7075f190a1 Signatures (minimized, use --include signatures) Code (hidden, use --include code) Payload (hidden, use --include payload) ``` ## Arguments ### Filename - Name: `` - Valid Input: file name. The first argument is the filename that contains the transaction RLP. ## Flags ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload`, `signatures` Specify fields to include in the result output. Applies only to the text output. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want the result to be saved ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/transactions/get-system-transactions.md --- title: Get a System Transaction description: How to get a Flow system transaction from the command line sidebar_position: 8 keywords: - flow cli - transactions - system transaction - system chunk - scheduled transactions - forte upgrade - blocks - block id - tx_id - fee-events - access api - emulator - testnet --- The Flow CLI provides a command to fetch the system transaction for a given block reference. You can optionally provide a transaction ID to target a specific system transaction within that block. ```shell flow transactions get-system [tx_id] ``` ::::warning Querying with a system transaction ID (`[tx_id]`) is part of the Forte network upgrade and is currently available on Flow Emulator (CLI v2.7.0+) and [Flow Testnet]. See the announcement for context: [Forte: Introducing Actions & Agents]. :::: ## Use cases - System chunk transaction for protocol operations: see [Epoch Scripts and Events] and [Staking rewards via system chunk]. - Transactions related to scheduled transactions: see [Introduction to Scheduled Transactions]. Consider `--include fee-events` for scheduled transaction fee details. ## Example use ```shell > flow transactions get-system latest --network mainnet Status ✅ SEALED ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Payer — Authorizers [] Proposal Key: — No Payload Signatures No Envelope Signatures Events: Index 0 Type A.1654653399040a61.FlowToken.TokensDeposited Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Values - amount (UFix64): 0.00100000 - to ({}?): 5068e27f275c546c Code (hidden, use --include code) Payload (hidden, use --include payload) ``` Select a specific system transaction within the block by ID: ```shell > flow transactions get-system latest 07a8...b433 --network mainnet ``` ## Arguments ### Block reference - Name: `` - Valid Input: a block ID (hex), the keyword `latest`, or a block height (number). The first argument is a reference to the block whose system transaction you want to fetch. ### Transaction ID (optional) - Name: `[tx_id]` - Valid Input: a transaction ID (hex). Optionally narrow the result to a specific system transaction within the referenced block. ## Flags ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload`, `signatures`, `fee-events` Specify fields to include in the result output. Applies only to the text output. ### Exclude fields - Flag: `--exclude` - Valid inputs: `events` Specify fields to exclude from the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or host address. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the host address of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create secure client connections when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current file system. Specify the filename where you want to save the result. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current file system. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. ## Notes System transactions currently cover: - System chunk transactions used by protocol operations. See an overview of system chunks and service events: - [Epoch Scripts and Events] - Scheduled transaction execution. Learn more: - [Introduction to Scheduled Transactions] More resources: - [Staking rewards via system chunk] - [Epoch schedule and system chunk transactions] [Forte: Introducing Actions & Agents]: https://flow.com/post/forte-introducing-actions-agents-supercharging-composability-and-automation [Flow Testnet]: ../../../../protocol/flow-networks/accessing-testnet.md [Epoch Scripts and Events]: ../../../../protocol/staking/05-epoch-scripts-events.md [Staking rewards via system chunk]: ../../../../protocol/staking/08-staking-rewards.md [Introduction to Scheduled Transactions]: ../../../../blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md [Epoch schedule and system chunk transactions]: ../../../../protocol/staking/03-schedule.md --- # File: /md/build/tools/flow-cli/transactions/get-transactions.md --- title: Get a Transaction description: How to get a Flow transaction from the command line sidebar_position: 2 --- The Flow CLI provides a command to fetch a transaction that was previously submitted to an Access API. ```shell flow transactions get ``` ## Example use ```shell > flow transactions get 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa --network mainnet Status ✅ SEALED ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Payer 18eb4ee6b3c026d2 Authorizers [18eb4ee6b3c026d2] Proposal Key: Address 18eb4ee6b3c026d2 Index 11 Sequence 17930 Payload Signature 0: 18eb4ee6b3c026d2 Payload Signature 1: 18eb4ee6b3c026d2 Envelope Signature 0: 18eb4ee6b3c026d2 Signatures (minimized, use --include signatures) Events: Index 0 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Values - amount (UFix64): 0.00100000 - from ({}?): 18eb4ee6b3c026d2 Index 1 Type A.1654653399040a61.FlowToken.TokensDeposited Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Values - amount (UFix64): 0.00100000 - to ({}?): 5068e27f275c546c Index 2 Type A.18eb4ee6b3c026d2.PrivateReceiverForwarder.PrivateDeposit Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa Values - amount (UFix64): 0.00100000 - to ({}?): 5068e27f275c546c Code (hidden, use --include code) Payload (hidden, use --include payload) ``` ## Arguments ### Transaction ID - Name: `` - Valid Input: transaction ID. The first argument is the ID (hash) of the transaction. ## Flags ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload`, `signatures` Specify fields to include in the result output. Applies only to the text output. ### Wait for seal - Flag: `--sealed` - Default: `false` Indicate whether to wait for the transaction to be sealed before itdisplays the result. ### Exclude fields - Flag: `--exclude` - Valid inputs: `events` Specify fields to exclude from the result output. Applies only to the text output. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want to save the result. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/transactions/profile-transactions.md --- title: Profile a Transaction description: How to profile the computational performance of a Flow transaction sidebar_position: 9 keywords: - flow cli - transactions - profiling - performance - optimization - computation - pprof - gas optimization - cadence profiling --- The Flow CLI provides a command to profile the computational performance of sealed transactions on any Flow network. This diagnostic tool generates detailed CPU profiles in the industry-standard `pprof` format, allowing you to analyze exactly where computation is being spent during transaction execution. :::info The command works by forking the blockchain state and replaying the transaction in an isolated environment, ensuring accurate profiling results that match the original execution. Learn more about state forking in the [Fork Testing guide](../fork-testing.md). ::: ```shell flow transactions profile --network [flags] ``` ## Use Cases Transaction profiling helps developers: - **Optimize Transaction Costs**: Identify computational bottlenecks and optimize gas-heavy operations - **Debug High Gas Usage**: Understand why a transaction consumed more computation than expected - **Analyze Production Transactions**: Profile real transactions on mainnet or testnet to understand actual performance - **Compare Implementations**: Evaluate different approaches by comparing their computational profiles - **Find Performance Issues**: Trace computation usage through contract calls and dependencies ## Example Usage Profile a mainnet transaction: ```shell > flow transactions profile 07a8...b433 --network mainnet Transaction Profiling Report ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Transaction ID: 07a8...b433 Network: mainnet Block Height: 12345678 Status: SEALED Events emitted: 5 Computation: 1234 Profile saved: profile-07a8b433.pb.gz Analyze with: go tool pprof -http=:8080 profile-07a8b433.pb.gz ``` Profile with custom output location: ```shell > flow transactions profile 0xabc123 --network testnet --output my-profile.pb.gz Profile saved: my-profile.pb.gz ``` Profile an emulator transaction: ```shell > flow transactions profile 0xdef456 --network emulator ``` ## Analyzing Profile Data The generated `.pb.gz` file can be analyzed using Go's pprof tools. If you don't have Go installed, see the [Go installation guide](https://go.dev/doc/install). ### Interactive Web Interface Open the profile in an interactive web interface: ```bash go tool pprof -http=:8080 profile-07a8b433.pb.gz ``` Then navigate to `http://localhost:8080` in your browser. The pprof web interface provides several visualization options: | View | Description | |------|-------------| | **Flame Graph** | Visual representation of call stacks with computation costs | | **Graph** | Directed graph showing call relationships | | **Top** | List of functions sorted by computation usage | | **Source** | Source code annotated with computation costs | ### Command-Line Analysis View top computation consumers: ```bash go tool pprof -top profile-07a8b433.pb.gz ``` List all functions with costs: ```bash go tool pprof -list=. profile-07a8b433.pb.gz ``` Generate a flame graph image: ```bash go tool pprof -png profile-07a8b433.pb.gz > profile.png ``` For comprehensive information on analyzing computation profiles, see the [Cadence Computation Profiling guide](../../../cadence/advanced-concepts/computation-profiling.md). ## How It Works The profiling process: 1. **Fetches the Transaction**: Retrieves the target sealed transaction by ID from the specified network 2. **Forks Blockchain State**: Creates a fork of the blockchain state from the block immediately before the transaction's block (uses the same forking mechanism as [Fork Testing](../fork-testing.md)) 3. **Replays Execution**: Replays all prior transactions in the same block to recreate the exact state 4. **Profiles Target Transaction**: Executes the target transaction with Cadence runtime profiling enabled 5. **Exports Profile**: Saves the profiling data to a pprof-compatible file This ensures the profile accurately reflects the transaction's execution in its original context. :::info The transaction profiling command uses Flow's state forking capabilities under the hood to create an accurate execution environment. Learn more about state forking in the [Fork Testing guide](../fork-testing.md). ::: ## Arguments ### Transaction ID - Name: `` - Valid Input: transaction ID (with or without `0x` prefix) The transaction ID to profile. The transaction must be sealed. ## Flags ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in `flow.json` - **Required** Specify which network the transaction was executed on (e.g., `mainnet`, `testnet`, `emulator`). ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: valid file path - Default: `profile-{tx_id_prefix}.pb.gz` Custom output file path for the profile data. The file will be saved in compressed pprof format (`.pb.gz`). ### Host - Flag: `--host` - Valid inputs: an IP address or hostname - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to fetch transaction data. This flag overrides any host defined by the `--network` flag. ### Network Key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when executing the command. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property Specify any property name from the result you want to return as the only value. ### Output Format - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results displayed in the console. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem Specify the filename where you want the result summary to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem - Default: `flow.json` Specify the path to the `flow.json` configuration file. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. ## Requirements ### Transaction Must Be Sealed Only sealed transactions can be profiled. Attempting to profile a pending or finalized transaction will result in an error. ```shell Error: transaction is not sealed (status: PENDING) ``` Wait for the transaction to be sealed before profiling, or use a different transaction. ### Network Configuration The network must be properly configured in your `flow.json` file: ```json { "networks": { "mainnet": "access.mainnet.nodes.onflow.org:9000", "testnet": "access.devnet.nodes.onflow.org:9000" } } ``` ### Go Toolchain (for analysis) To analyze the generated profile files, you need Go installed on your system. The `pprof` tool is included in the standard Go distribution. Install Go from: https://go.dev/doc/install ## Related Documentation - **[Cadence Computation Profiling](../../../cadence/advanced-concepts/computation-profiling.md)** - Comprehensive guide on profiling and optimization - **[Fork Testing Guide](../fork-testing.md)** - Learn more about the state forking capabilities used by this command - **[Testing Strategy](../../../cadence/smart-contracts/testing-strategy.md)** - How profiling fits into your overall testing and optimization workflow - **[Transaction Fees](../../../cadence/basics/fees.md)** - Understanding computation costs and fee optimization --- # File: /md/build/tools/flow-cli/transactions/send-signed-transactions.md --- title: Send Signed Transaction description: How to send a signed Flow transaction from the command line sidebar_position: 6 --- The Flow CLI provides a command to send signed transactions to any Flow Access API. Use this functionality in the following order: 1. Use the `build` command to build the transaction. 2. Use the `sign` command to sign with each account specified in the build process. 3. Use this command (`send-signed`) to submit the signed transaction to the Flow network. ```shell flow transactions send-signed ``` ## Example use ```shell > flow transactions send-signed ./signed.rlp Status ✅ SEALED ID 528332aceb288cdfe4d11d6522aa27bed94fb3266b812cb350eb3526ed489d99 Payer f8d6e0586b0a20c7 Authorizers [f8d6e0586b0a20c7] Proposal Key: Address f8d6e0586b0a20c7 Index 0 Sequence 0 No Payload Signatures Envelope Signature 0: f8d6e0586b0a20c7 Signatures (minimized, use --include signatures) Events: None Code (hidden, use --include code) Payload (hidden, use --include payload) ``` ## Arguments ### Signed code filename - Name: `signed transaction filename` - Valid inputs: Any filename and path valid on the system. The first argument is a path to a Cadence file that contains the transaction to be executed. ## Flags ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload` Specify fields to include in the result output. Applies only to the text output. ### Exclude fields - Flag: `--exclude` - Valid inputs: `events` Specify fields to exclude from the result output. Applies only to the text output. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want to save the result. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/transactions/send-transactions.md --- title: Send a Transaction description: How to send a Flow transaction from the command line sidebar_position: 1 --- The Flow CLI provides a command to sign and send transactions to any Flow Access API. ```shell flow transactions send [ ...] [flags] ``` ## Example use ```shell > flow transactions send ./tx.cdc "Hello" Status ✅ SEALED ID b04b6bcc3164f5ee6b77fa502c3a682e0db57fc47e5b8a8ef3b56aae50ad49c8 Payer f8d6e0586b0a20c7 Authorizers [f8d6e0586b0a20c7] Proposal Key: Address f8d6e0586b0a20c7 Index 0 Sequence 0 No Payload Signatures Envelope Signature 0: f8d6e0586b0a20c7 Signatures (minimized, use --include signatures) Events: None Code (hidden, use --include code) Payload (hidden, use --include payload) ``` Multiple arguments example: ```shell > flow transactions send tx1.cdc Foo 1 2 10.9 0x1 '[123,222]' '["a","b"]' ``` Transaction code: ``` transaction(a: String, b: Int, c: UInt16, d: UFix64, e: Address, f: [Int], g: [String]) { prepare(authorizer: &Account) {} } ``` In the above example, the `flow.json` file would look something like this: ```json { "accounts": { "my-testnet-account": { "address": "a2c4941b5f3c7151", "key": "12c5dfde...bb2e542f1af710bd1d40b2" } } } ``` JSON arguments from a file example: ```shell > flow transactions send tx1.cdc --args-json "$(cat args.json)" ``` ## Arguments ### Code filename - Name: `code filename` - Valid inputs: Any filename and path valid on the system. The first argument is a path to a Cadence file that contains the transaction to be executed. ### Arguments - Name: `argument` - Valid inputs: valid [cadence values] that matches the argument type in transaction code. Input arguments values that match types which correspond in the source code and passed in the same order. to can pass a `nil` value to optional arguments, send the transaction like this: `flow transactions send tx.cdc nil`. ## Flags ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload` Specify fields to include in the result output. Applies only to the text output. ### Code - Flag: `--code` ⚠️ No longer supported: use filename argument. ### Results - Flag: `--results` ⚠️ No longer supported: all transactions will provide result. ### Exclude fields - Flag: `--exclude` - Valid inputs: `events` Specify fields to exclude from the result output. Applies only to the text output. ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`). Specify the name of the account that will be used to sign the transaction. ### Proposer - Flag: `--proposer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used as the proposer in the transaction. ### Payer - Flag: `--payer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used as the payer in the transaction. ### Authorizer - Flag: `--authorizer` - Valid inputs: the name of a single or multiple comma-separated accounts defined in the configuration (`flow.json`) Specify the name of the account(s) that will be used as authorizer(s) in the transaction. If you want to provide multiple authorizers, separate them with commas (for example, `alice,bob`) ### Arguments JSON - Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. - Example: `flow transactions send ./tx.cdc '[{"type": "String", "value": "Hello World"}]'` Arguments passed to the Cadence transaction in Cadence JSON format. The Cadence JSON format contains `type` and `value` keys and is [documented here]. ### Compute Limit - Flag: `--compute-limit` - Valid inputs: an integer greater than zero. - Default: `1000` Specify the compute unit (gas) limit for this transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the command. This flag overrides any host defined by the `--network` flag. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: a case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify the format of the command results. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: a path in the current filesystem. Specify the filename where you want to save the result. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see during command execution. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: a path in the current filesystem. - Default: `flow.json` Specify the path to the `flow.json` configuration file. You can use the `-f` flag multiple times to merge several configuration files. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [cadence values]: https://cadencelang.dev/docs/1.0/json-cadence-spec [documented here]: https://cadencelang.dev/docs/1.0/json-cadence-spec --- # File: /md/build/tools/flow-cli/transactions/sign-transaction.md --- title: Sign a Transaction description: How to sign a Flow transaction from the command line sidebar_position: 5 --- The Flow CLI provides a command to sign transactions with options to specify authorizer accounts, payer accounts and proposer accounts. Use this functionality in the following order: 1. Use the `build` command to build the transaction. 2. Use this command (`sign`) to sign with each account specified in the build process. 3. Use the `send-signed` command to submit the signed transaction to the Flow network. ```shell flow transactions sign ``` ## Example use ```shell > flow transactions sign ./built.rlp --signer alice \ --filter payload --save signed.rlp Hash b03b18a8d9d30ff7c9f0fdaa80fcaab242c2f36eedb687dd9b368326311fe376 Payer f8d6e0586b0a20c7 Authorizers [f8d6e0586b0a20c7] Proposal Key: Address f8d6e0586b0a20c7 Index 0 Sequence 6 No Envelope Signatures Payload Signature 0: Address f8d6e0586b0a20c7 Signature b5b1dfed2a899037...164e1b224a7ac924018e7033b68b0df86769dd54 Key Index 0 Arguments (1): - Argument 0: {"type":"String","value":"Meow"} Code transaction(greeting: String) { let guest: Address prepare(authorizer: &Account) { self.guest = authorizer.address } execute { log(greeting.concat(",").concat(self.guest.toString())) } } Payload: f90184f...a199bfd9b837a11a0885f9104b54014750f5e3e5bfe4a5795968b0df86769dd54c0 ``` ## Arguments ### Built transaction filename or remote server URL - Name: `built transaction filename | --from-remote-url ` - Valid inputs: Any filename and path valid on the system or --from-remote-url flag and fully qualified remote server url. Specify the filename that contains valid transaction payload to use for signing. To be used with the `flow transaction build` command. When --from-remote-url flag is used the value needs to be a fully qualified url to transaction RLP Example: `flow transaction sign --from-remote-url https://fully/qualified/url --signer alice` ## Flags ### From remote Url - Flag: `--from-remote-url` - Valid input: `http(s)://fully/qualified/server/url` Specify this flag with a fully qualified url to transaction RLP. The RLP will be fetched from server then signed. The returned signed RLP is then posted to the remote url. This feature is to support protocol level multiple signature transaction coordination between multiple signers. The `--yes` flag is not supported and will fail `sign` command when this flag is used. This forces the user to verify the cadence code. ### Include fields - Flag: `--include` - Valid inputs: `code`, `payload`, `signatures` Specify fields to include in the result output. Applies only to the text output. ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`) Specify the name of the account that will be used to sign the transaction. ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the commands. ### Network key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: valid filename Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see while command executes. ### Configuration - Flag: `--conf` - Short Flag: `-f` - Valid inputs: valid filename Specify a filename for the configuration files. To provide multiple configuration files use the `-f` flag multiple times. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/utils/signature-generate.md --- title: Generate a Signature description: How to generate a new signature from the command line --- Generate a signature with the private key of the signer account. ```shell flow signatures generate ``` ⚠️ _Make sure the account you want to use for signing is saved in the `flow.json` configuration. The address of the account is not important, just the private key._ ## Example use ```shell > flow signatures generate 'The quick brown fox jumps over the lazy dog' --signer alice Signature b33eabfb05d374b...f09929da96f5beec167fd1f123ec Message The quick brown fox jumps over the lazy dog Public Key 0xc92a7c...042c4025d241fd430242368ce662d39636987 Hash Algorithm SHA3_256 Signature Algorithm ECDSA_P256 ``` ## Arguments ### Message - Name: `message` Message used for signing. ## Flags ### Signer - Flag: `--signer` - Valid inputs: the name of an account defined in the configuration (`flow.json`). Specify the name of the account that will be used to sign the transaction. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: valid filename Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see while the command executes. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: valid filename Specify a filename for the configuration files. To provide multiple configuration files, use the `-f` flag multiple times. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/utils/signature-verify.md --- title: Verify Signature description: How to verify a signature from the command line --- Verify validity of a signature based on provided message and public key of the signature creator. ```shell flow signatures verify ``` ## Example use ```shell > flow signatures verify 'The quick brown fox jumps over the lazy dog' b1c9eff5d829fdeaf2dad6308fc8033e3b8875bc185ef804ce5d0d980545ef5be0f98b47afc979d12272d257ce13c4b490e431bfcada485cb1d2e3f209be8d07 0xc92a7c72a78f8f046a79f8a5fe1ef72424258a55eb869f13e6133301d64ad025d3362d5df9e7c82289637af1431042c4025d241fd430242368ce662d39636987 Valid true Message The quick brown fox jumps over the lazy dog Signature b1c9eff5d829fdeaf2...7ce13c4b490eada485cb1d2e3f209be8d07 Public Key c92a7c72a78...1431042c4025d241fd430242368ce662d39636987 Hash Algorithm SHA3_256 Signature Algorithm ECDSA_P256 ``` ## Arguments ### Message - Name: `message` Message data used to create the signature. ### Signature - Name: `signature` Message signature that will be verified. ### Public Key - Name: `public key` Public key of the private key used to create the signature. ## Flags ### Public key signature algorithm - Flag: `--sig-algo` - Valid inputs: `"ECDSA_P256", "ECDSA_secp256k1"` Specify the ECDSA signature algorithm of the key pair used for signing. Flow supports the secp256k1 and P-256 curves. ### Public key hash algorithm - Flag: `--hash-algo` - Valid inputs: `"SHA2_256", "SHA3_256"` - Default: `"SHA3_256"` Specify the hash algorithm of the key pair used for signing. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Save - Flag: `--save` - Short Flag: `-s` - Valid inputs: valid filename Specify the filename where you want the result to be saved. ### Log - Flag: `--log` - Short Flag: `-l` - Valid inputs: `none`, `error`, `debug` - Default: `info` Specify the log level. Control how much output you want to see while the command executes. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/utils/snapshot-save.md --- title: Snapshot Save description: How to save a protocol snapshot from the command line --- The FLOW CLI provides a command to save the latest finalized protocol state snapshot ```shell flow snapshot save ``` ## Example use ```shell flow snapshot save /tmp/snapshot.json --network testnet ``` ### Example response ```shell snapshot saved: /tmp/snapshot.json ``` ## Arguments ### Output path - Name: `output path` - Valid Input: any valid string path Output path where the protocol snapshot JSON file will be saved. ## Flags ### Host - Flag: `--host` - Valid inputs: an IP address or hostname. - Default: `127.0.0.1:3569` (Flow Emulator) Specify the hostname of the Access API that will be used to execute the commands. ### Network Key - Flag: `--network-key` - Valid inputs: A valid network public key of the host in hex string format Specify the network public key of the Access API that will be used to create a secure GRPC client when you execute the command. ### Network - Flag: `--network` - Short Flag: `-n` - Valid inputs: the name of a network defined in the configuration (`flow.json`) - Default: `emulator` Specify which network you want the command to use for execution. ### Filter - Flag: `--filter` - Short Flag: `-x` - Valid inputs: case-sensitive name of the result property. Specify any property name from the result you want to return as the only value. ### Output - Flag: `--output` - Short Flag: `-o` - Valid inputs: `json`, `inline` Specify in which format you want to display the result. ### Version Check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. --- # File: /md/build/tools/flow-cli/utils/tools.md --- title: Development Tools description: How to start development tools using the Flow CLI --- The Flow CLI integrates different development tools, which you can now easily start and manage from a single place. Currently, the CLI supports starting [Flow Development Wallet] ## Flow Development Wallet The Flow Dev Wallet is a mock Flow wallet that simulates the protocols used by Flow CLient Library (FCL) to interact with the Flow blockchain on behalf of simulated user accounts. **Be sure you started the emulator before you run this command** _You can start it with the `flow emulator` command_. ```shell flow dev-wallet ``` _⚠️ This project implements an FCL compatible interface, but should **not** be used as a reference to build a production grade wallet._ After you start dev-wallet, you can set your fcl config to use it like below: ```javascript import * as fcl from "@onflow/fcl" fcl.config() // Point App at Emulator .put("accessNode.api", "http://localhost:8080") // Point FCL at dev-wallet (default port) .put("discovery.wallet", "http://localhost:8701/fcl/authn") ``` You can read more about how to set up dev-wallet at [Flow Dev Wallet Project]. ## Flags ### Port - Flag: `--port` - Valid inputs: Number - Default: `8701` Port on which the dev wallet server will listen. ### Emulator host - Flag: `--emulator-host` - Valid inputs: a hostname - Default: `http://localhost:8080` Specifies the host configuration for dev wallet. ### Configuration - Flag: `--config-path` - Short Flag: `-f` - Valid inputs: valid filename Specify a filename for the configuration files. To provide multiple configuration files, use the `-f` flag multiple times. ### Version check - Flag: `--skip-version-check` - Default: `false` Skip version check during start up to speed up process for slow connections. [Flow Development Wallet]: https://github.com/onflow/fcl-dev-wallet [Flow Dev Wallet Project]: https://github.com/onflow/fcl-dev-wallet --- # File: /md/build/tools/flow-dev-wallet.md --- title: Flow Dev Wallet sidebar_label: Flow Dev Wallet sidebar_position: 5 --- The Flow Dev Wallet is a mock Flow wallet that simulates the protocols the Flow Client Library ([FCL]) uses to interact with the Flow blockchain on behalf of simulated user accounts. :::warning This project implements an FCL compatible interface, but should **not** be used as a reference to build a production grade wallet. This project should only be used in aid of local development against a locally run instance of the Flow blockchain like the Flow emulator, and should never be used in conjunction with Flow Mainnet, Testnet, or any other instances of Flow. ::: :::info To see a full list of Flow compatible wallets, visit the [Wallets page] ::: ## Get started Before you use the dev wallet, you'll need to start the Flow emulator. ### Install the `flow-cli` The Flow emulator is bundled with the Flow CLI. Instructions for how to install the CLI can be found here: [flow-cli/install/] ### Create a `flow.json` file Run this command to create `flow.json` file (typically in your project's root directory): ```sh flow init --config-only ``` ### Start the Emulator To start the Emulator and deploy the contracts, run the following command from the directory that contains `flow.json` in your project: ```sh flow emulator start flow project deploy --network emulator ``` ### Start the dev wallet In a separate terminal session, start the dev wallet service. ```sh flow dev-wallet ``` ### Configure your JavaScript application The Flow Dev Wallet is designed to be used with [`@onflow/fcl`] version `1.0.0` or higher. The FCL package can be installed with: `npm install @onflow/fcl` or `yarn add @onflow/fcl`. To use the dev wallet, configure FCL to point to the address of a locally running [Flow emulator] and the dev wallet endpoint. ```javascript import * as fcl from '@onflow/fcl'; fcl .config() // Point App at Emulator REST API .put('accessNode.api', 'http://localhost:8888') // Point FCL at dev-wallet (default port) .put('discovery.wallet', 'http://localhost:8701/fcl/authn'); ``` :::info For a full example refer to [Authenticate using FCL snippet] ::: ### Test harness It's easy to use this FCL harness app as a barebones app to interact with the dev-wallet during development: Navigate to http://localhost:8701/harness ### Wallet Discovery [Wallet Discovery]offers a convenient modal and mechanism to authenticate users and connects to all wallets available in the Flow ecosystem. The following code from [Emerald Academy] can be added to your React app to turn on Wallet Discovery: ```javascript import { config, authenticate, unauthenticate, currentUser } from '@onflow/fcl'; import { useEffect, useState } from 'react'; const fclConfigInfo = { emulator: { accessNode: 'http://127.0.0.1:8888', discoveryWallet: 'http://localhost:8701/fcl/authn', discoveryAuthInclude: [], }, testnet: { accessNode: 'https://rest-testnet.onflow.org', discoveryWallet: 'https://fcl-discovery.onflow.org/testnet/authn', discoveryAuthnEndpoint: 'https://fcl-discovery.onflow.org/api/testnet/authn', // Adds in Dapper + Ledger discoveryAuthInclude: ['0x82ec283f88a62e65', '0x9d2e44203cb13051'], }, mainnet: { accessNode: 'https://rest-mainnet.onflow.org', discoveryWallet: 'https://fcl-discovery.onflow.org/authn', discoveryAuthnEndpoint: 'https://fcl-discovery.onflow.org/api/authn', // Adds in Dapper + Ledger discoveryAuthInclude: ['0xead892083b3e2c6c', '0xe5cd26afebe62781'], }, }; const network = 'emulator'; config({ 'walletconnect.projectId': 'YOUR_PROJECT_ID', // your WalletConnect project ID 'app.detail.title': 'Emerald Academy', // the name of your DApp 'app.detail.icon': 'https://academy.ecdao.org/favicon.png', // your DApps icon 'app.detail.description': 'Emerald Academy is a DApp for learning Flow', // a description of your DApp 'app.detail.url': 'https://academy.ecdao.org', // the URL of your DApp 'flow.network': network, 'accessNode.api': fclConfigInfo[network].accessNode, 'discovery.wallet': fclConfigInfo[network].discoveryWallet, 'discovery.authn.endpoint': fclConfigInfo[network].discoveryAuthnEndpoint, // adds in opt-in wallets like Dapper and Ledger 'discovery.authn.include': fclConfigInfo[network].discoveryAuthInclude, 'discovery.authn.exclude': ['0x1234567890abcdef'], // excludes chosen wallets by address }); export default function App() { const [user, setUser] = useState({ loggedIn: false, addr: '' }); // So that the user stays logged in // even if the page refreshes useEffect(() => { currentUser.subscribe(setUser); }, []); return (

    {user.loggedIn ? `Welcome, ${user.addr}!` : 'Please log in.'}

    ); } ``` ### Account or Address creation You can [create a new account] with the `&Account` constructor. When you do this, make sure to specify which account will pay for the creation fees. TO do this, set it as the payer. The account you choose to pay these fees must have enough money to cover the cost. If it doesn't, the process will stop and the account won't be created. ```cadence transaction(publicKey: String) { prepare(signer: &Account) { let key = PublicKey( publicKey: publicKey.decodeHex(), signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 ) let account = Account(payer: signer) account.keys.add( publicKey: key, hashAlgorithm: HashAlgorithm.SHA3_256, weight: 1000.0 ) } } ``` To create a new Flow account refer to these resources - [Create an Account with FCL snippet] - [Create an Account in Cadence snippet] ### Get Flow balance To retrieve the token balance of a specific account, you must write involves write a script that pulls data from onchain. The user may have both locked tokens as well as unlocked so to retrieve the total balance we would aggregate them together. ```javascript import * as fcl from '@onflow/fcl'; import * as t from '@onflow/types'; const CODE = ` import "FungibleToken" import "FlowToken" import "LockedTokens" access(all) fun main(address: Address): UFix64 { let account = getAccount(address) let unlockedVault = account .capabilities.get<&FlowToken.Vault>(/public/flowTokenBalance) .borrow() ?? panic("Could not borrow Balance reference to the Vault" .concat(" at path /public/flowTokenBalance!") .concat(" Make sure that the account address is correct ") .concat("and that it has properly set up its account with a FlowToken Vault.")) let unlockedBalance = unlockedVault.balance let lockedAccountInfoCap = account .capabilities.get <&LockedTokens.TokenHolder> (LockedTokens.LockedAccountInfoPublicPath) if !(lockedAccountInfoCap!.check()) { return unlockedBalance } let lockedAccountInfoRef = lockedAccountInfoCap!.borrow()! let lockedBalance = lockedAccountInfoRef.getLockedAccountBalance() return lockedBalance + unlockedBalance }`; export const getTotalFlowBalance = async (address) => { return await fcl.decode( await fcl.send([fcl.script(CODE), fcl.args([fcl.arg(address, t.Address)])]), ); }; ``` ## Contribute To release a new version of Dev Wallet is as simple as tagging and creating a release, a Github Action will then build a bundle of the Dev Wallet that can be used in other tools (such as CLI). If the update of the Dev Wallet is required in the CLI, create a seperate update PR on the CLI. For more information, visit the [fcl-dev-wallet GitHub repository]. ## More Additionally, you can explore these resources: - [Guide to Creating a Fungible Token on Flow] - [Tutorial on Fungible Tokens] - [Faucets] [FCL]: ../clients/fcl-js/index.md [Wallets page]: ../../../ecosystem/wallets.md [flow-cli/install/]: ../flow-cli/install.md [`@onflow/fcl`]: https://github.com/onflow/fcl-js [Flow emulator]: #start-the-emulator [Authenticate using FCL snippet]: https://academy.ecdao.org/en/snippets/fcl-authenticate [Wallet Discovery]: ../clients/fcl-js/discovery.md [Emerald Academy]: https://academy.ecdao.org/en/snippets/fcl-authenticate [create a new account]: https://cadence-lang.org/docs/language/accounts#account-creation [Create an Account with FCL snippet]: https://academy.ecdao.org/en/snippets/fcl-create-account [Create an Account in Cadence snippet]: https://academy.ecdao.org/en/snippets/cadence-create-account [fcl-dev-wallet GitHub repository]: https://github.com/onflow/fcl-dev-wallet [Guide to Creating a Fungible Token on Flow]: ../../../blockchain-development-tutorials/tokens/fungible-token-cadence.md [Tutorial on Fungible Tokens]: https://cadence-lang.org/docs/tutorial/fungible-tokens [Faucets]: ../../../ecosystem/faucets.md --- # File: /md/build/tools/react-native-sdk.mdx --- title: 'Flow React Native SDK' description: React Native hooks and components for interacting with the Flow blockchain. sidebar_position: 1 --- # Flow React Native SDK **The easiest way to build React Native apps on Flow.** A lightweight, TypeScript-first library for seamless Flow blockchain integration in your React Native apps. :::note This SDK shares the same hooks as the [Flow React SDK](../react-sdk/index.mdx), so if you're familiar with the web version, you'll feel right at home. The main differences are the React Native-specific components (`Connect`, `Profile`) and mobile wallet integrations. ::: ## Quick Start ### 1. Install ```bash npm install @onflow/react-native-sdk ``` ### 2. Wrap Your App Create a provider wrapper component: ```tsx title="components/flow-provider-wrapper.tsx" import { FlowProvider } from '@onflow/react-native-sdk'; import flowJSON from '../flow.json'; export function FlowProviderWrapper({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` Then wrap your app in the root layout: ```tsx title="app/_layout.tsx" import { FlowProviderWrapper } from '@/components/flow-provider-wrapper'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { View } from 'react-native'; export default function RootLayout() { return ( ); } ``` ### 3. Start Building ```tsx import { View, Text, Pressable } from 'react-native'; import { Connect, useFlowCurrentUser, useFlowQuery, } from '@onflow/react-native-sdk'; function MyApp() { const { user } = useFlowCurrentUser(); const { data: balance, isLoading, refetch, } = useFlowQuery({ cadence: ` import FlowToken from 0x7e60df042a9c0868 access(all) fun main(address: Address): UFix64 { let account = getAccount(address) let vaultRef = account.capabilities .get<&FlowToken.Vault>(/public/flowTokenBalance) .borrow() ?? panic("Could not borrow Balance reference") return vaultRef.balance } `, args: (arg, t) => [arg(user?.addr, t.Address)], query: { enabled: !!user?.addr }, }); return ( {user?.loggedIn && ( Welcome, {user.addr}! {isLoading ? ( Loading balance... ) : ( Balance: {balance ? String(balance) : '0.00'} FLOW )} refetch()}> Refresh )} ); } ``` :::info Starter Template Get started quickly with the [flow-react-native-sdk-starter](https://github.com/onflow/flow-react-native-sdk-starter) template which includes a pre-configured Expo project with wallet connection, balance queries, and transaction examples. ::: --- ## Configuration Options The `FlowProvider` accepts the following configuration: | Property | Description | | ------------------------ | ----------------------------------------------------------------------------- | | `accessNodeUrl` | REST endpoint for blockchain access (e.g., `https://rest-testnet.onflow.org`) | | `discoveryWallet` | URL for wallet discovery/selection UI | | `discoveryAuthnEndpoint` | API endpoint for authentication | | `flowNetwork` | Network selection: `"testnet"` or `"mainnet"` | | `appDetailTitle` | App name displayed in wallet | | `appDetailUrl` | App URL displayed in wallet | | `appDetailIcon` | App icon URL displayed in wallet | | `appDetailDescription` | App description displayed in wallet | | `walletconnectProjectId` | WalletConnect Cloud project ID | **Mainnet Configuration:** ```tsx config={{ accessNodeUrl: "https://rest-mainnet.onflow.org", discoveryWallet: "https://fcl-discovery.onflow.org/authn", discoveryAuthnEndpoint: "https://fcl-discovery.onflow.org/api/authn", flowNetwork: "mainnet", // ... other options }} ``` --- ## [Hooks](./hooks.md) **Cadence Hooks** for native Flow interactions: - Authentication & user management - Account details & balances - Block & transaction queries - Real-time event subscriptions - Script execution & mutations **Cross-VM Hooks** for bridging Cadence ↔ Flow EVM: - Atomic batch transactions - Token & NFT bridging - Cross-chain balance queries [→ View all hooks](./hooks.md) --- ## [Components](./components.md) Native UI components for React Native: - `` – Wallet authentication with balance display - `` – Standalone wallet information display [→ View all components](./components.md) --- ## Why Choose React Native SDK? **Developer Experience First** - TypeScript-native with full type safety - Familiar React Native patterns and conventions - Comprehensive error handling and loading states **Production Ready** - Built on battle-tested libraries (TanStack Query) - Automatic retries, caching, and background updates - Cross-VM support for hybrid Cadence/EVM applications **Mobile Native** - Native mobile wallet integrations via WalletConnect - React Native components that feel native - Expo and bare React Native support --- ## Need Help? - **[Hooks Documentation](./hooks.md)** – Detailed API reference for all hooks - **[Components Documentation](./components.md)** – UI components guide - **[Configuration Guide](../flow-cli/flow.json/configuration.md)** – Learn about configuring `flow.json` --- # File: /md/build/tools/react-native-sdk/components.md --- title: 'Components' description: Reusable UI components for Flow interactions in React Native. sidebar_position: 3 --- ## Connect A drop-in wallet connection component that handles the entire authentication flow. When disconnected, it displays a "Connect Wallet" button. When connected, it shows the user's address and opens a Profile modal on press. **Props:** - `onConnect?: () => void` – Callback triggered after successful authentication - `onDisconnect?: () => void` – Callback triggered after logout - `balanceType?: "cadence" | "evm" | "combined"` – Specifies which balance to display (default: `"cadence"`) - `"cadence"`: Shows the token balance from the Cadence side - `"evm"`: Shows the token balance from the Flow EVM side - `"combined"`: Shows the total combined token balance from both sides - `balanceTokens?: TokenConfig[]` – Optional array of token configurations to display in the balance selector - `modalEnabled?: boolean` – Whether to show the profile modal on press when connected (default: `true`) **Basic Usage:** The simplest way to add wallet connection to your app: ```tsx import { View, Text } from "react-native"; import { Connect } from "@onflow/react-native-sdk"; function WalletSection() { return ( Connect Wallet Connect your Flow wallet to interact with the blockchain. ); } ``` **With Callbacks:** ```tsx import { Connect } from "@onflow/react-native-sdk"; console.log("Wallet connected!")} onDisconnect={() => console.log("Wallet disconnected")} /> ``` **With Balance Display:** ```tsx import { Connect } from "@onflow/react-native-sdk"; ``` --- ## Profile A standalone component for displaying wallet information including account address and balance. Use this when you want to show user details separately from the Connect button. **Props:** - `onDisconnect?: () => void` – Callback triggered when the user presses the disconnect button - `balanceType?: "cadence" | "evm" | "combined"` – Specifies which balance to display (default: `"cadence"`) - `"cadence"`: Shows the token balance from the Cadence side - `"evm"`: Shows the token balance from the Flow EVM side - `"combined"`: Shows the total combined token balance from both sides - `balanceTokens?: TokenConfig[]` – Optional array of token configurations to display in the balance selector **Usage:** ```tsx import { View } from "react-native"; import { Profile, useFlowCurrentUser } from "@onflow/react-native-sdk"; function UserProfile() { const { user } = useFlowCurrentUser(); if (!user?.loggedIn) { return null; } return ( console.log("User disconnected")} /> ); } ``` --- # File: /md/build/tools/react-native-sdk/hooks.md --- title: 'Hooks' description: React hooks for interacting with the Flow blockchain in React Native. sidebar_position: 2 --- :::info Many of these hooks are built using [`@tanstack/react-query`](https://tanstack.com/query/latest), which provides powerful caching, revalidation, and background refetching features. As a result, you'll see return types like `UseQueryResult` and `UseMutationResult` throughout this section. Other types—such as `Account`, `Block`, and `CurrentUser`—are from the [Flow Client Library (FCL) TypeDefs](https://github.com/onflow/fcl-js/blob/master/packages/typedefs/src/index.ts). Refer to their respective documentation for full type definitions and usage patterns. ::: ## Cadence Hooks ### `useFlowCurrentUser` ```tsx import { useFlowCurrentUser } from "@onflow/react-native-sdk" ``` #### Parameters - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: - `user: CurrentUser` – The current user object from FCL - `authenticate: () => Promise` – Triggers wallet authentication - `unauthenticate: () => void` – Logs the user out ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function AuthComponent() { const { user, authenticate, unauthenticate } = useFlowCurrentUser() return ( {user?.loggedIn ? ( Logged in as {user?.addr} Logout ) : ( Login )} ) } ``` --- ### `useFlowAccount` ```tsx import { useFlowAccount } from "@onflow/react-native-sdk" ``` #### Parameters: - `address?: string` – Flow address (with or without `0x` prefix) - `query?: UseQueryOptions` – Optional TanStackQuery options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function AccountDetails() { const { data: account, isLoading, error, refetch } = useFlowAccount({ address: "0x1cf0e2f2f715450", query: { staleTime: 5000 }, }) if (isLoading) return Loading account... if (error) return Error fetching account: {error.message} if (!account) return No account data return ( Account: {account.address} Balance: {account.balance} refetch()}> Refetch ) } ``` --- ### `useFlowBlock` ```tsx import { useFlowBlock } from "@onflow/react-native-sdk" ``` #### Parameters: - `sealed?: boolean` – If `true`, fetch latest sealed block - `id?: string` – Block by ID - `height?: number` – Block by height - `query?: UseQueryOptions` – Optional TanStackQuery options - `flowClient?: FlowClient` - Optional `FlowClient` instance Only one of `sealed`, `id`, or `height` should be provided. #### Returns: `UseQueryResult` ```tsx import { View, Text } from 'react-native'; function LatestBlock() { const { data: block, isLoading, error } = useFlowBlock({ query: { staleTime: 10000 } }) if (isLoading) return Loading... if (error) return Error: {error.message} if (!block) return No block data. return ( Block {block.height} ID: {block.id} ) } ``` --- ### `useFlowChainId` ```tsx import { useFlowChainId } from "@onflow/react-native-sdk" ``` This hook retrieves the Flow chain ID, which is useful for identifying the current network. #### Parameters: - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query options like `staleTime`, `enabled`, etc. - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Valid chain IDs include: `testnet` (Flow Testnet), `mainnet` (Flow Mainnet), and `emulator` (Flow Emulator). The `flow-` prefix will be stripped from the chain ID returned by the access node (e.g. `flow-testnet` will return `testnet`). ```tsx import { View, Text } from 'react-native'; function ChainIdExample() { const { data: chainId, isLoading, error } = useFlowChainId({ query: { staleTime: 10000 }, }) if (isLoading) return Loading chain ID... if (error) return Error fetching chain ID: {error.message} return Current Flow Chain ID: {chainId} } ``` --- ### `useFlowClient` This hook returns the `FlowClient` for the current `` context. #### Parameters: - `flowClient?: FlowClient` - Optional `FlowClient` instance to override the result --- ### `useFlowConfig` ```tsx import { useFlowConfig } from "@onflow/react-native-sdk" ``` #### Returns: `FlowConfig` ```tsx import { View, Text } from 'react-native'; function MyComponent() { const config = useFlowConfig() return ( Current network: {config.flowNetwork} Current access node: {config.accessNodeUrl} ) } ``` --- ### `useFlowEvents` ```tsx import { useFlowEvents } from "@onflow/react-native-sdk" ``` #### Parameters: - `startBlockId?: string` – Optional ID of the block to start listening from - `startHeight?: number` – Optional block height to start listening from - `eventTypes?: string[]` – Array of event type strings (e.g., `A.0xDeaDBeef.Contract.EventName`) - `addresses?: string[]` – Filter by Flow addresses - `contracts?: string[]` – Filter by contract identifiers - `opts?: { heartbeatInterval?: number }` – Options for subscription heartbeat - `onEvent: (event: Event) => void` – Callback for each event received - `onError?: (error: Error) => void` – Optional error handler - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Example: ```tsx import { View, Text } from 'react-native'; function EventListener() { useFlowEvents({ eventTypes: ["A.0xDeaDBeef.SomeContract.SomeEvent"], onEvent: (event) => console.log("New event:", event), onError: (error) => console.error("Error:", error), }) return Listening for events... } ``` --- ### `useFlowQuery` ```tsx import { useFlowQuery } from "@onflow/react-native-sdk" ``` #### Parameters: - `cadence: string` – Cadence script to run - `args?: (arg, t) => unknown[]` – Function returning FCL arguments - `query?: UseQueryOptions` – Optional TanStackQuery options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function QueryExample() { const { data, isLoading, error, refetch } = useFlowQuery({ cadence: ` access(all) fun main(a: Int, b: Int): Int { return a + b } `, args: (arg, t) => [arg(1, t.Int), arg(2, t.Int)], query: { staleTime: 10000 }, }) if (isLoading) return Loading query... if (error) return Error: {error.message} return ( Result: {data} refetch()}> Refetch ) } ``` --- ### `useFlowQueryRaw` ```tsx import { useFlowQueryRaw } from "@onflow/react-native-sdk" ``` This hook is identical to `useFlowQuery` but returns the raw, non-decoded response data from the Flow blockchain. This is useful when you need access to the original response structure or want to handle decoding manually. #### Parameters: - `cadence: string` – Cadence script to run - `args?: (arg, t) => unknown[]` – Function returning FCL arguments - `query?: UseQueryOptions` – Optional TanStackQuery options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` The returned data will be in its raw, non-decoded format as received from the Flow access node. ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function QueryRawExample() { const { data: rawData, isLoading, error, refetch } = useFlowQueryRaw({ cadence: ` access(all) fun main(a: Int, b: Int): Int { return a + b } `, args: (arg, t) => [arg(1, t.Int), arg(2, t.Int)], query: { staleTime: 10000 }, }) if (isLoading) return Loading query... if (error) return Error: {error.message} return ( Raw Result: {JSON.stringify(rawData, null, 2)} refetch()}> Refetch ) } ``` --- ### `useFlowMutate` ```tsx import { useFlowMutate } from "@onflow/react-native-sdk" ``` #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseMutationResult` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; import * as fcl from '@onflow/fcl'; function CreatePage() { const { mutate, isPending, error, data: txId } = useFlowMutate({ mutation: { onSuccess: (txId) => console.log("TX ID:", txId), }, }) const sendTransaction = () => { mutate({ cadence: `transaction() { prepare(acct: &Account) { log(acct.address) } }`, args: (arg, t) => [], proposer: fcl.currentUser, payer: fcl.currentUser, authorizations: [], limit: 100, }) } return ( Send Transaction {isPending && Sending transaction...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- ### `useFlowRevertibleRandom` ```tsx import { useFlowRevertibleRandom } from "@onflow/react-native-sdk" ``` #### Parameters: - `min?: string` – Minimum random value (inclusive), as a UInt256 decimal string. Defaults to `"0"`. - `max: string` – Maximum random value (inclusive), as a UInt256 decimal string. **Required**. - `count?: number` – Number of random values to fetch (must be at least 1). Defaults to `1`. - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query settings like `staleTime`, `enabled`, `retry`, etc. - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Each `RevertibleRandomResult` includes: - `blockHeight: string` — The block height from which the random value was generated. - `value: string` — The random UInt256 value, returned as a decimal string. ```tsx import { View, Text, TouchableOpacity, FlatList } from 'react-native'; function RandomValues() { const { data: randoms, isLoading, error, refetch } = useFlowRevertibleRandom({ min: "0", max: "1000000000000000000000000", count: 3, query: { staleTime: 10000 }, }) if (isLoading) return Loading random numbers... if (error) return Error fetching random numbers: {error.message} if (!randoms) return No random values generated. return ( Generated Random Numbers idx.toString()} renderItem={({ item }) => ( Block {item.blockHeight}: {item.value} )} /> refetch()}> Regenerate ) } ``` #### Notes: * Randomness is generated using the **onchain `revertibleRandom`** function on Flow, producing pseudorandom values tied to block and script execution. * Values are **deterministic**: The values returned for identical calls within the same block will be identical. * If `count` is larger than one, the returned values are distinct. * This hook is designed for simple use cases that don't require unpredictability, such as randomized UIs. Since the hook uses script executions on existing blocks, the random source is already public and the randoms are predictable. * For **more advanced use cases** that **do** require onchain randomness logic via transactions, Flow provides built-in support using Cadence's `revertibleRandom` and [commit-reveal scheme]. [commit-reveal scheme]: ../../cadence/advanced-concepts/randomness#commit-reveal-scheme --- ### `useFlowTransaction` ```tsx import { useFlowTransaction } from "@onflow/react-native-sdk" ``` Fetches a Flow transaction by ID and returns the decoded transaction object. #### Parameters: * `txId?: string` – The Flow transaction ID or scheduled transaction ID to fetch. * `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query options like `staleTime`, `enabled`, etc. * `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function TransactionDetails({ txId }: { txId: string }) { const { data: transaction, isLoading, error, refetch } = useFlowTransaction({ txId, query: { staleTime: 10000 }, }) if (isLoading) return Loading transaction... if (error) return Error fetching transaction: {error.message} if (!transaction) return No transaction data. return ( Transaction ID: {transaction.id} Gas Limit: {transaction.gasLimit} Arguments: {JSON.stringify(transaction.arguments, null, 2)} refetch()}> Refetch ) } ``` --- ### `useFlowTransactionStatus` ```tsx import { useFlowTransactionStatus } from "@onflow/react-native-sdk" ``` #### Parameters: - `id: string` – Transaction ID or scheduled transaction ID to subscribe to - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: - `transactionStatus: TransactionStatus | null` - `error: Error | null` ```tsx import { View, Text } from 'react-native'; function TransactionStatusComponent() { const txId = "your-transaction-id-here" const { transactionStatus, error } = useFlowTransactionStatus({ id: txId }) if (error) return Error: {error.message} return Status: {transactionStatus?.statusString} } ``` --- ### `useFlowNftMetadata` ```tsx import { useFlowNftMetadata } from "@onflow/react-native-sdk" ``` This hook fetches NFT metadata including display information, traits, rarity, and collection details. #### Parameters: - `accountAddress?: string` – Flow address of the account holding the NFT - `tokenId?: string | number` – The NFT token ID - `publicPathIdentifier?: string` – Public path identifier for the collection - `query?: UseQueryOptions` – Optional TanStack Query options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Where `NftViewResult` is defined as: ```typescript interface NftViewResult { name: string description: string thumbnailUrl: string externalUrl?: string collectionName?: string collectionExternalUrl?: string tokenID: string traits?: Record rarity?: string serialNumber?: string } ``` ```tsx import { View, Text, Image, FlatList } from 'react-native'; function NftMetadataExample() { const { data: nft, isLoading, error } = useFlowNftMetadata({ accountAddress: "0x1cf0e2f2f715450", tokenId: "123", publicPathIdentifier: "exampleNFTCollection", query: { staleTime: 60000 }, }) if (isLoading) return Loading NFT metadata... if (error) return Error: {error.message} if (!nft) return NFT not found return ( {nft.name} {nft.description} {nft.collectionName && Collection: {nft.collectionName}} {nft.rarity && Rarity: {nft.rarity}} {nft.traits && ( Traits: {Object.entries(nft.traits).map(([key, value]) => ( {key}: {value} ))} )} ) } ``` --- ### `useFlowAuthz` ```tsx import { useFlowAuthz } from "@onflow/react-native-sdk" ``` A React hook that returns an authorization function for Flow transactions. If no custom authorization is provided, it returns the current user's wallet authorization. #### Parameters: - `authz?: AuthorizationFunction` – Optional custom authorization function - `flowClient?: FlowClient` - Optional `FlowClient` instance Where `AuthorizationFunction` is defined as: ```typescript type AuthorizationFunction = ( account: Partial ) => Partial | Promise> ``` #### Returns: `AuthorizationFunction` The authorization function is compatible with Flow transactions' authorizations parameter. ```tsx import { View, Text, TouchableOpacity } from 'react-native'; import * as fcl from '@onflow/fcl'; // Example 1: Using current user authorization function CurrentUserAuthExample() { const authorization = useFlowAuthz() const sendTransaction = async () => { const txId = await fcl.mutate({ cadence: ` transaction { prepare(signer: auth(Storage) &Account) { log(signer.address) } } `, authorizations: [authorization], limit: 100, }) console.log("Transaction ID:", txId) } return ( Send Transaction ) } ``` ```tsx // Example 2: Using custom authorization function function CustomAuthExample() { const customAuthz = (account) => ({ ...account, addr: "0xCUSTOMOADDRESS", keyId: 0, signingFunction: async (signable) => ({ signature: "0x...", }), }) const authorization = useFlowAuthz({ authz: customAuthz }) const sendTransaction = async () => { const txId = await fcl.mutate({ cadence: ` transaction { prepare(signer: auth(Storage) &Account) { log(signer.address) } } `, authorizations: [authorization], limit: 100, }) console.log("Transaction ID:", txId) } return ( Send Custom Auth Transaction ) } ``` --- ### `useFlowScheduledTransaction` ```tsx import { useFlowScheduledTransaction } from "@onflow/react-native-sdk" ``` Fetches a scheduled transaction by ID. #### Parameters: - `txId?: string` – Scheduled transaction ID - `includeHandlerData?: boolean` – Include handler data (default: false) - `query?: UseQueryOptions` – Optional TanStack Query options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Where `ScheduledTransaction` is defined as: ```typescript interface ScheduledTransaction { id: string priority: ScheduledTransactionPriority // 0 = Low, 1 = Medium, 2 = High executionEffort: bigint status: ScheduledTransactionStatus // 0 = Pending, 1 = Processing, 2 = Completed, 3 = Failed, 4 = Cancelled fees: { value: bigint formatted: string } scheduledTimestamp: number handlerTypeIdentifier: string handlerAddress: string handlerUUID?: string // Only included if includeHandlerData is true handlerResolvedViews?: {[viewType: string]: any} // Only included if includeHandlerData is true } ``` ```tsx import { View, Text } from 'react-native'; function ScheduledTransactionDetails({ txId }: { txId: string }) { const { data: transaction, isLoading, error } = useFlowScheduledTransaction({ txId, query: { staleTime: 10000 }, }) if (isLoading) return Loading scheduled transaction... if (error) return Error: {error.message} if (!transaction) return Transaction not found return ( Scheduled Transaction #{transaction.id} Status: {transaction.status} Priority: {transaction.priority} Fees: {transaction.fees.formatted} FLOW Handler: {transaction.handlerTypeIdentifier} ) } ``` --- ### `useFlowScheduledTransactionList` ```tsx import { useFlowScheduledTransactionList } from "@onflow/react-native-sdk" ``` Lists all scheduled transactions for an account. #### Parameters: - `account?: string` – Flow address to query - `includeHandlerData?: boolean` – Include handler data (default: false) - `query?: UseQueryOptions` – Optional TanStack Query options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` ```tsx import { View, Text, TouchableOpacity, FlatList } from 'react-native'; function ScheduledTransactionsList({ account }: { account: string }) { const { data: transactions, isLoading, error, refetch } = useFlowScheduledTransactionList({ account, query: { staleTime: 10000 }, }) if (isLoading) return Loading scheduled transactions... if (error) return Error: {error.message} if (!transactions || transactions.length === 0) return No scheduled transactions return ( Scheduled Transactions for {account} refetch()}> Refresh tx.id} renderItem={({ item: tx }) => ( Transaction #{tx.id} - Status: {tx.status} - Fees: {tx.fees.formatted} FLOW )} /> ) } ``` --- ### `useFlowScheduledTransactionCancel` ```tsx import { useFlowScheduledTransactionCancel } from "@onflow/react-native-sdk" ``` Cancels a scheduled transaction and refunds fees. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStack Query mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseFlowScheduledTransactionCancelResult` Where `UseFlowScheduledTransactionCancelResult` is defined as: ```typescript interface UseFlowScheduledTransactionCancelResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { cancelTransaction: (txId: string) => void cancelTransactionAsync: (txId: string) => Promise } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function CancelScheduledTransaction() { const { cancelTransactionAsync, isPending, error, data: txId } = useFlowScheduledTransactionCancel({ mutation: { onSuccess: (txId) => console.log("Cancel transaction ID:", txId), }, }) const handleCancel = async (scheduledTxId: string) => { try { const resultTxId = await cancelTransactionAsync(scheduledTxId) console.log("Successfully canceled scheduled transaction:", resultTxId) } catch (error) { console.error("Failed to cancel:", error) } } return ( handleCancel("42")} disabled={isPending}> Cancel Scheduled Transaction #42 {isPending && Canceling transaction...} {error && Error: {error.message}} {txId && Cancel Transaction ID: {txId}} ) } ``` --- ### `useFlowScheduledTransactionSetup` ```tsx import { useFlowScheduledTransactionSetup } from "@onflow/react-native-sdk" ``` Sets up the Transaction Scheduler Manager resource. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStack Query mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseFlowScheduledTransactionSetupResult` Where `UseFlowScheduledTransactionSetupResult` is defined as: ```typescript interface UseFlowScheduledTransactionSetupResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { setup: () => void setupAsync: () => Promise } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function SchedulerSetup() { const { setupAsync, isPending, error, data: txId } = useFlowScheduledTransactionSetup({ mutation: { onSuccess: (txId) => console.log("Setup transaction ID:", txId), }, }) const handleSetup = async () => { try { const resultTxId = await setupAsync() console.log("Scheduler setup successful:", resultTxId) } catch (error) { console.error("Setup failed:", error) } } return ( Setup Transaction Scheduler {isPending && Setting up scheduler...} {error && Error: {error.message}} {txId && Setup Transaction ID: {txId}} ) } ``` --- ## Cross-VM Hooks ### `useCrossVmBatchTransaction` ```tsx import { useCrossVmBatchTransaction } from "@onflow/react-native-sdk" ``` This hook allows you to execute multiple EVM transactions in a single atomic Cadence transaction. It is useful for batch processing EVM calls while ensuring they are executed together, either all succeeding or allowing for some to fail without affecting the others. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmBatchTransactionResult` Where `UseCrossVmBatchTransactionResult` is defined as: ```typescript interface UseCrossVmBatchTransactionResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { mutate: (calls: UseCrossVmBatchTransactionMutateArgs) => void mutateAsync: (calls: UseCrossVmBatchTransactionMutateArgs) => Promise } ``` Where `UseCrossVmBatchTransactionMutateArgs` is defined as: ```typescript interface UseCrossVmBatchTransactionMutateArgs { calls: EvmBatchCall[] mustPass?: boolean } ``` Where `EvmBatchCall` is defined as: ```typescript interface EvmBatchCall { // The target EVM contract address (as a string) address: string // The contract ABI fragment abi: Abi // The name of the function to call functionName: string // The function arguments args?: readonly unknown[] // The gas limit for the call gasLimit?: bigint // The value to send with the call value?: bigint } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function CrossVmBatchTransactionExample() { const { sendBatchTransaction, isPending, error, data: txId } = useCrossVmBatchTransaction({ mutation: { onSuccess: (txId) => console.log("TX ID:", txId), }, }) const sendTransaction = () => { const calls = [ { address: "0x1234567890abcdef", abi: { // ABI definition for the contract }, functionName: "transfer", args: ["0xabcdef1234567890", 100n], gasLimit: 21000n, }, ] sendBatchTransaction({calls}) } return ( Send Cross-VM Transaction {isPending && Sending transaction...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- ### `useCrossVmTokenBalance` ```tsx import { useCrossVmTokenBalance } from "@onflow/react-native-sdk" ``` Fetch the balance of a token balance for a given user across both Cadence and EVM environments. #### Parameters: - `owner: string` – Cadence address of the account whose token balances you want. - `vaultIdentifier?: string` – Optional Cadence resource identifier (e.g. "0x1cf0e2f2f715450.FlowToken.Vault") for onchain balance - `erc20AddressHexArg?: string` – Optional bridged ERC-20 contract address (hex) for EVM/COA balance - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query config (e.g. staleTime, enabled) - `flowClient?: FlowClient` - Optional `FlowClient` instance > **Note:** You must pass `owner`, and one of `vaultIdentifier` or `erc20AddressHexArg`. #### Returns: `UseQueryResult` Where `UseCrossVmTokenBalanceData` is defined as: ```typescript interface UseCrossVmTokenBalanceData { cadence: TokenBalance // Token balance of Cadence vault evm: TokenBalance // Token balance of EVM (COA stored in /storage/coa) combined: TokenBalance // Combined balance of both Cadence and EVM } ``` Where `TokenBalance` is defined as: ```typescript interface TokenBalance { value: bigint // Balance value in smallest unit formatted: string // Formatted balance string (e.g. "123.45") precision: number // Number of decimal places for the token } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function UseCrossVmTokenBalanceExample() { const { data, isLoading, error, refetch } = useCrossVmTokenBalance({ owner: '0x1e4aa0b87d10b141', vaultIdentifier: 'A.1654653399040a61.FlowToken.Vault', query: { staleTime: 10000 }, }); if (isLoading) return Loading token balance... if (error) return Error fetching token balance: {error.message} return ( Token Balances Cadence Balance: {data.cadence.formatted} (Value: {data.cadence.value.toString()}) EVM Balance: {data.evm.formatted} (Value: {data.evm.value.toString()}) Combined Balance: {data.combined.formatted} (Value: {data.combined.value.toString()}) refetch()}> Refetch ) } ``` --- ### `useCrossVmTransactionStatus` ```tsx import { useCrossVmTransactionStatus } from "@onflow/react-native-sdk" ``` Subscribes to status updates for a given Cross-VM Flow transaction ID that executes EVM calls. This hook monitors the transaction status and extracts EVM call results if available. #### Parameters: - `id?: string` – Optional Flow transaction ID to monitor - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmTransactionStatusResult` Where `UseCrossVmTransactionStatusResult` is defined as: ```typescript interface UseCrossVmTransactionStatusResult { transactionStatus: TransactionStatus | null // Latest transaction status, or null before any update evmResults?: CallOutcome[] // EVM transaction results, if available error: Error | null // Any error encountered during status updates } ``` Where `CallOutcome` is defined as: ```typescript interface CallOutcome { status: "passed" | "failed" | "skipped" // Status of the EVM call hash?: string // EVM transaction hash if available errorMessage?: string // Error message if the call failed } ``` ```tsx import { View, Text, FlatList } from 'react-native'; function CrossVmTransactionStatusComponent() { const txId = "your-cross-vm-transaction-id-here" const { transactionStatus, evmResults, error } = useCrossVmTransactionStatus({ id: txId }) if (error) return Error: {error.message} return ( Flow Status: {transactionStatus?.statusString} {evmResults && evmResults.length > 0 && ( EVM Call Results: idx.toString()} renderItem={({ item, index }) => ( Call {index}: {item.status} {item.hash && Hash: {item.hash}} {item.errorMessage && Error: {item.errorMessage}} )} /> )} ) } ``` --- ### `useCrossVmBridgeNftFromEvm` ```tsx import { useCrossVmBridgeNftFromEvm } from "@onflow/react-native-sdk" ``` This hook bridges NFTs from Flow EVM to Cadence. It withdraws an NFT from the signer's COA (Cadence Owned Account) in EVM and deposits it into their Cadence collection. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmBridgeNftFromEvmTxResult` Where `UseCrossVmBridgeNftFromEvmTxResult` is defined as: ```typescript interface UseCrossVmBridgeNftFromEvmTxResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeNftFromEvm: (args: UseCrossVmBridgeNftFromEvmTxMutateArgs) => void crossVmBridgeNftFromEvmAsync: (args: UseCrossVmBridgeNftFromEvmTxMutateArgs) => Promise } ``` Where `UseCrossVmBridgeNftFromEvmTxMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeNftFromEvmTxMutateArgs { nftIdentifier: string // Cadence type identifier (e.g., "A.0x123.MyNFT.NFT") nftId: string // EVM NFT ID as string representation of UInt256 } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function BridgeNftFromEvmExample() { const { crossVmBridgeNftFromEvm, isPending, error, data: txId } = useCrossVmBridgeNftFromEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeNftFromEvm({ nftIdentifier: "A.0x1cf0e2f2f715450.ExampleNFT.NFT", nftId: "123", }) } return ( Bridge NFT from EVM {isPending && Bridging NFT...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- ### `useCrossVmBridgeNftToEvm` ```tsx import { useCrossVmBridgeNftToEvm } from "@onflow/react-native-sdk" ``` This hook bridges NFTs from Cadence to Flow EVM and executes arbitrary EVM transactions atomically. It withdraws NFTs from the signer's Cadence collection and deposits them into their COA in EVM, then executes the provided EVM calls. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmBridgeNftToEvmTxResult` Where `UseCrossVmBridgeNftToEvmTxResult` is defined as: ```typescript interface UseCrossVmBridgeNftToEvmTxResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeNftToEvm: (args: UseCrossVmBridgeNftToEvmTxMutateArgs) => void crossVmBridgeNftToEvmAsync: (args: UseCrossVmBridgeNftToEvmTxMutateArgs) => Promise } ``` Where `UseCrossVmBridgeNftToEvmTxMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeNftToEvmTxMutateArgs { nftIdentifier: string // Cadence NFT type identifier nftIds: string[] // Array of NFT IDs to bridge calls: EvmBatchCall[] // Array of EVM calls to execute after bridging } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function BridgeNftToEvmExample() { const { crossVmBridgeNftToEvm, isPending, error, data: txId } = useCrossVmBridgeNftToEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeNftToEvm({ nftIdentifier: "A.0x1cf0e2f2f715450.ExampleNFT.NFT", nftIds: ["1", "2", "3"], calls: [ { address: "0x1234567890abcdef1234567890abcdef12345678", abi: myContractAbi, functionName: "transferNFT", args: ["0xRecipient", 1n], gasLimit: 100000n, }, ], }) } return ( Bridge NFTs to EVM {isPending && Bridging NFTs...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- ### `useCrossVmBridgeTokenFromEvm` ```tsx import { useCrossVmBridgeTokenFromEvm } from "@onflow/react-native-sdk" ``` This hook bridges fungible tokens from Flow EVM to Cadence. It withdraws tokens from the signer's COA in EVM and deposits them into their Cadence vault. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmBridgeTokenFromEvmResult` Where `UseCrossVmBridgeTokenFromEvmResult` is defined as: ```typescript interface UseCrossVmBridgeTokenFromEvmResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeTokenFromEvm: (args: UseCrossVmBridgeTokenFromEvmMutateArgs) => void crossVmBridgeTokenFromEvmAsync: (args: UseCrossVmBridgeTokenFromEvmMutateArgs) => Promise } ``` Where `UseCrossVmBridgeTokenFromEvmMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeTokenFromEvmMutateArgs { vaultIdentifier: string // Cadence vault type identifier (e.g., "A.0x123.FlowToken.Vault") amount: string // Amount as UInt256 string representation } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function BridgeTokenFromEvmExample() { const { crossVmBridgeTokenFromEvm, isPending, error, data: txId } = useCrossVmBridgeTokenFromEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeTokenFromEvm({ vaultIdentifier: "A.0x1654653399040a61.FlowToken.Vault", amount: "1000000000", // Amount in smallest unit }) } return ( Bridge Tokens from EVM {isPending && Bridging tokens...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- ### `useCrossVmBridgeTokenToEvm` ```tsx import { useCrossVmBridgeTokenToEvm } from "@onflow/react-native-sdk" ``` This hook bridges fungible tokens from Cadence to Flow EVM and executes arbitrary EVM transactions atomically. It withdraws tokens from the signer's Cadence vault and deposits them into their COA in EVM, then executes the provided EVM calls. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmBridgeTokenToEvmResult` Where `UseCrossVmBridgeTokenToEvmResult` is defined as: ```typescript interface UseCrossVmBridgeTokenToEvmResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeTokenToEvm: (args: UseCrossVmBridgeTokenToEvmMutateArgs) => void crossVmBridgeTokenToEvmAsync: (args: UseCrossVmBridgeTokenToEvmMutateArgs) => Promise } ``` Where `UseCrossVmBridgeTokenToEvmMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeTokenToEvmMutateArgs { vaultIdentifier: string // Cadence vault type identifier amount: string // Amount as decimal string (e.g., "1.5") calls: EvmBatchCall[] // Array of EVM calls to execute after bridging } ``` ```tsx import { View, Text, TouchableOpacity } from 'react-native'; function BridgeTokenToEvmExample() { const { crossVmBridgeTokenToEvm, isPending, error, data: txId } = useCrossVmBridgeTokenToEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeTokenToEvm({ vaultIdentifier: "A.0x1654653399040a61.FlowToken.Vault", amount: "10.5", calls: [ { address: "0x1234567890abcdef1234567890abcdef12345678", abi: erc20Abi, functionName: "transfer", args: ["0xRecipient", 1000000n], gasLimit: 100000n, }, ], }) } return ( Bridge Tokens to EVM {isPending && Bridging tokens...} {error && Error: {error.message}} {txId && Transaction ID: {txId}} ) } ``` --- # File: /md/build/tools/react-sdk.mdx --- title: 'Flow React SDK' description: React hooks and components for interacting with the Flow blockchain. sidebar_position: 1 --- import ReactSDKOverview from '@site/src/components/ReactSDKOverview'; import FlowProviderDemo from '@site/src/components/FlowProviderDemo'; import { Connect } from '@onflow/react-sdk'; # Flow React SDK **The easiest way to build React apps on Flow.** A lightweight, TypeScript-first library that makes Flow blockchain interactions feel native to React development. ## Quick Start ### 1. Install ```bash npm install @onflow/react-sdk ``` ### 2. Wrap Your App ```tsx import React from 'react'; import App from './App'; import { FlowProvider } from '@onflow/react-sdk'; import flowJSON from '../flow.json'; function Root() { return ( ); } export default Root; ``` :::tip Next.js Users Create a client component wrapper for the `FlowProvider`: ```tsx title="components/FlowProviderWrapper.tsx" 'use client'; import { FlowProvider } from '@onflow/react-sdk'; import flowJSON from '../flow.json'; export default function FlowProviderWrapper({ children }) { return ( {children} ); } ``` Then use it in your `layout.tsx`: ```tsx title="app/layout.tsx" import FlowProviderWrapper from '@/components/FlowProviderWrapper'; export default function RootLayout({ children }) { return ( {children} ); } ``` ::: ### 3. Start Building ```tsx import { useFlowCurrentUser, Connect, useFlowQuery } from '@onflow/react-sdk'; function MyApp() { const { user } = useFlowCurrentUser(); const { data: greeting } = useFlowQuery({ cadence: `access(all) fun main(): String { return "Hello, Flow!" }`, args: (arg, t) => [], }); return (
    {user?.loggedIn &&

    Welcome, {user.addr}!

    }

    {greeting}

    ); } ``` --- ## 🎣 [Hooks](./hooks.md) **Cadence Hooks** for native Flow interactions: - Authentication & user management - Account details & balances - Block & transaction queries - Real-time event subscriptions - Script execution & mutations **Cross-VM Hooks** for bridging Cadence ↔ Flow EVM: - Atomic batch transactions - Token & NFT bridging - Cross-chain balance queries [→ View all hooks](./hooks.md) --- ## 🎨 [Components](./components.md) Beautiful, accessible UI components: - `` – Wallet authentication with balance display - `` – Smart transaction execution - `` – Real-time transaction tracking - `` – Network-aware block explorer links [→ View all components](./components.md) --- ## Why Choose React SDK? **Developer Experience First** - TypeScript-native with full type safety - Familiar React patterns and conventions - Comprehensive error handling and loading states **Production Ready** - Built on battle-tested libraries (TanStack Query, Tailwind CSS) - Automatic retries, caching, and background updates - Cross-VM support for hybrid Cadence/EVM applications **Customizable** - Theme system for brand consistency - Composable hooks for custom UI - Dark mode support out of the box --- ## Need Help? - 📖 **[Hooks Documentation](./hooks.md)** – Detailed API reference for all hooks - 🎨 **[Components Documentation](./components.md)** – UI components and theming guide - 🔗 **[Configuration Guide](../flow-cli/flow.json/configuration.md)** – Learn about configuring `flow.json` --- # File: /md/build/tools/react-sdk/components.md --- title: 'Components' description: Reusable UI components for Flow interactions. sidebar_position: 3 --- import { Connect, TransactionDialog, TransactionLink, TransactionButton } from "@onflow/react-sdk" import { FlowProvider } from "@onflow/react-sdk" import FlowProviderDemo from '@site/src/components/FlowProviderDemo'; import TransactionDialogDemo from '@site/src/components/TransactionDialogDemo'; import PlaygroundButton from '@site/src/components/PlaygroundButton'; # React SDK Components ## Components ### `Connect` A drop-in wallet connection component with UI for copy address, logout, and balance display. Displays user scheduled transactions within its profile modal with support for multiple tokens.
    **Props:** - `variant?: ButtonProps["variant"]` – Optional button style variant (default: `"primary"`). - `onConnect?: () => void` – Callback triggered after successful authentication. - `onDisconnect?: () => void` – Callback triggered after logout. - `balanceType?: "cadence" | "evm" | "combined"` – Specifies which balance to display (default: `"cadence"`). Options: - `"cadence"`: Shows the token balance from the Cadence side. - `"evm"`: Shows the token balance from the Flow EVM side. - `"combined"`: Shows the total combined token balance from both sides. - `balanceTokens?: TokenConfig[]` – Optional array of token configurations to display in the balance selector. Each `TokenConfig` requires: - `symbol: string` – Token symbol (for example, "FLOW", "USDC") - `name: string` – Full token name. - Either `vaultIdentifier: string` (for Cadence tokens) or `erc20Address: string` (for EVM tokens). - `modalConfig?: ConnectModalConfig` – Optional configuration for the profile modal: - `scheduledTransactions.show?: boolean` – Whether to show the scheduled transactions tab (default: `false`). - `scheduledTransactions.filterHandlerTypes?: string[]` – Optional array of handler type identifiers to filter displayed transactions. - `modalEnabled?: boolean` – Whether to show the profile modal on click when connected (default: `true`). When `false`, click the button when connected to disconnect. :::note WalletConnect Support To turn on WalletConnect as a wallet option, add your registered project ID to the `walletconnectProjectId` field in your `FlowProvider` config. ::: ```tsx import { Connect } from "@onflow/react-sdk" console.log("Connected!")} onDisconnect={() => console.log("Logged out")} /> ``` #### Live Demo console.log("Connected!")} onDisconnect={() => console.log("Logged out")} /> --- ### `Profile` A standalone component used to display wallet information, such as account address, balance and optional scheduled transactions.
    **Props:** - `onDisconnect?: () => void` – Callback triggered when the user disconnects. - `balanceType?: "cadence" | "evm" | "combined"` – Specifies which balance to display (default: `"cadence"`). Options: - `"cadence"`: Shows the token balance from the Cadence side. - `"evm"`: Shows the token balance from the Flow EVM side. - `"combined"`: Shows the total combined token balance from both sides. - `balanceTokens?: TokenConfig[]` – Optional array of token configurations to display in the balance selector. Each `TokenConfig` requires: - `symbol: string` – Token symbol (for example, "FLOW", "USDC"). - `name: string` – Full token name. - Either `vaultIdentifier: string` (for Cadence tokens) or `erc20Address: string` (for EVM tokens). - `profileConfig?: ProfileConfig` – Optional configuration for the profile display: - `scheduledTransactions.show?: boolean` – Whether to show the scheduled transactions tab (default: `false`). - `scheduledTransactions.filterHandlerTypes?: string[]` – Optional array of handler type identifiers to filter displayed transactions. - `className?: string` – Optional custom CSS class. - `style?: React.CSSProperties` – Optional inline styles. :::note WalletConnect Support To turn on WalletConnect as a wallet option, add your registered project ID to the `walletconnectProjectId` field in your `FlowProvider` config. ::: ```tsx import { Profile } from "@onflow/react-sdk" console.log("User disconnected")} /> ``` --- ### `TransactionButton` Button component used to execute Flow transactions with built-in loading states and global transaction management.
    **Props:** - `transaction: Parameters[0]` – Flow transaction object to execute when clicked. - `label?: string` – Optional custom button label (default: `"Execute Transaction"`). - `mutation?: UseMutationOptions[0]>` – Optional TanStack React Query mutation options. - `...buttonProps` – All other `ButtonProps` except `onClick` and `children` (includes `variant`, `disabled`, `className`, and so on). ```tsx import { TransactionButton } from "@onflow/react-sdk" const myTransaction = { cadence: ` transaction() { prepare(acct: &Account) { log("Hello from ", acct.address) } } `, args: (arg, t) => [], limit: 100, } console.log("Transaction sent:", txId), onError: (error) => console.error("Transaction failed:", error), }} /> ``` #### Live Demo [], limit: 100, }} label="Demo Transaction" /> --- ### `TransactionDialog` Dialog component for real-time transaction status updates.
    **Props:** - `open: boolean` – Whether the dialog is open. - `onOpenChange: (open: boolean) => void` – Callback to open and close dialog - `txId?: string` – Optional Flow transaction ID or scheduled transaction ID to track. - `onSuccess?: () => void` – Optional callback when transaction is successful. - `pendingTitle?: string` – Optional custom pending state title. - `pendingDescription?: string` – Optional custom pending state description. - `successTitle?: string` – Optional custom success state title. - `successDescription?: string` – Optional custom success state description. - `closeOnSuccess?: boolean` – If `true`, closes the dialog automatically after success. ```tsx import { TransactionDialog } from "@onflow/react-sdk" ``` #### Live Demo --- ### `TransactionLink` Link to the block explorer with the appropriate network scoped to transaction ID or scheduled transaction ID.
    **Props:** - `txId: string` – The transaction ID or scheduled transaction ID to link to. - `variant?: ButtonProps["variant"]` – Optional button variant (defaults to `"link"`). ```tsx import { TransactionLink } from "@onflow/react-sdk" ``` #### Live Demo --- ### `NftCard` A component used to render a NFT with image, name, description, collection details, traits and external links. Features include loading states, error handling, dark mode support and optional custom actions.
    **Props:** - `accountAddress: string` – The Flow account address that owns the NFT. - `tokenId: string | number` – The ID of the NFT. - `publicPathIdentifier: string` – The public path identifier for the NFT collection (for example, "A.0b2a3299cc857e29.TopShot.Collection"). - `showTraits?: boolean` – Whether to display NFT traits or attributes (default: `false`). Shows up to four traits with a button to view all. - `showExtra?: boolean` – Whether to display additional information like serial number, rarity, and external links (default: `false`). - `actions?: NftCardAction[]` – Optional array of custom action buttons displayed in a dropdown menu. Each action requires: - `title: string` – Display text for the action. - `onClick: () => Promise | void` – Handler function called when action is clicked. - `className?: string` – Optional custom CSS class. - `style?: React.CSSProperties` – Optional inline styles. ```tsx import { NftCard } from "@onflow/react-sdk" { // Handle transfer logic } }, { title: "List for Sale", onClick: async () => { // Handle listing logic } } ]} /> ``` --- ### `ScheduledTransactionList` A component used to display scheduled transactions for a Flow account. Shows transaction metadata such as thumbnails, descriptions, priority, scheduled time, execution effort, fees and provides an optional transaction cancellation functionality.
    **Props:** - `address: string` – The Flow account address to fetch scheduled transactions for. - `filterHandlerTypes?: string[]` – Optional array of handler type identifiers to filter which transactions are displayed. Only transactions with matching `handlerTypeIdentifier` will be shown. - `cancelEnabled?: boolean` – Whether to show the cancel button for transactions (default: `true`). - `className?: string` – Optional custom CSS class. - `style?: React.CSSProperties` – Optional inline styles. - `flowClient?: UseFlowScheduledTransactionListArgs["flowClient"]` – Optional custom Flow client instance. ```tsx import { ScheduledTransactionList } from "@onflow/react-sdk" ``` --- ## Theming ### How theming works All UI components in `@onflow/react-sdk` are styled with [Tailwind CSS] utility classes. The kit supports both light and dark themes out of the box, and uses Tailwind's `dark:` variant for dark mode styling. To customize the look and feel of the kit, you can provide a custom theme to the `FlowProvider` via the `theme` prop. This allows you to override default colors and styles to better match your app's branding. ### Theme Colors The theme object accepts a `colors` property with the following options: | Property | Description | Default | |----------|-------------|---------| | `primary` | Primary action color (CTAs, main buttons) | `flow-bg-slate-900 dark:flow-bg-white` | | `primaryForeground` | Text color on primary backgrounds | `flow-text-white dark:flow-text-slate-900` | | `secondary` | Secondary action color (secondary buttons) | `flow-bg-slate-100 dark:flow-bg-slate-800` | | `secondaryForeground` | Text color on secondary backgrounds | `flow-text-slate-900 dark:flow-text-slate-100` | | `accent` | Accent color for highlights, selected states | `flow-bg-slate-800 dark:flow-bg-slate-200` | | `background` | Default background color (cards, modals) | `flow-bg-white dark:flow-bg-slate-800` | | `foreground` | Default text color | `flow-text-slate-900 dark:flow-text-slate-100` | | `muted` | Muted/subtle background color | `flow-bg-slate-100 dark:flow-bg-slate-700` | | `mutedForeground` | Muted text color | `flow-text-slate-500 dark:flow-text-slate-400` | | `border` | Border color | `flow-border-slate-200 dark:flow-border-slate-700` | | `success` | Success state color | `flow-text-green-600 dark:flow-text-green-400` | | `error` | Error state color | `flow-text-red-600 dark:flow-text-red-400` | | `link` | Link text color | `flow-text-slate-900 dark:flow-text-slate-100` | ### Example ```tsx import { FlowProvider } from "@onflow/react-sdk" const customTheme = { colors: { primary: "flow-bg-purple-600 dark:flow-bg-purple-400", primaryForeground: "flow-text-white dark:flow-text-purple-900", secondary: "flow-bg-emerald-500 dark:flow-bg-emerald-400", secondaryForeground: "flow-text-white dark:flow-text-emerald-900", accent: "flow-bg-purple-700 dark:flow-bg-purple-300", border: "flow-border-purple-200 dark:flow-border-purple-700", } } function App() { return ( ) } ``` You only need to specify the colors you want to override—any unspecified colors will use the default values. --- ## Dark mode ### How dark mode works Dark mode is **fully controlled by the parent app** with the `darkMode` prop on `FlowProvider`. The kit does not manage dark mode state internally—this gives you full control and ensures the kit always matches your app's theme. - `darkMode={false}` (default): Forces all kit components to use light mode styles. - `darkMode={true}`: Forces all kit components to use dark mode styles. - You can dynamically change the `darkMode` prop to switch themes at runtime. **Example:** ```tsx function App() { // Parent app manages dark mode state const [isDark, setIsDark] = useState(false) return ( ) } ``` **Access dark mode in components:** You can use the `useDarkMode` hook to check the current mode inside your components: ```tsx import { useDarkMode } from "@onflow/react-sdk" function MyComponent() { // useDarkMode only returns the current state, no setter const { isDark } = useDarkMode() return
    {isDark ? "Dark mode" : "Light mode"}
    } ``` #### Notes - The kit does **not** automatically follow system preferences or save user choices. It's your responsibility to manage and pass the correct `darkMode` value. - All kit components will automatically apply the correct Tailwind `dark:` classes based on the `darkMode` prop. - For best results, ensure your app's global theme and the kit's `darkMode` prop are always in sync. [Tailwind CSS]: https://tailwindcss.com/ --- # File: /md/build/tools/react-sdk/hooks.md --- title: 'Hooks' description: React hooks for interacting with the Flow blockchain. sidebar_position: 2 --- import PlaygroundButton from '@site/src/components/PlaygroundButton'; # React SDK Hooks :::info Many of these hooks are built with [`@tanstack/react-query`], which provides powerful caching, revalidation, and background refetch features. As a result, you'll see return types like `UseQueryResult` and `UseMutationResult` throughout this section. Other types—such as `Account`, `Block`, and `CurrentUser`—are from the [Flow Client Library (FCL) TypeDefs]. Refer to the hooks' respective documentation for full type definitions and usage patterns. ::: ## Cadence hooks ### `useFlowCurrentUser` ```tsx import { useFlowCurrentUser } from "@onflow/react-sdk" ``` #### Parameters - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: - `user: CurrentUser` – The current user object from FCL. - `authenticate: () => Promise` – Triggers wallet authentication. - `unauthenticate: () => void` – Logs the user out. :::note WalletConnect Support To turn on WalletConnect as a wallet option, add your registered project ID to the `walletconnectProjectId` field in your `FlowProvider` config. ::: ```tsx function AuthComponent() { const { user, authenticate, unauthenticate } = useFlowCurrentUser() return (
    {user?.loggedIn ? ( <>

    Logged in as {user?.addr}

    ) : ( )}
    ) } ``` --- ### `useFlowAccount` ```tsx import { useFlowAccount } from "@onflow/react-sdk" ``` #### Parameters: - `address?: string` – Flow address (with or without `0x` prefix). - `query?: UseQueryOptions` – Optional TanStackQuery options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` ```tsx function AccountDetails() { const { data: account, isLoading, error, refetch } = useFlowAccount({ address: "0x1cf0e2f2f715450", query: { staleTime: 5000 }, }) if (isLoading) return

    Loading account...

    if (error) return

    Error fetching account: {error.message}

    if (!account) return

    No account data

    return (

    Account: {account.address}

    Balance: {account.balance}

    {account.code}
    ) } ``` --- ### `useFlowBlock` ```tsx import { useFlowBlock } from "@onflow/react-sdk" ``` #### Parameters: - `sealed?: boolean` – If `true`, fetch latest sealed block. - `id?: string` – Block by ID. - `height?: number` – Block by height. - `query?: UseQueryOptions` – Optional TanStackQuery options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. Only one of `sealed`, `id`, or `height` should be provided. #### Returns: `UseQueryResult` ```tsx function LatestBlock() { const { data: block, isLoading, error } = useFlowBlock({ query: { staleTime: 10000 } }) if (isLoading) return

    Loading...

    if (error) return

    Error: {error.message}

    if (!block) return

    No block data.

    return (

    Block {block.height}

    ID: {block.id}

    ) } ``` --- ### `useFlowChainId` ```tsx import { useFlowChainId } from "@onflow/react-sdk" ``` This hook retrieves the Flow chain ID, which is useful to help identify the current network. #### Parameters: - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query options like `staleTime`, `enabled`, etc. - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Valid chain IDs include: `testnet` (Flow Testnet), `mainnet` (Flow Mainnet), and `emulator` (Flow Emulator). The `flow-` prefix will be stripped from the chain ID returned by the access node (for example, `flow-testnet` will return `testnet`). ```tsx function ChainIdExample() { const { data: chainId, isLoading, error } = useFlowChainId({ query: { staleTime: 10000 }, }) if (isLoading) return

    Loading chain ID...

    if (error) return

    Error fetching chain ID: {error.message}

    return
    Current Flow Chain ID: {chainId}
    } ``` --- ### `useFlowClient` This hook returns the `FlowClient` for the current `` context. #### Parameters: - `flowClient?: FlowClient` - Optional `FlowClient` instance to override the result. --- ### `useFlowConfig` ```tsx import { useFlowConfig } from "@onflow/react-sdk" ``` #### Returns: `FlowConfig` ```tsx function MyComponent() { const config = useFlowConfig() return (

    Current network: {config.flowNetwork}

    Current access node: {config.accessNodeUrl}

    ) } ``` --- ### `useFlowEvents` ```tsx import { useFlowEvents } from "@onflow/react-sdk" ``` #### Parameters: - `startBlockId?: string` – Optional ID of the block to start listening from. - `startHeight?: number` – Optional block height to start listening from. - `eventTypes?: string[]` – Array of event type strings (for example, `A.0xDeaDBeef.Contract.EventName`) - `addresses?: string[]` – Filter by Flow addresses. - `contracts?: string[]` – Filter by contract identifiers. - `opts?: { heartbeatInterval?: number }` – Options for subscription heartbeat. - `onEvent: (event: Event) => void` – Callback for each event received. - `onError?: (error: Error) => void` – Optional error handler. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Example: ```tsx function EventListener() { useFlowEvents({ eventTypes: ["A.0xDeaDBeef.SomeContract.SomeEvent"], onEvent: (event) => console.log("New event:", event), onError: (error) => console.error("Error:", error), }) return
    Listening for events...
    } ``` --- ### `useFlowQuery` ```tsx import { useFlowQuery } from "@onflow/react-sdk" ``` #### Parameters: - `cadence: string` – Cadence script to run. - `args?: (arg, t) => unknown[]` – Function that returns FCL arguments. - `query?: UseQueryOptions` – Optional TanStackQuery options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` ```tsx function QueryExample() { const { data, isLoading, error, refetch } = useFlowQuery({ cadence: ` access(all) fun main(a: Int, b: Int): Int { return a + b } `, args: (arg, t) => [arg(1, t.Int), arg(2, t.Int)], query: { staleTime: 10000 }, }) if (isLoading) return

    Loading query...

    if (error) return

    Error: {error.message}

    return (

    Result: {data}

    ) } ``` --- ### `useFlowQueryRaw` ```tsx import { useFlowQueryRaw } from "@onflow/react-sdk" ``` This hook is identical to `useFlowQuery` but returns the raw, non-decoded response data from the Flow blockchain. This is useful when you need access to the original response structure or want to handle decoding manually. #### Parameters: - `cadence: string` – Cadence script to run. - `args?: (arg, t) => unknown[]` – Function that returns FCL arguments. - `query?: UseQueryOptions` – Optional TanStackQuery options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` The returned data will be in its raw, non-decoded format as received from the Flow access node. ```tsx function QueryRawExample() { const { data: rawData, isLoading, error, refetch } = useFlowQueryRaw({ cadence: ` access(all) fun main(a: Int, b: Int): Int { return a + b } `, args: (arg, t) => [arg(1, t.Int), arg(2, t.Int)], query: { staleTime: 10000 }, }) if (isLoading) return

    Loading query...

    if (error) return

    Error: {error.message}

    return (

    Raw Result: {JSON.stringify(rawData, null, 2)}

    ) } ``` --- ### `useFlowMutate` ```tsx import { useFlowMutate } from "@onflow/react-sdk" ``` #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseMutationResult` ```tsx function CreatePage() { const { mutate, isPending, error, data: txId } = useFlowMutate({ mutation: { onSuccess: (txId) => console.log("TX ID:", txId), }, }) const sendTransaction = () => { mutate({ cadence: `transaction() { prepare(acct: &Account) { log(acct.address) } }`, args: (arg, t) => [], proposer: fcl.currentUser, payer: fcl.currentUser, authorizations: [], limit: 100, }) } return (
    {isPending &&

    Sending transaction...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` --- ### `useFlowRevertibleRandom` ```tsx import { useFlowRevertibleRandom } from "@onflow/react-sdk" ``` #### Parameters: - `min?: string` – Minimum random value (inclusive), as a UInt256 decimal string. Defaults to `"0"`. - `max: string` – Maximum random value (inclusive), as a UInt256 decimal string. **Required**. - `count?: number` – Number of random values to fetch (must be at least 1). Defaults to `1`. - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query settings like `staleTime`, `enabled`, `retry`, etc. - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseQueryResult` Each `RevertibleRandomResult` includes: - `blockHeight: string` — The block height from which the random value was generated. - `value: string` — The random UInt256 value, returned as a decimal string. ```tsx function RandomValues() { const { data: randoms, isLoading, error, refetch } = useFlowRevertibleRandom({ min: "0", max: "1000000000000000000000000", // Example large max count: 3, query: { staleTime: 10000 }, }) if (isLoading) return

    Loading random numbers...

    if (error) return

    Error fetching random numbers: {error.message}

    if (!randoms) return

    No random values generated.

    return (

    Generated Random Numbers

      {randoms.map((rand, idx) => (
    • Block {rand.blockHeight}: {rand.value}
    • ))}
    ) } ``` #### Notes: * Randomness is generated via the **onchain `revertibleRandom`** function on Flow, and produces pseudorandom values tied to block and script execution. * Values are **deterministic**: The values returned for identical calls within the same block will be identical. * If `count ` is larger than one, the returned values are distinct. * This hook is designed for simple use cases that don't require unpredictability, such as randomized UIs. Since the hook uses script executions on existing blocks, the random source is already public and the randoms are predictable. * For **more advanced use cases** that **do** require onchain randomness logic via transactions, Flow provides built-in support using Cadence's `revertibleRandom` and [commit-reveal scheme]. --- ### `useFlowTransaction` ```tsx import { useFlowTransaction } from "@onflow/react-sdk" ``` Fetches a Flow transaction by ID and returns the decoded transaction object. #### Parameters: * `txId?: string` – The Flow transaction ID or scheduled transaction ID to fetch. * `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query options like `staleTime`, `enabled`, and so on. * `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` ```tsx function TransactionDetails({ txId }: { txId: string }) { const { data: transaction, isLoading, error, refetch } = useFlowTransaction({ txId, query: { staleTime: 10000 }, }) if (isLoading) return

    Loading transaction...

    if (error) return

    Error fetching transaction: {error.message}

    if (!transaction) return

    No transaction data.

    return (

    Transaction ID: {transaction.id}

    Gas Limit: {transaction.gasLimit}

    Arguments: {JSON.stringify(transaction.arguments, null, 2)}
    ) } ``` --- ### `useFlowTransactionStatus` ```tsx import { useFlowTransactionStatus } from "@onflow/react-sdk" ``` #### Parameters: - `id: string` – Transaction ID or scheduled transaction ID to subscribe to. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: - `transactionStatus: TransactionStatus | null` - `error: Error | null` ```tsx function TransactionStatusComponent() { const txId = "your-transaction-id-here" const { transactionStatus, error } = useFlowTransactionStatus({ id: txId }) if (error) return
    Error: {error.message}
    ; return
    Status: {transactionStatus?.statusString}
    ; } ``` --- ### `useDarkMode` ```tsx import { useDarkMode } from "@onflow/react-sdk" ``` This hook provides access to the current dark mode state from the `FlowProvider`. It's useful if you want to conditionally render content or apply custom styling based on the current theme. #### Returns: - `isDark: boolean` – Whether dark mode is currently turned on. ```tsx function ThemeAwareComponent() { const { isDark } = useDarkMode() return (

    Current Theme: {isDark ? "Dark" : "Light"}

    This component adapts to the current theme!

    ) } ``` --- ### `useFlowNftMetadata` ```tsx import { useFlowNftMetadata } from "@onflow/react-sdk" ``` This hook fetches NFT metadata including display information, traits, rarity, and collection details. #### Parameters: - `accountAddress?: string` – Flow address of the account which holds the NFT. - `tokenId?: string | number` – The NFT token ID. - `publicPathIdentifier?: string` – Public path identifier for the collection. - `query?: UseQueryOptions` – Optional TanStack Query options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` Where `NftViewResult` is defined as: ```typescript interface NftViewResult { name: string description: string thumbnailUrl: string externalUrl?: string collectionName?: string collectionExternalUrl?: string tokenID: string traits?: Record rarity?: string serialNumber?: string } ``` ```tsx function NftMetadataExample() { const { data: nft, isLoading, error } = useFlowNftMetadata({ accountAddress: "0x1cf0e2f2f715450", tokenId: "123", publicPathIdentifier: "exampleNFTCollection", query: { staleTime: 60000 }, }) if (isLoading) return

    Loading NFT metadata...

    if (error) return

    Error: {error.message}

    if (!nft) return

    NFT not found

    return (

    {nft.name}

    {nft.name}

    {nft.description}

    {nft.collectionName &&

    Collection: {nft.collectionName}

    } {nft.rarity &&

    Rarity: {nft.rarity}

    } {nft.traits && (

    Traits:

      {Object.entries(nft.traits).map(([key, value]) => (
    • {key}: {value}
    • ))}
    )}
    ) } ``` --- ### `useFlowAuthz` ```tsx import { useFlowAuthz } from "@onflow/react-sdk" ``` A React hook that returns an authorization function for Flow transactions. If no custom authorization is provided, it returns the current user's wallet authorization. #### Parameters: - `authz?: AuthorizationFunction` – Optional custom authorization function. - `flowClient?: FlowClient` - Optional `FlowClient` instance. Where `AuthorizationFunction` is defined as: ```typescript type AuthorizationFunction = ( account: Partial ) => Partial | Promise> ``` #### Returns: `AuthorizationFunction` The authorization function is compatible with Flow transactions' authorizations parameter. ```tsx // Example 1: Using current user authorization function CurrentUserAuthExample() { const authorization = useFlowAuthz() const sendTransaction = async () => { const txId = await fcl.mutate({ cadence: ` transaction { prepare(signer: auth(Storage) &Account) { log(signer.address) } } `, authorizations: [authorization], limit: 100, }) console.log("Transaction ID:", txId) } return } ``` ```tsx // Example 2: Using custom authorization function function CustomAuthExample() { const customAuthz = (account) => ({ ...account, addr: "0xCUSTOMOADDRESS", keyId: 0, signingFunction: async (signable) => ({ signature: "0x...", }), }) const authorization = useFlowAuthz({ authz: customAuthz }) const sendTransaction = async () => { const txId = await fcl.mutate({ cadence: ` transaction { prepare(signer: auth(Storage) &Account) { log(signer.address) } } `, authorizations: [authorization], limit: 100, }) console.log("Transaction ID:", txId) } return } ``` --- ### `useFlowScheduledTransaction` ```tsx import { useFlowScheduledTransaction } from "@onflow/react-sdk" ``` Fetches a scheduled transaction by ID. #### Parameters: - `txId?: string` – Scheduled transaction ID. - `includeHandlerData?: boolean` – Include handler data (default: false) - `query?: UseQueryOptions` – Optional TanStack Query options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` Where `ScheduledTransaction` is defined as: ```typescript interface ScheduledTransaction { id: string priority: ScheduledTransactionPriority // 0 = Low, 1 = Medium, 2 = High executionEffort: bigint status: ScheduledTransactionStatus // 0 = Pending, 1 = Processing, 2 = Completed, 3 = Failed, 4 = Cancelled fees: { value: bigint formatted: string } scheduledTimestamp: number handlerTypeIdentifier: string handlerAddress: string handlerUUID?: string // Only included if includeHandlerData is true handlerResolvedViews?: {[viewType: string]: any} // Only included if includeHandlerData is true } ``` ```tsx function ScheduledTransactionDetails({ txId }: { txId: string }) { const { data: transaction, isLoading, error } = useFlowScheduledTransaction({ txId, query: { staleTime: 10000 }, }) if (isLoading) return

    Loading scheduled transaction...

    if (error) return

    Error: {error.message}

    if (!transaction) return

    Transaction not found

    return (

    Scheduled Transaction #{transaction.id}

    Status: {transaction.status}

    Priority: {transaction.priority}

    Fees: {transaction.fees.formatted} FLOW

    Handler: {transaction.handlerTypeIdentifier}

    ) } ``` --- ### `useFlowScheduledTransactionList` ```tsx import { useFlowScheduledTransactionList } from "@onflow/react-sdk" ``` Lists all scheduled transactions for an account. #### Parameters: - `account?: string` – Flow address to query. - `includeHandlerData?: boolean` – Include handler data (default: false). - `query?: UseQueryOptions` – Optional TanStack Query options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseQueryResult` ```tsx function ScheduledTransactionsList({ account }: { account: string }) { const { data: transactions, isLoading, error, refetch } = useFlowScheduledTransactionList({ account, query: { staleTime: 10000 }, }) if (isLoading) return

    Loading scheduled transactions...

    if (error) return

    Error: {error.message}

    if (!transactions || transactions.length === 0) return

    No scheduled transactions

    return (

    Scheduled Transactions for {account}

      {transactions.map((tx) => (
    • Transaction #{tx.id} - Status: {tx.status} - Fees: {tx.fees.formatted} FLOW
    • ))}
    ) } ``` --- ### `useFlowScheduledTransactionCancel` ```tsx import { useFlowScheduledTransactionCancel } from "@onflow/react-sdk" ``` Cancels a scheduled transaction and refunds fees. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStack Query mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseFlowScheduledTransactionCancelResult` Where `UseFlowScheduledTransactionCancelResult` is defined as: ```typescript interface UseFlowScheduledTransactionCancelResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { cancelTransaction: (txId: string) => void cancelTransactionAsync: (txId: string) => Promise } ``` ```tsx function CancelScheduledTransaction() { const { cancelTransactionAsync, isPending, error, data: txId } = useFlowScheduledTransactionCancel({ mutation: { onSuccess: (txId) => console.log("Cancel transaction ID:", txId), }, }) const handleCancel = async (scheduledTxId: string) => { try { const resultTxId = await cancelTransactionAsync(scheduledTxId) console.log("Successfully canceled scheduled transaction:", resultTxId) } catch (error) { console.error("Failed to cancel:", error) } } return (
    {isPending &&

    Canceling transaction...

    } {error &&

    Error: {error.message}

    } {txId &&

    Cancel Transaction ID: {txId}

    }
    ) } ``` --- ### `useFlowScheduledTransactionSetup` ```tsx import { useFlowScheduledTransactionSetup } from "@onflow/react-sdk" ``` Sets up the Transaction Scheduler Manager resource. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStack Query mutation options - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseFlowScheduledTransactionSetupResult` Where `UseFlowScheduledTransactionSetupResult` is defined as: ```typescript interface UseFlowScheduledTransactionSetupResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { setup: () => void setupAsync: () => Promise } ``` ```tsx function SchedulerSetup() { const { setupAsync, isPending, error, data: txId } = useFlowScheduledTransactionSetup({ mutation: { onSuccess: (txId) => console.log("Setup transaction ID:", txId), }, }) const handleSetup = async () => { try { const resultTxId = await setupAsync() console.log("Scheduler setup successful:", resultTxId) } catch (error) { console.error("Setup failed:", error) } } return (
    {isPending &&

    Setting up scheduler...

    } {error &&

    Error: {error.message}

    } {txId &&

    Setup Transaction ID: {txId}

    }
    ) } ``` --- ## Cross-VM Hooks ### `useCrossVmBatchTransaction` ```tsx import { useCrossVmBatchTransaction } from "@onflow/react-sdk" ``` This hook allows you to execute multiple EVM transactions in a single atomic Cadence transaction. It is useful for batch processing EVM calls and ensure they are executed together, either all succeeding or allowing for some to fail without affecting the others. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseCrossVmBatchTransactionResult` Where `UseCrossVmBatchTransactionResult` is defined as: ```typescript interface UseCrossVmBatchTransactionResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { mutate: (calls: UseCrossVmBatchTransactionMutateArgs) => void mutateAsync: (calls: UseCrossVmBatchTransactionMutateArgs) => Promise } ``` Where `UseCrossVmBatchTransactionMutateArgs` is defined as: ```typescript interface UseCrossVmBatchTransactionMutateArgs { calls: EvmBatchCall[] mustPass?: boolean } ``` Where `EvmBatchCall` is defined as: ```typescript interface EvmBatchCall { // The target EVM contract address (as a string) address: string // The contract ABI fragment abi: Abi // The name of the function to call functionName: string // The function arguments args?: readonly unknown[] // The gas limit for the call gasLimit?: bigint // The value to send with the call value?: bigint } ``` ```tsx function CrossVmBatchTransactionExample() { const { sendBatchTransaction, isPending, error, data: txId } = useCrossVmBatchTransaction({ mutation: { onSuccess: (txId) => console.log("TX ID:", txId), }, }) const sendTransaction = () => { const calls = [ { address: "0x1234567890abcdef", abi: { // ABI definition for the contract }, functionName: "transfer", args: ["0xabcdef1234567890", 100n], // Example arguments gasLimit: 21000n, // Example gas limit }, // Add more calls as needed ] sendBatchTransaction({calls}) } return (
    {isPending &&

    Sending transaction...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` --- ### `useCrossVmTokenBalance` ```tsx import { useCrossVmTokenBalance } from "@onflow/react-sdk" ``` Fetch the balance of a token balance for a given user across both Cadence and EVM environments. #### Parameters: - `owner: string` – Cadence address of the account whose token balances you want. - `vaultIdentifier?: string` – Optional Cadence resource identifier (efor example, "0x1cf0e2f2f715450.FlowToken.Vault") for onchain balance - `erc20AddressHexArg?: string` – Optional bridged ERC-20 contract address (hex) for EVM/COA balance - `query?: Omit, "queryKey" | "queryFn">` – Optional TanStack Query config (e.g. staleTime, enabled) - `flowClient?: FlowClient` - Optional `FlowClient` instance > **Note:** You must pass `owner`, and one of `vaultIdentifier` or `erc20AddressHexArg`. #### Returns: `UseQueryResult` Where `UseCrossVmTokenBalanceData` is defined as: ```typescript interface UseCrossVmTokenBalanceData { cadence: TokenBalance // Token balance of Cadence vault evm: TokenBalance // Token balance of EVM (COA stored in /storage/coa) combined: TokenBalance // Combined balance of both Cadence and EVM } ``` Where `TokenBalance` is defined as: ```typescript interface TokenBalance { value: bigint // Balance value in smallest unit formatted: string // Formatted balance string (e.g. "123.45") precision: number // Number of decimal places for the token } ``` ```tsx function UseCrossVmTokenBalanceExample() { const { data, isLoading, error, refetch } = useCrossVmTokenBalance({ owner: '0x1e4aa0b87d10b141', vaultIdentifier: 'A.1654653399040a61.FlowToken.Vault', query: { staleTime: 10000 }, }); if (isLoading) return

    Loading token balance...

    ; if (error) return

    Error fetching token balance: {error.message}

    ; return (

    Token Balances

    Cadence Balance: {data.cadence.formatted} (Value: {data.cadence.value})

    EVM Balance: {data.evm.formatted} (Value: {data.evm.value})

    Combined Balance: {data.combined.formatted} (Value: {data.combined.value})

    ) } ``` --- ### `useCrossVmTransactionStatus` ```tsx import { useCrossVmTransactionStatus } from "@onflow/react-sdk" ``` Subscribes to status updates for a given Cross-VM Flow transaction ID that executes EVM calls. This hook monitors the transaction status and extracts EVM call results if available. #### Parameters: - `id?: string` – Optional Flow transaction ID to monitor - `flowClient?: FlowClient` - Optional `FlowClient` instance #### Returns: `UseCrossVmTransactionStatusResult` Where `UseCrossVmTransactionStatusResult` is defined as: ```typescript interface UseCrossVmTransactionStatusResult { transactionStatus: TransactionStatus | null // Latest transaction status, or null before any update evmResults?: CallOutcome[] // EVM transaction results, if available error: Error | null // Any error encountered during status updates } ``` Where `CallOutcome` is defined as: ```typescript interface CallOutcome { status: "passed" | "failed" | "skipped" // Status of the EVM call hash?: string // EVM transaction hash if available errorMessage?: string // Error message if the call failed } ``` ```tsx function CrossVmTransactionStatusComponent() { const txId = "your-cross-vm-transaction-id-here" const { transactionStatus, evmResults, error } = useCrossVmTransactionStatus({ id: txId }) if (error) return
    Error: {error.message}
    return (
    Flow Status: {transactionStatus?.statusString}
    {evmResults && evmResults.length > 0 && (

    EVM Call Results:

      {evmResults.map((result, idx) => (
    • Status: {result.status} {result.hash && | Hash: {result.hash}} {result.errorMessage && | Error: {result.errorMessage}}
    • ))}
    )}
    ) } ``` --- ### `useCrossVmBridgeNftFromEvm` ```tsx import { useCrossVmBridgeNftFromEvm } from "@onflow/react-sdk" ``` This hook bridges NFTs from Flow EVM to Cadence. It withdraws an NFT from the signer's COA (Cadence Owned Account) in EVM and deposits it into their Cadence collection. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseCrossVmBridgeNftFromEvmTxResult` Where `UseCrossVmBridgeNftFromEvmTxResult` is defined as: ```typescript interface UseCrossVmBridgeNftFromEvmTxResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeNftFromEvm: (args: UseCrossVmBridgeNftFromEvmTxMutateArgs) => void crossVmBridgeNftFromEvmAsync: (args: UseCrossVmBridgeNftFromEvmTxMutateArgs) => Promise } ``` Where `UseCrossVmBridgeNftFromEvmTxMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeNftFromEvmTxMutateArgs { nftIdentifier: string // Cadence type identifier (e.g., "A.0x123.MyNFT.NFT") nftId: string // EVM NFT ID as string representation of UInt256 } ``` ```tsx function BridgeNftFromEvmExample() { const { crossVmBridgeNftFromEvm, isPending, error, data: txId } = useCrossVmBridgeNftFromEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeNftFromEvm({ nftIdentifier: "A.0x1cf0e2f2f715450.ExampleNFT.NFT", nftId: "123", }) } return (
    {isPending &&

    Bridging NFT...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` --- ### `useCrossVmBridgeNftToEvm` ```tsx import { useCrossVmBridgeNftToEvm } from "@onflow/react-sdk" ``` This hook bridges NFTs from Cadence to Flow EVM and executes arbitrary EVM transactions atomically. It withdraws NFTs from the signer's Cadence collection and deposits them into their COA in EVM, then executes the provided EVM calls. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseCrossVmBridgeNftToEvmTxResult` Where `UseCrossVmBridgeNftToEvmTxResult` is defined as: ```typescript interface UseCrossVmBridgeNftToEvmTxResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeNftToEvm: (args: UseCrossVmBridgeNftToEvmTxMutateArgs) => void crossVmBridgeNftToEvmAsync: (args: UseCrossVmBridgeNftToEvmTxMutateArgs) => Promise } ``` Where `UseCrossVmBridgeNftToEvmTxMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeNftToEvmTxMutateArgs { nftIdentifier: string // Cadence NFT type identifier nftIds: string[] // Array of NFT IDs to bridge calls: EvmBatchCall[] // Array of EVM calls to execute after bridging } ``` ```tsx function BridgeNftToEvmExample() { const { crossVmBridgeNftToEvm, isPending, error, data: txId } = useCrossVmBridgeNftToEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeNftToEvm({ nftIdentifier: "A.0x1cf0e2f2f715450.ExampleNFT.NFT", nftIds: ["1", "2", "3"], calls: [ { address: "0x1234567890abcdef1234567890abcdef12345678", abi: myContractAbi, functionName: "transferNFT", args: ["0xRecipient", 1n], gasLimit: 100000n, }, ], }) } return (
    {isPending &&

    Bridging NFTs...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` --- ### `useCrossVmBridgeTokenFromEvm` ```tsx import { useCrossVmBridgeTokenFromEvm } from "@onflow/react-sdk" ``` This hook bridges fungible tokens from Flow EVM to Cadence. It withdraws tokens from the signer's COA in EVM and deposits them into their Cadence vault. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseCrossVmBridgeTokenFromEvmResult` Where `UseCrossVmBridgeTokenFromEvmResult` is defined as: ```typescript interface UseCrossVmBridgeTokenFromEvmResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeTokenFromEvm: (args: UseCrossVmBridgeTokenFromEvmMutateArgs) => void crossVmBridgeTokenFromEvmAsync: (args: UseCrossVmBridgeTokenFromEvmMutateArgs) => Promise } ``` Where `UseCrossVmBridgeTokenFromEvmMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeTokenFromEvmMutateArgs { vaultIdentifier: string // Cadence vault type identifier (e.g., "A.0x123.FlowToken.Vault") amount: string // Amount as UInt256 string representation } ``` ```tsx function BridgeTokenFromEvmExample() { const { crossVmBridgeTokenFromEvm, isPending, error, data: txId } = useCrossVmBridgeTokenFromEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeTokenFromEvm({ vaultIdentifier: "A.0x1654653399040a61.FlowToken.Vault", amount: "1000000000", // Amount in smallest unit }) } return (
    {isPending &&

    Bridging tokens...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` --- ### `useCrossVmBridgeTokenToEvm` ```tsx import { useCrossVmBridgeTokenToEvm } from "@onflow/react-sdk" ``` This hook bridges fungible tokens from Cadence to Flow EVM and executes arbitrary EVM transactions atomically. It withdraws tokens from the signer's Cadence vault and deposits them into their COA in EVM, then executes the provided EVM calls. #### Parameters: - `mutation?: UseMutationOptions` – Optional TanStackQuery mutation options. - `flowClient?: FlowClient` - Optional `FlowClient` instance. #### Returns: `UseCrossVmBridgeTokenToEvmResult` Where `UseCrossVmBridgeTokenToEvmResult` is defined as: ```typescript interface UseCrossVmBridgeTokenToEvmResult extends Omit< UseMutationResult, "mutate" | "mutateAsync" > { crossVmBridgeTokenToEvm: (args: UseCrossVmBridgeTokenToEvmMutateArgs) => void crossVmBridgeTokenToEvmAsync: (args: UseCrossVmBridgeTokenToEvmMutateArgs) => Promise } ``` Where `UseCrossVmBridgeTokenToEvmMutateArgs` is defined as: ```typescript interface UseCrossVmBridgeTokenToEvmMutateArgs { vaultIdentifier: string // Cadence vault type identifier amount: string // Amount as decimal string (e.g., "1.5") calls: EvmBatchCall[] // Array of EVM calls to execute after bridging } ``` ```tsx function BridgeTokenToEvmExample() { const { crossVmBridgeTokenToEvm, isPending, error, data: txId } = useCrossVmBridgeTokenToEvm({ mutation: { onSuccess: (txId) => console.log("Transaction ID:", txId), }, }) const handleBridge = () => { crossVmBridgeTokenToEvm({ vaultIdentifier: "A.0x1654653399040a61.FlowToken.Vault", amount: "10.5", calls: [ { address: "0x1234567890abcdef1234567890abcdef12345678", abi: erc20Abi, functionName: "transfer", args: ["0xRecipient", 1000000n], gasLimit: 100000n, }, ], }) } return (
    {isPending &&

    Bridging tokens...

    } {error &&

    Error: {error.message}

    } {txId &&

    Transaction ID: {txId}

    }
    ) } ``` [`@tanstack/react-query`]: https://tanstack.com/query/latest [Flow Client Library (FCL) TypeDefs]: https://github.com/onflow/fcl-js/blob/master/packages/typedefs/src/index.ts [commit-reveal scheme]: ../../cadence/advanced-concepts/randomness#commit-reveal-scheme --- # File: /md/build/tools/vscode-extension.md --- title: Cadence VS Code Extension sidebar_position: 4 --- This extension integrates [Cadence], the resource-oriented smart contract programming language of [Flow], into [Visual Studio Code]. It provides features like syntax highlighting, type checking, code completion, etc. Note that most editing features (type checking, code completion, etc.) are implemented in the [Cadence Language Server]. ## Features - Syntax highlighting (including in Markdown code fences) - Run the emulator, submit transactions, scripts from the editor ## Installation To install the extension, ensure you have the [VS Code IDE installed]. Then, you can install the Cadence extension from the [VS Code Marketplace]. ## Develop the extension ### Prerequisites - Must have Typescript installed globally: `npm i -g typescript` ### Get started - Run the Typescript watcher: `tsc -watch -p ./` - Press `F5` in VSCode to launch the extension. - Manually reload the extension host when you make changes to TypeScript code. ### Configuration for extension host if missing (`launch.json`): ``` { "version": "0.2.0", "configurations": [ { "type": "extensionHost", "request": "launch", "name": "Launch Extension", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "outFiles": ["${workspaceFolder}/out/**/*.js"] } ] } ``` ### Build If you build the extension from source, you need to build both the extension itself and the Flow CLI (if you don't already have a version installed). Unless you're developing the extension or need access to unreleased features, use the Flow CLI install option (above). It's much easier! If you haven't already, install dependencies. ```shell script npm install ``` Next, build and package the extension. ```shell script npm run package ``` This will result in a `.vsix` file that contains the packaged extension. Install the packaged extension. ```shell script code --install-extension cadence-*.vsix ``` Restart VS Code and the extension is installed! [Flow]: https://www.onflow.org/ [Visual Studio Code]: https://code.visualstudio.com/ [Cadence Language Server]: https://github.com/onflow/cadence-tools/tree/master/languageserver [VS Code IDE installed]: https://code.visualstudio.com/docs/setup/mac [VS Code Marketplace]: https://marketplace.visualstudio.com/items?itemName=onflow.cadence --- # File: /md/build/tools/wallet-provider-spec.md --- title: Wallet Provider Spec sidebar_title: Draft v4 sidebar_position: 8 --- ## Status - **Last Updated:** June 20th 2022 - **Stable:** Yes - **Risk of Breaking Change:** Medium - **Compatibility:** `>= @onflow/fcl@1.0.0-alpha.0` ## Definitions This document is written with the perspective that _you_ who are reading this right now are an FCL Wallet Developer. All references to _you_ in this doc are done with this perspective in mind. # Overview Flow Client Library (FCL) approaches the idea of blockchain wallets on Flow in a different way than how wallets may be supported on other blockchains. For example, with FCL, a wallet is not necessarily limited to being a browser extension or even a native application on a users device. FCL offers wallet developers the flexibility and freedom to build many different types of applications. Since wallet applications can take on many forms, we needed to create a way for these varying applications to be able to communicate and work together. FCL acts in many ways as a protocol to facilitate communication and configuration between the different parties involved in a blockchain application. An _Application_ can use FCL to _authenticate_ users, and request _authorizations_ for transactions, as well as mutate and query the _Blockchain_. An application using FCL offers its _Users_ a way to connect and select any number of Wallet Providers and their Wallet Services. A selected _Wallet_ provides an Application's instance of FCL with configuration information about itself and its Wallet Services, allowing the _User_ and _Application_ to interact with them. In the following paragraphs, we'll explore ways in which you can integrate with FCL and provide implementations of various FCL services. We'll cover the following services: - Authentication (Authn) Service - Authorization (Authz) Service - User Signature Service - Pre-Authz Service # Service methods FCL Services are your way as a Wallet Provider to configure FCL with information about what your wallet can do. FCL uses what it calls `Service Methods` to perform your supported FCL services. Service Methods are the ways FCL can talk to your wallet. Your wallet gets to decide which of these service methods each of your supported services use to communicate with you. Sometimes services just configure FCL and that's it. An example of this can be seen with the Authentication Service and the OpenID Service. With those two services, you simply tell FCL "here is a bunch of info about the current user". (You will see that those two services both have a `method: "DATA"` field in them. Currently these are the only two cases that can be a data service.) Other services can be a little more complex. For example, they might require a back and forth communication between FCL and the Service in question. Ultimately, we want to do this back and forth via a secure back-channel (https requests to servers), **but in some situations that isn't a viable option, so there is also a front-channel option**. Where possible, you should aim to provide a back-channel support for services, and only fall back to a front-channel if absolutely necessary. Back-channel communications use `method: "HTTP/POST"`, while front-channel communications use `method: "IFRAME/RPC"`, `method: "POP/RPC"`, `method: "TAB/RPC` and `method: "EXT/RPC"`. | Service Method | Front | Back | | -------------- | ----- | ---- | | HTTP/POST | ⛔ | ✅ | | IFRAME/RPC | ✅ | ⛔ | | POP/RPC | ✅ | ⛔ | | TAB/RPC | ✅ | ⛔ | | EXT/RPC | ✅ | ⛔ | It's important to note that regardless of the method of communication, the data that is sent back and forth between the parties involved is the same. # Protocol schema definitions In this section, we define the schema of objects used in the protocol. While they are JavaScript objects, only features supported by JSON should be used. (Which means that conversion of an object to and from JSON should not result in any loss.) For the schema definition language, we choose TypeScript, so that the schema closely resembles the actual type definitions one would use to make an FCL implementation. **Note that currently there are no official type definitions available for FCL. If you use TypeScript, you will have to create your own type definitions (possibly based on the schema definitions presented in this document).** ## Common definitions In this section, we introduce some common definitions that the individual object definitions will derive from. First, lets define the kinds of FCL objects available: ```typescript type ObjectType = | 'PollingResponse' | 'Service' | 'Identity' | 'ServiceProvider' | 'AuthnResponse' | 'Signable' | 'CompositeSignature' | 'OpenID'; ``` The fields common to all FCL objects then can be defined as follows: ```typescript interface ObjectBase { f_vsn: Version; f_type: ObjectType; } ``` The `f_vsn` field is usually `1.0.0` for this specification, but some exceptions will be defined by passing a different `Version` type parameter to `ObjectBase`. All FCL objects carry an `f_type` field so that their types can be identified at runtime. ## FCL objects In this section, we will define the FCL objects with each `ObjectType`. We also define the union of them to mean any FCL object: ```typescript type FclObject = | PollingResponse | Service | Identity | ServiceProvider | AuthnResponse | Signable | CompositeSignature | OpenID; ``` ### `PollingResponse` ```typescript interface PollingResponse extends ObjectBase { f_type: 'PollingResponse'; status: 'APPROVED' | 'DECLINED' | 'PENDING' | 'REDIRECT'; reason: string | null; data?: FclObject; updates?: FclObject; local?: FclObject; } ``` Each response back to FCL must be "wrapped" in a `PollingResponse`. The `status` field determines the meaning of the response: - An `APPROVED` status means that the request has been approved. The `data` field should be present. - A `DECLINED` status means that the request has been declined. The `reason` field should contain a human readable reason for the refusal. - A `PENDING` status means that the request is being processed. More `PENDING` responses may follow, but eventually a non-pending status should be returned. The `updates` and `local` fields may be present. - The `REDIRECT` status is reserved, and should not be used by wallet services. In summary, zero or more `PENDING` responses should be followed by a non-pending response. It is entirely acceptable for your service to immediately return an `APPROVED` Polling Response, skipping a `PENDING` state. See also [PollingResponse]. Here are some examples of valid `PollingResponse` objects: ```javascript // APPROVED { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: ___, // what the service needs to send to FCL } // Declined { f_type: "PollingResponse", f_vsn: "1.0.0", status: "DECLINED", reason: "Declined by user." } // Pending - Simple { f_type: "PollingResponse", f_vsn: "1.0.0", status: "PENDING", updates: { f_type: "Service", f_vsn: "1.0.0", type: "back-channel-rpc", endpoint: "https://____", // where post request will be sent method: "HTTP/POST", data: {}, // will be included in the request's body params: {}, // will be included in the request's url } } // Pending - First Time with Local { f_type: "PollingResponse", f_vsn: "1.0.0", status: "PENDING", updates: { f_type: "Service", f_vsn: "1.0.0", type: "back-channel-rpc", endpoint: "https://____", // where post request will be sent method: "HTTP/POST", data: {}, // included in body of request params: {}, // included as query params on endpoint }, local: { f_type: "Service", f_vsn: "1.0.0", endpoint: "https://____", // the iframe that will be rendered, method: "VIEW/IFRAME", data: {}, // sent to frame when ready params: {}, // included as query params on endpoint } } ``` A `PollingResponse` can alternatively be constructed using `WalletUtils` when sending `"APPROVED"` or `"DECLINED"` responses. ```javascript import {WalletUtils} from "@onflow/fcl" // Approving a PollingResponse // Example using an AuthnResponse as the PollingResponse data WalletUtils.approve({ f_type: "AuthnResponse", f_vsn: "1.0.0" ... }) // Rejecting a PollingResponse // Supplies a reason for declining const reason = "User declined to authenticate." WalletUtils.decline(reason) ``` ### `Service` ```typescript type ServiceType = | 'authn' | 'authz' | 'user-signature' | 'pre-authz' | 'open-id' | 'back-channel-rpc' | 'authn-refresh'; type ServiceMethod = | 'HTTP/POST' | 'IFRAME/RPC' | 'POP/RPC' | 'TAB/RPC' | 'EXT/RPC' | 'DATA'; interface Service extends ObjectBase { f_type: 'Service'; type: ServiceType; method: ServiceMethod; uid: string; endpoint: string; id: string; identity: Identity; provider?: ServiceProvider; data?: FclObject; } ``` The meaning of the fields is as follows. - `type`: The type of this service. - `method`: The service method this service uses. `DATA` means that the purpose of this service is just to provide the information in this `Service` object, and no active communication services are provided. - `uid`: A unique identifier for the service. A common scheme for deriving this is to use `'wallet-name#${type}'`, where `${type}` refers to the type of this service. - `endpoint`: Defines where to communicate with the service. - When `method` is `EXT/RPC`, this can be an arbitrary unique string, and the extension will need to use it to identify its own services. A common scheme to derive the `endpoint` is to use `'ext:${address}'`, where `${address}` refers to the wallet's address. (See `ServiceProvider` for more information.) - `id`: The wallet's internal identifier for the user. If no other identifier is used, simply the user's flow account address can be used here. - `identity`: Information about the identity of the user. - `provider`: Information about the wallet. - `data`: Additional information used with a service of type `open-id`. See also: - [authn] - [authz] - [user-signature] - [pre-authz] - [open-id] - [back-channel-rpc] ### `Identity` This object is used to define the user's identity. ```typescript interface Identity extends ObjectBase { f_type: 'Identity'; address: string; keyId?: number; } ``` The meaning of the fields is as follows. - `address`: The flow account address of the user. - `keyId`: The id of the key associated with this account that will be used for signing. ### `ServiceProvider` This object is used to communicate information about a wallet. ```typescript interface ServiceProvider extends ObjectBase { f_type: 'ServiceProvider'; address: string; name?: string; description?: string; icon?: string; website?: string; supportUrl?: string; supportEmail?: string; } ``` The meaning of the fields is as follows. - `address`: A flow account addressthat the wallet owns. It is unspecified what this will be used for. - `name`: The name of the wallet. - `description`: A short description for the wallet. - `icon`: An image URL for the wallet's icon. - `website`: The wallet's website. - `supportUrl`: A URL the user can use to get support with the wallet. - `supportEmail`: An e-mail address the user can use to get support with the wallet. ### `AuthnResponse` This object is used to inform FCL about the services a wallet provides. ```typescript interface AuthnResponse extends ObjectBase { f_type: 'AuthnResponse'; addr: string; services: Service[]; } ``` The meaning of the fields is as follows. - `addr`: The flow account address of the user. - `services`: The list of services the wallet provides. ### `Signable` ```typescript interface Signable extends ObjectBase<'1.0.1'> { f_type: 'Signable'; addr: string; keyId: number; voucher: { cadence: string; refBlock: string; computeLimit: number; arguments: { type: string; value: unknown; }[]; proposalKey: { address: string; keyId: number; sequenceNum: number; }; payer: string; authorizers: string[]; }; } ``` The `WalletUtils.encodeMessageFromSignable` function can be used to calculate the message that needs to be signed. ### `CompositeSignature` ```typescript interface CompositeSignature extends ObjectBase { f_type: 'CompositeSignature'; addr: string; keyId: number; signature: string; } ``` See also [CompositeSignature]. ### `OpenID` TODO ## Miscellaneous objects ### `Message` ```typescript type MessageType = | 'FCL:VIEW:READY' | 'FCL:VIEW:READY:RESPONSE' | 'FCL:VIEW:RESPONSE' | 'FCL:VIEW:CLOSE'; type Message = { type: MessageType; }; ``` A message that indicates the status of the protocol invocation. This type is sometimes used as part of an _intersection type_. For example, the type `Message & PollingResponse` means a `PollingResponse` extended with the `type` field from `Message`. ### `ExtensionServiceInitiationMessage` ```typescript type ExtensionServiceInitiationMessage = { service: Service; }; ``` This object is used to invoke a service when the `EXT/RPC` service method is used. ## See also - [local-view] - [frame] # Service Methods ## IFRAME/RPC (Front Channel) `IFRAME/RPC` is the easiest to explain, so we will start with it: - An iframe is rendered (comes from the `endpoint` in the service). - The rendered iframe adds a listener and sends the `"FCL:VIEW:READY"` message. This can be simplified `WalletUtils.ready(callback)` - FCL will send the data to be dealt with: - Where `body` is the stuff you care about, `params` and `data` are additional information you can provide in the service object. - The wallet sends back an `"APPROVED"` or `"DECLINED"` post message. (It will be a `f_type: "PollingResponse"`, which we will get to in a bit). This can be simplified with `WalletUtils.approve` and `WalletUtils.decline` - If it's approved, the polling response's data field will need to be what FCL expects. - If it's declined, the polling response's reason field should say why it was declined. ```javascript export const WalletUtils.approve = data => { sendMsgToFCL("FCL:VIEW:RESPONSE", { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", reason: null, data: data, }) } export const WalletUtils.decline = reason => { sendMsgToFCL("FCL:VIEW:RESPONSE", { f_type: "PollingResponse", f_vsn: "1.0.0", status: "DECLINED", reason: reason, data: null, }) } ``` ```mermaid graph LR Start1(Start) --Bot 启动--> check1[检查群内的非 Authing 用户] --> addUser[添加 Authing 用户并消息提醒绑定手机号] --> End1(End) ``` ![IFRAME/RPC Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/iframe-rpc.png) ## POP/RPC | TAB/RPC (Front Channel) `POP/RPC` and `TAB/RPC` work in an almost entirely similar way to `IFRAME/RPC`, except instead of rendering the `method` in an iframe, we render it in a popup or new tab. The same communication protocol between the rendered view and FCL applies. ![POP/RPC Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/pop-rpc.png) ![TAB/RPC Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/tab-rpc.png) ## HTTP/POST (Back Channel) `HTTP/POST` initially sends a post request to the `endpoint` specified in the service, which will immediately return a `f_type: "PollingResponse"`. Like `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`, our goal is to eventually get an `APPROVED` or `DECLINED` polling response, and technically this endpoint could return one of those immediately. But more than likely, that isn't the case and it will be in a `PENDING` state (`PENDING` is not available to `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`). When the polling response is `PENDING` it requires an `updates` field that includes a service, `BackChannelRpc`, that FCL can use to request an updated `PollingResponse` from. FCL will use that `BackChannelRpc` to request a new `PollingResponse` which itself can be `APPROVED`, `DECLINED` or `PENDING`. If it is `APPROVED` FCL will return, otherwise if it is `DECLINED` FCL will error. However, if it is `PENDING`, it will use the `BackChannelRpc` supplied in the new `PollingResponse` updates field. It will repeat this cycle until it is either `APPROVED` or `DECLINED`. There is an additional optional feature that `HTTP/POST` activates in the first `PollingResponse` that is returned. This optional feature is the ability for FCL to render an iframe, popup or new tab, and you can trigger it when you supply a service `type: "VIEW/IFRAME"`, `type: "VIEW/POP"` or `type: "VIEW/TAB"` and the `endpoint` that the wallet wishes to render in the `local` field of the `PollingResponse`. This is a great way for a wallet provider to switch to a webpage if it's necessary to display a UI for the service it performs. ![HTTP/POST Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/http-post.png) ## EXT/RPC (Front Channel) `EXT/RPC` is used to activate and communicate between FCL and an installed web browser extension. (Though this specification is geared towards Chromium based browsers, it should be implementable in any browser with similar extension APIs available. From now on we will be using the word _Chrome_ to refer to Chromium based browsers.) An implementation of `EXT/RPC` needs to somehow activate communication between the application and the extension context. Implementing this is a bit more complex and usually relies on three key scripts to allow message passing between an installed extension and FCL. The separation of contexts enforced by Chrome and the availability of different Chrome APIs within those contexts require these scripts to be set up in a particular sequence so that the communication channels needed by FCL's `EXT/RPC` service method will work. The following is an overview of these scripts and the functionality they need to support FCL: - `background.js`: Used to launch the extension popup with `chrome.windows.create` if selected by the user from Discovery or set directly via `fcl.config.discovery.wallet`. - `content.js`: Used to proxy messages between the application to the extension via `chrome.runtime.sendMessage`. - `script.js`: Injected by `content.js` into the application's HTML page. It appends the extension authn service to the `window.fcl_extensions` array on page load. This allows FCL to confirm installation and send extension details to Discovery or launch your wallet as the default wallet. An example and guide that shows how to build an FCL compatible wallet extension on Flow can be found [here]. After the extension is activated (for example when the user selects it through the discovery service), the following communication protocol applies. (The term _send_ should specifically refer to using `window.postMessage` in the application context, as this is the only interface between the application and the extension. Since `window.postMessage` broadcasts messages to all message event handlers, each party should be careful to filter only the messages targeted at them.) - FCL sends an `ExtensionServiceInitiationMessage` object. It is the extension's responsibility to inspect the `endpoint` field of the service, and only activate itself (for example, via a popup) if it is the provider of this service. - To respond, the extension should send a `Message` with type `FCL:VIEW:READY`. (Usually this message will originate from the extension popup, and be relayed to the application context.) - FCL will send a `Message` with type `FCL:VIEW:READY:RESPONSE`. Additional fields specific to the service (such as `body`, `params` or `data`) are usually present. See the section on the specific service for a description of these fields. - The wallet sends back a `Message & PollingResponse` with type `FCL:VIEW:RESPONSE` with either an `APPROVED` or `DECLINED` status. - If it's approved, the polling response's data field will need to be what FCL expects. - If it's declined, the polling response's reason field should say why. The extension can send a `Message` with type `FCL:VIEW:CLOSE` at any point during this protocol to indicate an interruption. This will halt FCL's current routine. On the other hand, after a `PollingResponse` with either an `APPROVED` or `DECLINED` status is sent, the protocol is considered finished, and the extension should not send any further messages as part of this exchange. Conversely, when FCL sends a new `ExtensionServiceInitiationMessage`, the previous routine is interrupted. (This is the case even when the new service invocation is targeted at a different extension.) Because of the above restrictions, only single service invocation can be in progress at a time. Here is a code example for how an extension popup might send its response: ```javascript chrome.tabs.sendMessage(tabs[0].id, { f_type: 'PollingResponse', f_vsn: '1.0.0', status: 'APPROVED', reason: null, data: { f_type: 'AuthnResponse', f_vsn: '1.0.0', addr: address, services: services, }, }); ``` ![EXT/RPC Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/ext-rpc.png) ## `data` and `params` `data` and `params` are information that the wallet can provide in the service config that FCL will pass back to the service. - `params` will be added onto the `endpoint` as query params. - `data` will be included in the body of the `HTTP/POST` request or in the `FCL:VIEW:READY:RESPONSE` for a `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. # Authentication service In the following examples, we'll walk you through the process of how to build an authentication service. In FCL, wallets are configured by passing in a wallet provider's authentication URL or extension endpoint as the `discovery.wallet` config variable. You will need to make and expose a webpage or API hosted at an authentication endpoint that FCL will use. ```javascript // IN APPLICATION // configuring fcl to point at a wallet looks like this import { config } from '@onflow/fcl'; config({ 'discovery.wallet': 'url-or-endpoint-fcl-will-use-for-authentication', // FCL Discovery endpoint, wallet provider's authentication URL or extension endpoint 'discovery.wallet.method': 'IFRAME/RPC', // Optional. Available methods are "IFRAME/RPC", "POP/RPC", "TAB/RPC", "EXT/RPC" or "HTTP/POST", defaults to "IFRAME/RPC". }); ``` If the method specified is `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`, then the URL specified as `discovery.wallet` will be rendered as a webpage. If the configured method is `EXT/RPC`, `discovery.wallet` should be set to the extension's `authn` `endpoint`. Otherwise, if the method specified is `HTTP/POST`, then the authentication process will happen over HTTP requests. (While authentication can be accomplished with any of those service methods, this example will use the `IFRAME/RPC` service method.) After the Authentication webpage is rendered, the extension popup is activated, or the API is ready, you then need to tell FCL that it is ready. To do this, send a message to FCL, and FCL will send back a message with some additional information that you can use about the application that requests authentication on behalf of the user. The following example is using the `IFRAME/RPC` method. Your authentication webpage will likely resemble the following code: ```javascript // IN WALLET AUTHENTICATION FRAME import {WalletUtils} from "@onflow/fcl" function callback(data) { if (typeof data != "object") return if (data.type !== "FCL:VIEW:READY:RESPONSE") return ... // Do authentication things ... // Send back AuthnResponse WalletUtils.sendMsgToFCL("FCL:VIEW:RESPONSE", { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: { f_type: "AuthnResponse", f_vsn: "1.0.0" ... } }) // Alternatively be sent using WalletUtils.approve (or WalletUtils.decline) // which will wrap AuthnResponse in a PollingResponse WalletUtils.approve({ f_type: "AuthnResponse", f_vsn: "1.0.0" ... }) } // add event listener first WalletUtils.onMsgFromFCL("FCL:VIEW:READY:RESPONSE", callback) // tell fcl the wallet is ready WalletUtils.sendMsgToFCL("FCL:VIEW:READY") // alternatively adds "FCL:VIEW:READY:RESPONSE" listener and sends "FCL:VIEW:READY" WalletUtils.ready(callback) ``` During authentication, the application has a chance to request to you what they would like you to send back to them. These requests are included in the `FCL:VIEW:READY:RESPONSE` message sent to the wallet from FCL. An example of such a request is the OpenID service. The application can request for example that you to send them the email address of the current user. The application that requests this information does not mean you need to send it. It's entirely optional for you to do so. However, some applications may depend on whether you send the requested information back, and should you decline to do so, it may cause the application to not work. In the config, they can also tell you a variety of things about them, such as the name of their application or a URL for an icon of their application. You can use these pieces of information to customize your wallet's user experience should you desire to do so. Your wallet having a visual distinction from the application, but still a seamless and connected experience is our goal here. Whether your authentication process happens using a webpage with the `IFRAME/RPC`, `POP/RPC` or `TAB/RPC` methods, via an actiated extension using the `EXT/RPC` method, or via a backchannel to an API with the `HTTP/POST` method, the handshake is the same. The same messages are sent in all methods, however the transport mechanism changes. For `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC` methods, the transport is `window.postMessage()`, with the `HTTP/POST` method, the transport is HTTP post messages. As always, you must never trust anything you receive from an application. Always do your due-diligence and be alert as you are the user's first line of defense against potentially malicious applications. ### Authenticate your user It's important that you are confident that the user is who the user claims to be. Have them provide enough proof to you that you are okay with passing their details back to FCL. As an example, a Blotco authentication code is sent to the email a user enters at login. This code can be used as validation and is everything Blocto needs to be confident in the user's identity. ### When you know who your user is When you're confident in the user's identity, we can complete the authentication process. The authentication process is complete when FCL receives back a response that configures FCL with FCL Services for the current user. This response is extremely important to FCL. At its core it tells FCL who the user is, and then via the included services it tells FCL how the user authenticated, how to request transaction signatures, how to get a personal message signed and the user's email and other details if requested. In the future it may also include many more things! You can kind of think of FCL as a plugin system. But since those plugins exist elsewhere outside of FCL, FCL needs to be configured with information on how to communicate with them. What you send back to FCL is everything that it needs to communicate with the plugins that you supply. Your wallet is like a plugin to FCL, and these details tell FCL how to use you as a plugin. Here is an example of an authentication response: ```javascript // IN WALLET AUTHENTICATION FRAME import {WalletUtils} from "@onflow/fcl" WalletUtils.approve({ f_type: "AuthnResponse", f_vsn: "1.0.0", addr: "0xUSER", // The user's flow address services: [ // All the stuff that configures FCL // Authentication Service - REQUIRED { f_type: "Service", // It's a service! f_vsn: "1.0.0", // Follows the v1.0.0 spec for the service type: "authn", // the type of service it is method: "DATA", // It's data! uid: "amazing-wallet#authn", // A unique identifier for the service endpoint: "your-url-that-fcl-will-use-for-authentication", // should be the same as was passed into the config id: "0xUSER", // the wallet's internal id for the user, use flow address if you don't have one // The User's Info identity: { f_type: "Identity", // It's an Identity! f_vsn: "1.0.0", // Follows the v1.0.0 spec for an identity address: "0xUSER", // The user's address keyId: 0, // OPTIONAL - The User's KeyId they will use }, // The Wallet's Info provider: { f_type: "ServiceProvider", // It's a Service Provider f_vsn: "1.0.0", // Follows the v1.0.0 spec for service providers address: "0xWallet", // A flow address owned by the wallet name: "Amazing Wallet", // OPTIONAL - The name of your wallet. ie: "Dapper Wallet" or "Blocto Wallet" description: "The best wallet", // OPTIONAL - A short description for your wallet icon: "https://___", // OPTIONAL - Image url for your wallet's icon website: "https://___", // OPTIONAL - Your wallet's website supportUrl: "https://___", // OPTIONAL - An url the user can use to get support from you supportEmail: "help@aw.com", // OPTIONAL - An email the user can use to get support from you }, }, // Authorization Service { f_type: "Service", f_vsn: "1.0.0", type: "authz", uid: "amazing-wallet#authz", ... // We will cover this at length in the authorization section of this guide }, // User Signature Service { f_type: "Service", f_vsn: "1.0.0", type: "user-signature", uid: "amazing-wallet#user-signature", ... // We will cover this at length in the user signature section of this guide }, // OpenID Service { f_type: "Service", f_vsn: "1.0.0", type: "open-id", uid: "amazing-wallet#open-id", method: "DATA", data: { // only include data that was request, ideally only if the user approves the sharing of data, everything is optional f_type: "OpenID", f_vsn: "1.0.0", profile: { name: "Jeff", family_name: "D", // icky underscored names because of OpenID Connect spec given_name: "Jeffrey", middle_name: "FakeMiddleName", nickname: "JeffJeff", preferred_username: "Jeff", profile: "https://www.jeff.jeff/", picture: "https://avatars.onflow.org/avatar/jeff", website: "https://www.jeff.jeff/", gender: "male", birthday: "1900-01-01", // can use 0000 for year if year is not known zoneinfo: "America/Vancouver", locale: "en", updated_at: "1625588304427" }, email: { email: "jeff@jeff.jeff", email_verified: false, } }, } ] }) ``` ### Stop an authentication process From any frame, you can send a `FCL:VIEW:CLOSE` post message to FCL, which will halt FCL's current routine and close the frame. ```javascript import { WalletUtils } from '@onflow/fcl'; WalletUtils.sendMsgToFCL('FCL:VIEW:CLOSE'); ``` # Authorization service Authorization services are depicted with with a `type: "authz"`, and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return a `f_type: "CompositeSignature"`. An authorization service is expected to know the Account and the Key that will be used to sign the transaction at the time the service is sent to FCL (during authentication). ```javascript { f_type: "Service", f_vsn: "1.0.0", type: "authz", // say it's an authorization service uid: "amazing-wallet#authz", // standard service uid method: "HTTP/POST", // can also be `IFRAME/RPC` or `POP/RPC` endpoint: "https://____", // where to talk to the service identity: { f_type: "Identity", f_vsn: "1.0.0", address: "0xUser", // the address that the signature will be for keyId: 0, // the key for the address that the signature will be for }, data: {}, params: {}, } ``` FCL will use the `method` provided to request an array of composite signature from authorization service (Wrapped in a `PollingResponse`). The authorization service will be sent a `Signable`. The service is expected to construct an encoded message to sign from `Signable.voucher`. It then needs to hash the encoded message, and prepend a required [transaction domain tag]. Finally it signs the payload with the user/s keys, producing a signature. This signature, as a HEX string, is sent back to FCL as part of the `CompositeSignature` which includes the user address and keyID in the data property of a `PollingResponse`. ```elixir signature = signable.voucher |> encode |> hash |> tag |> sign |> convert_to_hex ``` The eventual response back from the authorization service will resolve to something like this: ```javascript { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: { f_type: "CompositeSignature", f_vsn: "1.0.0", addr: "0xUSER", keyId: 0, signature: "signature as hex value" } } ``` A `CompositeSignature` can alternatively be constructed using `WalletUtils` ```javascript import {WalletUtils} from "@onflow/fcl" WalletUtils.CompositeSignature(addr: String, keyId: Number, signature: Hex) ``` # User Signature service User Signature services are depicted with a `type: "user-signature"` and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return an array of `f_type: "CompositeSignature"`. The User Signature service is a stock (standard) service. ```javascript { f_type: "Service", f_vsn: "1.0.0", type: "user-signature", // say it's an user-signature service uid: "amazing-wallet#user-signature", // standard service uid method: "HTTP/POST", // can also be `IFRAME/RPC` endpoint: "https://___", // where to talk to the service data: {}, params: {}, } ``` FCL will use the `method` provided to request an array of composite signatures from the user signature service (Wrapped in a `PollingResponse`). The user signature service will be sent a `Signable`. The service is expected to tag the `Signable.message` and then sign it with enough keys to produce a full weight. The signatures need to be sent back to FCL as HEX strings in an array of `CompositeSignatures`. ```javascript // Pseudocode: // For every required signature import { WalletUtils } from '@onflow/fcl'; const encoded = WalletUtils.encodeMessageFromSignable(signable, signerAddress); const taggedMessage = tagMessage(encoded); // Tag the message to sign const signature = signMessage(taggedMessage); // Sign the message const hexSignature = signatureToHex(signature); // Convert the signature to hex, if required. return hexSignature; ``` The eventual response back from the user signature service should resolve to something like this: ```javascript { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: [ { f_type: "CompositeSignature", f_vsn: "1.0.0", addr: "0xUSER", keyId: 0, signature: "signature as hex value" }, { f_type: "CompositeSignature", f_vsn: "1.0.0", addr: "0xUSER", keyId: 1, signature: "signature as hex value" } ] } ``` # Pre Authz service This is a strange one, but extremely powerful. Use this service when a wallet is responsible for an account that signs as multiple roles of a transaction, and wants the ability to change the accounts on a per role basis. Pre Authz Services are depicted with a `type: "pre-authz"` and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return a `f_type: "PreAuthzResponse"`. The Pre Authz Service is a stock (standard) service. ```javascript { f_type: "Service", f_vsn: "1.0.0", type: "pre-authz", // say it's a pre-authz service uid: "amazing-wallet#pre-authz", // standard service uid method: "HTTP/POST", // can also be IFRAME/RPC, POP/RPC, TAB/RPC endpoint: "https://___", // where to talk to the service data: {}, params: {}, } ``` FCL will use the `method` provided to request a `PreAuthzReponse` (Wrapped in a `PollingResponse`). The Authorizations service will be sent a `PreSignable`. The pre-authz service is expected to look at the `PreSignable` and determine the breakdown of accounts to be used. The pre-authz service is expected to return `Authz` services for each role it is responsible for. A pre-authz service can only supply roles it is responsible for. If a pre-authz service is responsible for multiple roles, but it wants the same account to be responsible for all the roles, it will need to supply an Authz service per role. The eventual response back from the pre-authz service should resolve to something like this: ```javascript { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: { f_type: "PreAuthzResponse", f_vsn: "1.0.0", proposer: { // A single Authz Service f_type: "Service", f_vsn: "1.0.0", type: "authz", ... }, payer: [ // An array of Authz Services { f_type: "Service", f_vsn: "1.0.0", type: "authz", ... } ], authorization: [ // An array of Authz Services (it's singular because it only represents a singular authorization) { f_type: "Service", f_vsn: "1.0.0", type: "authz", ... } ], } } ``` # Authentication Refresh Service Since synchronization of a user's session is important to provide a seamless user experience when you use an app and transact with the Flow Blockchain, a way to confirm, extend, and refresh a user session can be provided by the wallet. Authentication Refresh Services should include a `type: "authn-refresh"`, `endpoint`, and supported `method` (`HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, or `EXT/RPC`). FCL will use the `endpoint` and service `method` provided to request updated authentication data. The `authn-refresh` service should refresh the user's session if necessary and return updated authentication configuration and user session data. The service is expected to return a `PollingResponse` with a new `AuthnResponse` as data. If user input is required, a `PENDING` `PollingResponse` can be returned with a `local` view for approval/re-submission of user details. The Authentication Refresh Service is a stock (standard) service. ```javascript { "f_type": "Service", "f_vsn": "1.0.0", "type": "authn-refresh", "uid": "uniqueDedupeKey", "endpoint": "https://rawr", "method": "HTTP/POST", // "HTTP/POST", // HTTP/POST | IFRAME/RPC | HTTP/RPC "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // wallet's internal id for the user "data": {}, // included in body of request "params": {}, // included as query params on endpoint url } ``` The provided `data` and `params` should include all the wallet needs to identify and re-authenticate the user if necessary. The eventual response back from the `authn-refresh` service should resolve to an `AuthnResponse` and look something like this: ```javascript { f_type: "PollingResponse", f_vsn: "1.0.0", status: "APPROVED", data: { f_type: "AuthnResponse", f_vsn: "1.0.0", addr: "0xUSER", services: [ // Authentication Service - REQUIRED { f_type: "Service", f_vsn: "1.0.0", type: "authn", ... }, // Authorization Service { f_type: "Service", f_vsn: "1.0.0", type: "authz", ... }, // Authentication Refresh Service { f_type: "Service", f_vsn: "1.0.0", type: "authn-refresh", ... } // Additional Services ], } } ``` [PollingResponse]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js [authn]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authn.js [authz]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authz.js [user-signature]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js [pre-authz]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/pre-authz.js [open-id]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/open-id.js [back-channel-rpc]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/back-channel-rpc.js [CompositeSignature]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js [local-view]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/local-view.js [frame]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/frame.js [here]: https://github.com/onflow/wallet-extension-example [transaction domain tag]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/encode/encode.ts#L18-L21 --- # File: /md/build/tools/wallet-provider-spec/authorization-function.md # Authorization Function ## Overview An Authorization Function is a function which allows the JS-SDK and Flow Client Library (FCL) to know which Flow account fulfills which signatory role in a transaction and how to recieve a signature on behalf of the supplied account. ## How to use an Authorization Function An Authorization Function is a function that you may use in place of an authorization in the Flow JS-SDK and FCL. An authorization is a concept that is used to denote a proposer, payer or authorizer for a transaction. An authorization can either be a data structure that represents an authorization, or a function which when called returns an authorization called an Authorization Function. In this document, we discuss the latter. To use an Authorization Function, you specify that Authorization Function as the authorization for a proposer, payer or authorizer for a transaction. > `fcl.currentUser().authorization` which is aliased to `fcl.authz` is itself an authorization function. It tells the underlying js-sdk the current users flow account will be used for the signatory role and supplies a signing function that allows the application to request a signature from the users wallet. Example 1: ```javascript import * as fcl from "@onflow/fcl" const myAuthorizationFunction = ... // An Authorization Function const response = fcl.send([ fcl.transaction`transaction() { prepare(acct: &Account) {} execute { log("Hello, Flow!") } }`, fcl.proposer(myAuthorizationFunction), fcl.payer(myAuthorizationFunction), fcl.authorizers([ myAuthorizationFunction ]) ]) ``` The builder functions, `fcl.proposer`, `fcl.payer` and `fcl.authorizations` each consume the Authorization Function and set it as the resolve field on the internal Account object it creates. During the resolve phase of the Flow JS-SDK and FCL, when [`resolveAccounts`] is called, the resolve field on each internal Account object is called, which means each Authorization Function is called appropriately and the account is _resolved_ into the data structure the authorizationFunction returns. These accounts are then deduped based on the a mix of the `addr`, `keyId` and `tempId` so that only a single signature request happens per `address` `keyId` pair. When [`resolveSignatures`] is called, the signing function for each `address` `keyId` pair is called, and returns a composite signature for each signatory role. ## How to create An Authorization Function Fortunately, it's relatively straight forward to create an Authorization Function. An Authorization Function needs to be able to do at minimum two things: - Who will sign -- Know which account is going to sign and the keyId of the key it will use to sign - How they sign -- Know how to get a signature for the supplied account and key from the first piece. The Authorization Function has a concept of an account. An account represent a possible signatory for the transaction. It includes who signs it as well as how it will be signed. The Authorization Function is passed an empty Account and needs to return an Account, your job when you make an Authorization Function is mostly to fill in this Account with the information so that the account you want to sign things can. Let's say we knew up front the account, keyId, and had a function that could sign things. ```javascript const ADDRESS = "0xba1132bc08f82fe2" const KEY_ID = 1 // this account on testnet has three keys, we want the one with an index of 1 (has a weight of 1000) const sign = msg => { /* ... returns signature (for the key above) for supplied message ... */ } ``` Our Authorization Function becomes about filling things in: Example 2: ```javascript const authorizationFunction = async (account) => { // authorization function need to return an account return { ...account, // bunch of defaults in here, we want to overload some of them though tempId: `${ADDRESS}-${KEY_ID}`, // tempIds are more of an advanced topic, for 99% of the times where you know the address and keyId you will want it to be a unique string per that address and keyId addr: ADDRESS, // the address of the signatory keyId: Number(KEY_ID), // this is the keyId for the accounts registered key that will be used to sign, make extra sure this is a number and not a string signingFunction: async signable => { // Singing functions are passed a signable and need to return a composite signature // signable.message is a hex string of what needs to be signed. return { addr: ADDRESS, // needs to be the same as the account.addr keyId: Number(KEY_ID), // needs to be the same as account.keyId, once again make sure its a number and not a string signature: sign(signable.message), // this needs to be a hex string of the signature, where signable.message is the hex value that needs to be signed } } } } ``` ## Async stuff Both the Authorization Function, and the account's Signing Function can be asynchronous. This means both of these functions can go and get the information needed elsewhere. Say each of your users had a `userId`. From this `userId` say you had an API call that could return the address and key needed for the Authorization Functions account. You could also have another endpoint that when posted the signable (includes what needs to be signed) and the `userId` it can return with the composite signature if your API decides its okay to sign (the signable has all sorts of info to help you decide). An Authorization Function that can do that could look something like this. Example 3: ```javascript const getAccount = (userId) => fetch(`/api/user/${userId}/account`).then(d => d.json()) const getSignature = (userId, signable) = fetch(`/api/user/${userId}/sign`, { method: "POST", headers: { "Content-Type": "application/json"}, body: JSON.stringify(signable), }) function authz (userId) { return async function authorizationFunction (account) { const {addr, keyId} = await getAccount(userId) return { ...account, tempId: `${addr}-${keyId}`, addr: addr, keyId: Number(keyId), signingFunction: signable => { return getSignature(userId, signable) } } } } ``` The above **Example 3** is the same as **Example 2**, but the information is gathered during the execution of the authorization function based on the supplied user id. ## How to create a Signing Function Creating a signing function is also relatively simple! To create a signing function, you specify a function which consumes a payload and returns a signature data structure. Example 3: ```javascript const signingFunction = ({ message, // The encoded string which needs to be used to produce the signature. addr, // The address of the Flow Account this signature is to be produced for. keyId, // The keyId of the key which is to be used to produce the signature. roles: { proposer, // A Boolean representing if this signature to be produced for a proposer. authorizer, // A Boolean representing if this signature to be produced for a authorizer. payer, // A Boolean representing if this signature to be produced for a payer. }, voucher, // The raw transactions information, can be used to create the message for additional safety and lack of trust in the supplied message. }) => { return { addr, // The address of the Flow Account this signature was produced for. keyId, // The keyId for which key was used to produce the signature. signature: produceSignature(message) // The hex encoded string representing the signature of the message. } } ``` [`resolveAccounts`]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L58 [`resolveSignatures`]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L62 --- # File: /md/build/tools/wallet-provider-spec/custodial.md # Introduction A Wallet Provider handles Authentications and Authorizations. They play a very important role of being the place the users control their information and approve transactions. One of Flow CLient Library's (FCL) core ideals is for the user to be in control of their data, a wallet provider is where many users will do just that. FCL has been built in a way that it doesn't need to know any intimate details about a Wallet Provider up front; they can be discovered when the users wishes to let the dApp know about them. This gives us a concept we call Bring Your Own Identity. # Identity Conceptually, FCL thinks of identity in two ways: Public and Private. Public identity will be stored on chain as a resource and publicly available to anyone that knows the Flow Address for the account. In FCL, you can easily retrieve a user's public identity: ```javascript import { user } from '@onflow/fcl'; const identity = await user(flowAddress).snapshot(); // ^ // `------ The public identity for `flowAddress` const unsub = user(flowAddress).subscribe((identity) => console.log(identity)); // ^ // `------- The public identity for `flowAddress` ``` Private identity will be stored by the Wallet Provider and only be available to the currentUser. In FCL, when you retrieve the currentUsers identity, it fetches both the public and the private identities, which merges the private into the public. Private info needs to be requested via scopes before the challenge step, more on that later. We highly recommend Wallet Providers let the user see what scopes are being requested, and decide what scopes to share with the dApp. Consumers of identities in FCL should always assume all data is optional, and should store as little as possible. FCL will make sure the users always see the latest. ```javascript import { config, currentUser, authenticate } from '@onflow/fcl'; config.put('challenge.scope', 'email'); // request the email scope const unsub = currentUser().subscribe((identity) => console.log(identity)); // ^ // `------- The private identity for the currentUser authenticate(); // trigger the challenge step (authenticate the user via a wallet provider) ``` # Identity data - All information in Identities are optional and may not be there. - All values can be stored on chain, but most probably shouldn't be. We would love to see Wallet Providers allow the user to control the following info publicly, sort of a public profile starter kit if you will. FCL will always publicly try to fetch these fields when asked for a user's information, and it will be up to the Wallet provider to make sure they are there and keep them up to date if the user wants to change them. - **`name`** -- A human readable name, alias, or nym for a dApp user's display name. - **`avatar`** -- A fully qualified url to a smaller image used to visually represent the dApp user. - **`cover`** -- A fully qualified url to a bigger image, could be used by the dApp for personalization. - **`color`** -- A six character hex color, could be used by the dApp for personalization. - **`bio`** -- A small amount of text that a user can use to express themselves. If we can give dApp developers a solid foundation of usable information that is in the direct control of the users from the very start, which we belive the above fields would do, our hopes are they can rely more on the chain and will need to store less in their own database. Private data on the other hand has more use cases than general data. It is pretty easy to imagine that you'd order something and need information like contact details and where to ship something. Eventually, we would love to see that sort of thing handled completely onchain, securely, privately and safely. In the interm ,it probably means that you'll store a copy of data in a database when it's needed, and a user allows it. The process for a dApp to receive private data is as follows: 1. The dApp requests the scopes they want up front `fcl.config().put("challenge.scope", "email+shippingAddress")`. 2. The user authenticates `fcl.authenticate()` and inside the Wallet Providers authentication process decides its okay for the dapp to know both the `email` and the `shippingAddress`. The user should be able to decide which information to share, if any at all. 3. When the dApp needs the information they can request it from FCLs current cache of data, if it isn't there, the dApp needs to be okay with that and adjust accodingly. Below are the scopes we want to support privately: FCL will only publicly and privately try to fetch these when specified up front by a dApp. - **`email`** - **`fullName`** - **`phone`** - **`textMessage`** - **`address`** - **`shippingAddress`** - **`location`** - **`publicKey`** All of the above are still subject to change as it is still early days. We would like to work closely with Wallet Providers to produce a robust, detailed and consistent spec for scopes. Feedback and thoughts are always welcome. # Authentication challenge Authentication can happen one of two ways: - Iframe Flow - Redirection Flow As a Wallet Provider, you will be expected to register a URL endpoint (and some other information) with a handshake service (FCL launches with one in which registration happens on chain and is completely open source (Apache-2.0 lincense)). This registered URL will be what is shown inside the iFrame or where the dapp users will be redirected. For the remainder of this documentation, we will refere to it as the _Authentication Endpoint_ and pair it with the `GET https://provider.com/flow/authentication` route. The Authentication Endpoint will receive the following data as query params: - `l6n` _(required)_ -- location (origin) of dApp. - `nonce` _(required)_ -- a random string supplied by the FCL. - `scope` _(optional)_ -- the scopes requested by the dApp. - `redirect` _(optional)_ -- where to redirect after the authentication challenge is complete. ``` GET https://provider.com/flow/authenticate ?l6n=https%3A%2F%2Fdapp.com &nonce=asdfasdfasdf &scope=email+shippingAddress &redirect=https%3A%2F%2Fdapp.com%2Fflow%2Fcallback The values will use javascripts `encodeURIComponent` function and scopes will be `+` deliminated. ``` We can tell that this challenge uses the Redirect Flow because of the inclusion of the redirect query param. The Iframe Flow will still need to be supported, as it will be the default flow for dapps. At this point, it's on the Wallet Provider to do their magic and be confident enough that the user is who they say they are. The user should then be shown in some form what the dApp wants via the scopes and allow them to opt in or out of anything they want. After the Wallet Provider is ready to hand back control to the dapp and FCL, it needs to redirect or emit a javascript `postMessage` event to complete the challenge. Redirecting will look like this: ``` GET https://dapp.com/flow/callback # supplied by the redirect query param above ?l6n=https%3A%2F%2Fdapp.com # the l6n supplied by FCL above &nonce=asdfasdfasdf # the nonce supplied by FCL above &addr=0xab4U9KMf # address for the users flow account (if available) -- will be used to fetch public identity information and hooks &padder=0xhMgqTff86 # address for the Wallet Providers account -- will be used to fetch provider information &code=afseasdfsadf # a token supplied to FCL from the Wallet Provider, FCL will use this token when requesting private information and hooks, can be any url safe value &exp=1650400809517 # when the code expires, a value of `0` will be considered as never expires &hks==https%3A%2F%2Fprovider.com%2Fhooks # a URL where FCL can request the private information and hooks ``` Iframe will look like this: ```javascript parent.postMessage( { type: 'FCL::CHALLENGE::RESPONSE', // used by FCL to know what kind of message this is addr: '0xab4U9KMf', paddr: '0xhMgqTff86', code: 'afseasdfsadf', exp: 1650400809517, hks: 'https://provider.com/hooks', nonce: 'asdfasdfasdf', l6n: decodeURIComponent(l6n), }, decodeURIComponent(l6n), ); ``` FCL should now have everything it needs to collect the Public, Private and Wallet Provider Info. The Wallet Provider info will be on chain so its not something that needs to be worried about here by the Wallet Provider. You should be aware of how to handle the hooks request, which was supplied to FCL via the `hks` value in the challenge response `https://provider.hooks`. The hooks request will be to the `hks` value supplied in the challenge response. The request will also include the code as a query param. ``` GET https://povider.com/hooks ?code=afseasdfsadf ``` This request needs to happen for a number of reasons. - If it fails, FCL knows something is wrong and will attempt to re-authenticate. - If is succeeds, FCL knows that the code it has is valid. - It creates a direct way for FCL to "verify" the user against the Wallet Provider. - It gives FCL a direct way to get Private Identity Information and Hooks. - The code can be passed to the backend to create a back-channel between the backend and the Wallet Provider. When users return to a dApp, if the code FCL stored hasnt expired, FCL will make this request again to stay up to date with the latest informtaion. FCL may also intermitently request this information before some critial actions. The hooks request should respond with the following JSON ```javascript const privateHooks = { addr: "0xab4U9KMf", // the flow address this user is using for the dapp keyId: 3, // the keyId the user wants to use when authorizing transaction identity: { // the identity information fcl always wants if its there, will be deep merged into public info name: "Bob the Builder", avatar: "https://avatars.onflow.org/avatar/0xab4U9KMf.svg" cover: "https://placekittens.com/g/900/300", color: "cccc00", bio: "", }, scoped: { // the private info request in the original challenge email: "bob@bob.bob", // the user said it was okay for the dapp to know the email shippingAddress: null, // the user said it was NOT okay for the dapp to know the shippingAddress }, provider: { addr: "0xhMgqTff86", // the flow address for the wallet provider (used in the identity composite id) pid: 2345432, // the wallet providers internal id for the user (used in the identity composite id) name: "Super Wallet", icon: "https://provider.com/assets/icon.svg", authn: "https://provider.com/flow/authenticate", } } ``` When FCL requested the Public info from the chain, it expects something like this. It will be on the Wallet Provider to keep this information up to date. ```javascript const publicHooks = { addr: "0xab4U9KMf", keyId: 2, identity: { name: "Bob the Builder", avatar: "https://avatars.onflow.org/avatar/0xab4U9KMf.svg" cover: "https://placekittens.com/g/900/300", color: "cccc00", bio: "", }, authorizations: [ { id: 345324539, addr: "0xhMgqTff86", method: "HTTP/POST", endpoint: "https://provider.com/flow/authorize", data: { id: 2345432 } } ] } ``` At this point, FCL can be fairly confident who the currentUser is and is ready to initiate transactions the user can authorize. # Authorization FCL will broadcast authorization requests to the Public and Private authorization hooks it knows for a User, in a process we call Asynchronous Remote Signing. The core concepts to this idea are: - Hooks tell FCL where to send authorization requests (Wallet Provider) - Wallet Provider responds imediately with: - a back-channel where FCL can request the results of the authorization. - some optional local hooks ways the currentUser can authorize. - FCL will trigger the local hooks if they are for the currentUser. - FCL will poll the back-channel requesting updates until an approval or denial is given. Below is the public authorization hook we received during the challenge above. ```javascript { id: 345324539, addr: "0xhMgqTff86", method: "HTTP/POST", endpoint: "https://provider.com/flow/authorize", data: { id: 2345432 } } ``` FCL will take that hook and do the following post requeset: ``` POST https://provider.com/flow/authorize ?id=2345432 --- { message: "...", // what needs to be signed (needs to be convered from hex to binary before signing) addr: "0xab4U9KMf", // the flow address that needs to sign keyId: 3, // the flow account keyId for the private key that needs to sign roles: { proposer: true, // this accounts sequence number will be used in the transaction authorizer: true, // this transaction can "move" and "modify" the accounts resources directly payer: true, // this transaction will be paid for by this account (also signifies that they are signing an envelopeMessage instead of a payloadMessage) }, interaction: {...} // needed to recreate the message if the Wallet Provider wants to verify the message. } ``` FCL expects something like this in response: ```javascript { status: "PENDING", reason: null, compositeSignature: null, authorizationUpdates: { method: "HTTP/POST", endpoint: "https://provider.com/flow/authorizations/4323", }, local: [ { method: "BROWSER/IFRAME", endpoint: "https://provider.com/authorizations/4324", width: "300", height: "600", background: "#ff0066" } ] } ``` That local hook will be consumed by FCL, which renders an iframe with the endpoint as the SRC. If the user is already authenticated, this screen could show them the Wallet Providers transaction approval process directly. Because FCL isnt relying on any communication to or from the Iframe, it can lock it down as much as possible, and remove it once the authorization is complete. While it displays the local hook, it will request the status of the authorization from the `authorizationUpdates` hook. ``` POST https://provider.com/flow/authorizations/4323 ``` We expect a response that has the same structure as the origin, but without the local hooks: ```javascript { status: "PENDING", reason: "", compositeSignature: null, authorizationUpdates: { method: "HTTP/POST", endpoint: "https://provider.com/flow/authorizations/4323", }, } ``` FCL will then follow the new `authorizationUpdates` hooks until the status changes to `"APPROVED"` or `"DECLINED"`. If the authorization is declined, it should include a reason if possible. ```javascript { status: "DECLINED", reason: "They said no", } ``` If the authorization is approved, it should include a composite signature: ```javascript { status: "APPROVED", compositeSignature: { addr: "0xab4U9KMf", // the flow address that needs to sign keyId: 3, // the flow account keyId for the private key that needs to sign signature: "..." // binary signature of message encoded as hex } } ``` FCL can now submit the transaction to the Flow blockchain. # TL;DR Wallet Provider Register Provider with FCL Handshake and implement five Endpoints. - `GET flow/authenticate` -> `parent.postMessage(..., l6n)` - `GET flow/hooks?code=___` -> `{ ...identityAndHooks }` - `POST flow/authorize` -> `{ status, reason, compositeSignature, authorizationUpdates, local }` - `POST authorizations/:authorization_id` - `GET authorizations/:authorization_id` ![diagram showing current fcl authn and authz flow](./assets/fcl-ars-auth-v3.2.png) --- # File: /md/build/tools/wallet-provider-spec/provable-authn.md # Provable Authn To improve UX/DX and encourage seamless integration with App backends and services, `fcl.authenticate` has been upgraded. Additional data is sent in the body of `FCL:VIEW:READY:RESPONSE`. This data includes what the wallet needs to build a message for signing with the user’s private keys. The signature can be returned as part of an optional `account-proof` service with the `FCL:VIEW:RESPONSE`. When provided by the wallet, this **signature** and additional **account-proof data** is available to the App via `fcl.currentUser` services. The service data can be used to recreate the message, and verify the signature on the Flow Blockchain. For example, it can be sent to the App’s backend and after it validates the signature and the other account-proof data, it can safely associate the included account address to a user and log them in. --- ## TL;DR Wallet Provider 1. Wallet receives Authn `FCL:VIEW:READY:RESPONSE` request and parses out the `appIdentifier`, and `nonce`. 2. The Wallet authenticates the user however they choose to do, and determines the user's account `address`. 3. The wallet must validate the `appIdentifier` against the RFC 6454 origin of the request if it matches the format of a [RFC 3986] URI. Requests with a mismatch should be rejected. Some legacy systems may use arbitrary strings as `appIdentifier` and not [RFC 6454] origins. In this case, wallets should display a warning to the user that the app identifier does not match the origin of the request. 4. Wallet prepares and signs the message: - Encodes the `appIdentifier`, `nonce`, and `address` along with the `"FCL-ACCOUNT-PROOF-V0.0"` domain separation tag, [via the encoding scheme described below]. - Signs the message with the `signatureAlgorithm` and `hashAlgorithm` specified on user's key. **We strongly recommend that the wallet display the message data and receive user approval before signing.** 6. Wallet sends back this new service and data along with the other service configuration when it completes Authn. ### Account proof message encoding The account proof message is encoded as follows: ```text MESSAGE = USER_DOMAIN_TAG || RLP_ENCODE([ APP_IDENTIFIER, ADDRESS, NONCE ]) ``` with the following values: - `ACCOUNT_PROOF_DOMAIN_TAG` is the constant `"FCL-ACCOUNT-PROOF-V0.0"`, encoded as UTF-8 byte array and right-padded with zero bytes to a length of 32 bytes. - `APP_IDENTIFIER` is an arbitrary length string. - `ADDRESS` is a byte array that contains the address bytes, left-padded with zero bytes to a length of eight bytes. - `NONCE` is an byte array with a minimum length of 32 bytes. `RLP_ENCODE` is a function that performs [RLP encoding] and returns the encoded value as bytes. ### JavaScript signing example ```javascript // Using WalletUtils import {WalletUtils} from "@onflow/fcl" WalletUtils.onMessageFromFcl( (data, {origin}) => { const {address, nonce, appIdentifier} = data.data // Check if the appIdentifier is a valid RFC 3986 URI if (!isRfc3986Uri(appIdentifier)) { // Warn the user that the appIdentifier does not match the origin and to proceed with caution } else if (origin !== appIdentifier) { // Reject the request if the appIdentifier is a valid RFC 3986 URI but does not match the origin throw new Error("Invalid appIdentifier") } const message = WalletUtils.encodeAccountProof( appIdentifier, // A human readable string to identify your application during signing address, // Flow address of the user authenticating nonce, // minimum 32-btye nonce ) sign(privateKey, message) // Without using FCL WalletUtils const ACCOUNT_PROOF_DOMAIN_TAG = rightPaddedHexBuffer( Buffer.from("FCL-ACCOUNT-PROOF-V0.0").toString("hex"), 32 ) const message = rlp([appIdentifier, address, nonce]) const prependUserDomainTag = (message) => ACCOUNT_PROOF_DOMAIN_TAG + message sign(privateKey, prependUserDomainTag(message)) } ) ``` ```json // Authentication Proof Service { f_type: "Service", // Its a service! f_vsn: "1.0.0", // Follows the v1.0.0 spec for the service type: "account-proof", // the type of service it is method: "DATA", // Its data! uid: "awesome-wallet#account-proof", // A unique identifier for the service data: { f_type: "account-proof", f_vsn: "1.0.0" // The user's address (8 bytes, i.e 16 hex characters) address: "0xf8d6e0586b0a20c7", // Nonce signed by the current account-proof (minimum 32 bytes in total, i.e 64 hex characters) nonce: "75f8587e5bd5f9dcc9909d0dae1f0ac5814458b2ae129620502cb936fde7120a", signatures: [CompositeSignature], } } ``` [RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986 [RFC 6454]: https://www.rfc-editor.org/rfc/rfc6454.html [via the encoding scheme described below]: #account-proof-message-encoding [RLP encoding]: https://eth.wiki/fundamentals/rlp --- # File: /md/build/tools/wallet-provider-spec/user-signature.md # User Signature ## Status - **Last Updated:** June 1st, 2021 - **Stable:** Yes - **Risk of Breaking Change:** Low - **Compatibility:** `>= @onflow/fcl@0.0.71` # Overview and introduction **Personally sign data via FCL Compatible Wallets** **Flow Client Library (FCL)** now incldues **`signUserMessage()`**, which allows you to send unencrypted message data to a connected wallet provider or service to be signed with a user's private key. An application or service can verify a signature against a user's public key on the **Flow Blockchain**, which provides proof a user controls the account's private key. **Use Cases** - **Authentication**: Sign a piece of data with a provate key to cryptographically verify the ownership of a **Flow** account. - **Improved Application Login** - **Increased security**: Arguably more secure than proof of ownership by email and password. - **Simplified UX**: No application password required. - **Increased privacy**: No email or third party authentication service needed. - **Message Validation**: Assure that a message sent or received wasn't tampered with. - **Multisig contracts** - **Decentralised exchanges** - **Meta transactions** # Config and authentication As a prerequisite, **FCL** is configured to point to the Wallet Provider's Authentication Endpoint. No additional configuration is required. > During development (and on mainnet), you can configure Flow Client Library (FCL) to use the wallet directly. To do this, set the **Wallet Discovery Url** to the wallet provider's **Authentication Endpoint** by configuring FCL like this `config().put("discovery.wallet", "https://my-awesome-wallet-provider.com/fcl/authenticate")`. For more information about common Configuration Keys, see [How to Configure FCL] 1. A user initiates authentication with the wallet provider via application UI. 2. The wallet confirms a user's identity and sends back information used to configure **FCL** for future user actions in the application. 3. Included in the authentication response should be the provider's [Key Services](#) which includes a **`user-signature`** service for use with **`signUserMessage()`**. # User signature service A [user-signature service] is a standard service, with methods for **IFRAME/RPC** or **HTTP/POST**. The `user-signature` service receives a signable message from **FCL** and returns a standard [PollingResponse] with an array of [CompositeSignatures] or `null` as the data. A status of **Approved** needs to have an array of composite signatures as data. A status of **Declined** needs to include a reason why. A **Pending** status needs to include an updates service and can include a local. A service that uses the **`IFRAME/RPC`** method can only respond with `approved` or `declined`, as `pending` is not valid for iframes. When `signUserMessage()` is called by the application, **FCL** uses the service method to decide how to send the signable to the wallet. The Wallet is responsible for prepending the signable with the correct `UserDomainTag`, hashing, and signing the message. # Signing sequence 1. Application sends message to signing service. **FCL expects a hexadecimal string**. 3. Wallet and Service tags the message with required `UserDomainTag` (see below), hashes, and signs with the `signatureAlgorithm` specified on account key. 2. Wallet makes available a Composite Signature that consists of `addr`, `keyId`, and `signature` **as a hex string**. ### UserDomainTag The **`UserDomainTag`** is the prefix of all signed user space payloads. Before hashing and signing the message, the wallet must add a specified DOMAIN TAG. > currently **"FLOW-V0.0-user"** A domain tag is encoded as **UTF-8 bytes, right padded to a total length of 32 bytes**, prepended to the message. The signature can now be verified on the Flow blockchain. The following illustrates an example with `fcl.verifyUserSignatures` ```javascript /** * Verify a valid signature/s for an account on Flow. * * @param {string} msg - A message string in hexadecimal format * @param {Array} compSigs - An array of Composite Signatures * @param {string} compSigs[].addr - The account address * @param {number} compSigs[].keyId - The account keyId * @param {string} compSigs[].signature - The signature to verify * @return {bool} * * @example * * const isValid = await fcl.verifyUserSignatures( * Buffer.from('FOO').toString("hex"), * [{f_type: "CompositeSignature", f_vsn: "1.0.0", addr: "0x123", keyId: 0, signature: "abc123"}] * ) */ ``` ## TL;DR Wallet Provider - Register with **FCL** and provide signing service endpoint. No further configuration is needed. - On receipt of message, prompt user to approve or decline. - Prepend `UserDomainTag`, hash and sign the message with the signatureAlgorithm specified on user's key. - Return a standard `PollingResponse` with an array of `CompositeSignatures` as data or `null` and `reason` if declined. [How to Configure FCL]: ../clients/fcl-js/packages-docs/fcl/index.md#common-configuration-keys [user-signature service]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js [PollingResponse]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js#L5 [CompositeSignatures]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js#L4 --- # File: /md/defi.md --- title: Defi description: Mechanisms that connect different blockchain networks, allowing secure and decentralized transfer of assets and data across platforms. keywords: - DeFi - liquidity - decentralized finance - blockchain networks - cross-chain transfer - decentralized transfer - Flow blockchain - DEX - lending platforms - stablecoin protocols - liquidity solutions - Consumer DeFi - Forte - Flow Actions - Scheduled Transactions sidebar_position: 1 --- # Defi on Flow **Fast, scalable, and capital-efficient DeFi.** Flow delivers a seamless DeFi experience without congestion, unlocking new possibilities for developers and users alike. Flow is a purpose-built L1 blockchain designed for large-scale consumer finance applications and automated DeFi. It enables developers to build high-performance **DEXs, lending platforms, stablecoin protocols, and liquidity solutions**—all without the friction of high fees or complex scaling layers. ## Why DeFi on Flow? **Ultra-low fees** - Cost-efficient swaps, lending, and staking **Fast finality** - Transactions confirmed in seconds with guaranteed execution **MEV resistance** - Equitable access without frontrunning or hidden fees **Capital-efficient execution** - No congestion, seamless scaling **Composable DeFi** - Built-in interoperability between assets and protocols **Automated execution** - Native scheduling and autonomous workflows with Forte **Start integrating DeFi on Flow today.** **[DeFi Contracts](./defi-contracts-mainnet.md)** **[Cross-chain swaps](./cross-chain-swaps.md)** **[Read the FAQ](./faq.md)** Explore the [FlowVerse DeFi ecosystem](https://www.flowverse.co/?categories=defi) page for more information. ## Build with Forte The **Forte network upgrade** transforms Flow into an autonomous, intelligent network capable of executing complex DeFi workflows without external dependencies. Forte introduces native time scheduling, protocol-level composability, and precision financial calculations that enable entirely new categories of applications. **Flow Actions** enable protocol-native, composable operations that link together standardized DeFi primitives—sources, sinks, swappers, price oracles, and more—into atomic, protocol-agnostic workflows. A single transaction can claim rewards, swap assets, add liquidity, and restake LP tokens without any off-chain orchestration. **Scheduled Transactions** introduce the first truly onchain time scheduler, enabling recurring actions, deferred settlements, and autonomous portfolio management without external cron jobs or trusted servers. DeFi protocols can become self-maintaining: positions compound automatically, vaults adjust exposure based on time or events, and protocols enforce predictable behavior entirely onchain. Combined with **128-bit fixed-point arithmetic** for lossless financial calculations and **native WebAuthn support** for seedless user onboarding, Forte reduces development time for consumer finance applications from months to mere days. Learn more about building automated DeFi with [Flow Actions](../blockchain-development-tutorials/forte/flow-actions/index.md) and [Scheduled Transactions](../blockchain-development-tutorials/forte/scheduled-transactions/scheduled-transactions-introduction.md). ## DeFi Partners
    ## Kittypunch [Kittypunch][1] is a next-generation decentralized exchange on Flow offering high-speed, low-cost token swaps and an intuitive trading experience. With a focus on community engagement and gamified finance, Kittypunch brings fresh energy to DeFi on Flow. Learn more [here][9]. ## Trado.one [Trado.one][2] is a permissionless decentralized exchange that enables seamless token swaps on Flow. It offers an efficient, user-friendly interface and liquidity tools aimed at developers and retail users alike. Learn more [here][10]. ## Increment.fi [Increment.fi][3] is a composable DeFi platform on Flow offering lending, borrowing, staking, and exchange functionalities. It enables developers to integrate financial primitives into their apps through modular smart contracts. Learn more [here][11]. ## More.Markets [More.Markets][4] provides decentralized lending and borrowing on Flow. Users can supply assets to earn yield or borrow assets against collateral with dynamic interest rates, offering capital efficiency and transparency. Learn more [here][12]. ## Sturdy.Finance [Sturdy.Finance][5] is a DeFi protocol that facilitates interest-free borrowing using yield-bearing assets. Built for Flow, it empowers users with efficient leverage while maintaining security and composability. Learn more [here][13]. ## Ankr **Tags:** Liquid Staking [Ankr][6] brings liquid staking to Flow, allowing users to stake FLOW tokens and receive liquid derivatives that can be used in other DeFi protocols. This increases capital efficiency while securing the network. Learn more [here][14]. ## Sudocat [Sudocat][7] offers a decentralized trading dashboard built for Flow. With tools for analytics, portfolio tracking, and execution, it enhances the trading experience for onchain users and DeFi enthusiasts. Learn more [here][15]. ## Hitdex [Hitdex][8] is a trading app native to Flow that combines a user-friendly interface with advanced execution logic. It supports seamless token swaps and is optimized for both speed and low fees. Learn more [here][16]. ## Rally [Rally][17] is your wallet, made social. Welcome to the new internet built for us to rally together, offering innovative social trading features and community-driven financial experiences on Flow. ## Hype.meme [Hype.meme][18] is a platform to trade memes, now live on the App Store. It brings meme trading to the Flow blockchain with a focus on community-driven content and viral assets. ## Vaultopolis [Vaultopolis][19] tokenizes Top Shot Moments into TSHOT, providing trading opportunities, yield generation, and liquidity solutions for NBA Top Shot collectors on Flow. ## Izumi.finance [Izumi.finance][20] is a multi-chain DeFi protocol providing one-stop DEX-as-a-Service (DaaS) solutions, enabling seamless cross-chain liquidity and trading infrastructure on Flow. ## Bridged USDC [Bridged USDC][21] represents [USDC][25] tokens bridged across blockchains for cross-chain use, providing stable liquidity and payment solutions within the Flow ecosystem. ## USDF USDF is a blockchain-backed bank token enhancing payments and providing stable value transfer capabilities within the Flow DeFi ecosystem. ## Pumpflow [Pumpflow][23] enables users to launch and verify meme tokens in seconds on the Flow blockchain, providing a streamlined token creation and distribution platform. ## Fixes.world [Fixes.world][24] is an autonomous programmable token universe that allows users to launch, distribute, and trade tokens with advanced automation and governance features. ## Tally [Credit card debt][26] has met its match. Save thousands with our advanced credit line system.
    [1]: https://www.kittypunch.xyz/ [2]: https://www.trado.one/ [3]: https://app.increment.fi/ [4]: https://www.more.markets/ [5]: https://sturdy.finance/ [6]: https://www.ankr.com/ [7]: https://www.sudocat.ai/ [8]: https://www.hitdex.com/#/welcome [9]: https://kittypunch.gitbook.io/kittypunch-docs [10]: https://docs-perp.trado.one/ [11]: https://docs.increment.fi/ [12]: https://docs.more.markets/ [13]: https://docs.sturdy.finance/ [14]: https://www.ankr.com/docs/ [15]: https://docs.sudocat.ai/ [16]: https://www.hitdex.com/#/welcome [17]: https://rally.xyz/ [18]: https://hype.meme/ [19]: https://vaultopolis.com/ [20]: https://izumi.finance/ [21]: https://bridge.flow.com/ [23]: https://www.pumpflow.meme/ [24]: https://fixes.world/ [25]: https://www.circle.com/usdc [26]: https://www.tally.xyz/ ## Securing DeFi on Flow EVM Flow EVM supports secure multi-sig from Safe.Global (formerly Gnosis Safe) or Fireblocks. Both offer robust solutions for developers seeking to enhance the security and management of tokenized assets with a focus on institutional-grade practices and scalability. - [Safe.Global](https://safe.flow.com/) - [Fireblocks](https://www.fireblocks.com/) --- --- # File: /md/defi/add-token-to-metamask.md --- title: How To Add Token To MetaMask description: How to import a Flow token in MetaMask sidebar_label: Add Token To MetaMask sidebar_position: 6 keywords: - add token to MetaMask - MetaMask Flow EVM - Flow EVM token - import token MetaMask - add custom token MetaMask - Flow token contract address - Flow EVM DeFi - MetaMask Flow EVM network - how to add token MetaMask - Flow EVM block explorer - Flow EVM Dexscreener - Gecko Terminal Flow EVM - Dexscreener Flow EVM - evm.flowscan.io tokens - wrapped Flow MetaMask - Flow EVM liquidity - Flow EVM DeFi tokens - import Flow token - Flow blockchain MetaMask --- import MetaMaskImage from './add_custom_token_metamask.gif'; import AddWrappedFlow from './add_wrapped_flow_to_metamask_2.png'; :::info Want to add a token to MetaMask on Flow EVM? You can find a list of tokens on Flow at [evm.flowscan.io/tokens][1] or check the most popular DeFi token contract addresses [here][2] ::: ## Objectives After completing this guide, you'll be able to: - Add ERC20 tokens to MetaMask via [Flowscan][5]. - Manually add a custom token to MetaMask using a contract address. - Locate Flow EVM token contract addresses on various platforms. ## Add a Token to MetaMask via Flowscan 1. Visit [https://evm.flowscan.io/tokens][1] to find the token you want to add to MetaMask. 2. Hover over the token you want to import, and a MetaMask icon will appear. Click on it to add the token. :::note If you haven't yet added the Flow EVM network to your MetaMask, you will receive a prompt to add the network before adding the token. ::: ![Add Token Via Flowscan](add_wrapped_flow_to_metamask.jpg) Add wrapped Flow ## Add a Custom Token in MetaMask Wallet If you have a token contract address, you can manually add it to MetaMask. ### Steps to Add a Custom Token 1. Open your MetaMask and go to the **Tokens** tab. 2. Scroll down and click on **Import tokens**. 3. Enter the token contract address. 4. Click **Next**, then **Import** to add the token. Add custom token in MetaMask ## How to Find The Token Contract Address You can find token contract addresses on: - [Flowscan Token List][1] - The [DeFi Contracts][2] page - [Dexscreener Flow EVM][3] - [Gecko Terminal Flow EVM][4] If you're unsure, check the Flow EVM block explorer at [evm.flowscan.io][5] [1]: https://evm.flowscan.io/tokens [2]: /defi/defi-contracts-mainnet [3]: https://dexscreener.com/flowevm [4]: https://www.geckoterminal.com/flow-evm/pools [5]: https://evm.flowscan.io --- # File: /md/defi/band-oracle.md --- id: band-oracle title: Band Oracle on Flow description: Band Protocol provides decentralized oracle services on Flow, delivering real-time price feeds and data for DeFi applications. keywords: - Band Oracle - Band Protocol - oracle - price feeds - data feeds - DeFi - Flow blockchain - Flow Cadence - smart contracts sidebar_position: 7 sidebar_label: Band Oracle --- import CopyButton from '@site/src/components/CopyButton'; # Band Oracle with Cadence The Band Protocol Oracle contract enables Flow blockchain applications to access real-time price data from the [Band Protocol Oracle network](https://faq.bandprotocol.com/). The oracle provides a comprehensive set of cryptocurrency and fiat currency price quotes from the Band Standard Dataset, making them available to any Cadence application, contract, or transaction. ## Contract Addresses | Network | Address | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | Explorer | | ------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | | Testnet | `0x9fb6606c300b5051` | | [View Contract](https://testnet.flowscan.io/contract/A.9fb6606c300b5051.BandOracle) | | Mainnet | `0x6801a6222ebf784a` | | [View Contract](https://flowscan.io/contract/A.6801a6222ebf784a.BandOracle) | ## Supported Symbols ### Cryptocurrency Pairs (against USD) - **Major**: ETH, FLOW, USDC, USDT, WBTC, BNB, XRP, ADA, DOGE, POL (MATIC) - **Layer 1**: SOL, DOT, AVAX, ATOM, XLM, TRX, SUI - **DeFi**: AAVE, LINK, CRV, OP, UNI, SUSHI, CAKE, DYDX, 1INCH, BAT - **Others**: LTC, SHIB, DAI, FTM ### Fiat Currency Pairs (against USD) - **Asian**: KRW, INR, HKD, TWD, THB, JPY, MYR, PHP, CNY, SGD - **European**: PLN, CZK, EUR, GBP, CHF, RUB, SEK, TRY - **Americas**: BRL, CAD - **Oceanic**: AUD, NZD ## How It Works ### Architecture The Band Oracle contract maintains a decentralized price feed system with three key components: 1. **Data Storage**: Price data is stored in a contract-level dictionary `symbolsRefData: {String: RefData}` where each symbol maps to its latest price information. 2. **Data Updates**: Authorized BandChain relayers continuously update price data from the Band Protocol network to keep prices current. 3. **Data Access**: Any user or contract can query the latest price data through public functions, enabling real-time price integrations. ### Data Structure Price data is stored using the `RefData` struct: ```cadence access(all) struct RefData { // USD-rate, multiplied by 1e9 access(all) var rate: UInt64 // UNIX epoch when data was last resolved access(all) var timestamp: UInt64 // BandChain request identifier for this data access(all) var requestID: UInt64 } ``` When querying prices, you receive a `ReferenceData` struct: ```cadence access(all) struct ReferenceData { // Rate as integer multiplied by 1e18 access(all) var integerE18Rate: UInt256 // Rate as a fixed-point decimal access(all) var fixedPointRate: UFix64 // Timestamp of base symbol data access(all) var baseTimestamp: UInt64 // Timestamp of quote symbol data access(all) var quoteTimestamp: UInt64 } ``` ### Data Normalization All price data is stored with a USD conversion rate. When you query for price conversions between two non-USD symbols, the contract derives the rate from their respective USD rates. For example, to get ETH/EUR, the contract calculates: `(ETH/USD) / (EUR/USD)`. ## Features ### Price Queries - Query any supported symbol pair in real-time - Get both integer (e18 precision) and fixed-point decimal rates - Access timestamp information to verify data freshness - Track BandChain request IDs for transparency ### Fee Structure - Configurable fee system for oracle usage (currently set to zero) - Fee collected in FLOW tokens - Query current fee using `BandOracle.getFee()` ### Event Monitoring The contract emits events to notify applications of updates: ```cadence // Emitted when symbol prices are updated access(all) event BandOracleSymbolsUpdated( symbols: [String], relayerID: UInt64, requestID: UInt64 ) // Emitted when a symbol is removed access(all) event BandOracleSymbolRemoved(symbol: String) ``` ## Usage Guide ### Basic Price Query (Transaction) To query price data from a transaction: ```cadence import "BandOracle" import "FlowToken" import "FungibleToken" transaction(baseSymbol: String, quoteSymbol: String) { let payment: @{FungibleToken.Vault} prepare(acct: auth(BorrowValue) &Account) { // Borrow reference to user's FLOW vault let vaultRef = acct.storage.borrow( from: /storage/flowTokenVault ) ?? panic("Cannot borrow reference to signer's FLOW vault") // Withdraw payment for oracle fee self.payment <- vaultRef.withdraw(amount: BandOracle.getFee()) } execute { // Get reference data let priceData = BandOracle.getReferenceData( baseSymbol: baseSymbol, quoteSymbol: quoteSymbol, payment: <- self.payment ) log("Rate (fixed-point): ".concat(priceData.fixedPointRate.toString())) log("Rate (integer e18): ".concat(priceData.integerE18Rate.toString())) log("Base timestamp: ".concat(priceData.baseTimestamp.toString())) log("Quote timestamp: ".concat(priceData.quoteTimestamp.toString())) } } ``` ### Example: ETH/USD Price ```cadence // Get ETH price in USD let priceData = BandOracle.getReferenceData( baseSymbol: "ETH", quoteSymbol: "USD", payment: <- flowPayment ) // priceData.fixedPointRate contains ETH price in USD ``` ### Example: Cross-Currency Conversion ```cadence // Get EUR price in JPY let priceData = BandOracle.getReferenceData( baseSymbol: "EUR", quoteSymbol: "JPY", payment: <- flowPayment ) // priceData.fixedPointRate contains EUR/JPY exchange rate ``` ### Contract Integration Here's how to integrate the oracle into your smart contract: ```cadence import "BandOracle" import "FlowToken" import "FungibleToken" access(all) contract MyDeFiContract { // Store a vault to pay for oracle fees access(self) let oracleFeeVault: @{FungibleToken.Vault} access(all) fun getTokenPriceInUSD(tokenSymbol: String): UFix64 { // Withdraw payment for oracle let payment <- self.oracleFeeVault.withdraw( amount: BandOracle.getFee() ) // Query the oracle let priceData = BandOracle.getReferenceData( baseSymbol: tokenSymbol, quoteSymbol: "USD", payment: <- payment ) return priceData.fixedPointRate } access(all) fun swapTokens(amount: UFix64, maxPrice: UFix64) { // Get current price let currentPrice = self.getTokenPriceInUSD(tokenSymbol: "ETH") // Verify price is acceptable if currentPrice > maxPrice { panic("Price too high") } // Proceed with swap logic... } init() { // Initialize vault for oracle fees self.oracleFeeVault <- FlowToken.createEmptyVault( vaultType: Type<@FlowToken.Vault>() ) } } ``` ## Best Practices ### 1. Listen for Price Updates Monitor the `BandOracleSymbolsUpdated` event to keep your contract's stored prices up-to-date: ```cadence // Listen for this event in your application access(all) event BandOracleSymbolsUpdated( symbols: [String], relayerID: UInt64, requestID: UInt64 ) ``` When you detect an update for symbols your app uses, trigger a transaction to refresh your stored prices. ## Advanced Features ### Converting Between Number Formats The contract provides a utility function to convert between integer and fixed-point representations: ```cadence // Convert e18 integer to fixed-point decimal let fixedPoint = BandOracle.e18ToFixedPoint(rate: integerE18Rate) ``` ### Fee Management For contract administrators, the oracle supports dynamic fee configuration: ```cadence // Query current fee let currentFee = BandOracle.getFee() // Fee can be updated by the fee collector (admin only) // feeCollector.setFee(fee: 0.001) // 0.001 FLOW per query ``` ## Resources - [Band Protocol FAQ](https://faq.bandprotocol.com/) - [Band Standard Dataset](https://data.bandprotocol.com/) - [Cadence Language Reference](https://cadence-lang.org/) --- **Note**: The oracle currently charges no fees for usage, but this may change in the future. Always check `BandOracle.getFee()` before querying to ensure your contract has sufficient FLOW tokens allocated. --- # File: /md/defi/cross-chain-swaps.md --- id: cross-chain-swaps title: Cross-chain swaps on Flow EVM description: An overview of cross-chain options on Flow EVM keywords: - cross-chain token swap - cross-chain bridge - intent based bridge - liquidity based bridge - bridges - Flow blockchain - Flow EVM - EVM - Relay.link - Stargate - LayerZero - Celer sidebar_position: 5 sidebar_label: Cross-chain swaps on Flow EVM --- import Details from '@theme/Details'; The following bridges offer cross-chain swaps (token bridging including swap) to or from Flow EVM. ## Liquidity Pool Based Cross-chain Swaps ### Stargate [Stargate](https://stargate.finance) employs unified liquidity pools shared across multiple chains to enable native asset transfers and cross-chain swaps without wrapped tokens. It is built on LayerZero's cross-chain messaging protocol. ### Celer [Celer](https://cbridge.celer.network) is a hybrid liquidity network bridge that combines multiple bridging models and is based on the Celer Inter-Chain Messaging Framework. ## Intent Based Cross-chain Swaps Intent based bridges do not depend on pre-funded liquidity pools which can improve user experience, transaction speed and capital efficiency. ### Relay [Relay.link](https://relay.link/bridge/base) allows users to specify desired cross-chain swap outcomes for orders. --- # File: /md/defi/defi-contracts-mainnet.md --- id: defi-contracts-mainnet title: DeFi Contracts on Flow Mainnet description: A reference table of frequently used DeFi contracts on Flow, including their addresses for both Flow EVM and Flow Cadence. keywords: - DeFi contracts - Flow blockchain - Flow EVM - Flow Cadence - Flow EVM Mainnet - Flow Cadence Mainnet - stablecoins - wrapped assets - AMMs - DEXs - KittyPunch - PunchSwap sidebar_position: 3 sidebar_label: DeFi Contracts Mainnet --- import StablecoinsWrappedAssetsTable from '@site/src/components/defi-contracts/StablecoinsWrappedAssetsTable'; import CopyButton from '@site/src/components/CopyButton'; Flow is a Layer 1 blockchain that supports EVM equivalency, offering two environments Flow EVM and Flow Cadence. Fungible and non-fungible tokens can seamlessly transfer between these environments via the native VM token bridge. As a result, many tokens have both a Flow EVM mainnet contract address and a Flow Cadence mainnet contract address, allowing developers to choose their preferred environment. Below is a list of commonly used DeFi contracts on Flow Mainnet: [Switch to DeFi Contracts on Testnet](./defi-contracts-testnet.md) ## Stablecoins & Wrapped Assets #### Flow EVM Mainnet #### Flow Cadence Mainnet ## AMMs & DEXs #### Flow EVM Mainnet FlowSwap | Contract | EVM Mainnet Address | | --------------------------------------------- | -------------------------------------------- | | [UniswapV2Factory (FlowSwap)][38] | `0x681D1bFE03522e0727730Ba02a05CD3C0a08fa30` | | [UniswapV2Router02 (FlowSwap)][39] | `0x2B30D97457d44dE9fb0329D9a2C1DF6B7ae1401d` | | [UniswapV2Pair (FlowSwap)][40] | `0x48d63C4E4481fd0A01Fb82A3B191C6685c361f02` | | [UniswapV3Factory (FlowSwap)][41] | `0xca6d7Bb03334bBf135902e1d919a5feccb461632` | | [NonfungiblePositionManager (FlowSwap)][42] | `0xf7F20a346E3097C7d38afDDA65c7C802950195C7` | | [SwapRouter02 (FlowSwap)][43] | `0xeEDC6Ff75e1b10B903D9013c358e446a73d35341` | | [QuoterV2 (FlowSwap)][44] | `0x370A8DF17742867a44e56223EC20D82092242C85` | | [V3Migrator (FlowSwap)][45] | `0x5C65D5C7E0154f519B7dC4558915A7016F41aa50` | | [UniswapV3Staker (FlowSwap)][46] | `0x990A0564B7d90656494Ba7A7E1e874038cc35f5d` | | [TickLens (FlowSwap)][47] | `0x513A58591c8E502543D629748076857a71C6079D` | | [NFTDescriptor (FlowSwap)][48] | `0x99187C0a0AF166b40C393FAE5FDaC688ed5b6989` | | [v3_nft_position_descriptor (FlowSwap)][49] | `0x36D8296B9B73dE9d5Ec09ACc6a7c02cC40Ad9780` | | [TransparentUpgradeableProxy (FlowSwap)][50] | `0xB231Aa6F8636373f5233eeaD13FFEB199659d484` | | [UniswapV3Pool (FlowSwap)][51] | `0xd21C58aDaf1d1119FE40413b45A5f43d23d58DF3` | | [UniversalRouter (FlowSwap)][52] | `0x5fE87847fe20a6C30921620F52B06a4A3740aa61` | | [Permit2 (FlowSwap)][53] | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | [FusionXInterfaceMulticall (FlowSwap)][54] | `0x8B5eB800B8d9cF702ff3DD0047ac31bBD411B82a` | | [proxy_admin (FlowSwap)][55] | `0x026932f97995201527B4c9Bc2ea6854C02BB0AdC` | #### Flow EVM Mainnet KittyPunch | Contract Name | Flow EVM Mainnet Address | Docs | | -------------------------------------------- | -------------------------------------------- | ---------------------- | | [StableKittyFactoryNG (KittyPunch)][1] | `0x4412140D52C1F5834469a061927811Abb6026dB7` | [Docs][kittypunch-doc] | | [TwoKittyFactory (KittyPunch)][2] | `0xf0E48dC92f66E246244dd9F33b02f57b0E69fBa9` | [Docs][kittypunch-doc] | | [TriKittyFactory (KittyPunch)][3] | `0xebd098c60b1089f362AC9cfAd9134CBD29408226` | [Docs][kittypunch-doc] | | [KittyRouterNgPoolsOnly (KittyPunch)][4] | `0x87048a97526c4B66b71004927D24F61DEFcD6375` | [Docs][kittypunch-doc] | | [PunchSwapV2Router02 (KittyPunch)][5] | `0xf45AFe28fd5519d5f8C1d4787a4D5f724C0eFa4d` | [Docs][kittypunch-doc] | | [PunchSwapV2Factory (KittyPunch)][6] | `0x29372c22459a4e373851798bFd6808e71EA34A71` | [Docs][kittypunch-doc] | | [TrenchesTokensBuyer (KittyPunch)][7] | `0x6D0e081Acc28eA9ee6b7fD293eC03F97147b026d` | [Docs][kittypunch-doc] | #### Flow Cadence Mainnet | Contract Name | Flow Cadence Mainnet Address | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | Docs | | ---------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | [SwapFactory (IncrementFi)][22] | `0xb063c16cac85dbd1` | | [Docs][incrementfi-doc] | | [SwapPair (IncrementFi)][23] | `0xecbda466e7f191c7` | | [Docs][incrementfi-doc] | | [SwapError (IncrementFi)][24] | `0xb78ef7afa52ff906` | | [Docs][incrementfi-doc] | | [SwapInterfaces (IncrementFi)][25] | `0xb78ef7afa52ff906` | | [Docs][incrementfi-doc] | | [SwapConfig (IncrementFi)][26] | `0xb78ef7afa52ff906` | | [Docs][incrementfi-doc] | | [SwapRouter (IncrementFi)][27] | `0xa6850776a94e6551` | | [Docs][incrementfi-doc] | ## Bridges & Cross-Chain Messaging | Bridge / Protocol | Reference Docs | | -------------------------------------------- | ------------------------ | | Stargate Bridge ([stargate.finance][8]) | [Mainnet Contracts][9] | | Hyperlane Bridge ([trump.hyperlane.xyz][10]) | [Mainnet Contracts][11] | | Flow Bridge ([bridge.flow.com][12]) | [Superbridge Docs][13] | | Celer cBridge ([cbridge.celer.network][14]) | [Celer cBridge Docs][15] | | Relay ([relay.link][36]) | [Relay Contracts][37] | | LayerZero | [Mainnet Contracts][16] | | Axelar | [Axelar Docs][17] | ## Omni Fungible Tokens (PYUSD → USDF) #### Solana Mainnet | Contract Name | Contract Address | | -------------------- | ---------------------------------------------- | | PYUSD Program ID | `28EyPNAi9BMTvGuCaQrptMXjpWUi7wx8SxAFVoSZxSXe` | | PYUSD Mint | `2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo` | | PYUSD Mint Authority | `22mKJkKjGEQ3rampp5YKaSsaYZ52BUkcnUN6evXGsXzz` | | PYUSD Escrow | `6z3QyVS36nQ9fk2YvToxqJqXqtAFsSijqgHxpzKyG5xn` | | PYUSD OFT Store | `2KUb8dcZR9LyrSg4RdkQx91xX6mPQLpS1MEo6gwfvLZk` | #### Ethereum Mainnet | Contract Name | Contract Address | | ------------- | -------------------------------------------- | | PYUSD Token | `0x6c3ea9036406852006290770BEdFcAbA0e23A0e8` | | PYUSD Locker | `0xFA0e06B54986ad96DE87a8c56Fea76FBD8d493F8` | ## Oracles #### Flow EVM Mainnet | Contract Name | Flow EVM Mainnet Address | | ------------------------------ | -------------------------------------------- | | [ERC1967Proxy (Pyth)][18] | `0x2880aB155794e7179c9eE2e38200202908C17B43` | | [ERC1967Proxy (Stork)][28] | `0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62` | #### Flow Cadence Mainnet | Contract Name | Flow Cadence Mainnet Address | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | Docs | | ----------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | [PublicPriceOracle (IncrementFi)][19] | `0xec67451f8a58216a` | | [Docs][incrementfi-doc] | | [BandOracle (Band) Protocol][33] | `0x6801a6222ebf784a` | | [Docs][band-oracle-doc] | ## Ethereum Attestation Service More information can be found on the Credora docs site for [EAS on Flow](https://credora.gitbook.io/eas-for-flow). | Contract Name | Flow EVM Mainnet Address | | ------------------------------------------------------- | -------------------------------------------- | | [SchemaRegistry (Ethereum Attestation Service)][20] | `0xB0cF748a05AEA8D59e15834446CFC95bcFF510F0` | | [EAS (Ethereum Attestation Service)][21] | `0xc6376222F6E009A705a34dbF1dF72fEf8efB3964` | [1]: https://evm.flowscan.io/address/0x4412140D52C1F5834469a061927811Abb6026dB7?tab=contract [2]: https://evm.flowscan.io/address/0xf0E48dC92f66E246244dd9F33b02f57b0E69fBa9?tab=contract [3]: https://evm.flowscan.io/address/0xebd098c60b1089f362AC9cfAd9134CBD29408226?tab=contract [4]: https://evm.flowscan.io/address/0x87048a97526c4B66b71004927D24F61DEFcD6375?tab=contract [5]: https://evm.flowscan.io/address/0xf45AFe28fd5519d5f8C1d4787a4D5f724C0eFa4d?tab=contract [6]: https://evm.flowscan.io/address/0x29372c22459a4e373851798bFd6808e71EA34A71?tab=contract [7]: https://evm.flowscan.io/address/0x6D0e081Acc28eA9ee6b7fD293eC03F97147b026d?tab=contract [8]: https://stargate.finance/bridge?srcChain=ethereum&srcToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&dstChain=flow&dstToken=0xF1815bd50389c46847f0Bda824eC8da914045D14 [9]: https://stargateprotocol.gitbook.io/stargate/v2-developer-docs/technical-reference/mainnet-contracts#flow [10]: https://trump.hyperlane.xyz/ [11]: https://docs.hyperlane.xyz/docs/reference/addresses/mailbox-addresses [12]: https://bridge.flow.com/ [13]: https://docs.superbridge.app/ [14]: https://cbridge.celer.network/1/747/USDC-intermediary [15]: https://cbridge-docs.celer.network/tutorial/flow-cadence-bridging-guide [16]: https://docs.layerzero.network/v1/developers/evm/technical-reference/deployed-contracts?chains=flow [17]: https://docs.axelar.dev/validator/external-chains/flow/ [18]: https://evm.flowscan.io/address/0x2880aB155794e7179c9eE2e38200202908C17B43?tab=contract [19]: https://flowscan.io/contract/A.ec67451f8a58216a.PublicPriceOracle [20]: https://evm.flowscan.io/address/0xB0cF748a05AEA8D59e15834446CFC95bcFF510F0?tab=contract [21]: https://evm.flowscan.io/address/0xc6376222F6E009A705a34dbF1dF72fEf8efB3964?tab=contract [22]: https://flowscan.io/contract/A.b063c16cac85dbd1.SwapFactory [23]: https://flowscan.io/contract/A.ecbda466e7f191c7.SwapPair [24]: https://flowscan.io/contract/A.b78ef7afa52ff906.SwapError [25]: https://flowscan.io/contract/A.b78ef7afa52ff906.SwapInterfaces [26]: https://flowscan.io/contract/A.b78ef7afa52ff906.SwapConfig [27]: https://flowscan.io/contract/A.a6850776a94e6551.SwapRouter [28]: https://evm.flowscan.io/address/0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62?tab=contract [29]: https://evm-testnet.flowscan.io/address/0x97900F59828Da4187607Cb8F84f49e3944199d18?tab=contract [30]: https://evm-testnet.flowscan.io/address/0xBCF2dA8f82fb032A2474c92Ec5b70C95A83fc0cc?tab=contract [31]: https://testnet.flowscan.io/contract/A.8232ce4a3aff4e94.PublicPriceOracle [32]: https://testnet.flowscan.io/contract/A.9fb6606c300b5051.BandOracle [33]: https://flowscan.io/contract/A.6801a6222ebf784a.BandOracle [36]: https://relay.link/bridge [37]: https://docs.relay.link/resources/contract-addresses [band-oracle-doc]: ./band-oracle [incrementfi-doc]: https://docs.increment.fi/ [kittypunch-doc]: https://kittypunch.gitbook.io/kittypunch-docs [38]: https://www.flowscan.io/evm/contract/0x681D1bFE03522e0727730Ba02a05CD3C0a08fa30 [39]: https://www.flowscan.io/evm/contract/0x2B30D97457d44dE9fb0329D9a2C1DF6B7ae1401d [40]: https://www.flowscan.io/evm/contract/0x48d63C4E4481fd0A01Fb82A3B191C6685c361f02 [41]: https://www.flowscan.io/evm/contract/0xca6d7Bb03334bBf135902e1d919a5feccb461632 [42]: https://www.flowscan.io/evm/contract/0xf7F20a346E3097C7d38afDDA65c7C802950195C7 [43]: https://www.flowscan.io/evm/contract/0xeEDC6Ff75e1b10B903D9013c358e446a73d35341 [44]: https://www.flowscan.io/evm/contract/0x370A8DF17742867a44e56223EC20D82092242C85 [45]: https://www.flowscan.io/evm/contract/0x5C65D5C7E0154f519B7dC4558915A7016F41aa50 [46]: https://www.flowscan.io/evm/contract/0x990A0564B7d90656494Ba7A7E1e874038cc35f5d [47]: https://www.flowscan.io/evm/contract/0x513A58591c8E502543D629748076857a71C6079D [48]: https://www.flowscan.io/evm/contract/0x99187C0a0AF166b40C393FAE5FDaC688ed5b6989 [49]: https://www.flowscan.io/evm/contract/0x36D8296B9B73dE9d5Ec09ACc6a7c02cC40Ad9780 [50]: https://www.flowscan.io/evm/contract/0xB231Aa6F8636373f5233eeaD13FFEB199659d484 [51]: https://www.flowscan.io/evm/contract/0xd21C58aDaf1d1119FE40413b45A5f43d23d58DF3 [52]: https://www.flowscan.io/evm/contract/0x5fE87847fe20a6C30921620F52B06a4A3740aa61 [53]: https://www.flowscan.io/evm/contract/0x000000000022D473030F116dDEE9F6B43aC78BA3 [54]: https://www.flowscan.io/evm/contract/0x8B5eB800B8d9cF702ff3DD0047ac31bBD411B82a [55]: https://www.flowscan.io/evm/contract/0x026932f97995201527B4c9Bc2ea6854C02BB0AdC --- # File: /md/defi/defi-contracts-testnet.md --- id: defi-contracts-testnet title: DeFi Contracts on Flow Testnet description: Frequently used DeFi contracts and resources on Flow **Testnet**, covering Flow EVM and Flow Cadence with addresses, explorers, and faucets. keywords: - Flow Testnet - Flow EVM Testnet - Flow Cadence Testnet - DeFi contracts - stablecoins - wrapped assets - AMMs - DEXs - oracles - EAS sidebar_position: 4 sidebar_label: DeFi Contracts Testnet --- import CopyButton from '@site/src/components/CopyButton'; Flow is a Layer 1 blockchain that supports EVM equivalency, offering two environments Flow EVM and Flow Cadence. Fungible and non-fungible tokens can seamlessly transfer between these environments via the native VM token bridge. As a result, many tokens have both a Flow EVM mainnet contract address and a Flow Cadence mainnet contract address, allowing developers to choose their preferred environment. Below is a list of commonly used DeFi contracts on Flow Testnet: [Switch to DeFi Contracts on Mainnet](./defi-contracts-mainnet.md) ## Stablecoins & Wrapped Assets #### Flow EVM Testnet | Token | EVM Testnet Address | How to Get | | ------------------------ | -------------------------------------------- | ----------- | | FLOW (native, non-erc20) | — | [Faucet][1] | | [WFLOW][2] | `0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` | [Swap][28] | | [MOET][3] | `0x51f5cc5f50afb81e8f23c926080fa38c3024b238` | [Swap][29] | | [MockUSDC][4] | `0xd431955D55a99EF69BEb96BA34718d0f9fBc91b1` | [Swap][30] | | [mUSDC][5] | `0x4154d5B0E2931a0A1E5b733f19161aa7D2fc4b95` | [Swap][31] | | [PYUSD0][6] | `0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F` | [Swap][32] | | [USD Flow][7] | `0xf2E5A325f7D678DA511E66B1c0Ad7D5ba4dF93D3` | — | | [USDC.e][8] | `0x9B7550D337bB449b89C6f9C926C3b976b6f4095b` | — | | [ankrFLOW][9] | `0xe132751AB5A14ac0bD3Cb40571a9248Ee7a2a9EA` | — | | [ankrFLOWEVM][10] | `0x8E3DC6E937B560ce6a1Aaa78AfC775228969D16c` | — | | [ETHf][11] | `0x059A77239daFa770977DD9f1E98632C3E4559848` | [Mint][14] | | [BTCf][12] | `0x208d09d2a6Dd176e3e95b3F0DE172A7471C5B2d6` | [Mint][15] | | [cbBTC][13] | `0x30F44C64725727F2001E6C1eF6e6CE9c7aB91dC3` | [Mint][16] | #### Flow Cadence Testnet | Token | Cadence Testnet Address | Cadence Contract Name | | -------------------- | ----------------------- | ------------------------------------------------------------ | | [FLOW][17] | `0x7e60df042a9c0868` | `FlowToken` | | [MOET][18] | `0xd27920b6384e2a78` | `MOET` | | [USDC][19] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_d431955d55a99ef69beb96ba34718d0f9fbc91b1` | | [mUSDC][20] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_4154d5b0e2931a0a1e5b733f19161aa7d2fc4b95` | | [USDF (Mock)][21] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_d7d43ab7b365f0d0789ae83f4385fa710ffdc98f` | | [USDF (PYUSD)][22] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_f2e5a325f7d678da511e66b1c0ad7d5ba4df93d3` | | [USDC.e (Celer)][23] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_9b7550d337bb449b89c6f9c926c3b976b6f4095b` | | [ankrFLOWEVM][24] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_8e3dc6e937b560ce6a1aaa78afc775228969d16c` | | [WETH][25] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_059a77239dafa770977dd9f1e98632c3e4559848` | | [WBTC][26] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_208d09d2a6dd176e3e95b3f0de172a7471c5b2d6` | | [cbBTC][27] | `0xdfc20aee650fcbdf` | `EVMVMBridgedToken_30f44c64725727f2001e6c1ef6e6ce9c7ab91dc3` | ## Vaults #### Flow EVM Testnet | Contract | Address | | -------------------- | -------------------------------------------- | | [MockTauVault][69] | `0x72104434BEc686B47a72bCa9b998624238BD2Ffb` | | [MockYieldVault][70] | `0x217aAC9594EcB6d3f6667A214CF579dd29ce78dd` | ## AMMs & DEXs #### Flow EVM Testnet | Contract | EVM Testnet Address | | -------------------------------------------- | -------------------------------------------- | | [UniswapV2Factory (FlowSwap)][33] | `0x7d726261FB76B264fc20eA1f19D900D760136566` | | [UniswapV2Router02 (FlowSwap)][34] | `0x524E1291c109BE27FDE48De97cAf0B3c0F02A68f` | | [UniswapV2Pair (FlowSwap)][35] | `0x21E3aa01561d7D869785aAedB14130C5807C5A12` | | [UniswapV3Factory (FlowSwap)][36] | `0x92657b195e22b69E4779BBD09Fa3CD46F0CF8e39` | | [NonfungiblePositionManager (FlowSwap)][37] | `0x8b9F96390EC35d5859937c7c5D68Ff6D5CFC312f` | | [SwapRouter02 (FlowSwap)][38] | `0x2Db6468229F6fB1a77d248Dbb1c386760C257804` | | [QuoterV2 (FlowSwap)][39] | `0xA1e0E4CCACA34a738f03cFB1EAbAb16331FA3E2c` | | [V3Migrator (FlowSwap)][40] | `0x00a101726ff770cd8ed53E8376b9440Bad40CAd9` | | [UniswapV3Staker (FlowSwap)][41] | `0x04400857ad69EaA7dd6fEF1C329E80E50BD30b76` | | [TickLens (FlowSwap)][42] | `0x36D9bDCbA840F5bcb95EE7bD54a86808aef6581F` | | [NFTDescriptor (FlowSwap)][43] | `0x6982D5Cb80Cd7E2cb7C0d0B8452841471Bc84Bc2` | | [v3_nft_position_descriptor (FlowSwap)][44] | `0x61f4e983A72d9BD8429154982A3d9fCF3A1D98d0` | | [TransparentUpgradeableProxy (FlowSwap)][45] | `0xE0895150a7c84e8fB9fecCE72F4C80c130C80fDa` | | [UniswapV3Pool (FlowSwap)][46] | `0xa4Db57e3d3c6674FA02a2f3a667d3C22Fe17efF4` | | [UniversalRouter (FlowSwap)][47] | `0xB685ab04Dfef74c135A2ed4003441fF124AFF9a0` | | [Permit2 (FlowSwap)][48] | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | [FusionXInterfaceMulticall (FlowSwap)][49] | `0x02b9B840CDCEe84510a02cc85f351CAaD41f46CE` | | [proxy_admin (FlowSwap)][50] | `0xf4011F45A666dC7eC54445a710c3aae735F7E890` | | [StableKittyFactoryNG (KittyPunch)][51] | `0x0699C35C0104e478f510531F5Dfc3F9313ae49D1` | | [TwoKittyFactory (KittyPunch)][52] | `0xeaa5949471C7B31ae97D3a52483028aE595E8e83` | | [TriKittyFactory (KittyPunch)][53] | `0x62aC6e05Bac04702bF744106499F72f200297121` | | [KittyRouterNgPoolsOnly (KittyPunch)][54] | `0x70e8C797f698De61787A7275628713077723694` | | [PunchSwapV2Router02 (KittyPunch)][55] | `0xeD53235cC3E9d2d464E9c408B95948836648870B` | | [PunchSwapV2Factory (KittyPunch)][56] | `0x0f6C2EF40FA42B2F0E0a9f5987b2f3F8Af3C173f` | #### Flow Cadence Testnet | Contract | Cadence Testnet Address | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | | ------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | [StableSwapFactory (IncrementFi)][57] | `0x6ca93d49c45a249f` | | | [SwapFactory (IncrementFi)][58] | `0x6ca93d49c45a249f` | | | [SwapPair (IncrementFi)][59] | `0x7afd587a5d5e2efe` | | | [SwapConfig (IncrementFi)][60] | `0x8d5b9dd833e176da` | | | [SwapError (IncrementFi)][61] | `0x8d5b9dd833e176da` | | | [SwapInterfaces (IncrementFi)][62] | `0x8d5b9dd833e176da` | | ## Bridges & Cross-Chain Messaging | Bridge / Protocol | Reference Docs | | ----------------------------- | ----------------- | | PYUSD -> USDF (LayerZero OFT) | [GitHub Repo][63] | ## Omni Fungible Tokens (PYUSD → USDF) #### Solana Devnet/Testnet | Contract Name | Contract Address | | -------------------- | ---------------------------------------------- | | PYUSD Program ID | `D6RHLYN7x69Cb5Y7dFj9T9uJrJCVT9Bt1LT71xHf7QqK` | | PYUSD Mint | `CXk2AMBfi3TwaEL2468s6zP8xq9NxTXjp9gjMgzeUynM` | | PYUSD Mint Authority | `A6v157j6XFJXwtT5VWXX7uLYTUrxcYGXB8R6rxrgr9hQ` | | PYUSD Escrow | `FKt7QuGTkFWHVt7RVgtEsh3rVRZMaeCdQBseyQ9Vf1PN` | | PYUSD OFT Store | `CFVgSccTEXbs3hN7gnCHx3FAa1L5j5StsKABTPuMaAYo` | #### Sepolia Testnet | Contract Name | Contract Address | | ------------- | -------------------------------------------- | | MyOFTAdapter | `0x9D6e122780974a917952D70646dD50D2C4f906ae` | | PYUSDLocker | `0xb077Ef2833Fd7b426146839a86100708c37bfa65` | | MyFungi | `0x39dBc26413e6eEe40265E4a7ddc5abDC64849781` | #### Arbitrum Sepolia Testnet | Contract Name | Contract Address | | ------------- | -------------------------------------------- | | MyOFTAdapter | `0xDD3BFfb358eF34C2964CB9ce29013D071d59094C` | | PYUSDLocker | `0x4e2dCCAfe86719B7BFfAc3b1041031dDd07aF5fF` | | MyFungi | `0x1605B1067Ce0D294786A09368f38063Df50C0e92` | ## Oracles #### Flow EVM Testnet | Contract | EVM Testnet Address | | ------------------------- | -------------------------------------------- | | [Pyth (ERC1967Proxy)][68] | `0x2880aB155794e7179c9eE2e38200202908C17B43` | #### Flow Cadence Testnet | Contract Name | Flow Cadence Testnet Address | [CLI](https://developers.flow.com/build/tools/flow-cli/dependency-manager) | Docs | | ----------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | [PublicPriceOracle.cdc (IncrementFi)][66] | `0x8232ce4a3aff4e94` | | [Docs][incrementfi-doc] | | [BandOracle.cdc (Band)][67] | `0x9fb6606c300b5051` | | [Docs][band-oracle-doc] | ## Ethereum Attestation Service More information can be found on the Credora docs site for [EAS on Flow](https://credora.gitbook.io/eas-for-flow). Testnet EAS Explorer: [https://flow-testnet.easscan.credora.io](https://flow-testnet.easscan.credora.io) | Contract Name | Flow EVM Testnet Address | | ------------------------------------------------------- | -------------------------------------------- | | [SchemaRegistry.sol (Ethereum Attestation Service)][64] | `0x97900F59828Da4187607Cb8F84f49e3944199d18` | | [EAS.sol (Ethereum Attestation Service)][65] | `0xBCF2dA8f82fb032A2474c92Ec5b70C95A83fc0cc` | [1]: https://faucet.flow.com/fund-account [2]: https://evm-testnet.flowscan.io/address/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e [3]: https://evm-testnet.flowscan.io/address/0x51f5cc5f50afb81e8f23c926080fa38c3024b238 [4]: https://evm-testnet.flowscan.io/address/0xd431955D55a99EF69BEb96BA34718d0f9fBc91b1 [5]: https://evm-testnet.flowscan.io/address/0x4154d5B0E2931a0A1E5b733f19161aa7D2fc4b95 [6]: https://evm-testnet.flowscan.io/address/0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F [7]: https://evm-testnet.flowscan.io/address/0xf2E5A325f7D678DA511E66B1c0Ad7D5ba4dF93D3 [8]: https://evm-testnet.flowscan.io/address/0x9B7550D337bB449b89C6f9C926C3b976b6f4095b [9]: https://evm-testnet.flowscan.io/address/0xe132751AB5A14ac0bD3Cb40571a9248Ee7a2a9EA [10]: https://evm-testnet.flowscan.io/address/0x8E3DC6E937B560ce6a1Aaa78AfC775228969D16c [11]: https://evm-testnet.flowscan.io/address/0x059A77239daFa770977DD9f1E98632C3E4559848 [12]: https://evm-testnet.flowscan.io/address/0x208d09d2a6Dd176e3e95b3F0DE172A7471C5B2d6 [13]: https://evm-testnet.flowscan.io/address/0x30F44C64725727F2001E6C1eF6e6CE9c7aB91dC3 [14]: https://evm-testnet.flowscan.io/address/0x059A77239daFa770977DD9f1E98632C3E4559848?tab=read_write_contract#0x40c10f19 [15]: https://evm-testnet.flowscan.io/address/0x208d09d2a6Dd176e3e95b3F0DE172A7471C5B2d6?tab=read_write_contract#0x40c10f19 [16]: https://evm-testnet.flowscan.io/address/0x30F44C64725727F2001E6C1eF6e6CE9c7aB91dC3?tab=read_write_contract#0x40c10f19 [17]: https://testnet.flowscan.io/contract/A.7e60df042a9c0868.FlowToken?tab=deployments [18]: https://testnet.flowscan.io/contract/A.d27920b6384e2a78.MOET?tab=deployments [19]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_d431955d55a99ef69beb96ba34718d0f9fbc91b1?tab=deployments [20]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_4154d5b0e2931a0a1e5b733f19161aa7d2fc4b95?tab=deployments [21]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_d7d43ab7b365f0d0789ae83f4385fa710ffdc98f?tab=deployments [22]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_f2e5a325f7d678da511e66b1c0ad7d5ba4df93d3?tab=deployments [23]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_9b7550d337bb449b89c6f9c926c3b976b6f4095b?tab=deployments [24]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_8e3dc6e937b560ce6a1aaa78afc775228969d16c?tab=deployments [25]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_059a77239dafa770977dd9f1e98632c3e4559848?tab=deployments [26]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_208d09d2a6dd176e3e95b3f0de172a7471c5b2d6?tab=deployments [27]: https://testnet.flowscan.io/contract/A.dfc20aee650fcbdf.EVMVMBridgedToken_30f44c64725727f2001e6c1ef6e6ce9c7ab91dc3?tab=deployments [28]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e [29]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0x51F5cC5f50afB81e8F23C926080FA38C3024b238 [30]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0xd431955D55a99EF69BEb96BA34718d0f9fBc91b1 [31]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0x4154d5B0E2931a0A1E5b733f19161aa7D2fc4b95 [32]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F [33]: https://testnet.flowscan.io/evm/contract/0x7d726261FB76B264fc20eA1f19D900D760136566 [34]: https://testnet.flowscan.io/evm/contract/0x524E1291c109BE27FDE48De97cAf0B3c0F02A68f [35]: https://testnet.flowscan.io/evm/contract/0x21E3aa01561d7D869785aAedB14130C5807C5A12 [36]: https://testnet.flowscan.io/evm/contract/0x92657b195e22b69E4779BBD09Fa3CD46F0CF8e39 [37]: https://testnet.flowscan.io/evm/contract/0x8b9F96390EC35d5859937c7c5D68Ff6D5CFC312f [38]: https://testnet.flowscan.io/evm/contract/0x2Db6468229F6fB1a77d248Dbb1c386760C257804 [39]: https://testnet.flowscan.io/evm/contract/0xA1e0E4CCACA34a738f03cFB1EAbAb16331FA3E2c [40]: https://testnet.flowscan.io/evm/contract/0x00a101726ff770cd8ed53E8376b9440Bad40CAd9 [41]: https://testnet.flowscan.io/evm/contract/0x04400857ad69EaA7dd6fEF1C329E80E50BD30b76 [42]: https://testnet.flowscan.io/evm/contract/0x36D9bDCbA840F5bcb95EE7bD54a86808aef6581F [43]: https://testnet.flowscan.io/evm/contract/0x6982D5Cb80Cd7E2cb7C0d0B8452841471Bc84Bc2 [44]: https://testnet.flowscan.io/evm/contract/0x61f4e983A72d9BD8429154982A3d9fCF3A1D98d0 [45]: https://testnet.flowscan.io/evm/contract/0xE0895150a7c84e8fB9fecCE72F4C80c130C80fDa [46]: https://testnet.flowscan.io/evm/contract/0xa4Db57e3d3c6674FA02a2f3a667d3C22Fe17efF4 [47]: https://testnet.flowscan.io/evm/contract/0xB685ab04Dfef74c135A2ed4003441fF124AFF9a0 [48]: https://testnet.flowscan.io/evm/contract/0x000000000022D473030F116dDEE9F6B43aC78BA3 [49]: https://testnet.flowscan.io/evm/contract/0x02b9B840CDCEe84510a02cc85f351CAaD41f46CE [50]: https://testnet.flowscan.io/evm/contract/0xf4011F45A666dC7eC54445a710c3aae735F7E890 [51]: https://evm-testnet.flowscan.io/address/0x0699C35C0104e478f510531F5Dfc3F9313ae49D1 [52]: https://evm-testnet.flowscan.io/address/0xeaa5949471C7B31ae97D3a52483028aE595E8e83 [53]: https://evm-testnet.flowscan.io/address/0x62aC6e05Bac04702bF744106499F72f200297121 [54]: https://evm-testnet.flowscan.io/address/0x70e8C797f698De61787A7275628713077723694 [55]: https://evm-testnet.flowscan.io/address/0xeD53235cC3E9d2d464E9c408B95948836648870B [56]: https://evm-testnet.flowscan.io/address/0x0f6C2EF40FA42B2F0E0a9f5987b2f3F8Af3C173f [57]: https://testnet.flowscan.io/contract/A.6ca93d49c45a249f.StableSwapFactory?tab=deployments [58]: https://testnet.flowscan.io/contract/A.6ca93d49c45a249f.SwapFactory?tab=deployments [59]: https://testnet.flowscan.io/contract/A.7afd587a5d5e2efe.SwapPair?tab=deployments [60]: https://testnet.flowscan.io/contract/A.8d5b9dd833e176da.SwapConfig?tab=deployments [61]: https://testnet.flowscan.io/contract/A.8d5b9dd833e176da.SwapError?tab=deployments [62]: https://testnet.flowscan.io/contract/A.8d5b9dd833e176da.SwapInterfaces?tab=deployments [63]: https://github.com/onflow/flow-bridge-app?tab=readme-ov-file#evm-testnets [64]: https://evm-testnet.flowscan.io/address/0x97900F59828Da4187607Cb8F84f49e3944199d18?tab=contract [65]: https://evm-testnet.flowscan.io/address/0xBCF2dA8f82fb032A2474c92Ec5b70C95A83fc0cc?tab=contract [66]: https://flowscan.io/contract/A.ec67451f8a58216a.PublicPriceOracle [67]: https://testnet.flowscan.io/contract/A.9fb6606c300b5051.BandOracle [68]: https://evm-testnet.flowscan.io/address/0x2880aB155794e7179c9eE2e38200202908C17B43 [69]: https://evm-testnet.flowscan.io/address/0x72104434BEc686B47a72bCa9b998624238BD2Ffb [70]: https://evm-testnet.flowscan.io/address/0x217aAC9594EcB6d3f6667A214CF579dd29ce78dd [band-oracle-doc]: ./band-oracle [incrementfi-doc]: https://docs.increment.fi/ --- # File: /md/defi/faq.md --- id: faq title: Stablecoins & Bridges on Flow FAQ description: Frequently asked questions about stablecoins, liquidity, and bridging on the Flow blockchain. keywords: - stablecoins - bridges - Flow blockchain - USDC - USDT - USDF - decentralized exchanges - DEX - yield farming - liquidity sidebar_position: 8 sidebar_label: Stablecoins & Bridges FAQ --- import Details from '@theme/Details'; # Defi FAQ Below are common questions regarding stablecoins, liquidity, and bridging on Flow. Click on each question to expand and view the answer. ## Bridging and Gas
    No, Flow uses $FLOW as the gas token. [WETH](./defi-contracts-mainnet#stablecoins--wrapped-assets) is supported on Flow EVM when [bridging](../ecosystem/bridges.md) from another chain. $WFLOW is used as an ERC20 in DeFi apps and not used for gas.
    When using Flow EVM for the first time, your EOA will automatically be credited 0.05 FLOW to cover gas costs when bridging into Flow. If further top-ups are required you can use [Gas.zip](https://www.gas.zip/). See [Fees](../../build/evm/fees) for information on gas pricing. Flow Wallet users do not pay for gas since the wallet subsidizes all transaction fees.
    ## Stablecoins on Flow
    USDC (USD Coin) - Issued by Circle USDT (Tether USD) - Issued by Tether USDF (USD Flow) - Backed by PYUSD (PayPal USD) issued by PayPal
    You can find all the contract addresses for the stablecoins and bridges on Flow here: [DeFi Contracts on Flow][0]
    Stablecoins can be traded on major Flow-based decentralized exchanges (DEXs) like: - KittyPunch, PunchSwap - [https://swap.kittypunch.xyz/][1] - IncrementFi, IncrementSwap - [https://app.increment.fi/swap][2]
    You can earn yield through: - Lending Platforms - Supply stablecoins on [IncrementFi][3], [Sturdy Finance][19] & [MoreMarkets][4] to earn interest. - Liquidity Pools - Provide liquidity on [IncrementFi][5] or [KittyPunch][6] to earn trading fees and farm LP tokens.
    Stablecoins on Flow are designed to be secure and efficient but as with any blockchain asset, there are risks to be aware of: - Depegging - While rare, some stablecoins have lost their peg in the past due to liquidity issues or market crashes. Flow stablecoins like USDC and USDF are backed by trusted issuers to maintain stability. - Smart Contract Risks - Bugs or exploits in DeFi platforms can lead to losses. - Centralization Risks - USDC and USDT are controlled by centralized issuers who can freeze assets. - Bridging Risks - Flow stablecoins (USDC, USDT, USDF) use LayerZero for bridging, a secure and widely adopted cross-chain solution. While all bridges carry some risk, LayerZero is built with advanced security measures to reduce vulnerabilities.
    You can bridge USDC, USDT, and USDF via [https://bridge.flow.com/][8] or [https://stargate.finance/bridge][9] ### Step-by-step example USDC to Flow 1. Go to any of the bridges (e.g. [https://stargate.finance/bridge][9]) 2. Connect your wallet that holds USDC 3. Select the source chain (e.g. Ethereum, BNB Chain, Base) 4. Choose Flow as the destination chain 5. Enter the amount of USDC you want to bridge 6. Approve and confirm the transaction 7. Wait for the transfer to complete - It usually takes a few minutes
    Flow’s transaction fees are extremely low (typically less than $0.000179 per transaction), making stablecoin transfers and trading much cheaper than on any other chain. In many cases, Flow Wallet or Flow-based apps sponsor the gas fees, meaning users can transact stablecoins without paying any gas. This makes Flow an ideal chain for cost-efficient DeFi transactions.
    Stablecoins can be used for payments on Flow with services like: [Beezie][10], [Flowty][11], [Flowverse][12] and many other platforms.
    - DeFi integrations with RWAs (Real World Assets). - Stay tuned on [Flow X account][13] or via the community [Flowverse][14]
    ## Stargate and LayerZero on Flow
    LayerZero is an omnichain interoperability protocol that enables seamless cross-chain communication between different blockchains. It allows assets, messages, and data to move securely between chains without relying on traditional bridges.
    Stargate is a liquidity transfer protocol built on LayerZero that allows users to bridge assets across multiple blockchains with minimal slippage and deep liquidity.
    With Stargate now supporting Flow, users can bridge assets to and from Flow blockchain via [Stargate Finance][9]. This enables Flow to interact with other major chains like Ethereum, Base, Arbitrum One, and BNB Chain, unlocking global onchain liquidity for Flow-based apps and DeFi protocols.
    Currently, Stargate supports bridging USDC, USDT, and ETH between Flow and other chains. Additional assets may be added in the future.
    - Total fees: You pay gas fees + relayer fees, typically less than $1.5 per bridge transaction. - Gas fees vary depending on network congestion and gas prices. - Bridging from Ethereum costs around 0.0003868 ETH (~$1.04) in gas fees, plus LayerZero relayer fees of 0.00003536 ETH ($0.095). - Flow’s transaction fees are extremely low (typically less than $0.000179 per transaction), making stablecoin transfers and trading significantly cheaper than other chains. - In many cases, Flow Wallet or Flow-based apps sponsor gas fees, allowing users to bridge and transact stablecoins with zero cost on Flow.
    - Most transactions settle within a few minutes (~3 mins). - Congestion on the source chain can cause delays.
    Stargate is built on LayerZero, a well-audited and widely used interoperability protocol. - Secure & Trusted – Used by top DeFi ecosystems with rigorous security audits. - Efficient & Cost-Effective – Fast transactions with low fees, especially on Flow. - Reliable Bridged Assets – USDC, USDT, and ETH bridged via Stargate are fully supported in Flow’s DeFi ecosystem. Tip: Always verify official links to ensure a safe bridging experience.
    - Direct USDC transfers between Flow and other blockchains. - Unlocks cross-chain DeFi use cases (e.g., lending, trading, staking). - Low fees and high-speed transactions on Flow.
    Currently, Stargate only supports stablecoins like USDC and USDT, but NFT and asset bridging may be possible in the future via LayerZero-based messaging.
    - **DeFi**: Seamless liquidity transfer between Flow and other ecosystems. - **Gaming**: Cross-chain in-game assets & currency settlements. - **Payments**: Fast and low-cost USDC/USDT/USDF transactions. - **NFTs**: Future potential for cross-chain NFT bridging.
    You can use any EVM wallet such as Metamask, Coinbase Wallet, and Flow Wallet.
    You can see a full list of stablecoins here: [DeFi Contracts on Flow][0] Trading pools for USDF and USDC (USDC via Stargate) are already live and available for immediate use on Flow EVM and can be seamlessly transferred to any Flow Cadence address.
    Cadence applications can use USDC.e as the default, but they now also have the option to support USDF or USDC based on their needs. If you have questions you can join [Flow Discord][15] to get free technical support.
    ## Support and Additional Resources
    - Use [Stargate’s Explorer][9] to track your transfer. - You can also check Flow transactions on [evm.flowscan.io][16] - You can also visit [https://bridge.flow.com/][8] and connect your wallet to view activity.
    - **Stargate Discord**: [https://discord.com/invite/9sFqx9U][17] - **Flow Discord**: [https://discord.gg/flow][15]
    - **Flow Twitter/X:** [https://x.com/flow_blockchain][13] - **Flow Discord**: [https://discord.gg/flow][15]
    [0]: ./defi-contracts-mainnet.md [1]: https://swap.kittypunch.xyz/ [2]: https://app.increment.fi/swap?in=A.1654653399040a61.FlowToken&out= [3]: https://app.increment.fi/dashboard [4]: https://app.more.markets/ [5]: https://app.increment.fi/liquidity [6]: https://www.kittypunch.xyz/ [7]: https://app.kittypunch.xyz/ [8]: https://bridge.flow.com/ [9]: https://stargate.finance/bridge [10]: https://beezie.io/ [11]: https://www.flowty.io/ [12]: https://nft.flowverse.co/ [13]: https://x.com/flow_blockchain [14]: https://x.com/flowverse_ [15]: https://discord.gg/flow [16]: https://evm.flowscan.io [17]: https://discord.com/invite/9sFqx9U [19]: https://v2.sturdy.finance/overview [20]: https://www.flowverse.co/?categories=defi --- # File: /md/defi/forte.md --- title: Build with Forte ↙ sidebar_position: 2 --- # Quickstart Go to [Forte](../../blockchain-development-tutorials/forte) import {Redirect} from '@docusaurus/router'; ; --- # File: /md/defi/pyusd0-integration-guide.md --- id: pyusd0-integration-guide title: PYUSD0 Integration Guide description: A developer reference for integrating PYUSD0 on Flow, covering token architecture, contract addresses, bridging mechanics, and migration from USDF. keywords: - PYUSD0 - PYUSD - stablecoins - Flow blockchain - Flow EVM - Flow Cadence - Flow EVM Mainnet - LayerZero - OFT - omnichain fungible token - bridges - stablecoin liquidity - USDF migration - decentralized exchanges - DEX sidebar_position: 9 sidebar_label: PYUSD0 Integration --- # PYUSD0 Integration Guide ## Overview This guide is for developers and protocols integrating PYUSD0 on Flow. PYUSD0 is an OFT (Omnichain Fungible Token) and brings pre-native PayPal USD support to Flow with seamless cross-chain transfers across 140+ chains via Stargate. It replaces USDF as Flow's canonical USD stablecoin. For users please read the [migration guide.][6] ## Contract Addresses ### Flow EVM Mainnet | Contract | Address | | --------------------------------- | -------------------------------------------- | | [PYUSD0][2] | `0x99af3eea856556646c98c8b9b2548fe815240750` | | [Migration Pool (USDF/PYUSD0)][3] | `0x6ddDFa511A940cA3fD5Ec7F6a4f23947cA30f030` | ### Flow Cadence Mainnet | Token Name | Contract Address | Contract Name | | ----------- | -------------------- | ------------------------------------------------------------ | | [PYUSD0][1] | `0x1e4aa0b87d10b141` | `EVMVMBridgedToken_99af3eea856556646c98c8b9b2548fe815240750` | ### Testnet | Contract | Address | | ----------- | -------------------------------------------- | | [PYUSD0][7] | `0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F` | This is a stand-in token for testing purposes only. Mainnet PYUSD0 requires real PYUSD locked via LayerZero. The testnet contract has a [mint function][8] and a [liquidity pool][9] so you can mint or swap tokens for development. ### **Deprecated (USDF)** | Contract | Address | Status | | -------- | -------------------------------------------- | ----------- | | USDF | `0x2aaBea2058b5aC2D339b163C6Ab6f2b6d53aabED` | Deprecating | ## **Token Specifications** ``` Name: PYUSD0 Symbol: PYUSD0 Decimals: 6 Standard: ERC-20 + LayerZero OFT Backing: 1:1 PYUSD (PayPal USD) ``` ## About PYUSD0 PYUSD0 is a pre-native token deployed via LayerZero's Asset0 program, alongside other stablecoins like USDG0 (backed by Robinhood, Kraken, Mastercard) and AUSD0. It's fully backed 1:1 by PayPal USD. When Paxos later deploys native PYUSD directly on Flow, PYUSD0 balances will automatically upgrade with no user or developer action required. ## **Code Examples** Visit [the GitHub Repository][5] for code examples on bridging PYUSD0 via LayerZero OFT. ## **Migration Path for Existing USDF Integrations** 1. **Add PYUSD0 support** alongside USDF 2. **Update defaults** to use PYUSD0 instead of USDF 3. **Communicate to users** about migration timeline 4. **Deprecate USDF** after grace period [1]: https://www.flowscan.io/contract/A.1e4aa0b87d10b141.EVMVMBridgedToken_99af3eea856556646c98c8b9b2548fe815240750 [2]: https://evm.flowscan.io/token/0x99aF3EeA856556646C98c8B9b2548Fe815240750 [3]: https://evm.flowscan.io/token/0x6ddDFa511A940cA3fD5Ec7F6a4f23947cA30f030?tab=contract [4]: https://github.com/paxosglobal/paxos-token-contracts/blob/master/contracts/stablecoins/PYUSD.sol [5]: https://github.com/onflow/flow-bridge-app/tree/main/ethereum-oapp [6]: https://flow.com/post/pyusd0-migration-guide [7]: https://evm-testnet.flowscan.io/address/0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F [8]: https://evm-testnet.flowscan.io/token/0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F?tab=read_write_contract [9]: https://flowswap.io/swap?chain=flow-testnet&inputCurrency=NATIVE&outputCurrency=0xd7d43ab7b365f0d0789aE83F4385fA710FfdC98F --- # File: /md/ecosystem.mdx --- sidebar_position: 1 title: Ecosystem Index description: Access essential tools, knowledge, and community connections for the Flow Blockchain ecosystem. --- import DocCardList from '@theme/DocCardList'; import { isSamePath } from '@docusaurus/theme-common/internal'; import { useDocsSidebar } from '@docusaurus/plugin-content-docs/client'; import { useLocation } from '@docusaurus/router'; !isSamePath(item.href, useLocation().pathname)), { type: 'link', label: 'Flow Forum', href: 'https://forum.flow.com/', description: 'Engage with the Flow community, discuss ideas, and seek support on the Flow Blockchain Forum.', customProps: { icon: "🏛️" }, }, { type: 'link', label: 'FLIPs', href: 'https://github.com/onflow/flips', description: 'Flow Improvement Proposals (FLIPs) serve as a platform for engaging the Flow community in development, harnessing the collective ideas, insights, and expertise of contributors and experts while ensuring widespread communication of design changes.', customProps: { icon: "📜" }, }, ] }/> --- # File: /md/ecosystem/auditors.md --- sidebar_position: 12 description: | Third-party auditors for Cadence contract auditing sidebar_custom_props: icon: 🔎 --- # Auditors The following companies provide independent contract auditing services of Cadence smart contracts for the Flow ecosystem.
    ## Nagra (Kudelski) [Nagra](https://www.nagra.com/) a multinational provider of security services including blockchain security and smart contract auditing of Cadence. ## NCC Group [NCC Group](https://www.nccgroup.com/us/) a global provider of security services including blockchain security and smart contract auditing of Cadence. ## QuantStamp Inc. [Quantstamp Inc.](https://quantstamp.com/) specializing in blockchain security and smart contract auditing including Cadence. ## Halborn Inc. [Halborn Inc.](https://www.halborn.com/) specializing in blockchain security and smart contract auditing including Cadence. ## Oak Security [Oak Security](https://www.oaksecurity.io/) a specialist in smart contract auditing security including Cadence. ## Emerald City Academy [Emerald City Academy - Shield](https://docs.ecdao.org/auditing/shield) a Cadence-specialized, community-driven auditing service also offering development support and review for Cadence smart contracts.
    --- # File: /md/ecosystem/block-explorers.md --- sidebar_position: 3 description: | User-friendly online tools that provide visual representations of blockchain data, facilitating easy navigation through transactions, blocks, addresses, and smart contracts while ensuring real-time insights and transparency. sidebar_custom_props: icon: 🔍 --- # Flow Block Explorers Block explorers are user-friendly online tools that visually present blockchain data, allowing users to easily navigate transactions, blocks, addresses, and smart contracts while providing real-time insights and transparency.
    ## Flowscan [Flowscan](https://flowscan.io/) provides various tools to explore the blockchain, including: - Blocks - Transactions - Contracts - Network activity (transaction count) - Transaction cost (compute unit/gas fee) - Validators information ### Supported networks: - [Mainnet](https://flowscan.io/) - [Testnet](https://testnet.flowscan.io/) ## Flowscan [EVM] [Flowscan](https://evm-testnet.flowscan.io/) is based off of the popular Blockscout and provides a user-friendly interface to search for specific EVM transactions, blocks, monitor network health, and track the status of various smart contracts and tokens on Flow EVM. Features include: - Real-Time Data: View live transaction data, blocks, and account activities. - Search Functionality: Search for specific transactions, blocks, and accounts. - Smart Contract Analysis: Examine smart contract statuses and interactions. - Token Tracking: Monitor the creation and transfer of various tokens on the Flow network. ### Supported networks: - [Testnet](https://evm-testnet.flowscan.io/) - [Mainnet](https://evm.flowscan.io/) ## Flow View [Flow View](https://www.flowview.app/) offers comprehensive tools to view accounts' information, including: - Address, balance, and storage - Public keys and their weights - Staking information - Tokens, collections, and listings - Deployed smart contracts ### Supported networks: - [Mainnet](https://www.flowview.app/) - [Testnet](https://testnet.flowview.app/) - [Emulator (local)](https://emulator.flowview.app/) ## Contract Browser [Contract Browser](https://contractbrowser.com/) provides tools for viewing deployed smart contracts, their dependencies, and dependents. Features include: - Search, view, and verify smart contract source code - Detailed smart contract information [Testnet Contract Browser](https://testnet.contractbrowser.com/) is also available for `Testnet Flow`.
    --- # File: /md/ecosystem/bridges.md --- sidebar_position: 6 description: Mechanisms that connect different blockchain networks, allowing secure and decentralized transfer of assets and data across platforms. sidebar_custom_props: icon: ⛓️ --- # Bridges Bridges are mechanisms that connect different blockchain networks, enabling secure and decentralized transfers of assets and data across various platforms.
    ## Stargate Bridge [Stargate Bridge][1] is a cross-chain liquidity transfer protocol powered by LayerZero that enables seamless bridging of assets between Flow and major blockchain networks like Ethereum, BNB Chain, and Base. It uses LayerZero's omnichain messaging capabilities which allow Stargate to provide deep liquidity pools, low slippage, and cost-efficient transfers for stablecoins and cryptocurrencies such as USDC, USDT, USDF, and ETH. ## Flow Bridge [Flow Bridge][2] is a user-friendly, decentralized bridge designed to facilitate secure transfers of stablecoins like USDC, USDT, and USDF to and from Flow blockchain. Powered by Superbridge, it leverages native bridge contracts for official Superchain rollups, ensuring users receive the canonical asset on Flow with minimal trust assumptions. Flow Bridge supports transfers from 24+ chains, with ultra-low transaction fees and fast settlement times. ## LayerZero [LayerZero][3] is a decentralized omnichain interoperability protocol that enables direct cross-chain messaging and asset transfers. It uses a unique Ultra Light Node (ULN) architecture combined with decentralized verifiers to ensure secure message delivery across chains. LayerZero supports multiple EVM-compatible chains and provides developers with tools to build cross-chain applications through its EndpointV2 contracts. ## Hyperlane [Hyperlane][4] is a modular interoperability framework that enables secure cross-chain communication. It allows developers to build applications that can seamlessly interact across different blockchain networks. Hyperlane's architecture emphasizes security and flexibility, providing customizable security models and permissionless interoperability between any blockchain networks. ## Celer cBridge [Celer cBridge][5] is a decentralized and non-custodial asset bridge that supports more than 150 tokens across over 40 blockchains and layer-2 rollups. It is built on top of the [Celer][6] Inter-chain Message Framework. cBridge has facilitated over $13 billion in cross-chain asset transfer volume across 40+ blockchains for more than 350,000 unique users. It is rapidly growing and expanding to support more blockchains and layer-2 solutions. ## Axelar [Axelar][7] is a decentralized cross-chain network connecting over 55 blockchains, facilitating asset transfers and smart contract programmability. It features a proof-of-stake consensus for security and supports cross-chain applications through General Message Passing (GMP). Integrations with platforms like [Squid][8] enable easy token swaps across networks like Ethereum and Polygon. ## Relay [Relay][9] is an intent-based bridge enabling high-speed bridging and connecting over 30 blockchains. Unlike consensus validator based decentralized bridges, Relay's permissioned relayer model stores outbound tokens on the origin chain and issues tokens on the destination chain allowing for low-cost, fast bridging of assets. ## Relay.link [Relay.link][10] provides instant, low-cost swapping, bridging, and cross-chain execution across 73+ chains. It offers a comprehensive solution for users looking to bridge assets and execute cross-chain transactions with minimal fees and maximum efficiency. ## Bridge.Flow.com [Bridge.Flow.com][11] is a digital asset bridge powered by Superbridge, specifically designed to connect Ethereum and Flow EVM Mainnet. It provides secure and efficient asset transfers between these two major blockchain networks, enabling seamless interoperability for users and developers.
    [1]: https://stargate.finance/bridge [2]: https://bridge.flow.com/ [3]: https://docs.layerzero.network/ [4]: https://www.usenexus.org/ [5]: https://cbridge.celer.network/ [6]: https://celer.network/ [7]: https://www.axelar.network/ [8]: https://www.squidrouter.com/ [9]: https://relay.link/bridge [10]: https://relay.link/ [11]: https://bridge.flow.com/ --- # File: /md/ecosystem/bug-bounty.mdx --- sidebar_position: 13 description: Report security vulnerabilities in Flow and earn rewards through the official bug bounty program. sidebar_custom_props: icon: 🐛 --- import Icon from '@site/src/components/Icon'; import { IconName } from '@site/src/types/icons'; # Bug Bounty Program

    Discover Flow's bug bounty program, which rewards white hat hackers for finding vulnerabilities and helping keep the network secure.

    [Submit a Vulnerability Report →](https://hackenproof.com/blog/for-hackers/flow-new-bounty-target)
    --- # File: /md/ecosystem/collectibles.md --- title: Collectibles & NFTs description: Digital collectibles, NFTs, and trading platforms on Flow blockchain, featuring major brands and innovative marketplaces. keywords: - collectibles - NFTs - digital collectibles - NFT marketplaces - Flow blockchain - Disney - Mattel - OpenSea - NFT tools - digital assets sidebar_position: 7 sidebar_custom_props: icon: 🎨 --- # Collectibles & NFTs on Flow **Where major brands meet blockchain innovation.** Flow powers the next generation of digital collectibles, bringing together iconic brands like Disney, Mattel, and the NBA with cutting-edge NFT technology. Flow's high-performance infrastructure enables seamless experiences for collectors, creators, and traders, supporting everything from major brand collaborations to independent artist marketplaces. ## Why Collectibles on Flow? **Brand partnerships** – Official collectibles from Disney, Mattel, NBA, and more **Scalable infrastructure** – Handle millions of transactions without congestion **User-friendly experience** – Intuitive interfaces for mainstream adoption **Comprehensive ecosystem** – Marketplaces, tools, and trading platforms **Start exploring collectibles on Flow today.** ## Collectibles Partners
    ## Disney Pinnacle [Disney Pinnacle][1] allows users to chase, collect and trade digital pins from their favorite Disney, Pixar and STAR WARS characters. The platform brings beloved franchises to the blockchain with official digital collectibles. ## HotWheels [HotWheels][3] is the official collectors platform for Mattel Hot Wheels! Collectors can own and trade digital versions of their favorite die-cast cars and racing memorabilia. ## TicketMaster [TicketMaster][4] is here to connect you to the events you love. The platform leverages Flow's infrastructure to provide secure, verifiable digital tickets and event-related collectibles. ## Flovatar [Flovatar][5] is the ultimate creativity platform on the Flow blockchain. Users can create unique and customizable NFTs, fostering artistic expression and community engagement. ## Flunks [Flunks][6] are cute but mischievous high-schoolers wreaking havoc #onFlow. This gaming collectible project combines playful characters with blockchain technology. ## Tibles [Tibles][7] invites users to download the new FuggClub app! Or don't. But the Fugglers see your life choices. This quirky collectible project adds humor to the NFT space. ## 24Karat [24Karat][8] is a marketing platform for creators, brands, and retailers. It provides an NFT marketplace, community app, and loyalty program SaaS solutions. ## BlockAsset [BlockAsset][9] is The Sports Network $BLOCK, offering sports-related digital collectibles and trading opportunities for sports enthusiasts. ## Beezie [Beezie][10] is transforming collectibles by bringing traditional collecting into the digital age with innovative blockchain solutions. ## Animoca [Animoca][11] delivers digital property rights to consumers, helping to establish the open metaverse. The company focuses on creating interoperable digital assets and experiences.
    ## NFT Marketplaces
    ## OpenSea [OpenSea][12] is the largest NFT marketplace. Buy, sell, & discover the internet of goods. The platform provides comprehensive tools for creators and collectors across multiple blockchains. ## Flowty.io [Flowty.io][13] is a secondary NFT marketplace on Flow blockchain for collectors & traders. It specializes in Flow-native assets and provides a dedicated trading environment. ## Flowfun.xyz [Flowfun.xyz][14] is the @flow_blockchain EVM native NFT launchpad. It provides tools and infrastructure for launching new NFT projects on Flow. ## Buildtree.io [Buildtree.io][15] powers the next generation of advanced onchain assets. The platform enables complex NFT functionality and innovative digital asset creation. ## Mintify.xyz [Mintify.xyz][16] is The Best Place To Trade Digital Assets Onchain. It offers comprehensive trading tools and analytics for digital asset enthusiasts.
    ## NFT Tools
    ## Find.xyz [Find.xyz][17] is a Flow blockchain platform to trade NFTs with named wallets & profiles. It provides enhanced discoverability and trading capabilities for NFT collectors.
    Explore the [FlowVerse Collectibles ecosystem](https://www.flowverse.co/?categories=collectibles) page for more collectibles! [1]: https://disneypinnacle.com/ [3]: https://virtual.mattel.com/explore/FLOW:A.d0bcefdf1e67ea85.HWGarageCardV2?sort=cheapest [4]: https://ticketmastercollectibles.com/explore [5]: https://flovatar.com/ [6]: https://www.flunks.io/ [7]: https://www.tibles.com/ [8]: https://www.24karat.io/ [9]: https://www.blockasset.co/dashboard [10]: https://beezie.io/ [11]: https://www.animocabrands.com/ [12]: https://opensea.io/ [13]: https://flowty.io/ [14]: https://flowfun.xyz/ [15]: https://buildtree.io/ [16]: https://mintify.xyz/ [17]: https://find.xyz/ --- --- # File: /md/ecosystem/data-indexers.md --- title: Data Indexers sidebar_label: Data Indexers sidebar_position: 4 --- # Data Indexers When building applications that leverage Flow data, developers have multiple Data Indexers to choose from. These platforms offer flexible options, allowing you to index all data on Flow, including information from both the Cadence VM and EVM. Alternatively, if your application doesn't require Cadence, you can opt to index only EVM data. This flexibility ensures that you can tailor your data indexing strategy to fit the specific needs of your application. ### Moralis [Moralis](https://moralis.io/) provides a robust suite of data APIs designed to support a wide array of blockchain applications. These APIs deliver both indexed and real-time data across 16+ blockchain networks, including comprehensive details on portfolio and wallet balances, NFT data, token metrics, price feeds, candlestick charts, and net worth calculations. Moralis enhances this data with additional layers of metadata, parsed events, and address labels to provide deeper insights and context. **Getting Started with Moralis** To integrate Moralis into your project, begin by [creating an account](https://moralis.io/). Detailed API references and integration guides are available in the Moralis [documentation](https://docs.moralis.io/). For step-by-step tutorials and use cases, visit their [YouTube channel](https://www.youtube.com/c/MoralisWeb3). ### **Alchemy** [Alchemy](https://www.alchemy.com/) is a powerful blockchain development platform that provides enhanced APIs and advanced analytics to streamline the process of creating and scaling Web3 applications. **Getting Started with Alchemy** To begin using Alchemy, developers can [sign up for an account](https://www.alchemy.com/) on the Alchemy website. The platform offers extensive [documentation](https://docs.alchemy.com/) including API references, tutorials, and guides to help developers integrate Alchemy into their projects. --- # File: /md/ecosystem/developer-profile.md --- title: Developer Profile sidebar_label: Developer Profile description: Learn about Developer Profile - Flow's upcoming developer recognition and learning platform. Create your builder profile. In the future, you'll be able to track your learning journey, showcase your projects, and connect with the Flow ecosystem. sidebar_position: 4 sidebar_custom_props: icon: ⭐ keywords: - Developer Profile program - Flow developers - builder profiles - learning paths - developer challenges - Flow ecosystem - developer recognition - Flow community - learning platform - developer achievements - Flow certification - builder network - developer tools - Flow education - developer onboarding --- The Developer Profile is the beginning of Flow's developer recognition and learning platform where builders can create profiles, track their learning journey, complete challenges, and showcase their contributions to the Flow ecosystem. It's fully onchain! :::tip The Developer Profile is currently in alpha. Please let us know what you think! ::: ## What is the Developer Profile? The Developer Profile is a platform designed to help developers: - Create and manage their Flow builder profiles - Track their learning progress through structured paths - Complete challenges and earn recognition - Connect with other builders in the ecosystem - Showcase their projects and contributions Currently, the Developer Profile is in alpha. You can: - Create your profile - Share your GitHub handle - Add your deployed contract addresses - Complete the First Challenge ## Getting Started The Developer Profile is accessed by logging in with your wallet. Click the "Connect Button" in the top right. ![Developer Profile Connect](profile-connect.png) :::tip The Developer Profile is on Flow Cadence. You'll need to use a wallet that supports Cadence transactions, such as the [Flow Wallet] or [Blocto]. ::: Once connected, you can see your progress by clicking `Progress` in the dropdown menu. ![Progress](progress.png) ![Developer Profile Progress](profile-progress.png) ### 1. Create Your Profile Click `Profile` in the dropdown menu. - Choose a unique handle - Add your Github handle - Add your deployed contract addresses - Share how you discovered Flow ### 2. Complete the First Challenge **After** you've created your profile, you can complete the first challenge. Click `Progress` in the dropdown menu. Click the button at the bottom of the page. Complete the challenge! ## Next Steps We'll be updating the Developer Profile with new features and challenges. Check back soon! [Flow Wallet]: https://wallet.flow.com/ [Blocto]: https://blocto.app/ --- # File: /md/ecosystem/developer-support-hub.md --- title: Developer Support Hub description: Complete support ecosystem for Flow builders - perks, grants, funding, and expert guidance sidebar_position: 2 --- # Developer Support Hub We're builders supporting builders. Get everything you need to succeed on Flow - from launch perks to funding connections. ## Your Journey: From Idea to Success Follow this roadmap to build and scale your Flow project. Each stage unlocks new support and opportunities. ### Stage 1: Idea to MVP **Goal:** Turn your concept into working code

    Your mission:

    • Create pitch deck or design doc
    • Build your first prototype
    • Ship a demo or hackathon project

    Support tools:

    --- ### Stage 2: MVP to Live Product **Goal:** Get real users on mainnet

    Your mission:

    • Deploy to Flow mainnet
    • Integrate with ecosystem partners
    • Gather user feedback and iterate
    • Build core features users love

    Support tools:

    • Contact us at builders [at] flow.com for direct technical and marketing support
    • Apply for Grants with GrantDAO to access community-voted funding
    --- ### Stage 3: Live Product to Revenue **Goal:** Generate sustainable revenue

    Your mission:

    • Acquire paying users/customers
    • Track key business metrics
    • Build revenue-generating features

    Support tools:

    --- Follow the roadmap above based on your project stage to get the right support at the right time. :::tip Ready to build? The best support is a thriving project. Start building today and tap into our ecosystem when you need it. **[Start with Cadence]** **[Start with Solidity (EVM)]** ::: [Start with Cadence]: ../../blockchain-development-tutorials/cadence/getting-started/index.md [Start with Solidity (EVM)]: ../../build/evm/quickstart.md --- # File: /md/ecosystem/developer-support-hub/builder-perks.md --- title: Builder Perks description: Exclusive perks and benefits for Flow builders sidebar_position: 1 --- import Modal from "@site/src/ui/design-system/src/lib/Components/Modal"; import ActionCard from "@site/src/components/ActionCard"; import React from "react"; import { useCurrentUser } from "@site/src/hooks/use-current-user"; import { useProgress } from "@site/src/hooks/use-progress"; import { useProfile } from "@site/src/hooks/use-profile"; import ProfileModal from "@site/src/components/ProfileModal"; import ConnectButton from "@site/src/components/ConnectButton"; import { Button } from "@site/src/ui/design-system/src/lib/Components/Button"; export const BuilderPerks = () => { const [activeModal, setActiveModal] = React.useState(null); const [isProfileModalOpen, setIsProfileModalOpen] = React.useState(false); const { user, logIn } = useCurrentUser(); const { profile } = useProfile(user?.addr); const { getProgress } = useProgress(); const progress = user?.addr ? getProgress() : 0; const isProfileComplete = progress === 1; const handleCardClick = (modalId) => { if (!user?.loggedIn || !isProfileComplete) { setActiveModal('profile-check'); } else { setActiveModal(modalId); } }; return (
    handleCardClick('gas-subsidy')} /> handleCardClick('alchemy')} /> handleCardClick('quicknode')} /> handleCardClick('thirdweb')} /> handleCardClick('olympix')} /> handleCardClick('uniblock')} />
    {/* Profile Check Modal */} setActiveModal(null)} title="Complete Your Profile" >

    {!user?.loggedIn ? "Please connect your wallet to access Flow Builder Perks." : "Complete your Flow Builder Profile to access exclusive perks."}

    {user?.loggedIn && (
    {Math.floor(progress * 100)}%

    Profile completion status

    )} {!user?.loggedIn ? ( setActiveModal(null)} /> ) : ( )}
    setIsProfileModalOpen(false)} /> {/* QuickNode Modal */} setActiveModal(null)} title="QuickNode" >

    $100 Credit for Builders

    Enterprise-grade Web3 infrastructure

    QuickNode

    Features

    • Lightning-fast RPC endpoints
    • Real-time WebSocket streams
    • Historical data backfills
    • Multi-chain support
    • Advanced monitoring tools
    • 24/7 developer support

    How to Claim

    1. Visit QuickNode signup →
    2. Enter promo code:
    BUILDWITHFLOW
    {/* Olympix Modal */} setActiveModal(null)} title="Olympix" >

    $2000 in Security Tools Credits

    Developer-first security assurance

    Olympix

    Features

    Team Size
    1-2 developers
    Revenue Requirement
    $5,000+ per year

    How to Apply

    Send email to:
    sarah@olympix.ai
    CC:builders@flow.com
    Include:
    • Subject: "Olympix Flow Perk"
    • Your GitHub username
    • Link to your project
    {/* Gas Subsidy Modal */} setActiveModal(null)} title="Gas Subsidy" >

    Get Gas to Launch Today

    Flow is offering all builders a one-time subsidy to bring their project to life and scale

    Flow

    Features

    Transaction Coverage
    Sponsor up to 10,000 user transactions
    Usage Impact
    Up to 4x more app engagement
    Contract Deployment
    Launch your contracts on Mainnet for FREE

    How to Apply

    Send email to:
    builders@flow.com
    Include:
    • Subject: "Gas Subsidy Request"
    • Your GitHub username
    • Your deployer address
    {/* Alchemy Modal */} setActiveModal(null)} title="Alchemy" >

    Startup Program Access

    Accelerate your project growth

    Alchemy

    Features

    • Free infrastructure credits
    • Product discounts
    • Technical support
    • Partner network access
    • Launch amplification
    • Community perks

    How to Apply

    1. Apply to Startup Program →
    2. Send follow-up email:
    To: zaib@alchemy.com
    CC: builders@flow.com
    Subject: "Flow Alchemy Perk"
    Include your GitHub username and project link
    {/* Thirdweb Modal */} setActiveModal(null)} title="Thirdweb" >

    1 Month Growth & Engine Access

    $99 value in production tools

    Thirdweb

    Features

    • Production-grade RPCs
    • Custom branding options
    • User analytics dashboard
    • Transaction management
    • Automatic retries
    • Team access controls

    How to Claim

    1. Visit Thirdweb Dashboard →
    2. Apply coupon code:
    3WEBDEV-FLOW
    3. Upgrade to Growth tier and deploy your Engine instance
    {/* Uniblock Modal */} setActiveModal(null)} title="Uniblock" >

    3 Months Pro Access + $500 OFF

    Enterprise-grade API aggregation platform, connect to 1500+ APIs with optimized pricing and dynamic API switching that gurantees 0% downtime

    Uniblock

    Features

    • 50+ data partners
    • 100+ protocols
    • 1500+ APIs
    • Load balancing
    • Auto fallback
    • Auto retry

    How to Apply

    Send email to:
    hello@uniblock.dev
    CC: builders@flow.com
    Include:
    • Subject: "Uniblock Flow Builder Perk"
    • Brief project description
    • Link to relevant materials
    ); }; --- # File: /md/ecosystem/developer-support-hub/grants.md --- title: Grants description: Discover grant opportunities and funding programs available for Flow developers sidebar_position: 2 sidebar_custom_props: icon: 🌱 --- # Flow Ecosystem Grant Programs The Flow Foundation provides grants to support developers and teams building tools and products that enhance the Flow ecosystem. We have two grant programs for different types of projects: - 🌱 **GrantDAO Program**: For pre-startup or post-hackathon projects, with a focus on building on Flow. This is a community-driven program and the grantees are voted on by the community. - 💡 **Ecosystem Grants**: For startup projects that are looking for funding to support their growth. This is a top-down program and the Flow Foundation will evaluate the grant applications. ## GrantDAO Program The Flow GrantDAO is allocating over several million FLOW to ecosystem builders and gives teams an opportunity to secure funding while working on bringing their project to life and solving real problems. Each round we will set at least 50k FLOW as the prize pool. After completing an application and getting approved, projects are funded based on community MACI voting. Voting happens throughout the round and payouts occur at the end of the round period. Projects that graduate from this program and show strong signs of traction or utilization may be offered additional startup advisory support from the Flow Foundation as well as fundraising opportunities and training. :::info Round 1 has been closed on August 16, 2025. Please stay tuned for the next round. ::: ### How to Apply 1. Visit the [Flow GrantDAO](https://dorahacks.io/flow) website 2. Submit an application of your BUIDL 3. Wait for the results of the round. If selected, you will be funded ### Focus Areas (Round 1) We are particularly interested in projects across these key areas: - 📈 **DeFi**: Innovative decentralized finance solutions - 💡 **Cutting-Edge Innovation and Widespread Adoption**: Projects that push the boundaries of what's possible in the Flow ecosystem - 💼 **Public Goods**: Projects that provide essential infrastructure or services to the Flow ecosystem ### Voting Results - Round 1: [Voting Results](https://dorahacks.io/flow/result?round_seq=1) ## Ecosystem Grants The ecosystem grants program is aimed at supporting the growth of Flow ecosystem projects. The Flow Foundation will evaluate the grant applications and select the grantees. 1. Please make a proposal and clearly describe your project. Include: - A milestone-based roadmap, and a clear timeline for each milestone - The amount of funding you are requesting for each milestone - How you will use the funding - How you will measure the success of the project 2. Send the proposal to Flow Foundation at [bd@flowfoundation.org](mailto:bd@flowfoundation.org) 3. Wait for our response for your proposal, our BD team will get back to you as soon as possible. --- # File: /md/ecosystem/developer-support-hub/vcs-and-funds.md --- title: VCs & Funds description: Connect with venture capital firms and investment funds supporting Flow projects sidebar_position: 3 sidebar_custom_props: icon: 💼 --- # VCs & Funds Building a successful Web3 project requires more than just great technology - having the right investors and partners can make all the difference. The Flow ecosystem is supported by some of the world's leading venture capital firms and crypto-native funds. Research shows that warm introductions can increase your chances of securing funding by up to 500% more liley compared to cold outreach. The Flow Foundation can help facilitate introductions to our network of trusted investors who have a deep understanding of the Flow ecosystem and web3. Reach out to your local dev-rel in [office hours](https://calendar.google.com/calendar/u/0/embed?src=c_47978f5cd9da636cadc6b8473102b5092c1a865dd010558393ecb7f9fd0c9ad0@group.calendar.google.com) or [discord](https://discord.gg/flow) to get a warm introduction.
    ## Generating a Non-Custodial Account A non-custodial account will make sure you are the only one holding the keys to your account. You can follow the following steps to add a non-custodial account: First, generate a new key pair with the [Flow CLI](https://github.com/onflow/flow-cli): ```sh > flow keys generate --network=mainnet 🔴️ Store private key safely and don't share with anyone! Private Key 5b438... Public Key 1bdc5... ``` > **Note**: By default, this command generates an ECDSA key pair on the P-256 curve. Keep in mind the CLI is intended for development purposes only and is not recommended for production use. Handling keys using a Key Management Service is the best practice. Take a note of the public key and go back to Flow Port. Open the ["Create a new account" page](https://port.flow.com/transaction?hash=a0a78aa7821144efd5ebb974bb52ba04609ce76c3863af9d45348db93937cf98&showcode=false&weight=1000&halg=3). On the page, enter your public key from the CLI, ensure the hash algorithm is set to `SHA3_256` and the weight is set to `1000`. Finally, check the box confirming correctness and hit 'Submit'. > **Important**: Your account needs to have at least 0.002 FLOW for the account creation. More details can be found [in this guide](../../build/cadence/basics/fees.md#storage). Once the transaction is sealed, you should scroll down to the events section and locate the `flow.AccountCreated` event with the newly generated address. ![flow-port-sealed](port-sealed-tx.png) Make sure to take a note of the address. If you want to verify the public key for this address, you can visit [flow-view-source](https://flow-view-source.com/). ## Important Mainnet Smart Contract Addresses You can review [all available core contracts](../../build/cadence/core-contracts/index.md) deployed to the mainnet to identify which ones you want to import. --- # File: /md/protocol/flow-networks/accessing-testnet.md --- title: Flow Testnet sidebar_label: Testnet sidebar_position: 3 description: Guide to Testnet access --- ## About Flow Testnet Flow Testnet is Flow's official testing and development network. It is intended to provide a staging and testing environment for dApp developers. It aims to balance similarity with Mainnet with being a productive development environment, resulting in the following key differences: - Testnet has significantly fewer validator nodes, resulting in a faster block rate compared to Mainnet - Testnet is configured with shorter epochs (about 12 hours, compared to 7 days on Mainnet) - Testnet receives software upgrades up to 2 weeks before Mainnet ## Accessing Flow Testnet Flow Testnet is available for access at this URL: ``` access.devnet.nodes.onflow.org:9000 ``` For example, to access the network using the [Flow Go SDK](https://github.com/onflow/flow-go-sdk): ```go import "github.com/onflow/flow-go-sdk/client" func main() { flowAccessAddress := "access.devnet.nodes.onflow.org:9000" flowClient, _ := client.New(flowAccessAddress, grpc.WithInsecure()) // ... } ``` ### Generating Testnet Key Pair You can generate a new key pair with the [Flow CLI](https://github.com/onflow/flow-cli) as follows: ```sh > flow keys generate 🙏 If you want to create an account on Testnet with the generated keys use this link: https://testnet-faucet.onflow.org/?key= cc1c3d72... 🔴️ Store private key safely and don't share with anyone! Private Key 246256f3... Public Key cc1c3d72... ``` **Note: By default, this command generates an ECDSA key pair on the P-256 curve. Keep in mind, the CLI is intended for development purposes only and is not recommended for production use. Handling keys using a Key Management Service is the best practice.** ## Account Creation and Token Funding Requests Accounts and tokens for testing can be obtained through the [testnet faucet](https://testnet-faucet.onflow.org/). If you generated the keypair through the CLI, you can click on the URL provided to create an account and request testnet FLOW tokens. ## Important Smart Contract Addresses You can review [all available core contracts](../../build/cadence/core-contracts/index.md) deployed to the Testnet to identify which ones you want to import. --- # File: /md/protocol/flow-port.md --- title: Flow Port description: How to use Flow Port --- Flow Port is an account management tool for Flow. Flow Port allows you to create an account using a wallet provider, manage your account and assets across Flow's VMs and perform staking and delegating actions on Flow. Typically, your wallet provider will support most of these features. However, should your wallet provider not do so, or should you wish to use this tool for any other reason, Flow Foundation makes it available for you. ## Creating an Account In order to access Flow Port, you must have a valid Flow address. If you do not have a Flow address you can create one by installing a [Flow compatible wallet](../../ecosystem/wallets). ### Flow Wallet #### Creating Account Through Flow Port: Navigate To Flow Port 1. Using Google Chrome, Navigate to [Flow Port](https://port.flow.com/). 2. Click on 'Sign Up' 3. Click on Flow Wallet and choose Chrome extension or Mobile 4. You should be logged into Flow Port! You can now see your account address in Flow Port and access Flow features for your account ### Ledger #### Before You Start 1. Ensure you have: - a.) [Ledger Live](https://www.ledger.com/ledger-live) installed on your computer - b.) [Initialized](https://support.ledger.com/hc/en-us/articles/360017362160-Flow-FLOW-?support=true) your Ledger Device. #### Install the Flow App 1. Connect your Ledger Device to your computer and open Ledger Live. 2. Make sure your Ledger device firmware is up to date. You can check this by clicking on **‘Manager’** from the side navigation bar. Choose to install the update if one is available - a.) NOTE: Sometimes the install option doesn't show up, or it is not clickable. If this is the case, wait for a little bit of time to see if it appears, or restart the ledger live app if necessary. 3. On the Manager screen in Ledger Live and search for ‘Flow’. 4. You should see the Flow App. Install it and follow the instructions on the device. - a.) NOTE: If the Flow App does not appear, it may be because you are on an outdated version. Please ensure you are on the most updated version. #### Navigate to Flow Port to Create an Address 1. Navigate to [Flow Port](https://port.flow.com/). 2. Click on 'Sign Up' if you need to create a new Flow Account. 3. Click on Ledger. 4. Follow the prompts on the screen. Plug in your Ledger device and open the Flow App. 5. Click on Create an account. Follow the prompts on your Ledger device. 6. Once your account address is created, you will be automatically logged into Flow Port. ## Staking & Delegating For a detailed walkthrough on how to use Flow Port for staking and delegating, please read the [Flow Port staking walkthrough](./staking-guide.md) ### How Do I Stake or Delegate? So you have decided you want to be a part of the Flow Network. Welcome! You are joining a group of people from all around the world that are a part of a movement centered around bringing decentralization, user empowerment, and transparency into the world. Below is a step-by-step guide that will assist you in the staking & delegation process. ### Staking via a Custody Provider If you are using a custody provider who controls your account and private keys for you, such as Kraken, Finoa, or Coinlist, they all have different policies and processes for what you need to do to stake your tokens, the rewards you receive, and the fees that they take from your staking rewards. ### Starting a Manual Staking Transaction 1. You need to have FLOW in order to stake. Please see the [FLOW Token](../../build/cadence//core-contracts//03-flow-token.md) reference for information on how to become a FLOW holder. 2. Once you have FLOW tokens in your account, you can start staking through [Flow Port](https://port.flow.com/) or, if applicable, with your [custody provider](#staking-via-a-custody-provider). 3. If you are using Flow Port, log-in with your Flow account address and navigate to the Stake/Delegate page. See the Manual Staking/Delegating section below for more information about what to do next. ### Manual Staking/Delegating If you are not using a custody provider, there is more responsibility that you have to accept, because you have complete control of your tokens. You need to ensure that you are well informed about the staking process and potentially node operation process because you will have to manage those on your own. Please read the [staking documentation](../../protocol/staking/index.md) before continuing with this guide. Below are the various options you can choose. Please be aware, that at this time you can only have 1 stake or 1 delegate per account. This means that if you want to do multiple stakes, multiple delegates, or a mixture of stakes and delegates, you will need to create multiple accounts to do so. Please read them carefully as it will help you understand which route is best for your situation: - Staking your own Node: You are responsible for running and maintaining a Flow Node. You are also solely responsible for providing the minimum stake for your selected node (minimum 135,000 FLOW) and you have the technical know-how and bandwidth to run and operate a node in the Flow protocol. - Delegating: You have FLOW tokens and you want to stake, without having to run your own node and/or have the full minimum stake required to run your own node. You can ‘delegate’ any amount of your FLOW tokens to an existing node operator and you will earn rewards. Please see a list [here](https://github.com/onflow/flow/blob/master/nodeoperators/NodeOperatorList.md) for all node operators that you can delegate to. This list will be updated as new node operators are onboarded onto the network.' ### Staking Your Own Node 1. Once you have navigated to the staking/delegating page in Flow Port, click on the 'Stake a Node' option. 2. Next, select the type of node you will be running. 3. Input the amount of Flow you wish to stake with that node. You must stake at least the minimum in order for your stake request to be successfully processed. You are able to provide the minimum stake across multiple transactions. Meaning, you could execute your stake transaction with half of the minimum required. Then, before the next epoch, you can choose to 'Add Flow' to that pending stake to get it to the minimum stake required. 4. Run the [bootstrapping instructions](../../protocol/node-ops/node-operation/node-bootstrap.md) and provide the remaining technical details needed to stake a node. ### Delegating 1. Once you have navigated to the staking/delegating page in Flow Port, click on the Delegate option. 2. Next, you will specify which node operator you would like to delegate to and how many tokens you want to delegate to them. 3. Execute the transaction. You will now see your pending delegation that will be processed during the next epoch. 4. At this point, you can also cancel the pending delegation. On the pending delegation, you will see an `X` that you can click to initiate the cancelation transaction. ## I Have Successfully Executed a Stake Transaction, Now What? - Now that you have executed a stake transaction in either Flow Port or your custody provider’s portal, that transaction will sit in a pending status until it is processed, which will be at the next [Epoch](../../protocol/staking/index.md#epochs) Date (which is currently weekly). - During the next [Epoch](../../protocol/staking/index.md#epochs), the transaction will be processed. If successful, the provided FLOW will be staked and the associated Node would be either **a)** included in the network protocol if it is a new node or **b)** continue to operate as is in the network protocol. - You are now a part of Flow, and will begin to earn rewards for being a valued member of the network! ## What Else Can I Do? - Add additional stake to your existing stake. Any added FLOW will again sit in a pending status and be processed at the next epoch. - Withdraw/re-stake your earned rewards. If you decide to withdraw your rewards, this action will happen instantly. If you decide to re-stake your rewards, the request will again sit in a pending status and will be processed at the next [Epoch](../../protocol/staking/index.md#epochs). - Withdraw Rewards and send your earnings to other accounts. If you decide that you want to withdraw your rewards and send those earnings to other accounts via the 'Send FLOW' function, you should first withdraw your rewards. Once in your account, you can send these funds to any other account via the 'Send FLOW' option. - Request to be unstaked from the network. The unstake request will sit in a pending status for two epochs. Once it is processed, the amount that has been unstaked will sit in your unstaked FLOW amount and can now be withdrawn or re-staked. - Change the node you are staked/delegated to. If your staked/delegated node has no FLOW actively staked and you have completely withdrawn all unstaked amounts and rewards associated with the node, then you can move your stake to a different node. Click on the `Change Node` button to initiate this process. Please note that this feature is only visible once you get your active stake/delegate into the appropriate status. ## FAQs 1. Why do I have multiple 'Keys' on my account? If you created your account with Blocto, you will see that you have multiple keys that exist on your account in the 'Dashboard': 1 with weight 1 (device key): This is generated on Blocto and sent to users' device when they login with email. 1 with weight 999 (Blocto service key): This is kept in Blocto's secure key management service and is used to sign transaction. 1 with weight 1000 (recovery key): This is kept in Blocto's secure key management service and is only used when user wants to switch to non-custodial mode. Normally if a user wants to send a Flow transaction, it requires signature from both the key on users' device and a key from Blocto service. Making it harder for hackers to steal your assets. 2. Where can I find a list of node operators to delegate to? - a.) Please see a list [here](https://github.com/onflow/flow/blob/master/nodeoperators/NodeOperatorList.md) for all node operators that you can delegate to. This list will be updated as new node operators are onboarded onto the network. 3. I am currently running a node on the network already and have already gone through the staking process once. Do I need to execute a new stake every time there is a new epoch? - a.) Once you successfully stake your node and become part of the network, you do not need to submit a new staking request each and every epoch. Your node will be automatically staked from epoch to epoch. This also means that your Node ID will remain the same from epoch to epoch. If you want to unstake your node from the network, then you will follow the process of unstaking your node. 4. I have a Blocto account and I see that I can stake both in Flow Port and in Blocto's mobile app. What is the difference? - a.) If you go through Flow Port, you can choose any node operator within the Flow network to delegate any amount of your Flow Tokens to. If you go through Blocto's mobile site, you will only be able to stake to Blocto run nodes. You can read more about Blocto's staking process by referencing [here](https://guide.blocto.app/article/stake-flow-tokens-step-by-step-with-blocto). 5. Do I need to use my Ledger device to view information about my account (e.g. my balance and current staked or delegated FLOW)? - a.) No you do not! You only need your Ledger device to sign transactions. If you want to view your account, you can do so without your Ledger. You can do this by navigating directly to the appropriate desired page URL, while inputting your address into the URL itself. For quick reference, below is a list of these URLs and where you would input your address: - Dashboard: https://port.flow.com/account/[AccountAddress] - Stake & Delegate: https://port.flow.com/stake-delegate/[AccountAddress] 6. I am clicking 'submit' to execute a transaction, but nothing is happening. How can I unblock myself? - a.) Please disable any pop-up blockers and ad blockers you have and refresh the page. If you are still experiencing issues, please reach out via [Discord](https://discord.gg/flow) in the appropriate channel. --- # File: /md/protocol/flow-port/staking-guide.md --- title: Flow Port Staking Guide --- This guide provides step-by-step instructions for using the Flow Port to stake your FLOW tokens and start earning rewards. Currently, Flow Port only supports staking or delegating using tokens held in Blocto or Ledger wallets. If you're new to the concepts of staking and delegating you can [read this guide](../../protocol/staking/index.md) to learn more. ## First Step When you arrive in Port, select **Stake & Delegate** from the left-hand menu. You should be taken to this page. ![Flow Port Staking pt. 0](port-stake-0-00.png) From here you can decide whether to stake or delegate. - Select **Stake** if you plan to stake a node you're running. - Select **Delegate** to delegate your stake to another Node Operator. You don't need to know which Node Operator, you'll be provided with a list to choose from. If you are not running your own node you scan skip directly to the [delegation section](#delegating) ## Stake a Node Users who will be running their own nodes can stake them using the Flow Port. #### Prerequisites In order to stake your node, you'll need to have the required amount of FLOW for your node type. You'll also need the following information about your node: - Node ID - Network Address - Networking Key - Staking Key - Machine Account Public Key (for collection/consensus nodes only) If you don't have this information, go [here](../../protocol/node-ops/node-operation/node-bootstrap.md#step-1---run-genesis-bootstrap) for instructions on how to acquire it. ### Begin Staking First, select the type of node you'll be running by choosing from the list. You must have the required amount of locked FLOW in your account. ![Flow Port Staking](port-stake-0-02.png) Once you selected your node type, click next and specify how much you'd like to stake. The minimum amount for your node type is required, but you may stake as much as you like beyond that. Here's the screen you should see: ![Flow Port Staking](port-stake-0-03.png) Clicking next will take you to the final screen, where you'll need to enter information about your node you previously obtained. If you don't have this information, go [here](../../protocol/node-ops/node-operation/node-bootstrap.md#step-1---run-genesis-bootstrap) for instructions on how to acquire it. Here's the screen you should see: ![Flow Port Staking](port-stake-0-04.png) Clicking next will take you to a confirmation screen. This is your chance to double-check that you've entered your information correctly. If you're ready, check the box confirming your information and click submit to send the transaction that will stake your node! You should see a transaction status screen like this: ![Flow Port Staking](port-stake-0-05.png) **Note:** If your transaction fails, double-check the information you provided.

    If you return to the home screen, you'll be able to see your staking request in progress! ![Flow Port Staking](port-stake-4.png) ## Delegating Delegating is the process of staking your locked FLOW to nodes which are being run by another party. #### Prerequisites In order to delegate your stake to another node, you'll need to know the **node operator ID** of the operator who is running the nodes you wish to stake. Here is a list of node operator IDs you can delegate to: [List of Available Node Operators](https://github.com/onflow/flow/blob/master/nodeoperators/NodeOperatorList.md) ### Enter a Node Operator ID Simply enter the ID of the node operator of your choice and click next. ![Flow Port Staking](port-delegate-1.png) ### Enter an amount Next you'll enter an amount of FLOW you would like to delegate. When delegating you may send any amount to the node operator. ![Flow Port Staking](port-delegate-2.png) Click next to reach the confirmation screen. Confirm the details of your delegation request and click submit! ![Flow Port Staking](port-delegate-3.png) Once your transaction is submitted, you can monitor its status from this screen, or return to the Flow Port home screen. ![Flow Port Staking](port-delegate-4.png) **Note:** If you transaction fails, double-check the information you provided.

    That's it! You've successfully delegated stake to your chosen node operator! ## Returning to Port Within Flow Port, navigate to the ‘Stake & Delegate’ page to see details about your existing staked and/or delegated tokens. This will also show you the rewards you have earned for your staked/delegated tokens. ![Flow Port Staking pt. 1](port-stake-1.png) From here, you can do a few different things with your rewards: - You can choose to **re-stake** them to the associated node. - You can choose to **withdraw** them to your wallet. ## Re-staking Flow Port will not automatically re-stake your rewards. To re-stake your rewards, simply hover your cursor over the 3 dots next to the rewards field: ![Flow Port Re-Staking](port-stake-2.png) Click on the Restake option. This will take you to a screen that looks like the below. Input the amount of rewards you want to re-stake, acknowledge the transaction inputs and click submit: ![Flow Port Re-Staking](port-stake-3.png) Once the transition is processed, you can reference the Stake & Delegate page again to see the pending stake now: ![Flow Port Re-Staking](port-stake-4.png) ## Withdraw your Rewards To withdraw your rewards, simply hover your cursor over the 3 dots next to the rewards field, and click on ‘Withdraw’. ![Flow Port Re-Staking](port-stake-5.png) Input the amount that you want to withdraw to your wallet, acknowledge the transaction inputs and click submit: ![Flow Port Re-Staking](port-stake-6.png) Once the transition is processed, you can now see the withdrawn rewards in your balance and you are now free to do other actions with them (send them to other accounts, delegate to a node, etc). --- # File: /md/protocol/governance.md --- title: Governance description: Learn about Flow's governance model and how it's empowering our community of users and builders. sidebar_position: 5 sidebar_custom_props: icon: 🏛️ --- ## Participation Participating in the governance process can take three forms: - Being elected as a council member on the governing committee - Putting forth a proposal for the community to vote on - Staking to receive voting rights Votes will be weighted based on locked tokens. All tokens staked by node operators will be eligible for voting, but other users can lock up their tokens to be given voting power. Anyone will be able to stake a Flow token to vote on issues (even if they aren’t participating as a staked node). ## Token Holder Rights Tokens may be staked for operation or governance rights which gives holders the right to participate in running a node and/or to participate in public votes. ## Process Proposals can be brought forward on a public forum where they will be evaluated by the governing committee. All decisions are made publicly and any stakeholder has the opportunity to organize grassroots action to veto specific decisions or to vote in or remove council members. To ensure the progress of the network, the elected council first assesses the proposal and selects an answer they agree to be the "default choice". Voters can freely vote how they choose, but having a well-considered default allows forward progress without being blocked by passive participants. All decisions are voted on by all participants and decisions made by the council must be ratified by a public vote on the network. ### Timing Vote outcomes and upcoming votes will be published every Friday by 7am PT. All upcoming votes are available for review and voting for at least two weeks following their publication. ## Protocol Set Parameters The following parameters will be set on the network on day 1 and will not be candidates for a public vote when the network first launches. - The staking ratio preserved between each node type - The maximum inflation rate - The role of FLOW as the main reserve asset for collateralized secondary tokens (e.g. stablecoins) - The mechanism through which transaction inclusion, computation, and storage fees are determined and paid for ## Early Governance of the Protocol Once governance is enabled, the community can participate in the following: - Protocol upgrades, including things like: - the consensus algorithm - the low-level network communication structure - the execution environment - the number of seats available for each node type - Management of Ecosystem Development Fund, including: - issuance of grants - bug & feature bounties - Selecting council members - Committee budgets for each of the operational arms of the Foundation, including the executive, technical, operational, legal, pricing, financial, and marketing branches. - Management of legal affairs, including: - enforcing license and patent infringements - issuing takedown notices and copyright infringement - freezing accounts if illegal activity occurs - updating the community, security, contribution policies During the Bootstrapping Phase, anyone may apply online to be set as a Validator by the Company. Approved Validators must then Stake a fixed minimum of FLOWs based on Validator type. Other FLOW holders may become “Delegators” when they dedicate or “Delegate” their FLOWs to approved Node Operators as a signal that they believe that Validator to be an effective and honest participant of the network. Staking and Delegation features are already enabled as of the Effective Date. Each Validator makes an individual decision of which Protocol Version they choose to use. Since the value of blockchain networks is primarily due to the collectively verified execution state, there is a strong incentive for Validators to choose a Protocol Version that is compatible with the Protocol Version selected by the majority of other Validators. As a practical matter, the Protocol Version chosen by the overwhelming majority of Validators is likely to be the most recent Protocol Version produced and recommended by the Core Team, provided the proposed changes are not contentious. However, if a significant fraction of the community disagrees with any aspect of the most recent Protocol Version, they can band together to use a previous Protocol Version, or some other Protocol Version defined independently from the Core Team. This process of a “contentious forking” is rare, but does have several precedents in other networks (REF: Ethereum Classic, Bitcoin Cash). The process by which the Core Team chooses the updates for each new Protocol Version follows the open process described above, using GitHub as an open discussion platform to gauge the priorities and needs of the entire Flow ecosystem. The proposed changes by the Core Team will be announced and discussed well before they are implemented, and any community member can propose their own changes or contribute code updates to implement any proposed changes. The details of a new Protocol Version are publicly available no less than 14 days before that version is formally recommended for use by Validators (a “Release”), with the complete implementation source code visible for no less than 7 days before a Release. --- # File: /md/protocol/network-architecture.md --- title: Flow Network Architecture sidebar_position: 1 --- Flow introduces a new paradigm in blockchain architecture - **an [MEV-resistant] modular design** that combines decentralized sequencers, stateless validators, and an execution layer built for **rich atomic composability across complex state**. This novel structure achieves scalability, decentralization, and security - without compromise - ensuring both [user safety] and long-term sustainability.
    ![flow_gif](images/flow_node_types_1.gif)
    Flow ships what roll-ups only promise: **a full modular feature-set on a single L1**. - **Decentralized sequencing** via Consensus + Collector roles - no central bottleneck and [MEV-resistance] - **Native data availability** - Flow architecture ensures all necessary state data is available within the protocol, allowing nodes to verify state without relying on an external data availability layer - **Execution / verification split** for lightweight validators, yet one global state for atomic composability - **Protocol-level [account abstraction]:** multi-key wallets, compute unit (gas) sponsorship, scoped capabilities - **Dual runtimes:** [EVM equivalence] alongside Cadence, so Solidity and resource-oriented contracts live side-by-side To appreciate architecture of Flow, it's important to first understand the core challenges in building performant blockchains - and then explore how the Flow **multi-role architecture** addresses them directly. ## What Problems Does the Flow Multi-Role Architecture Solve? ### 1. The Blockchain Trilemma The blockchain trilemma proposes that networks can only optimize for **two out of three** fundamental properties: **decentralization**, **security**, and **scalability**. For example: - Bitcoin and Ethereum prioritize decentralization and security, but suffer from scalability limitations. - Networks like Solana, Aptos, and Sui improve scalability and security, but centralize validator infrastructure, compromising decentralization. Multi-chain solutions (e.g. Cosmos), Layer 2s (e.g. Optimism), and cross-chain bridges aim to overcome this tradeoff. However, they do so by **splitting trust** across **independent security domains**, often with fewer validators - introducing new attack vectors and lowering security guarantees. ![scenario_1](images/trilemma.png) ### 2. MEV and End-User Exploitation Most blockchains inherently expose users to **Maximum (or Miner) Extractable Value (MEV)** - a phenomenon where block proposers reorder or censor transactions to extract value, or third parties manipulate priority fees to insert transactions before and/or after a target transaction. This results in users paying a hidden "invisible tax", experiencing failed transactions, or in extreme cases **having hundreds of thousands of dollars stolen from them**. Even in large, decentralized networks, MEV can be exploited by colluding actors, creating **unfair outcomes for individual users**. ### 3. Energy Inefficiency and Unsustainable Growth Proof-of-Work chains like Bitcoin are **energy-intensive** and require constant hardware upgrades to stay viable. While Proof-of-Stake reduces the carbon footprint, it still scales poorly. As usage grows, so does the **onchain state and throughput demand**, requiring validators to **vertically scale** their hardware-leading to greater **centralization pressure** and higher energy consumption. ## Rethinking Blockchain Design ![Flow Banner](./images/banner.png) In traditional blockchains like Ethereum and Bitcoin, every full node is responsible for **all aspects of transaction processing**-from validating the full state to executing smart contracts and maintaining history. This ensures security but is inherently inefficient and limits scalability in terms of **throughput** and **data capacity**. Second-generation blockchains typically address this in one of two ways: 1. **Reduce decentralization** by requiring high-performance hardware (e.g. Solana), or 2. **Increase complexity** by fragmenting the network with techniques like sharding (e.g. Optimism and other L2s). The first approach risks validator centralization and cartelization. The second shifts the burden to developers, who must manage cross-shard or cross-chain infrastructure - **introducing developer overhead and new failure modes**. Flow offers a new path: **pipelining** applied to blockchain networks. ### The Flow Alternative: Modular Design and Pipelining Flow applies pipelining, a proven method from manufacturing and computing, to blockchain consensus and execution. Rather than making each node perform all duties, Flow splits the responsibilities across **five specialized node roles**. Every transaction is still validated by the network - but **each node only handles a specific stage**, allowing them to specialize and **optimize for their role**. This specialization unlocks massive gains in **throughput**, **efficiency**, and **scalability** - without sacrificing decentralization or composability. ### Flow Node Roles and What They Do | | Node type | Responsibility | What do the nodes of this role do? | | ---------------------------------------- | :----------: | :----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ![collection](images/collection.png) | Collection | Collection nodes act as a censorship-resistant data availability layer, which caches transactions for subsequent execution. | Collection nodes order transactions into batches known as collection. | | ![consensus](images/consensus.png) | Consensus | The consensus committee serves as the security authority in the network and orchestrates the Flow transaction processing pipeline. | Consensus nodes order collections into blocks and commit execution results after verification. | | ![execution](images/execution.png) | Execution | Execution nodes provide the computational resources for executing transactions and maintaining the state. | Execution nodes execute the transaction and record state changes. | | ![verification](images/verification.png) | Verification | Verification nodes ensure that transactions are truthfully executed. | Verification nodes verify the work of the execution nodes. They either approve or disagree with their results, reporting their findings to the consensus nodes. | | ![access](images/access.png) | Access | Access Nodes route transactions into the network and replicate (parts of) the state and transaction results for external clients to query. | Access node serve the API calls to send and read data from the chain. | ### Further reading 1. [Primer on multi-role architecture](https://flow.com/primer#primer-multinode) 2. [Technical papers](https://flow.com/technical-paper) 3. [Core protocol vision](https://flow.com/core-protocol-vision) 4. [Medium article from Jan which deep dives into the Flow architecture](https://jan-bernatik.medium.com/introduction-to-flow-blockchain-7532977c8af8) In the next section, lets look at how Flow multi-role architecture solves those three big problems with blockchains. [MEV-resistant]: ../../build/cadence/basics/mev-resistance.md [user safety]: ./user-safety.md [MEV-resistance]: ../../build/cadence/basics/mev-resistance.md [account abstraction]: ../../build/cadence/basics/accounts.md [EVM equivalence]: ../../build/evm/quickstart.md --- # File: /md/protocol/network-architecture/solving-blockchain-trilemma.md --- title: Solving the blockchain trilemma sidebar_label: Solving the blockchain trilemma sidebar_position: 2 --- # Solving the blockchain trilemma In a monolithic architecture, all nodes perform every task. As network usage grows, the transaction processing capacity of the individual nodes becomes a limiting factor, restricting the network’s throughput and latency. The amount of data that can be stored onchain is limited since nodes have a finite storage capacity. The only way to scale monolithic blockchains is by increasing the capacity of each node by adding more CPU, memory, and storage (i.e. vertical scaling, an approach taken by Solana). However, this solution comes at the cost of decentralization. As nodes scale vertically, they become more expensive to run, and eventually, only a few operators can afford to run such high-performance, high-capacity nodes. Worse, energy consumption for every node in the network increases over time, making the chain environmentally unsustainable. Through its multi-role architecture, Flow implements a modular pipeline for processing transactions. This design allows the network to scale by tuning the level of decentralization at each specific step without sharding the state and fragmenting the network into smaller security zones. The modular pipeline is composed of Collection, Consensus, Execution and Verification Nodes. ![pipeline](images/pipeline.png) ## Separating Consensus from Compute At a high level, the pipeline essentially separates consensus from transaction computation. Non-deterministic (or “subjective”) processes such as determining the inclusion and order of transactions are decided by the broadly decentralized consensus committee. The deterministic (or “objective”) task of computing the result of those ordered transactions is done independently by a small number of specialized execution nodes. Collection and consensus are highly decentralized and achieve high levels of redundancy through a large number of lightweight, cost-effective nodes, numbering in the thousands, operated by several hundred different operators. These steps guarantee resilient transaction ordering (assuming that a malicious actor can only compromise a limited number of nodes). In comparison, transaction execution has low decentralization and redundancy (10 or less) with more powerful and expensive nodes. To accommodate for the anticipated growth of onchain state without sharding, only the execution nodes have to be scaled vertically. All other node types can continue to run low-cost hardware. The execution nodes may eventually be scaled up to small data centers. ![scaling_flow](images/scaling_flow.png) Low decentralization for transaction execution might appear to compromise decentralization of the whole network, as it is conceivable that a malicious actor might compromise a dominant fraction of nodes participating in execution. However, correctness of the transaction results is still guaranteed by the verification step, which also requires reasonably high redundancy, again with a large number of lighter and less expensive verification nodes to withstand compromisation attempts. Every node in Flow makes the protocol stronger, and the network can grow as needed to achieve different objectives: - More censorship resistance? Add more collection nodes - More decentralized block production? Add more consensus nodes - Need to accommodate higher transaction throughput and state storage? Scale up execution nodes - Do node operators want to reinforce network security with modest node hardware and low stake? Add more verification nodes. - Need access to chain data locally? Add access nodes. In contrast, when traditional Layer 1 blockchains add more nodes to increase decentralization, they do so without providing any additional benefits. ![verying_redundancy](images/varying_redudancy.png) > Flow’s architectural goals are to provide a throughput of at least 1M TPS, ingest at least ½ GB of transaction data per second and store and serve a very large state of one Patebyte and beyond. Thus, Flow’s multi-role architecture solves the blockchain trilemma: 1. **Scalability**: Scale to thousands of times higher throughput and onchain storage capacity. 2. **Decentralization**: Except for the execution nodes, all nodes are light weight and low cost, lowering the barrier to entry and ensuring participation from a diverse set of node operators—big and small 3. **Security**: Maintain a shared non-sharded execution environment for all operations on the network and use a secure in-built platform to build on. ![trilemma_solved](images/flow_trillema_solved.png) --- # File: /md/protocol/network-architecture/sustainability.md --- title: Sustainability sidebar_label: Sustainability sidebar_position: 3 --- # Sustainability with Flow It's no secret that Proof of Stake blockchains are better for the environment. As Web3 becomes more widely adopted, we engaged with Deloitte Canada to validate how much energy it uses. And the results are astounding: Flow uses just 0.18 GWh annually, based on 2021 usage – or in simpler terms, minting an NFT on Flow takes less energy than a Google search or Instagram post. In addition to operating on a Proof of Stake consensus system, Flow’s multi-role node architecture securely divides the processing between specialized node types, making the network significantly more efficient than other blockchain architectures. As network usage grows, vertical scaling is only needed for the execution nodes (as they execute transactions and persist all the chain state). Because the increase in energy and hardware consumption over time is restricted to a small subset of the nodes in the network, this drastically limits the environmental footprint of the chain. The overall energy use of the network won’t increase significantly even if the activity increases by 100x or more, making the per-transaction energy footprint decrease over time. Read more about it [here](https://flow.com/post/flow-blockchain-sustainability-energy-deloitte-report-nft). --- # File: /md/protocol/network-architecture/user-safety.md --- title: User safety sidebar_label: User safety sidebar_position: 4 --- # User Safety with Flow The monolithic node design of common L1s such as Bitcoin and Ethereum overly privileges operator control over block production. This makes the chain vulnerable to censorship and MEV attacks. This problem is exacerbated by L2s with centralized sequencers. ERC-4337 is also susceptible to MEV on the user operations via bundlers. ![mev](images/mev_attack.png) Flow’s multi-role architecture provides censorship & MEV resistance by design: - Transactions are randomly assigned to collection nodes for inclusion in collections and eventually in blocks. Each collection node only sees a subset of transactions. - There is already a distinct separation between the proposers (represented by the collection nodes) and the builders (represented by the consensus nodes). This separation essentially provides an inherent implementation of "proposer-builder separation," a concept currently being explored by Ethereum. With this separation, even if the collection nodes were to reorder the transactions, there is no incentive for the consensus nodes to prefer one collection node’s proposal over another. ![mev_protection](images/mev_protection_in_flow.png) --- # File: /md/protocol/node-ops.md --- title: Node Operations sidebar_position: 1 --- # Hello Node Operator! Flow nodes are vital components of the Flow blockchain. These nodes are responsible for a variety of network operations to maintain the distributed ledger. ## Why Run a Node? --- By running your own node, you have direct access to the evolving state of the network, without having to rely on third parties. This increases privacy and security, reduces reliance on external servers, and helps balance load distribution. By running a node, you also directly contribute to the security and decentralization of the whole network. Flow multirole architecture makes it more scalable and provides several node types that you as a node operator can pick and choose from. ## Which Node Should You Run? --- The different types of nodes are described [here](./node-operation/node-roles.md). As node operator, you can choose to run any of the different types of node that best fits your needs. The nodes are classified as follows, ![Flownodesdiagram.png](./node-operation/Flownodesdiagram.png) ## Light Node A.K.A. Observer Node --- The light node is one of the easiest nodes to spin up and can be run by Dapp developers who need the latest block data available locally, e.g. a wallet application that needs to track the latest block ID and height. In addition to supporting dapps, an observer node can also be run by access node operators who want to scale their access nodes' endpoints. Access node operators can spin up geographically dispersed observer nodes which can talk to their staked access nodes and to each other. The observer node is not staked but still provides the same API as the access node. :::info To run a light node, follow this [guide](./light-nodes/observer-node.md) ::: ## Full Node --- In a nutshell, Full Nodes are staked network participants that drive network progress, e.g. by creating and executing new blocks. They are the primary contributors to network safety (all of them validate the correctness of the consensus process and secure the network additionally through their role-specific tasks). In comparison, Light Nodes don't contribute to the networks progress. Though, they help to secure the network by also validating the integrity of the consensus process. - The Access node is a full node that serves as an RPC node and acts as a gateway node for the network. - The Validator node (Collection, Consensus, Verification and Execution) is a full node that plays a role in block generation. ### Access Node --- If you want local access to the protocol state data (blocks, collections, transactions) and do not want to use one of the community access nodes you can run an access node. Dapp developers, chain explorers, chain analytics and others who want exclusive access to chain data and not be subject to the rate-limits on the community access node can choose to run an access node. An access node is minimally staked for network security. The central goal for Access Nodes is to provide RPC functionality to its node operator. In comparison, contributing to protocol progress (e.g. routing transactions to collector clusters, relaying blocks to the unstaked peer-to-peer network, etc.) should only take up a marginal fraction an Access Node's computational resources. Furthermore, Access Node operators can freely rate-limit the amount of resources their Access Node dedicates to supporting the broader ecosystem. Therefore, Access Nodes do not receive staking rewards. :::info Launch an access node using QuickNode [https://www.quicknode.com/chains/flow](https://www.quicknode.com/chains/flow) ::: :::info To run a self-hosted access node, follow this [guide](./access-nodes/access-node-setup.md) ::: :::tip Alternately, instead of running an access node, you can use the [Flow community](../access-onchain-data/index.md) access nodes or the ones run by any of the other node operators. ::: ### Validator Node --- You can also be a core participant in running the Flow network and contribute to securing it. Depending on your preference, you could run one or any combination of the following node roles: - Collection Nodes collaboratively create batches of transactions (in Flow terminology collections). - Consensus Nodes create blocks, schedule them for asynchronous execution, and commit execution results once they are verified (so called sealing). In addition, they orchestrate the Flow protocol and enforce protocol compliance. - Execution Nodes asynchronously execute blocks. They are the power-houses in the protocol, providing the vast computational resources available to Flow transactions. - Verification Nodes check the execution results in a distributed manner. Nodes with these roles are staked and also receive staking rewards. ### Running a Staked Node --- To run a staked node (node type access, collection, consensus, verification or execution) the node must: * be registered with sufficient stake * be authorized by the governance working group Before proceeding, ensure you have the stake required for your new node and that your node will be authorized by the governance working group (apply [here](https://github.com/onflow/flow-validator)). To set up a new staked node after it has been authorized by the Flow governance working group, you will need to complete the following steps: 1. [Provision](./node-operation/node-setup.md) the machine on which your node will run. 2. [Generate and register](./node-operation/node-bootstrap.md) your node identity. 3. [Start](./node-operation/node-bootstrap.md#step-3---start-your-flow-node) your node! --- # File: /md/protocol/node-ops/access-nodes/access-node-configuration-options.md --- title: Serving execution data sidebar_label: Execution Data sidebar_position: 2 --- Flow chain data comprises of two parts, 1. Protocol state data - This refers to the blocks, collection, transaction that are being continuously added to the chain. 2. Execution state data - This refers to what makes up the execution state and includes transaction events and account balances. The access node by default syncs the protocol state data and has been now updated to also sync the execution state data. This guide provides an overview of how to use the execution data sync feature of the Access node. ## Setup node’s directory The access node typically has the following directory structure: ```bash $ tree flow_access flow_access/ ├── bootstrap │ ├── private-root-information (with corresponding AN data) │ └── execution-state │ └── public-root-information │ ├── node-id │ └── node-info.pub.NODE_ID.json │ └── root-protocol-state-snapshot.json (the genesis data) └── data (directory used by the node to store block data) │ └── execution-data │ └── execution-state ``` ## Setup execution data indexing First, your node needs to download and index the execution data. There are 3 steps: 1. Enable Execution Data Sync 2. Download the root checkpoint file 3. Configure the node to run the indexer 4. Use the indexed data in the Access API. As of **`mainnet24`** / **`devnet49`**, Access nodes can be configured to index execution data to support local script execution, and serving all of the Access API endpoints using local data. There are different setup procedures depending on if you are enabling indexing immediately after a network upgrade, or at some point between upgrades. # Enable Execution Data Sync This is enabled by default, so as long as you didn’t explicitly disable it, the data should already be available. 1. Make sure that either `--execution-data-sync-enabled` is not set, or is set to `true` 2. Make sure that you have a path configured for `--execution-data-dir`, otherwise the data will be written to the running user’s home directory, which is most likely inside the container’s volume. For example, you can create a folder within the node’s data directory `/data/execution-data/`. There are some additional flags available, but you most likely do not need to change them. ## **Option 1: Enabling Indexing at the Beginning of a Spork** ### Download the root protocol state snapshot The `root-protocol-state-snapshot.json` is generated for each [spork](https://developers.flow.com/protocol/node-ops/node-operation/network-upgrade) and contains the genesis data for that spork. It is published and made available after each spork. The download location is specified [here](https://github.com/onflow/flow/blob/master/sporks.json) under [rootProtocolStateSnapshot](https://github.com/onflow/flow/blob/master/sporks.json#L16). Store the **`root-protocol-state-snapshot.json`** into the **`/bootstrap/public-root-information/`** folder. ### Download the root checkpoint The root checkpoint for the network is used by Execution nodes and Access nodes to bootstrap their local execution state database with a known trusted snapshot. The checkpoint contains 18 files that make up the merkle trie used to store the blockchain’s state. The root checkpoint for each spork is hosted in GCP. You can find the link for the specific network in the [`sporks.json`](https://github.com/onflow/flow/blob/master/sporks.json) file. Here’s the URL for `mainnet24`: [https://github.com/onflow/flow/blob/52ee94b830c2d413f0e86c1e346154f84c2643a4/sporks.json#L15](https://github.com/onflow/flow/blob/52ee94b830c2d413f0e86c1e346154f84c2643a4/sporks.json#L15) The URL in that file will point to a file named `root.checkpoint`. This is the base file and is fairly small. There are 17 additional files that make up the actual data, named `root.checkpoint.000`, `root.checkpoint.001`, …, `root.checkpoint.016`. If you have `gsutil` installed, you can download them all easily with the following command. ```bash gsutil -m cp "gs://flow-genesis-bootstrap/[network]-execution/public-root-information/root.checkpoint*" . ``` Where `[network]` is the network you are downloading for. For example, `mainnet-24` or `testnet-49`. Once the files are downloaded, you can either move them to `/bootstrap/execution-state/` within the node’s bootstrap directory or put them in any mounted directory and reference the location with this cli flag: `--execution-state-checkpoint=/path/to/root.checkpoint`. The naming of files should be `root.checkpoint.*`. ## **Option 2: Enabling Indexing Mid-Spork** ### Identify the root checkpoint The root checkpoint for the network is used by Execution and Access nodes to bootstrap their local execution state database with a known trusted snapshot. The checkpoint contains 18 files that make up the merkle trie used to store the blockchain’s state. Root checkpoints are periodically generated on Flow Foundation execution nodes and uploaded to a GCP bucket. You can see a list of available checkpoints [here](https://console.cloud.google.com/storage/browser/flow-genesis-bootstrap/checkpoints), or list them using the [gsutil](https://cloud.google.com/storage/docs/gsutil) command ```bash gsutil ls "gs://flow-genesis-bootstrap/checkpoints/" ``` The checkpoint paths are in the format `flow-genesis-bootstrap/checkpoints/[network]/[epoch number]-[block height]/`. Where - `[network]` is the network the checkpoint is from. For example, `mainnet` or `testnet`. - `[epoch number]` is the epoch number when the checkpoint was taken. You can find the current epoch number on the [flowscan.io](https://flowscan.io/) home page. - `[block height]` is the block height at which the checkpoint was taken. Make sure that the checkpoint you select is from an epoch when your node was part of the network. ### Download the root checkpoint Once you have selected the checkpoint to download, you can download the files. If you have `gsutil` installed, you can download them all easily with the following command. ```bash gsutil -m cp "gs://flow-genesis-bootstrap/checkpoints/[network]/[epoch number]-[block height]/root.checkpoint*" . ``` Once the files are downloaded, you can either move them to `/bootstrap/execution-state/` within the node’s bootstrap directory or put them in any mounted directory and reference the location with this cli flag: `--execution-state-checkpoint=/path/to/root.checkpoint`. The naming of files should be `root.checkpoint*`. ### Download the root protocol state snapshot Access nodes require that the data in the root checkpoint corresponds to the root block in the `root-protocol-state-snapshot.json` file. It's important to download the snapshot for the correct height, otherwise bootstrapping will fail with an error described in the Troubleshooting section. You can download the `root-protocol-state-snapshot.json` file generated by the Execution from the same GCP bucket. ```bash gsutil cp "gs://flow-genesis-bootstrap/checkpoints/[network]/[epoch number]-[block height]/root-protocol-state-snapshot.json" . ``` Alternatively, you can download it directly from a trusted Access node using the `GetProtocolStateSnapshotByHeight` gRPC endpoint with the corresponding height. You will get a `base64` encoded snapshot which decodes into a json object. At this time, this endpoint is only support using the grpc API. Store the **`root-protocol-state-snapshot.json`** into the **`/bootstrap/public-root-information/`** folder. # Configure the node to run the indexer Now you have the execution sync setup and the root checkpoint in place, it’s time to configure the node to index all of the data so it can be used for script execution. There are 3 cli flags that you will need to add: - `--execution-data-indexing-enabled=true` This will enable the indexer. - `--store-tx-result-error-messages=true` This will fetch and index transaction result error messages from execution nodes. - `--execution-state-dir` This defines the path where the registers db will be stored. A good default is on the same drive as the protocol db. e.g. `/data/execution-state` # Start your node Now that all the settings to enable indexing are in place, you can start your node. At a minimum, you will need the following flags: ``` --execution-data-indexing-enabled=true --store-tx-result-error-messages=true --execution-state-dir=/data/execution-state --execution-data-dir=/data/execution-data ``` For better visibility of the process, you can also add `-p 8080:8080` - export port 8080 from your docker container, so you could inspect the metrics `--loglevel=info` - for checking logs. Notes on what to expect: - On startup, the node will load the checkpoint into the `execution-state` db. For `devnet48`, this takes 20-30 min depending on the node’s specs. For `mainnet24`, it takes >45 min. The loading time will increase over time. You can follow along with the process by grepping your logs for `register_bootstrap`. - After the checkpoint is loaded, the indexer will begin ingesting the downloaded execution data. This will take several hours to days depending on if the data was already downloaded and the hardware specs of the node. - If your node already had all the data, it will index all of it as quickly as possible. This will likely cause the node to run with a high CPU. When you restart the node for the first time with syncing enabled, it will sync execution data for all blocks from the network. # Use the indexed data in the Access API ### Setup Local Script Execution Local execution is controlled with the `--script-execution-mode` flag, which can have one of the following values: - `execution-nodes-only` (default): Requests are executed using an upstream execution node. - `failover` (recommended): Requests are executed locally first. If the execution fails for any reason besides a script error, it is retried on an upstream execution node. If data for the block is not available yet locally, the script is also retried on the EN. - `compare`: Requests are executed both locally and on an execution node, and a comparison of the results and errors are logged. - `local-only`: Requests are executed locally and the result is returned directly. There are a few other flags available to configure some limits used while executing scripts: - `--script-execution-computation-limit`: Controls the maximum computation that can be used by a script. The default is `100,000` which is the same as used on ENs. - `--script-execution-timeout`: Controls the maximum runtime for a script before it times out. Default is `10s`. - `--script-execution-max-error-length`: Controls the maximum number of characters to include in script error messages. Default is `1000`. - `--script-execution-log-time-threshold`: Controls the run time after which a log message is emitted about the script. Default is `1s`. - `--script-execution-min-height`: Controls the lowest block height to allow for script execution. Default: `no limit`. - `--script-execution-max-height`: Controls the highest block height to allow for script execution. Default: `no limit`. - `--register-cache-size`: Controls the number of registers to cache for script execution. Default: `0 (no cache)`. ### Setup Using Local Data with Transaction Results and Events Local data usage for transaction results and events are controlled with the `--tx-result-query-mode` and `--event-query-mode` corresponding flags, which can have one of the following values: - `execution-nodes-only` (default): Requests are forwarded to an upstream execution node. - `failover` (recommended): - `failover` (recommended): Requests are handled locally first. If the processing fails for any reason, it is retried on an upstream execution node. If data for the block is not available yet locally, the script is also retried on the EN. - `local-only`: Requests are handled locally and the result is returned directly. # Troubleshooting - If the root checkpoint file is missing or invalid, the node will crash. It must be taken from the same block as the `root-protocol-state-snapshot.json` used to start your node. - If you don’t set one the `--execution-data-dir` and `--execution-state-dir` flags, the data will be written to the home directory inside the container (likely `/root`). This may cause your container to run out of disk space and crash, or lose all data each time the container is restarted. - If your node crashes or restarts before the checkpoint finishes loading, you will need to stop the node, delete the `execution-state` directory, and start it again. Resuming is currently not supported. - If you see the following message then your `checkpoint` and `root-protocol-state-snapshot` are not for the same height. ```json { "level":"error", ... "module":"execution_indexer", "sub_module":"job_queue", "error":"could not query processable jobs: could not read job at index 75792641, failed to get execution data for height 75792641: blob QmSZRu2SHN32d9SCkz9KXEtX3M3PozhzksMuYgNdMgmBwH not found", "message":"failed to check processables" } ``` - You can check if the execution sync and index heights are increasing by querying the metrics endpoint: ``` curl localhost:8080/metrics | grep highest_download_height curl -s localhost:8080/metrics | grep highest_indexed_height ``` # Execution Data Sync The Execution Sync protocol is enabled by default on Access nodes, and uses the bitswap protocol developed by Protocol Labs to share data trustlessly over a peer-to-peer network. When enabled, nodes will download execution data for each block as it is sealed, and contribute to sharing the data with its peers. The data is also made available to systems within the node, such as the `ExecutionDataAPI`. Below is a list of the available CLI flags to control the behavior of Execution Sync requester engine. | Flag | Type | Description | | -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | execution-data-sync-enabled | bool | Whether to enable the execution data sync protocol. Default is true | | execution-data-dir | string | Directory to use for Execution Data database. Default is in the user’s home directory. | | execution-data-start-height | uint64 | Height of first block to sync execution data from when starting with an empty Execution Data database. Default is the node’s root block. | | execution-data-max-search-ahead | uint64 | Max number of heights to search ahead of the lowest outstanding execution data height. This limits the number non-consecutive objects that will be downloaded if an earlier block is unavailable. Default is 5000. | | execution-data-fetch-timeout | duration | Initial timeout to use when fetching execution data from the network. timeout increases using an incremental backoff until execution-data-max-fetch-timeout. Default is 10m. | | execution-data-max-fetch-timeout | duration | Maximum timeout to use when fetching execution data from the network. Default is 10s | | execution-data-retry-delay | duration | Initial delay for exponential backoff when fetching execution data fails. Default is 1s | | execution-data-max-retry-delay | duration | Maximum delay for exponential backoff when fetching execution data fails. Default is 5m | # Execution Data Indexer Below is a list of the available CLI flags to control the behavior of Execution Data Indexer. | Flag | Type | Description | |---------------------------------|--------|-------------------------------------------------------------------------------------------------------------| | execution-data-indexing-enabled | bool | Whether to enable the execution data indexing. Default is false. | | store-tx-result-error-messages | bool | Whether to enable storing transaction error messages into the db. Default is false. | | execution-state-dir | string | Directory to use for execution-state database. Default is in the user’s home directory. | | execution-state-checkpoint | string | Location of execution-state checkpoint (root.checkpoint.\*) files. | | event-query-mode | string | Mode to use when querying events. one of [local-only, execution-nodes-only(default), failover] | | tx-result-query-mode | string | Mode to use when querying transaction results. one of [local-only, execution-nodes-only(default), failover] | Below is a list of the available CLI flags to control the behavior of Script Execution. | Flag | Type | Description | | ----------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | script-execution-mode | string | Mode to use when executing scripts. one of [local-only, execution-nodes-only, failover, compare ] | | script-execution-computation-limit | uint64 | Maximum number of computation units a locally executed script can use. Default: 100000 | | script-execution-max-error-length | int | Maximum number characters to include in error message strings. additional characters are truncated. Default: 1000 | | script-execution-log-time-threshold | duration | Emit a log for any scripts that take over this threshold. Default: 1s | | script-execution-timeout | duration | The timeout value for locally executed scripts. Default: 10s | | script-execution-min-height | uint64 | Lowest block height to allow for script execution. Default: no limit | | script-execution-max-height | uint64 | Highest block height to allow for script execution. default: no limit | | register-cache-type | string | Type of backend cache to use for registers [lru, arc, 2q] | | register-cache-size | uint | Number of registers to cache for script execution. Default: 0 (no cache) | | program-cache-size | uint | [experimental] number of blocks to cache for cadence programs. use 0 to disable cache. Default: 0. Note: this is an experimental feature and may cause nodes to become unstable under certain workloads. Use with caution. | # Resources FLIP: [https://github.com/onflow/flips/blob/main/protocol/20230309-accessnode-event-streaming-api.md](https://github.com/onflow/flips/blob/main/protocol/20230309-accessnode-event-streaming-api.md) Protobuf: [https://github.com/onflow/flow/blob/master/protobuf/flow/executiondata/executiondata.proto](https://github.com/onflow/flow/blob/master/protobuf/flow/executiondata/executiondata.proto) --- # File: /md/protocol/node-ops/access-nodes/access-node-setup.md --- title: Setting Up a Flow Access Node sidebar_label: Access Node Setup sidebar_position: 1 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; This guide is for running a permissonless Access node on Flow. If you are planning to run a different type of staked node then see [node bootstrap](../node-operation/node-bootstrap.md). Permissionless Access nodes allow any operator to run a Flow Access node. Unlike the other staked nodes, a permissionless access node does not have to be approved by the service account before it can join the network, hence the term "permissionless". The goal is to make all node types permissionless and this is the first step towards achieving that goal. ## Who Should Run a Permissionless Access Node? dApp developers can choose to run their own private permissionless access node and move away from using the community access nodes. This will also allow them to not be subjected to the API rate limits of the public access nodes. Node operators can also run their own permissionless access node and provide access to that node as a service. Chain analytics, audit and exploration applications can run such an access node and do not have to rely on third parties for the state of the network. ## Timing New nodes are able to join the network each time a new epoch begins. An epoch is a period of time (approximately one week) when the node operators in the network are constant. At epoch boundaries, newly staked node operators are able to join the network and existing node operators which have unstaked may exit the network. You can read more about epochs [here](../../staking/03-schedule.md). In order to join the network at epoch N+1, the access node **must** be registered with at least 100 FLOW staked prior to the end of epoch N's Staking Auction Phase. Currently on mainnet, the staking auction starts every Wednesday at around 20:00 UTC and ends on the next Wednesday at around 12:00 UTC. Since this deadline may shift slightly from epoch to epoch, we recommend the node be staked by _Wednesday, 8:00 UTC_ to be able to join the network in the next epoch. Confirmation of a new node's inclusion in epoch N+1 is included in the [`EpochSetup` event](../../staking/05-epoch-scripts-events.md#flowepochepochsetup). ![Flow Epoch Schedule](../node-operation/epoch-startup-order.png) ## Limitations There are five open slots for access nodes every epoch. You can view the exact epoch phase transition time [here](https://dashboard.flow.com/) under `Epoch Phase`. To summarize, | **Epoch** | **Epoch Phase** | | | :-------: | :--------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | N | Staking auction starts | Three new access node slots are opened. Anyone can register their access nodes | | N | Staking auction ends | Three of the nodes registered during this epoch are randomly selected to be a part of the network in the next epoch. No more nodes can register until the next epoch starts. | | N+1 | Epoch N+1 starts | The newly selected nodes can now participate in the network. Three new slots are opened. | ## How To Run a Permissionless Access Node? :::note To run an access node you will need to provision a machine or virtual machine to run your node software. Please follow the [node-provisioning](../node-operation/node-provisioning.md) guide for it. You can provision the machine before or after your node has been chosen. ::: At a high level, to run a permissionless Access node, you will have to do the following steps: 1. Generate the node identity (private and public keys, node ID etc.). 2. Stake the node with 100 FLOW by the end of the staking phase of the current epoch (see [timing](#timing)) by providing the node information generated in step 1. 3. You can verify if your node ID was selected by the onchain random selection process on Wednesday at around 20:00 UTC when the next epoch starts. 4. If your node ID was selected, you can provision and start running the node. If your node wasn't selected, your tokens will have been refunded to your unstaked bucket in the staking smart contract. When the next epoch begins, you can try committing tokens again in a future epoch to get a new spot. Following is a detail explanation of these four steps. If you want to run multiple access nodes, you will have to run through these steps for each node. ## Step 1 - Generate Node Information ### Download the Bootstrapping Kit ```shell curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools.tar tar -xvf boot-tools.tar ``` ```shell CheckSHA256 sha256sum ./boot-tools/bootstrap 0c2ad1bdf27aaa449a2b54975324ecef7c78fa39512cf8c2448165fedf106eb4 ./boot-tools/bootstrap ``` > If you have downloaded the bootstrapping kit previously, ensure the SHA256 hash for it still matches. If not, re-download to ensure you are using the most up-to-date version. ### Generate Your Node Identity ```shell ######################################################### # Generate Keys $ mkdir ./bootstrap # YOUR_NODE_ADDRESS: FQDN associated to your instance $ ./boot-tools/bootstrap key --address ":3569" --role access -o ./bootstrap ``` ```shell Example $./boot-tools/bootstrap key --address "flowaccess.mycompany.com:3569" --role access -o ./bootstrap DBG will generate networking key INF generated networking key DBG will generate staking key INF generated staking key DBG will generate db encryption key INF generated db encryption key DBG assembling node information address=flowaccess.mycompany.com:3569 DBG encoded public staking and network keys networkPubKey=f493a74704f6961ae7903e062ecd58d990672858eff99aece7bfbccf3aa02de8f1a624ecbf21a01e8b2f4a5854c231fbe218edd7762a34fea881f3958a215305 stakingPubKey=ae8dcf81f3a70d72036b7ba2c586ed37ed0eb82b9c0a4aab998a8420f98894f94c14f84fa716e93654d3940fc0c8ff4d19b504c90a5b4918b28f421e9d3659dc2b7e246025ebeffea0d83cceefe315d7ed346dbe412fdac51b64997d97d29f7e INF wrote file bootstrap/public-root-information/node-id INF wrote file bootstrap/private-root-information/private-node-info_e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5/node-info.priv.json INF wrote file bootstrap/private-root-information/private-node-info_e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5/secretsdb-key INF wrote file bootstrap/public-root-information/node-info.pub.e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5.json $tree ./bootstrap/ ./bootstrap/ ├── private-root-information │ └── private-node-info_e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5 │ ├── node-info.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5.json 3 directories, 4 files ``` :::warning _Use a fully qualified domain name for the network address. Please also include the port number in the network address e.g. `flowaccess.mycompany.com:3569`_ ::: :::warning _Do not include the prefix `http://` in the network address._ ::: :::tip If you would like to stake multiple access nodes, please ensure you generate a unique identity for each node. ::: Your node identity has now been generated. Your node ID can be found in the file `./bootstrap/public-root-information/node-id`. ```shell Example $cat ./bootstrap/public-root-information/node-id e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5 ``` :::info All your private keys should be in the `bootstrap` folder created earlier. Please take a back up of the entire folder. ::: ## Step 2 - Stake the Node You need to now register the node on chain by staking the node via [Flow Port](https://port.onflow.org/). [Here](../../flow-port/staking-guide.md) is a guide on how to use Flow port if you are not familiar with it. If you are staking via a custody provider or would like to directly submit a staking transaction instead follow this [guide](../../staking/index.md#how-do-i-stake). Fund you Flow account with at least 100.01 FLOW tokens, which covers the required stake plus the storage deposit. On Flow port, choose `Stake and Delegate` -> `Start Staking` or `Stake Again` and then choose Access node as the option. ![choose_access_flowport](../node-operation/choose_access_flowport.png) On the next screen, provide the node details of you node. Those node details (`Node ID`, `Network Address`, `Networking Key` and `Staking Key`) can be found in the file: `./bootstrap/public-root-information/node-info.pub..json`. ```shell Example $cat ./bootstrap/public-root-information/node-info.pub. e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5.json { "Role": "access", "Address": "flowaccess.mycompany.com:3569", "NodeID": "e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5", "Weight": 0, "NetworkPubKey": "f493a74704f6961ae7903e062ecd58d990672858eff99aece7bfbccf3aa02de8f1a624ecbf21a01e8b2f4a5854c231fbe218edd7762a34fea881f3958a215305", "StakingPubKey": "ae8dcf81f3a70d72036b7ba2c586ed37ed0eb82b9c0a4aab998a8420f98894f94c14f84fa716e93654d3940fc0c8ff4d19b504c90a5b4918b28f421e9d3659dc2b7e246025ebeffea0d83cceefe315d7ed346dbe412fdac51b64997d97d29f7e" } ``` #### Example ![node_details_permissionless_an](../node-operation/node_details_permissionless_an.png) On the next screen, ensure that you stake 100 FLOW token. #### Example ![transaction_register_node_permissionless_an](../node-operation/transaction_register_node_permissionless_an.png) Submit the Transaction. ## Step 3 - Verify That Your Node ID Was Selected On Wednesday at around 12:00 UTC, the staking auction for the current epoch will end and five nodes from candidate list of nodes will be chosen at random by the staking contract to be part of the next epoch. :::note If all 5 slots have been taken from the previous epoch, then no new access nodes will be chosen (see #limitations) ::: There are several ways to verify whether your node was chosen as explained below. When you stake the node, the tokens will show up under the `tokensCommitted` bucket. After the staking auction ends, if the node is selected, the tokens remain in the `tokensCommitted` bucket and are moved to the `tokensStaked` bucket at the end of the epoch. If the node is not selected, the tokens are moved to the `tokensUnstaked` bucket. ### Check Using Flow Port You can check these balances on Flow Port before and after the epoch transition that will occur on Wednesday (see [timing](#timing)). When you stake the node, you should see the following on Flow Port under `Stake & Delegate` ![Staked_node](../node-operation/Staked_FlowPort.png) After the epoch transition, if you see you token balance under the Staked Amount then your node got chosen. ![Staked_node](../node-operation/Selected_FlowPort.png) Instead, if you see that your token balance is under the Unstaked Amount, then your node did not get chosen. ![Unstaked_node](../node-operation/Unstaked_FlowPort.png) ### Check Using Flow CLI You can also check these balance using [Flow Cli](https://github.com/onflow/flow-cli). Once you have downloaded and installed Flow Cli, you can query the account balance using the command, ```shell flow accounts staking-info -n mainnet ``` For Example, the following node was chosen as Tokens staked is 100. ```shell Example $ flow accounts staking-info 0xefdfb20806315bfa -n testnet Account staking info: ID: "e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5" Initial Weight: 100 Networking Address: "flowaccess.mycompany.com:3569" Networking Key: "f493a74704f6961ae7903e062ecd58d990672858eff99aece7bfbccf3aa02de8f1a624ecbf21a01e8b2f4a5854c231fbe218edd7762a34fea881f3958a215305" Role: 5 Staking Key: "ae8dcf81f3a70d72036b7ba2c586ed37ed0eb82b9c0a4aab998a8420f98894f94c14f84fa716e93654d3940fc0c8ff4d19b504c90a5b4918b28f421e9d3659dc2b7e246025ebeffea0d83cceefe315d7ed346dbe412fdac51b64997d97d29f7e" Tokens Committed: 0.00000000 Tokens To Unstake: 100.00000000 Tokens Rewarded: 0.00000000 Tokens Staked: 100.00000000 Tokens Unstaked: 0.00000000 Tokens Unstaking: 0.00000000 Node Total Stake (including delegators): 0.00000000 ``` ### Epoch Setup Event Alternatively, if you can monitor events, look for [the epoch setup event](../../staking/05-epoch-scripts-events.md#flowepochepochsetup) that gets emitted by the epoch contract. That event is emitted at the end of epoch N's staking auction and contains a list of node IDs that are confirmed for the next epoch. ## Step 4 - Start Your Node If your node was selected as part of Step 3, you can now start your node. First you'll need to provision a machine or virtual machine to run your node software. Please see follow the [node-provisioning](../node-operation/node-provisioning.md) guide for it. The access node can be run as a Docker container with the following command. Be sure to set `$VERSION` below to the version tag (e.g. `v1.2.3`) corresponding to the latest **released** version [here](https://github.com/onflow/flow-go/releases) for version releases). Set `$NODEID` to your node's ID (see [Generate Your Node Identity](#generate-your-node-identity) section above). ```shell docker run --rm \ -v $PWD/bootstrap:/bootstrap:ro \ -v $PWD/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/access:$VERSION \ --nodeid=$NODEID \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --rpc-addr=0.0.0.0:9000 \ --http-addr=0.0.0.0:8000 \ --rest-addr=0.0.0.0:80 \ --rpc-metrics-enabled=true \ --bind 0.0.0.0:3569 \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --dynamic-startup-epoch-phase=EpochPhaseStaking \ --loglevel=error ``` ```shell docker run --rm \ -v $PWD/bootstrap:/bootstrap:ro \ -v $PWD/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/access:$VERSION \ --nodeid=$NODEID \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --rpc-addr=0.0.0.0:9000 \ --http-addr=0.0.0.0:8000 \ --rest-addr=0.0.0.0:80 \ --rpc-metrics-enabled=true \ --bind 0.0.0.0:3569 \ --dynamic-startup-access-address=secure.testnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d \ --dynamic-startup-epoch-phase=EpochPhaseStaking \ --loglevel=error ``` For example, if your Node ID is `e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5` and the software version is `v1.2.3`, the Docker command would be the following: ```shell Example docker run --rm \ -v $PWD/bootstrap:/bootstrap:ro \ -v $PWD/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/access:v1.2.3 \ --nodeid=e737ec6efbd26ef43bf676911cdc5a11ba15fc6562d05413e6589fccdd6c06d5 \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --rpc-addr=0.0.0.0:9000 \ --http-addr=0.0.0.0:8000 \ --rest-addr=0.0.0.0:80 \ --rpc-metrics-enabled=true \ --bind 0.0.0.0:3569 \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --dynamic-startup-epoch-phase=EpochPhaseStaking \ --loglevel=error ``` > If you would like your node to sync from the start of the last network upgrade, then please see the instructions [here](../node-operation/network-upgrade) Alternatively, you can build a binary for the access node to run it without using Docker. To build the access node binary, see the instructions [here](https://github.com/onflow/flow-go?tab=readme-ov-file#building-a-binary-for-the-access-node). Please make sure to git checkout the latest release tag before building the binary. ```shell $PWD/flow-go/flow_access_node \ --nodeid=e1a8b231156ab6f2a5c6f862c933baf5e5c2e7cf019b509c7c91f4ddb0a13398 \ --bootstrapdir=$PWD/bootstrap \ --datadir=$PWD/data/protocol \ --secretsdir=$PWD/data/secrets \ --execution-data-dir=$PWD/data/execution_data \ --rpc-addr=0.0.0.0:9000 \ --secure-rpc-addr=0.0.0.0:9001 \ --http-addr=0.0.0.0:8000 \ --rest-addr=0.0.0.0:8070 \ --admin-addr=localhost:9002 \ --bind=0.0.0.0:3569 \ --dht-enabled=false \ --grpc-compressor=gzip \ --profiler-dir=$PWD/data/profiler \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --dynamic-startup-epoch-phase=EpochPhaseStaking ``` ```shell $PWD/flow-go/flow_access_node \ --nodeid=e1a8b231156ab6f2a5c6f862c933baf5e5c2e7cf019b509c7c91f4ddb0a13398 \ --bootstrapdir=$PWD/bootstrap \ --datadir=$PWD/data/protocol \ --secretsdir=$PWD/data/secrets \ --execution-data-dir=$PWD/data/execution_data \ --rpc-addr=0.0.0.0:9000 \ --secure-rpc-addr=0.0.0.0:9001 \ --http-addr=0.0.0.0:8000 \ --rest-addr=0.0.0.0:8070 \ --admin-addr=localhost:9002 \ --bind=0.0.0.0:3569 \ --dht-enabled=false \ --grpc-compressor=gzip \ --profiler-dir=$PWD/data/profiler \ --dynamic-startup-access-address=secure.testnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d \ --dynamic-startup-epoch-phase=EpochPhaseStaking ``` For a more mature setup, it is recommended that you run the container using systemd as described [here](../node-operation/node-setup.md#systemd) > 🚀 The access node should now be up and running, and you should be able to query the node using Flow CLI or curl, ```shell Example flow blocks get latest --host localhost:9000 ``` ```shell Example curl http://localhost/v1/blocks?height=sealed ``` ## Monitoring and Metrics The node publishes several Prometheus metrics. See [Monitoring Node Health](../node-operation/monitoring-nodes.md) to setup node monitoring. ### Node Status The metrics for the node should be able to provide a good overview of the status of the node. If we want to get a quick snapshot of the status of the node, and if it's properly participating in the network, you can check the `consensus_compliance_finalized_height` or `consensus_compliance_sealed_height` metric, and ensure that it is not zero and strictly increasing. ```shell curl localhost:8080/metrics | grep consensus_compliance_sealed_height # HELP consensus_compliance_sealed_height the last sealed height # TYPE consensus_compliance_sealed_height gauge consensus_compliance_sealed_height 1.132054e+06 ``` ## FAQs ### Will the access node receive rewards? No, the access nodes do not receive any rewards. ### Why is there a 100 FLOW token minimum? As mentioned in the [FLIP](https://github.com/onflow/flips/blob/main/protocol/20220719-automated-slot-assignment.md), the minimum is required to prevent certain vulnerabilities in the smart contract that are a result of having a zero minimum stake requirement. ### Can the Access node be unstaked? Yes, like any other staked node, the Access node can be unstaked. The staked tokens will be moved to the unstaked bucket in the subsequent epoch. ### How to see all the access nodes that have staked? When the nodes are initially staked, they are all added to the candidate list of nodes before the end of the epoch staking phase. The list can be retrieved from the chain by executing the [get_candidate_nodes](https://github.com/onflow/flow-core-contracts/blob/48ba17d3386023d70817197a20effbc5d16339b3/transactions/idTableStaking/scripts/get_candidate_nodes.cdc) script which returns the candidate list for the current epoch. ```shell $ flow scripts execute ./transactions/idTableStaking/scripts/get_candidate_nodes.cdc -n mainnet ``` ### How to check the availability of open access nodes slots for the next epoch? The limits for the open slots are defined in the staking contract and can be queried from the chain by executing the [get_slot_limits](https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_slot_limits.cdc) script. Node types are defined [here](https://github.com/onflow/flow-core-contracts/blob/5696ec5e3e6aa5fc10762cbfeb42b9c5c0b8ddbe/contracts/FlowIDTableStaking.cdc#L114-L119) ```shell $ flow scripts execute ./transactions/idTableStaking/scripts/get_slot_limits.cdc --args-json '[{ "type":"UInt8", "value":"5"}]' -n mainnet Result: 118 ``` Example: there are 115 access nodes already part of the network. Hence, the total number of new nodes that can join are 118 - 115 = 3. --- # File: /md/protocol/node-ops/evm-gateway/evm-gateway-setup.md --- title: Setting up an EVM Gateway node sidebar_label: EVM Gateway Setup sidebar_position: 2 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; This guide is for running the [EVM Gateway](https://github.com/onflow/flow-evm-gateway) node on Flow. The EVM Gateway implements the [Ethereum JSON-RPC specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) and is the only node type which accepts EVM client connections. The EVM Gateway consumes Flow protocol state from the configured Flow Access Node and persists the indexed EVM state locally to service EVM client requests. It submits EVM transactions it receives into the Flow network, wrapped in a Cadence transaction, and mutating EVM state when executed. Non-mutating RPC methods only query the local state index of the gateway and are never forwarded to Access Nodes. It does not participate in the block production process and requires no stake. ## Anyone can run EVM Gateway The EVM Gateway can serve as a dedicated private RPC, a performance scaling solution, and a free gas provider offering similar capabilities to centralized middleware providers like Infura, Alchemy, etc. at a fraction of the cost. EVM Gateway nodes connect directly to the Flow network with no middleware, giving you full control. If you are just getting started building your application, you can use the [public EVM Gateway](https://developers.flow.com/evm/networks). Applications generating high call volumes to the JSON-RPC may have hit rate limits on Flow public EVM Gateway and may benefit from running their own gateway to remove rate limits. Self-hosted gateways connect directly to public Flow Access Nodes, which may also optionally be [run](../access-nodes/access-node-setup.md). :::info Apps can use EVM gateway to subsidize user transaction fees for smoother onboarding ::: Alternatively, you can also choose from any of the following providers who provide the EVM Gateway as a managed service along with other value added services on top. 1. [Alchemy](https://www.alchemy.com/flow) 2. [ThirdWeb](https://thirdweb.com/flow) 3. [Moralis](https://docs.moralis.com/web3-data-api/evm/chains/flow) 4. [QuickNode](https://www.quicknode.com/chains/flow) ## Gas Sponsorship Custom EVM Gateways can be set up to sponsor all gas for all EVM transactions passed through them. See the tutorial for [Gas Free EVM Endpoint](../../../blockchain-development-tutorials/gasless-transactions/sponsored-transactions-evm-endpoint.md) for more details. ## Hardware specifications The EVM Gateway is a lightweight node which runs on commodity hardware and cloud VMs. It can be run on GCP **standard** and AWS **large** VM types for low to moderate volume app co-location use-cases. However, higher volume use cases may require larger instance types and more testing. An inactive node requires less than 200MB memory when run in Docker and data storage growth corresponds with Flow EVM transaction growth. Listed below are theoretical RPS maximums based on Flow mainnet CPU and memory resource utilization metrics and linear scaling assumptions. ### Google Cloud Platform (GCP) VM Types | VM Type | vCPUs | Memory (GB) | Estimated Max Requests/s | | -------------- | ----- | ----------- | ------------------------ | | n2-standard-2 | 2 | 8 | ~2,950 | | c4a-standard-1 | 1 | 4 | ~1,475 | | c4a-standard-2 | 2 | 8 | ~2,950 | | n2-highmem-4 | 4 | 32 | ~11,800 | | c3-standard-8 | 8 | 32 | ~29,500 | ### Amazon Web Services (AWS) EC2 Instance Types | Instance Type | vCPUs | Memory (GB) | Estimated Max Requests/s | | ------------- | ----- | ----------- | ------------------------ | | m6i.large | 2 | 8 | ~2,950 | | c6i.large | 2 | 4 | ~3,687 | | m6i.xlarge | 4 | 16 | ~11,800 | | c6i.2xlarge | 8 | 16 | ~29,500 | | t3.2xlarge | 8 | 32 | ~17,700 | # How To Run EVM Gateway ## Step 1 - Account Creation The EVM Gateway's role in mediating EVM transactions over to Cadence is how it accrues fees from handling client transactions. Since the gateway submits Cadence transactions wrapping EVM transaction payloads to the Flow Access Node the transaction fee for that must be paid by the EVM Gateway. The account used for funding gateway Cadence transactions must be a COA, not an EOA. `--coa-address` is configured with the Cadence address of the COA account and the `--coa-key` must belong to the same account. The `--coinbase` account accrues EVM Gateway fees from EVM client transactions and can be either an EVM EOA or COA address. It is acceptable to create a single Cadence account for the COA and use the EVM address associated with that for the COINBASE address. ### Create Flow account to use for COA If you don't already have a Flow account you will need to create one. 1. Install [Flow Wallet](https://wallet.flow.com/) 2. Once installed you will be able to copy the wallet address, similar to _0x1844efeb3fef2242_ 3. Obtain account private key from
    Settings -> Account List -> Choose Main account -> Private Key -> [Password prompt]
    4. Ensure the wallet is funded from a CEX or other wallet
    Install [Flow CLI](https://developers.flow.com/tools/flow-cli/install) if not already installed. ```bash flow keys generate ``` This will output something similar to: ` ```bash 🔴️ Store private key safely and don't share with anyone! Private Key 3cf8334d.....95c3c54a28e4ad1 Public Key 33a13ade6....85f1b49a197747 Mnemonic often scare peanut ... boil corn change Derivation Path m/44'/539'/0'/0/0 Signature Algorithm ECDSA_P256 ``` Visit https://faucet.flow.com/, and use the generated `Public Key`, to create and fund your Flow testnet account.
    ## Step 2 - Build the gateway To run EVM Gateway on bare metal or in a VM without the use of docker, select the '_Build from source_' tab otherwise refer to the '_Build using Docker_' tab. This will build the EVM gateway binary from source. ```bash git clone https://github.com/onflow/flow-evm-gateway.git cd flow-evm-gateway git checkout $(curl -s https://api.github.com/repos/onflow/flow-evm-gateway/releases/latest | jq -r .tag_name) CGO_ENABLED=1 go build -o evm-gateway cmd/main/main.go chmod a+x evm-gateway mv evm-gateway /usr/bin ``` ```bash git clone https://github.com/onflow/flow-evm-gateway.git cd flow-evm-gateway git checkout $(curl -s https://api.github.com/repos/onflow/flow-evm-gateway/releases/latest | jq -r .tag_name) make docker-build ``` Registry versions available for download can be found [here](https://console.cloud.google.com/artifacts/docker/dl-flow-devex-production/us-west1/development/flow-evm-gateway). ```bash docker pull us-west1-docker.pkg.dev/dl-flow-devex-production/development/flow-evm-gateway:${VERSION} ``` ## Step 3 - Start Your Node Operators will need to refer to the gateway [configuration flags](https://github.com/onflow/flow-evm-gateway?tab=readme-ov-file#configuration-flags) and make adjustments that align with the desired deployment topology. ### EVM Coinbase address If this is your first time setting up the gateway we need to ensure that an EVM COA or EOA address is available to configure the `COINBASE`. This account can be an account created using Metamask or other web3.js wallet, or otherwise can be the EVM address corresponding to the Flow Wallet COA account created above. If you haven't already got an EVM address and you have the COA account created by Flow Wallet above then follow the steps below: - Click top left burger icon to show current profile - Click 'Enable the path to Flow EVM' button - Your EVM account will now be available to use in the left nav account view - When you switch to that account you can obtain its EVM address ### COA Address and Key COA address and private key is configured for `--coa-address` & `--coa-key` configuration flags. If running multiple EVM Gateway hosts it is standard to share the same COA address and key across _n_ hosts. ### Run the gateway Ensure that the following ENV variables have been set. Add/update as required if your configuration differs from those listed. ```bash # Set required environment variables export ACCESS_NODE_GRPC_HOST="access.mainnet.nodes.onflow.org:9000" # or access.devnet.nodes.onflow.org:9000 for testnet export FLOW_NETWORK_ID="flow-mainnet" # or flow-testnet export INIT_CADENCE_HEIGHT="85981135" # 211176670 for testnet export COINBASE="${EVM_ADDRESS_WITHOUT_0x}" export COA_ADDRESS="${CADENCE_ACCOUNT_ADDRESS_WITHOUT_0x}" export COA_KEY="${CADENCE_ACCOUNT_PRIVATE_KEY_WITHOUT_0x}" export GAS_PRICE="100" # operators can set this to 0 for zero cost transactions. The linked COA account will pay for transactions on users behalf # $\{ACCESS_NODE_SPORK_HOSTS\} are comma separated # testnet: access-001.devnet51.nodes.onflow.org:9000 # mainnet: access-001.mainnet25.nodes.onflow.org:9000 ``` ACCESS_NODE_SPORK_HOSTS is used by the gateway to track state across Flow sporks. These are generally infrequent with only one planned spork per year. A canonical list of required hosts can be found in the EVM Gateway [Makefile](https://github.com/onflow/flow-evm-gateway/blob/main/Makefile#L9). **Create EVM Gateway service** ```bash sudo tee </dev/null /etc/systemd/system/gateway.service [Unit] Description=Gateway daemon After=network-online.target [Service] User=$USER ExecStart=/usr/bin/evm-gateway \ --access-node-grpc-host=$ACCESS_NODE_GRPC_HOST \ --access-node-spork-hosts=$ACCESS_NODE_SPORK_HOSTS \ --flow-network-id=$FLOW_NETWORK_ID \ --init-cadence-height=$INIT_CADENCE_HEIGHT \ --ws-enabled=true \ --coinbase=$COINBASE \ --coa-address=$COA_ADDRESS \ --coa-key=$COA_KEY \ --rate-limit=9999999 \ --rpc-host=0.0.0.0 \ --gas-price=$GAS_PRICE \ --tx-state-validation=local-index Restart=always RestartSec=3 LimitNOFILE=4096 [Install] WantedBy=multi-user.target EOF cat /etc/systemd/system/gateway.service sudo systemctl enable gateway ``` **Start all services** ```bash sudo systemctl daemon-reload sudo systemctl restart access-node sudo systemctl restart gateway ``` **Check logs** ```bash # change log settings to persistent if not already sed -i 's/#Storage=auto/Storage=persistent/g' /etc/systemd/journald.conf sudo systemctl restart systemd-journald journalctl -u gateway.service -f -n 100 ``` It may be necessary to make local changes to the `docker-run` target to add params which are needed for your requirements. If you pulled a specific image from the gateway container registry ensure that the `$VERSION` environment variable is set to the same as the image version you pulled. ```bash cd flow-evm-gateway make docker-run ``` Additional options are available as follows ```bash DOCKER_RUN_DETACHED=true DOCKER_HOST_MOUNT=[host mount directory] DOCKER_HOST_PORT=[desired port to expose on host] DOCKER_HOST_METRICS_PORT=[desired port to expose on host for metrics] # Example usage make DOCKER_RUN_DETACHED=true DOCKER_HOST_PORT=1234 DOCKER_HOST_MOUNT=/my/host/dir docker-run ``` ### Startup bootstrap indexing Once your EVM Gateway is up and running you will see it indexing the Flow network which was configured. At the present time this is a lengthy process (possibly 1-3 days, depending on CPU core count) during which time the gateway will not respond to queries. Once the data is fully indexed the gateway can serve requests to clients. To speed up gateway setup we recommend backing up the `/${GATEWAY_HOME_DIR}/data` directory to use when creating additional nodes using the same release version. We are currently working on an export/import feature that will enable gateway operators to store state snapshots to bootstrap newly created nodes without the delay. :::note If you are upgrading the gateway from pre-v1.0.0 release versions the indexed data directory will need to be reindexed from genesis. You will not be able to re-use the DB data dir from the previous versions. ::: ### Account and Key Management When operating an EVM Gateway it is important to understand how keys are configured and used. Each gateway instance must be configured with a Flow account address, which is sufficiently funded and which it uses to pay to wrap EVM transactions into a Cadence transaction when submitting to the Flow Access Node. This can be configured with a standalone private key file for the `--coa-key` config flag. Alternatively, you may also use cloud KMS providers per the guidance below. #### COA Account signing key rotation The gateway implements a signing key rotation scheme to scale the use of the `COA_KEY` and enable it to be used across many EVM client transactions. This is configured by reusing the COA public key to create new onchain signing keys. Although it may seem counter-intuitive to use the same COA public key when creating new signing keys, the newly added keys all occupy different key slots on the account which enables the gateway to support concurrent transaction signing without causing [nonce collisions](https://developers.flow.com/build/cadence/advanced-concepts/scaling#problem) for EVM clients. Assuming there is already one key on the account, the following example transaction adds 100 copies of that key: ```swift transaction { prepare(signer: auth(AddKey) &Account) { let firstKey = signer.keys.get(keyIndex: 0)! let range: InclusiveRange = InclusiveRange(1, 100, step: 1) for element in range { signer.keys.add( publicKey: firstKey.publicKey, hashAlgorithm: HashAlgorithm.SHA2_256, weight: 1000.0 ) } } } ``` Signing keys which are added to the COA account are required to use the same hashing algorithm as the key being copied. If keys are added while the gateway is running it will need to be restarted to make use of the new keys. :::note If you are operating your EVM Gateway(s) to relay traffic for Flow EVM, or if you otherwise anticipate high volumes of transactions we recommend configuring 2000 signing keys or more. Signing key utilization increases proportionately with transaction throughput growth. A large number of keys are recommended for live networks because keys have a lengthy cool down period of 600 blocks (approx 10 minutes) before they are re-used. This is to avoid nonce collisions from re-using the key too soon. ::: You can track signing key utilization as a metric, see `evm_gateway_available_signing_keys` below. #### KMS Configuration EVM Gateway allows for Google and AWS Key Management Service (KMS) integration, which is the recommended way of setting up the gateway for live networks. It is only required to configure a single KMS key for the Flow account configured as the gateway `COA_ACCOUNT`. ``` --coa-cloud-kms-project-id=your-project-kms-id \ --coa-cloud-kms-location-id=global \ --coa-cloud-kms-key-ring-id=your-project-kms-key-ring-id \ --coa-cloud-kms-key=example-gcp-kms@1 \ ``` ### Monitoring and Metrics The EVM Gateway reports Prometheus metrics which are a way to monitor the gateway's availability and progress. The database folder size may also need to be monitored to prevent disk full issues. **Metric labels** ```bash evm_gateway_api_errors_total # Total count of API errors for period evm_gateway_api_request_duration_seconds_bucket # Histogram metric buckets for API request durations evm_gateway_api_request_duration_seconds_count # Histogram metric API request count for period evm_gateway_api_request_duration_seconds_sum # Histogram metric API request sum of values for period evm_gateway_api_server_panics_total # Total count of server panics for period evm_gateway_blocks_indexed_total # Total count of EVM blocks indexed evm_gateway_cadence_block_height # Cadence block height evm_gateway_evm_account_interactions_total # Count of unique accounts observed for period evm_gateway_evm_block_height # EVM block height evm_gateway_operator_balance # Gateway node COA operator account balance evm_gateway_trace_download_errors_total # Total count of trace download errors evm_gateway_txs_indexed_total # Total count of indexed transactions evm_gateway_available_signing_keys # Total count of available COA signing keys ``` Alerts are recommended to be configured on server panics, low operator balance, available signing keys and disk usage metrics. **Metrics port** ``` --metrics-port 8080 \ ``` ### Node Status For basic node status or keepalive monitoring we recommend automated checks on the following monotonically increasing counter: ``` curl -s -XPOST 'your-evm-gw-host:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jq -r '.result' | xargs printf "%d\n" 10020239 ``` ## Troubleshooting Join our [Discord](https://discord.com/invite/J6fFnh2xx6) and use the `#flow-evm` channel to ask any questions you may have about EVM Gateway. ### No signing keys available ```bash Failed to send transaction: no signing keys available ``` This message indicates that the GW has used all its available signing keys. Please refer to the [Account and Key Management](./evm-gateway-setup#account-and-key-management) documentation to add more signing keys to your COA. ### Database version inconsistency/corruption If you see a similar message to this from an aborted startup the gateway database directory is not compatible with the schema versions of the runtime, or there may be corruption. In this instance we recommend that you delete the contents of the EVM GW data directory. ```bash Jan 16 17:00:57 nodename docker[6552]: {"level":"error","error":"failed to open db for dir: /flow-evm-gateway/db, with: pebble: manifest file \"MANIFEST-018340\" for DB \"/flow-evm-gateway/db\": comparer name from file \"leveldb.BytewiseComparator\" != comparer name from Options \"flow.MVCCComparer\"","time":"2025-01-16T17:00:57Z","message":"Gateway runtime error"} ``` ### State stream configuration If you are running an Access Node on the same logical host as the EVM Gateway you may see the following log entries. ```bash failure in event subscription at height ${INIT-CADENCE-HEIGHT}, with: recoverable: disconnected: error receiving event: rpc error: code = Unimplemented desc = unknown service flow.executiondata.ExecutionDataAPI” ``` ```bash component execution data indexer initialization failed: could not verify checkpoint file: could not find expected root hash e6d4f4c755666c21d7456441b4d33d3521e5e030b3eae391295577e9130fd715 in checkpoint file which contains: [e10d3c53608a1f195b7969fbc06763285281f64595be491630a1e1bdfbe69161] ``` To resolve this configure `--state-stream-addr` to use the same address/port combination which is set for Access Node `--rpc-addr`. This is required by the gateway to allow both the streaming and non-streaming APIs to query using the same connection. ### Access Node not fully synced The following log entry will occur when the EVM Gateway attempts to sync with the Access Node but it has not yet synced up to latest block ```bash failure in event subscription at height ${INIT-CADENCE-HEIGHT}, with: recoverable: disconnected: error receiving event: rpc error: code = FailedPrecondition desc = could not get start height: failed to get lowest indexed height: index not initialized ``` --- # File: /md/protocol/node-ops/light-nodes/observer-node.md --- title: Light Node a.k.a Observer Node sidebar_label: Light Node Setup sidebar_position: 1 --- A light node also known as the observer node is similar to an access node and provides a locally accessible, continuously updated, verified copy of the block data. It serves the [gRPC Access API](../../access-onchain-data/index.md) but unlike an access node, an light node does not need to be staked, and **anyone** can run it without being added to the approved list of nodes. The light node bootstraps by connecting to an access node and becoming part of the public network comprised of access nodes and other light nodes. It then continuously receives blocks, which the consensus nodes are adding to the chain, either directly from access nodes or from other light nodes that are part of the public network. However, it makes no trust assumption of the upstream access node or the light node which is providing the block and locally verifies that the blocks that are received are the correct extension of the chain e.g. after receiving valid blocks A, B and C when it receives block D, it verifies that block D is indeed signed by the consensus nodes and is a valid next block. The received block data is indexed and made available via the Access API. For Collection, Transactions and Account queries, it delegates those requests to the upstream access node. Similarly, transactions and scripts sent to a light node are also forwarded to the upstream access node. Future versions of the light node will be able to serve this data locally as well. Since the light node is not staked, it does not produce or execute blocks but instead serves as an _unstaked access node_ that can be easily run on any consumer-grade computer which has enough disk space. ![Observer nodes](../node-operation/observer.png) ## Who should run a light node? The light node provides an alternative to running an access node. Hence, it is ideal for Dapps that need access to the latest block data locally on a machine they control. Examples include a wallet application that needs to track the latest block ID and height. Alternatively, access node operators that want to scale their access node endpoints geographically can spin up light nodes in different regions, which can talk to their staked access node and to each other. ## Running an light node ### Hardware In general, any consumer-grade computer with a decent network connection and sufficient disk space should be able to run a light node. Minimum requirements - CPU with 2+ cores - 4 GB RAM minimum - 300 GB SSD disk - 10Mbps network connection ### Steps to run a light node > [Here](https://www.loom.com/share/990a725531754106b91d8ccec6244219) is video walk-though of these 👇 steps. #### Step 1 - Generate the node directory structure The light node requires the following directory structure, ```shell $ tree flow_observer flow_observer/ ├── bootstrap │ ├── network.key (file containing the node private network key) │ └── public-root-information │ └── root-protocol-state-snapshot.json (the genesis data of the current spork) └── data (directory used by the light node to store block data) ``` Create the parent and the sub-directories e.g. ```shell mkdir -p flow_observer/bootstrap/public-root-information mkdir flow_observer/data ``` #### Step 2 - Generate the network key Like any other Flow node, the light node also needs a networking [ECDSA key](https://github.com/onflow/flow-go/blob/master/cmd/bootstrap/utils/key_generation.go#L52-L54) to talk to the network. Download the Bootstrapping kit, and generate the networking key. ```shell curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools.tar tar -xvf boot-tools.tar ./boot-tools/bootstrap observer-network-key --output-file ./flow_observer/bootstrap/network.key ``` _If you are running on a mac, download the boot-tools for mac to generate the key_ ```shell # For M1 curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools-m1.tar # For Intel Mac curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools-intel-mac.tar ``` #### Step 3 - Download the root-protocol-state-snapshot.json file for the current spork The `root-protocol-state-snapshot.json` is generated for each [spork](../node-operation/network-upgrade) and contains the genesis data for that spork. It is published and made available after each spork. The download location is specified [here](https://github.com/onflow/flow/blob/master/sporks.json) under [rootProtocolStateSnapshot](https://github.com/onflow/flow/blob/master/sporks.json#L16) and can be downloaded as follows, For mainnet find the latest spork version from [sporks.json](https://github.com/onflow/flow/blob/master/sporks.json) and then download the `root-protocol-state-snapshot.json` and the signature file for it. ```shell wget -P ./flow_observer/bootstrap/public-root-information https://storage.googleapis.com/flow-genesis-bootstrap/mainnet--execution/public-root-information/root-protocol-state-snapshot.json wget -P ./flow_observer/bootstrap/public-root-information https://storage.googleapis.com/flow-genesis-bootstrap/mainnet--execution/public-root-information/root-protocol-state-snapshot.json.asc ``` Similarly, for testnet find the latest spork version from [sporks.json](https://github.com/onflow/flow/blob/master/sporks.json) and then download the `root-protocol-state-snapshot.json` and the signature file for it. ```shell wget -P ./flow_observer/bootstrap/public-root-information https://storage.googleapis.com/flow-genesis-bootstrap/testnet-/public-root-information/root-protocol-state-snapshot.json wget -P ./flow_observer/bootstrap/public-root-information https://storage.googleapis.com/flow-genesis-bootstrap/testnet-/public-root-information/root-protocol-state-snapshot.json.asc ``` ##### Verify the PGP signature Add the `flow-signer@onflow.org` public key ```shell gpg --keyserver keys.openpgp.org --search-keys flow-signer@onflow.org gpg: data source: http://keys.openpgp.org:11371 (1) Flow Team (Flow Full Observer node snapshot verification master key) < 256 bit ECDSA key CB5264F7FD4CDD27, created: 2021-09-15 Keys 1-1 of 1 for "flow-signer@onflow.org". Enter number(s), N)ext, or Q)uit > 1 ``` Verify the root-snapshot file ```shell gpg --verify ./flow_observer/bootstrap/public-root-information/root-protocol-state-snapshot.json.asc gpg: assuming signed data in 'bootstrap/public-root-information/root-protocol-state-snapshot.json' gpg: Signature made Wed Sep 15 11:34:33 2021 PDT gpg: using ECDSA key 40CD95717AC463E61EE3B285B718CA310EDB542F gpg: Good signature from "Flow Team (Flow Full Observer node snapshot verification master key) " [unknown] gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 7D23 8D1A E6D3 2A71 8ECD 8611 CB52 64F7 FD4C DD27 Subkey fingerprint: 40CD 9571 7AC4 63E6 1EE3 B285 B718 CA31 0EDB 542F ``` Alternately, if you don't care about the blocks before the current block, you can request the current root-snapshot file via the [Flow CLI](../../../build/tools/flow-cli/index.md). For mainnet ```shell flow snapshot save ./flow_observer/bootstrap/public-root-information/root-protocol-state-snapshot.json --host secure.mainnet.nodes.onflow.org:9001 --network-key 28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae ``` For testnet ```shell flow snapshot save ./flow_observer/bootstrap/public-root-information/root-protocol-state-snapshot.json --host secure.testnet.nodes.onflow.org:9001 --network-key ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d ``` #### Step 4 - Start the node The light node can be run as a docker container ##### Observer for Flow Mainnet ```shell docker run --rm \ -v $PWD/flow_observer/bootstrap:/bootstrap:ro \ -v $PWD/flow_observer/data:/data:rw \ --name flow_observer \ -p 80:80 \ -p 3569:3569 \ -p 9000:9000 \ -p 9001:9001 \ gcr.io/flow-container-registry/observer:v0.27.2 \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --bind 0.0.0.0:3569 \ --rest-addr=:80 \ --loglevel=error \ --secretsdir=/data/secrets \ --upstream-node-addresses=secure.mainnet.nodes.onflow.org:9001 \ --upstream-node-public-keys=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --bootstrap-node-addresses=secure.mainnet.nodes.onflow.org:3570 \ --bootstrap-node-public-keys=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --observer-networking-key-path=/bootstrap/network.key ``` ##### Observer for Flow Testnet ```shell docker run --rm \ -v $PWD/flow_observer/bootstrap:/bootstrap:ro \ -v $PWD/flow_observer/data:/data:rw \ --name flow_observer \ -p 80:80 \ -p 3569:3569 \ -p 9000:9000 \ -p 9001:9001 \ gcr.io/flow-container-registry/observer:v0.27.2 \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --bind 0.0.0.0:3569 \ --rest-addr=:80 \ --loglevel=error \ --secretsdir=/data/secrets \ --upstream-node-addresses=secure.devnet.nodes.onflow.org:9001 \ --upstream-node-public-keys=ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d \ --bootstrap-node-addresses=secure.devnet.nodes.onflow.org:3570 \ --bootstrap-node-public-keys=ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d \ --observer-networking-key-path=/bootstrap/network.key ``` The light node acts as a DHT client and bootstraps from upstream access nodes which run the DHT server. The upstream bootstrap server is specified using the `bootstrap-node-addresses` which is the comma-separated list of hostnames of the access nodes. The `bootstrap-node-public-keys` is the list of the corresponding networking public key of those nodes. The light node delegates many of the API calls to the upstream access nodes. The `upstream-node-addresses` is the list of access node hostnames to which this light node can delegate to. The list can be different from the bootstrap node list. The `bootstrap-node-public-key` is the list of the corresponding networking public key of those nodes. > In the above docker commands, the Flow community access nodes are being used as the upstream access nodes. However, any other Flow access node that supports a light node can be used _All parameters and their explanation can be found [here](https://github.com/onflow/flow-go/blob/82da35141ff095fbf75ce2c950efec240ad38565/cmd/access/node_builder/access_node_builder.go#L523-L558)_ 🚀 **The node should now be up and running** You can now query the node for blocks, transaction etc. similar to how you would query an access node. e.g. querying the gRPC API endpoint using Flow CLI ```shell flow blocks get latest --host localhost:9000 ``` e.g. querying the REST API endpoint using curl ```shell curl "http://localhost/v1/blocks?height=sealed" ``` The light node, like the other type of Flow nodes, also produces Prometheus metrics that can be used to monitor node health. More on that [here](../node-operation/node-setup.md#monitoring-and-metrics) ## FAQs ### Does the light node need to be staked? No, the light node is not a staked node. ### Can any access node be used to bootstrap a light node? No, only Access nodes which have explicitly turned ON support for light nodes can be used to bootstrap a light node. The public access nodes that support light nodes are listed below. Apart from these, other public access nodes run by node operators other than the Flow foundation team may choose to support light nodes. ### How can an access node turn ON support for light node? An access node can support a light node by passing in the following two parameters when starting the access node ```shell --supports-observer=true --public-network-address=0.0.0.0:3570 ``` `public-network-address` is the address the light nodes will connect to. ### Are light nodes subject to rate limits? The light node serves all the [Block related queries](https://github.com/onflow/flow/blob/master/protobuf/flow/access/access.proto#L24-L42) from is local database. These are not subjected to any rate limits. However, it proxies all the other requests to the access node and those will be rate limited as per the rate limits defined on that access node. ### Flow community access nodes that support connections from light nodes #### For mainnet Access-007: - Host: `access-007.[current mainnet spork].nodes.onflow.org` - Public Key: `28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae` Access-008: - Host: `access-008.[current mainnet spork].nodes.onflow.org` - Public Key: `11742552d21ac93da37ccda09661792977e2ca548a3b26d05f22a51ae1d99b9b75c8a9b3b40b38206b38951e98e4d145f0010f8942fd82ddf0fb1d670202264a` #### For testnet Access-001: - Host: `access-001.[current devnet spork].nodes.onflow.org` - Public Key: `ba69f7d2e82b9edf25b103c195cd371cf0cc047ef8884a9bbe331e62982d46daeebf836f7445a2ac16741013b192959d8ad26998aff12f2adc67a99e1eb2988d` While the public keys remain the same, the hostnames change each spork to include the spork name. Substitute `[current mainnet spork]` and `[current devnet spork]` with the appropriate spork name (e.g. `mainnet20`). See [Past Sporks](../node-operation/past-upgrades) for the current spork for each network. --- # File: /md/protocol/node-ops/node-operation/byzantine-node-attack-response.md --- title: Byzantine Node Attack Response sidebar_label: Byzantine Attack Response description: How to respond to a byzantine node attack on the network sidebar_position: 3 --- Flow, like most blockchains, forms an open decentralized peer-to-peer network between all of the nodes on the network. Due to its decentralized nature, there is a potential for nodes to behave maliciously (byzantine) and intentionally try to harm the network. There are a variety of protections within the node software to deal with invalid messages - message signatures, sender authorization, payload validation, etc. These protections guard the network against many types of attacks. However, there could still be a byzantine node that spams other nodes in the network with invalid messages at volumes that are intended to impact node performance. While this will not compromise the security of the network it could impact network liveness. This guide explains how to detect such a node and what actions you should take as a node operator to deal with such byzantine nodes. Responding to an attack from a byzantine node requires the following: 1. Immediate action to block network traffic originating from the byzantine node to your node. 2. Raising a governance FLIP to remove the node from the network as described in this [FLIP](https://github.com/onflow/flips/blob/main/governance/20230105-identify-errant-node.md). 3. A service account transaction to set the node weight to 0. This guide focuses on the first action. ## Admin Server Flow nodes have an admin server which exposes a simple REST API for interacting with the node. See the [README](https://github.com/onflow/flow-go/blob/master/admin/README.md) for some useful examples. It is disabled by default. ### Enable the Admin Server To enable to admin server, 1. Add the following option to the node's CLI flags. ``` --admin-addr=localhost:9002 ``` > Note: The port does not have to be 9002. You can choose any free port. > ⚠️ Do NOT expose the port outside the machine and always use localhost:port 2. Reboot the node to apply the new setting. You can then verify it’s working by logging into the machine via ssh and running, ``` curl localhost:9002 ``` This should return a json response message as below. ``` {"code":5,"message":"Not Found","details":[]} ``` If you instead get a connection rejected message then it’s not configured correctly. ## Detecting a Byzantine Node There are 2 general categories of byzantine attacks: 1. Safety attacks - are attacks where a node attempts to corrupt or modify the state of the blockchain outside of normal protocol rules. 2. Liveness attacks - sometimes called spamming attacks, are when a node attempts to disrupt the network by abusing their access to waste network and node resources. This generally results in degraded performance. Flow nodes are protected against safety attacks, but liveness attacks are extremely difficult to completely prevent. To close the gap, we rely on coordination between node operators to detect and block abusive nodes. ### Metrics Flow nodes generate a variety of metrics that can be used to measure the node's performance and identify abnormal behavior. Most metrics are only useful in the context of "normal" operation, so it is a good idea to regularly review them to build an understanding of what is "normal". Metrics to watch: * CPU, memory, network connections, network I/O, file descriptors * `network_authorization_*` - counts the number of unauthorized/invalid messages received * `network_queue_message_queue_size` - measures the number of incoming messages waiting to be processed * `network_engine_messages_received_total` - measures the number of messages received from the network There are many other metrics, but these are a good starting point. If you notice any anomalous trends, review the logs for additional context. ### Logs Log events related to suspicious activity are logged with the label `"suspicious":true`. This is helpful to identify the most relevant logs, but there are legitimate cases when these logs are emitted, so they cannot be used as a definitive indicator of malicious activity. Two examples of expected log messages are: * `rejected inbound connection` - You may see this error if an operator unstaked their node between sporks, but never shut it down. The node will continue to operate as usual, but peers will not have it in their identity table and will (correctly) reject incoming connections. * `middleware does not have subscription for the channel ID indicated in the unicast message received` - This is commonly logged during node startup when receiving messages before all of the components have finished registering their channels with the network layer. It is NOT expected after startup. The following is an example of a log message indicating an Access node attempted to send a message it is not authorized to send: ``` { "level": "error", "node_role": "collection", "node_id": "4a6f7264616e20536368616c6d00a875801849f2b5bea9e9d2c9603f00e5d533", "module": "network_slashing_consumer", "peer_id": "QmY2kby3xt3ugu2QqJP5w24rP4HSakYgDFpAJy1ifSRkF7", "networking_offense": "unauthorized_sender", "message_type": "messages.BlockProposal", "channel": "sync-committee", "protocol": "publish", "suspicious": true, "role": "access", "sender_id": "f9237c896507b8d654165c36b61c9a3080e6dd042dea562a4a494fbd73133634", "time": "2023-01-24T21:10:32.74684667Z", "message": "potential slashable offense: sender role not authorized to send message on channel" } ``` ### Identifying the Source of Malicious Traffic Most log messages include either the node ID or peer ID. Peer ID is the ID used to identify nodes on by the libp2p library. Peer IDs are derived from the node's networking public key, so there is a 1:1 mapping between node ID and peer ID. The two simplest ways to match a node ID to a peer ID: 1. `inbound connection established` and `outbound connection established` log messages contain both the node and peer IDs 2. The following admin command will return the node info for a given peer ID: ``` curl localhost:9002/admin/run_command \ -H 'Content-Type: application/json' \ -d '{"commandName": "get-latest-identity", "data": { "peer_id": "QmY2kby3xt3ugu2QqJP5w24rP4HSakYgDFpAJy1ifSRkF7" }}' ``` If you cannot find any log messages at the current log level, you may need to enable debug logging. See the admin server's [README](https://github.com/onflow/flow-go/blob/master/admin/README.md) for an example. ## Reporting the Byzantine Node Report the suspicious node on Discord in the `#flow-validators-alerts` channel along with all the evidence you have collected (log messages, other networking related metrics, etc). This will alert other node operators who can review their nodes to corroborate the report. Using evidence from multiple operators, a consensus can be reached about the suspicious node, and appropriate action can be taken. ## Blocking a Byzantine Node Once a consensus is reached about the suspicious node on Discord among the node operators, the suspicious node can be blocked using the admin command. ``` curl localhost: 9002/admin/run_command \ -H 'Content-Type: application/json' \ -d '{"commandName": "set-config","data": {"network-id-provider-blocklist": [""]}} ``` After blocking the node, all traffic coming from the node will be rejected and you should only see logs about reject messages and connections for that node ID. ## Unblocking a Node If you need to unblock a node, you can use the same command to remove the node ID from the blocklist. Simply run it again with an empty list to remove all blocked nodes, or an existing list with the specific node ID you want to unblock removed. The following command returns a list of the currently blocked nodes. ``` curl localhost: 9002/admin/run_command \ -H 'Content-Type: application/json' \ -d '{"commandName": "get-config", "data": "network-id-provider-blocklist"} ``` After unblocking the node, connections and traffic coming from the node should resume. --- # File: /md/protocol/node-ops/node-operation/db-encryption-existing-operator.md --- title: Database Encryption for Existing Node Operators sidebar_label: Database Encryption for Existing Node Operators description: Instructions for existing Node Operators to follow to create a machine account for their collection or consensus nodes. sidebar_position: 4 --- In Mainnet14, the DKG (distributed key generation) is turned on, requiring storage of dynamically generated confidential data (random beacon keys). These are stored in a separate database which is new with the Mainnet14 release. All node operators joining after Mainnet14 will generate encryption keys for this database through the node bootstrapping and staking process. We strongly recommend all node operators (especially consensus node operators) generate an encryption key for this database. This guide demonstrates how to enable encryption for this database for existing operators. ## Downloading Bootstrap Utility If you have downloaded the bootstrapping kit previously, ensure that you do this step again to get the latest copy of the bootstrapping kit since there have been significant changes to it. Follow the instructions [here](./node-bootstrap.md#download-the-bootstrapping-kit) to download the latest version of the bootstrapping kit, then return to this page. ## Generate Database Encryption Key You will need to generate an encryption key for the database using the `bootstrap` utility. Ensure you run the following commands on the machine you use to run your node software. The bootstrap directory passed to the `-o` flag must be the same bootstrap directory used by your node. The default location is `/var/flow/bootstrap`, but double-check your setup before continuing. ```shell GenerateEncryptionKey $./boot-tools/bootstrap db-encryption-key -o ./bootstrap INF generated db encryption key INF wrote file bootstrap/private-root-information/private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9secretsdb-key $tree ./bootstrap/ ./bootstrap ├── private-root-information │ └── private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9 │ ├── node-info.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9.json 3 directories, 4 files ``` --- # File: /md/protocol/node-ops/node-operation/faq.md --- title: Operator FAQ sidebar_position: 1 --- # Operator FAQ ### Can anybody run a node? What is the approval process? Anyone can run an [observer node](../light-nodes/observer-node.md). Anyone can run an Access Node after registering and staking. See [Access Node Setup](../access-nodes/access-node-setup.md) for detailed instructions. For the other node roles, individuals can go through an application process that involves asking about their background and experience contributing to decentralized projects. To pursue an application, please create an issue [here](https://github.com/onflow/flow-validator). Pending approval from the governance committee, new node operators will be onboarded and invited to join a webinar to meet the team and share more about how they’ll grow the community. Node Operators are invited to join and participate in Flow's Node Validator Discord channel for setup questions and network announcements. In the future, running a validator node on Flow will be permissionless. See the [autonomy roadmap](https://flow.com/protocol-autonomy-roadmap) for details. ### How do I generate keys? Please follow the instructions provided here: [Generate Your Node Keys](./node-bootstrap.md#generate-your-node-keys) ### How do I check on the status of my node? Please follow the instructions provided here: [Monitoring nodes](./monitoring-nodes.md) ### Can I bootstrap and run a node at any time? Flow allows nodes to join/leave the network each time a new epoch begins (roughly once per week). See [Staking & Epochs](../../staking/index.md#epochs) for general information and [Node Setup](./node-bootstrap.md#timing) for a guide to running a new node. ### Would it hurt the network to have a node that constantly spins up and down? All staked nodes except access nodes, have to be online at all time. A staked node, other than an access node, which is not online can cause severe degradation of network performance and will be subjected to slashing of rewards. A way to prevent this is to check your equipment meets Flow's [recommended requirements](./node-provisioning.md#hardware-requirements), periodically checking for updates and announcements in Discord but also using a node monitoring system for when your node does go offline. ### Does Flow has a regular schedule for network upgrades? Network upgrades (also referred to as "sporks") occur once every year. These are large-scale upgrades that require all node operators to upgrade software versions. Between network upgrades, there are also Height Coordinated Upgrades (HCU) which occur approximately once every two months. These are targeted upgrades for the transaction execution environment and require only Execution Nodes and Verification Nodes to upgrade software versions. Upcoming sporks are announced in advance on the `#flow-validators-announcements` [Discord channel](https://discord.gg/flow) and on the [status](https://status.flow.com/) page. The `#flow-validators-announcements` channel is also used to coordinate during the spork process with all the node operators. ### How do I update the node software during a network upgrade? The new software version for a spork will be published in advance on [GitHub](https://github.com/onflow/flow-go/releases) and announced in the `#flow-validators-announcements` [Discord channel](https://discord.gg/flow). ### Is there any way to know if a node is currently online? To verify if a node is online, please [setup metrics](./faq.md#how-do-i-check-on-the-status-of-my-node) for the node. ### Can I migrate a node to a new machine? Yes, as long as you retain the `boostrap` information which includes the node staking key, networking key, network address and port from the old node to the new. More on this [here](./node-migration.md) ### Where can I find how many nodes are currently running Flow? If you are running a node, then you most definitely have this information on your node in the file `/public-root-information/node-infos.pub.json`. If you are not running a node, you can find this information by using a Cadence script to query the [Staking Smart Contract](../../../build/cadence/core-contracts/06-staking-contract-reference.md) or check [flowscan](https://www.flowscan.io/node). ### Why do I need to update my node's ulimit? Flow nodes create network connections to other nodes on the network to participate in the protocol. The node's operating system represents these connections as file descriptors, and uses soft and hard limits to control the number of open files. The node software uses these limits to manage how many connections it will open and accept from other nodes. If the limit is too low, the node will not be able to communicate with its peers, preventing it from functioning properly. --- # File: /md/protocol/node-ops/node-operation/guides/genesis-bootstrap.md --- title: Genesis Bootstrapping --- All nodes joining the network in May are required to go through this process as part of the Genesis Bootstrapping. ## Overview To kickstart the Flow network and build the first block, all the nodes that will participate in the first round of consensus need to be known and have exchanged some metadata in advance. This guide will take you through setting up your nodes, running the initial metadata and key generation, exchanging data back and forth with the Flow team, and then finally starting your nodes to join the network. ## Before You Begin The Flow consensus algorithm depends on there always being a previous block, which means your nodes cannot start until _after_ the Genesis block has been signed. The process of signing that block will be done by the Flow team, and can only be done after every node has completed the first half of the bootstrapping process, which assures that all the identities are included. Since the Flow team needs to wait for metadata from all participants, it will take hours to even days until the Flow network can start. The bootstrapping process will be in 2 phases, with the Flow team signing the Genesis block between the two. The bootstrapping process deals with a number of different keys. Make sure you understand their usage and terminology by reviewing the [Node Keys Guide](../node-bootstrap.md#generate-your-node-keys). ## Download the Bootstrapping Toolkit Both phases of the bootstrapping are automated with scripts. Pull a copy onto each of your nodes and extract it. ```shell Pull-boot-tools ~ $ curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools.tar ~ $ tar -xvf boot-tools.tar ``` ## Generate Your Node Keys Start the bootstrapping process by generating your Staking Key and Networking Key. Use your Node Address that you generated in [Setting Up a Node](../node-setup.md) in the `--address` flag, and the node role. Your Node Address must be a publicly routable IPv4 address or valid DNS name that points to your node. This is how other nodes in the network will communicate with you. ```shell Generate-bootstrap-keys" ~ $ mkdir ./bootstrap ~ $ ./boot-tools/bootstrap key --address \"${YOUR_NODE_ADDRESS}:3569\" --role ${YOUR_NODE_ROLE} -o ./bootstrap ``` By default, the bootstrap script uses the kernel entropy source, either via a `getrandom` syscall or `/dev/urandom`. If you have a more secure source of entropy, like a hardware device, you can specify `--staking-seed` and `--networking-seed`, to provide your own seeds. Run the `bootstrap` command with no flags to print usage information." The key pairs generated in the bootstrapping process are extremely sensitive and must be managed securely. This guide does not deal with storing the keys in a secure backup or controlling access, as the right approach to this will vary from user to user, but it is something you must consider. Private keys are suffixed with `.priv.json`, their public counterparts are not sensitive and can be shared freely. This command generates two keys, a Staking Key and a Network Key, and stores them both in a `.node-info` file. Both these keys are needed during runtime and must be present as a file to start your flow node. For more details around all the keys that are needed to run nodes and their usage, see the [Node Keys](../node-bootstrap.md#generate-your-node-keys) overview. The bootstrapping process will create a file structure similar to the following ```text bootstrap-directory ~ └──bootstrap ├──{id}.node-info.priv.json └──{id}.node-info.pub.json", ``` ## Upload Public Keys To mint the Genesis Block, the Flow team will need the public Staking and Network keys from all your nodes. **If you have previously joined our networks, and you are generating your keys again. Ensure that you take a backup of your keys before generating it again** To facilitate this, the boot-tools directory comes with a script `push-keys` that will bundle your `*.pub.json` files and send it to the flow team. You can inspect this script to make sure no private key material is being bundled or uploaded. The data not encrypted before being sent as the public keys involved are not sensitive. In phase 2 of the bootstrapping process, the Flow team will need to securely issue each node a Random Beacon key. This key is again sensitive and unique to your node. To enable this, the `push-keys` script also generates another key pair called the Transit Key. The public key of this pair will be uploaded along with the Staking and Network keys, and your Random Beacon key will be encrypted with it before being sent to you. You must keep your Transit Key until you have received and decrypted your Random Beacon key from the Flow team. The transit script here need a `-t` token parameter flag. This token will have been provided to you by the Flow team out of band. Reach out to your contact if you don't have your token. ```shell Upload-public-keys # If you joined our network previously, make sure to take a backup! cp /path/to/bootstrap /path/to/bootstrap.bak $ ./boot-tools/transit push -d ./bootstrap -t ${TOKEN} -role ${YOUR_NODE_ROLE} Running push Generating keypair Uploading ... Uploaded 400 bytes ``` Once you've run the bootstrap and are confident in your setup, run the transit push command only once. If you bootstrap again and transit push again with a new node ID, it will count against your quota of Nodes. Exceeding your quota will result in a long back and forth with the Flow team to see which node is the extra one. ## Update Node Config As flow node requires a `--nodeid` flag to start. You will need to pass in the contents of the `node-id` into either your container, `runtime-config.env` file, or hard coded into the `systemd` unit file which the flow team provides. You can get the `node-id` from the metadata that you pulled. It will be at: `/path/to/bootstrap/public-genesis-information/node-id` ### Wait Now the ball is in the Flow team's court. As soon as all nodes have completed the above steps, the Genesis block will be created and distributed to you. Join the [Flow discord server](https://chat.onflow.org) if you haven't already and stay tuned for updates. Your nodes need not be online during this waiting period if you want to suspend them to reduce cost, but you must not lose your key material. For the Genesis Block, your nodes will start pre-staked, which means no action on your part is needed to get your nodes staked. For more details on staking check the guide on [Staking and Rewards](../../../staking/08-staking-rewards.md). ## Receive Your Random Beacon Keys When the Flow team gives the go-ahead, your Random Beacon keys will be available for retrieval. Each Node will need to pull their own keys down individually. ```shell Pull-beacon-keys ~ $ ./boot-tools/transit pull -d ./bootstrap -t ${TOKEN} -role ${YOUR_NODE_ROLE} Fetching keys for node ID FEF5CCFD-DC66-4EF6-8ADB-C93D9B6AE5A4 Decrypting Keys Keys available ``` Pulling your keys will also pull a bunch of additional metadata needed for the bootstrapping process. In the end, your bootstrap directory should look like this: ```text bootstrap-directory ~ bootstrap/ ├── private-genesis-information │ └── private-node-info_{node id} │ ├── node-info.priv.json │ └── random-beacon.priv.json ├── public-genesis-information │ ├── dkg-data.pub.json │ ├── genesis-block.json │ ├── genesis-cluster-block.{cid}.json │ ├── genesis-cluster-block.{cid}.json │ ├── genesis-cluster-qc.{cid}.json │ ├── genesis-cluster-qc.{cid}.json │ ├── genesis-commit.json │ ├── genesis-qc.json │ ├── node-id │ ├── node-info.pub.{node id}.json │ └── node-infos.pub.json ├── ``` Unlike staking and account keys, the beacon keys are not randomly generated, and depend on inputs from all consensus nodes on the network. In typical Flow network operation, these keys will be dynamically generated on demand by the consensus nodes communicating. However for genesis, as the consensus nodes aren't communicating yet, the Flow team will generate and distribute them to kickstart the process. ## Move Genesis Data This bootstrapping data is needed by your node at each startup, so it must be present on disk. Where in the filesystem you store this data is up to you, but you may not change the folder structure generated by the bootstrapping process. By default, flow stores this data under `/var/flow/bootstrap`. ## New Images Once the Genesis block has been minted, it will be included into the official container images so that it's available to all nodes. Pull the new images, which should now be version `v1.0.0`. ## Start Your Nodes Once every node has puled its keys and fetched the new images, the network is ready to start. Make sure you're part of the [Discord Chat](https://discord.com/invite/flow). Once all nodes are ready, updates will be provided to everyone. Start your systems, let's make some blocks! --- # File: /md/protocol/node-ops/node-operation/guides/spork-practice.md --- title: Spork Practice --- ## Sporking The actual process of Sporking will mostly be covered by the Node Operators Quick Guide, and will not be covered here. [Spork](../network-upgrade) Instead, we'll aim to give some instructions for those that want to Practice the process themselves, before joining the Mainnet Spork. This guide assumes you have access to the Flow-Go repo, which you'll need to build up-to-date containers and run code snippets. [](https://github.com/onflow/flow-go) ## Local Testnet One way to get a good feel of the network without too much interaction with infrastructure is to play with the local testnet, which we've named the Flow Local Instrumented Test Environment (FLITE). [https://github.com/onflow/flow-go/blob/master/integration/localnet/README.md](https://github.com/onflow/flow-go/blob/master/integration/localnet/README.md) FLITE will allow you to start a full flow network locally, which means starting all 5 roles required for a functioning network. Instructions for initializing and starting the local network are provided in the README above. When Starting FLITE, it will build all the docker images required for the network. This can also be done manually ahead of time, using `make docker-build-flow` from the root directory of `flow-go` ## Remote Testnet If you would like more control over the nodes, beyond what docker compose can provide, or you wish to deploy the docker images to separate VMs, to more closely imitate Mainnet, you will have to manually run bootstrapping for a specific configuration of nodes that you would like to test. [](https://github.com/onflow/flow-go/blob/master/cmd/bootstrap/README.md) Example files are available in the `cmd/bootstrap/example_files` folder. Where the `node-config.json` will usually store all flow's nodes, whereas partner node info usually goes into a separate folder. The last file, which will need to be manually populated, is the partner stakes file, which takes the IDs of all the partner nodes and associates a stake. For now, this can be arbitrary. Once you have all the information, you can make use of the `finalize` command: [](https://github.com/onflow/flow-go/tree/master/cmd/bootstrap#example-1) And generate the bootstrapping folder required to start up your nodes. Once you have the bootstrapping folder, you'll be able to start up all the nodes that were included in the bootstrapping process. [Node Setup Docker](../node-setup.md#docker) The startup command will look very similar to what is provided in the quick guide. One such example, assuming we named our bootstrap folder `bootstrap`: ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/execution:latest \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --rpc-addr=0.0.0.0:9000 \ --nclusters=${FLOW_NETWORK_COLLECTION_CLUSTER_COUNT} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` The two missing pieces of info here are `FLOW_GO_NODE_ID` which will have been generated from the bootstrap process, and will depend on which node you're trying to run, and `FLOW_NETWORK_COLLECTION_CLUSTER_COUNT` which we've been defaulting to `2` ## Practice Testnet Lastly, if the goal is to practice the entire Sporking procedure, including `transit` of staking and networking keys, and joining a network, we can help spin up a Testnet temporarily for this purpose. This will require quite a bit of coordination, and will basically be the same steps as the Mainnet spork, so please let us know if this is something you'd like to do and we’ll connect to plan accordingly. --- # File: /md/protocol/node-ops/node-operation/guides/starting-nodes.md --- title: Starting Your Nodes --- Prior to starting up your nodes make sure you have the following items completed: 1. Bootstrap process completed with the bootstrap directory handy (default: `/var/flow/bootstrap`) 2. Flow `data` directory created (default: `/var/flow/data`) 3. [node config](../node-bootstrap.md) ready 4. Firewall exposes TCP/3569, and if you are running `access` node also the GRPC port (default: TCP/9000) For more details head back to [Setting up your node](../node-setup.md#prepare-your-node-to-start) When you have all the above completed, you can start your Flow node via `systemd` or `docker`. ## systemd Ensure that you downloaded the systemd unit file. If you haven't, follow the [Set your node to start](../node-setup.md#prepare-your-node-to-start) guide to get your unit file and enabled. Once you have your Flow service enabled you can now start your service: `systemctl start flow` ## Docker If you don't have have systemd on your system, or prefer not to use systemd, you can run the following `docker` commands for your respective Flow role to start your node! ### Access ``` docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/access:v0.0.6-alpha \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --rpc-addr=0.0.0.0:9000 \ --ingress-addr=${FLOW_NETWORK_COLLECTION_NODE} \ --script-addr=${FLOW_NETWORK_EXECUTION_NODE} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Collection ``` docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/collection:v0.0.6-alpha \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --rpc-addr=0.0.0.0:9000 \ --nclusters=${FLOW_NETWORK_COLLECTION_CLUSTER_COUNT} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Consensus ``` docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/consensus:v0.0.6-alpha \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --nclusters=${FLOW_NETWORK_COLLECTION_CLUSTER_COUNT} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Execution ``` docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/execution:v0.0.6-alpha \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --ingress-addr=0.0.0.0:9000 \ --nclusters=${FLOW_NETWORK_COLLECTION_CLUSTER_COUNT} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Verification ``` docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ gcr.io/flow-container-registry/verification:v0.0.6-alpha \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --nclusters=${FLOW_NETWORK_COLLECTION_CLUSTER_COUNT} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Additional Flags #### Networking Layer All networking layer settings are initialized to default values from the [config/default-config.yml](https://github.com/onflow/flow-go/blob/master/config/default-config.yml) file when the Flow node starts up. Each attribute in this YAML file matches a flag name, allowing you to override the default setting by specifying the corresponding flag in the `docker run` command. For instance, to change the `networking-connection-pruning` setting, use its matching flag name (`networking-connection-pruning`) and desired value in the `docker run` command. --- # File: /md/protocol/node-ops/node-operation/hcu.md --- title: Height coordinated upgrade (HCU) sidebar_label: Height coordinated upgrade --- ## Overview To enables rapid development of the Flow Protocol, the height coordinated upgrade method is used to roll out non-breaking changes such as bug fixes, feature implementations and security fixes. ## HCU versus Network Upgrade A [Network Upgrade (spork)](./network-upgrade) requires a coordinated network upgrade process where node operators upgrade their node software and re-initialize with a consolidated representation of the previous network upgrade's state. It is used to roll out changes which may be non-backward compatible with respect to the protocol and the execution state. Network upgrade entails a network downtime as all nodes in the system are upgraded and brought back online. Network upgrades are only executed once every year. A height coordinated upgrade (HCU) on the other hand allows the execution and the verification nodes to be upgraded without stopping the network. There is no network downtime during an HCU but the transaction execution will stop for few minutes while the execution nodes restart. Currently, an HCU is only used to update the execution and the verification nodes. For other node types, a simple rolling upgrade is used where operators are asked to upgrade their nodes async. ## HCU process The HCU is executed in two parts. The first part is executed by the service committee. In this, the version boundary at which the execution nodes and verification nodes should stop is set on chain by submitting the [set_version_boundary](https://github.com/onflow/flow-core-contracts/blob/master/transactions/nodeVersionBeacon/admin/set_version_boundary.cdc) transaction. The version boundary includes the block height at which the two node types should stop and the new node software version that the nodes should compare after a restart. The second part is executed by the node operator. In this the node operator, monitors the execution and verification node that they are running. When the nodes reach the height set on chain, they stop if their version is lower then the version specified in the version boundary. At this point, the operator should update the node version to the new node software version and start the node again. The node will continue from where it left off. The block height and the node version will be announced by the Flow team on Discord as well as the [forum page](https://forum.onflow.org/c/mainnet-sporks/36). It can also be directly queried from the chain using the following script. ``` TODO: insert flow cli command here to query the block version details. ``` --- # File: /md/protocol/node-ops/node-operation/machine-existing-operator.md --- title: Machine Accounts for Existing Node Operators sidebar_label: Machine Accounts for Existing Node Operators description: Instructions for existing Node Operators to follow to create a machine account for their collection or consensus nodes. sidebar_position: 6 --- The [Flow Epoch Preparation Protocol](../../staking/04-epoch-preparation.md) requires that `collection` and `consensus` nodes use an automated [machine account](../../staking/09-qc-dkg.md#machine-accounts) to participate in important processes required to start the next epoch. (QC and DKG, respectively) Starting on Thursday, August 26th 2021, all collector and consensus nodes who register with Flow Port will automatically create and initialize this machine account as part of their node registration. If you have an existing `consensus` or `collection` node that you registered with Flow Port before Thursday August 26th, you will need to create this Machine Account manually in order to participate in epochs. You will need to create one Machine Account for each `consensus` or `collection` node that you operate. This guide will walk you through creating a Machine Account and getting it set up. During this process you will generate a new private key which will have sole control over your machine account. This private key will be stored on the machine you use to run your node, alongside your staking and networking keys. Loss of any of these keys (staking, networking, or machine account) will require you to un-stake your tokens, start a completely new node, and register the new node to continue participating in the Flow network, which takes multiple weeks. ## Downloading Bootstrap Utility If you have downloaded the bootstrapping kit previously, ensure that you do this step again to get the latest copy of the bootstrapping kit since there have been significant changes to it. Follow the instructions [here](./node-bootstrap.md#download-the-bootstrapping-kit) to download the latest version of the bootstrapping kit, then return to this page. ## Generate Machine Account key You will need to generate a Machine account private key using the `bootstrap` utility. Ensure you run the following commands on the machine you use to run your node software. The bootstrap directory passed to the `-o` flag must be the same bootstrap directory used by your node. The default location is `/var/flow/bootstrap`, but double-check your setup before continuing. ```shell GenerateMachineAccountKey $./boot-tools/bootstrap machine-account-key -o ./bootstrap INF generated machine account private key INF encoded machine account public key for entry to Flow Port machineAccountPubKey=f847b84031d9f47b88435e4ea828310529d2c60e806395da50d3dd0dd2f32e2de336fb44eb06488645673850897d7cc017701d7e6272a1ab7f2f125aede46363e973444a02038203e8 INF wrote file bootstrap/private-root-information/private-node-info_6f6e98c983dbd9aa69320452949b81abeab2ac591a247f55f19f4dbf0b477d26/node-machine-account-key.priv.json $tree ./bootstrap/ ./bootstrap ├── private-root-information │ └── private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9 │ ├── node-info.priv.json │ └── node-machine-account-key.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9.json 3 directories, 4 files ``` ## Create Machine Account You will now need to copy the Machine account public key displayed in the terminal output and head over to [Flow Port](../../../protocol/flow-port/staking-guide.md#stake-a-node) to submit a transaction to create a Machine Account. For example, from the example above, we would copy `f847...` from this line: ```shell Example INF encoded machine account public key for entry to Flow Port machineAccountPubKey=f847b84031d9f47b88435e4ea828310529d2c60e806395da50d3dd0dd2f32e2de336fb44eb06488645673850897d7cc017701d7e6272a1ab7f2f125aede46363e973444a02038203e8 ``` This process will create your machine account for you and show you your machine account's address, which you will need to save for the next step. ## Finalize Machine Account setup You will now need to use the `bootstrap` utility to run `machine-account` with the created address to finalize the set up of your Machine account. ```shell $ ./boot-tools/bootstrap machine-account --address ${YOUR_MACHINE_ACCOUNT_ADDRESS} -o ./bootstrap ``` ```shell Example $./boot-tools/bootstrap machine-account --address 0x1de23de44985c7e7 -o ./bootstrap INF read machine account private key json DBG encoded public machine account key machineAccountPubKey=2743786d1ff1bf7d7026d693a774210eaa54728343859baab62e2df7f71a370651f4c7fd239d07af170e484eedd4f3c2df47103f6c39baf2eb2a50f67bbcba6a INF wrote file bootstrap/private-root-information/private-node-info_6f6e98c983dbd9aa69320452949b81abeab2ac591a247f55f19f4dbf0b477d26/node-machine-account-info.priv.json $tree ./bootstrap/ ./bootstrap ├── private-root-information │ └── private-node-info_d60bd55ee616c5c297cae1d5cfb7f65e7e04014d9c4abe595af2fd83f3cfe160 │ ├── node-info.priv.json │ ├── node-machine-account-info.priv.json │ └── node-machine-account-key.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.d60bd55ee616c5c297cae1d5cfb7f65e7e04014d9c4abe595af2fd83f3cfe160.json 3 directories, 5 files ``` After running this step, you should see the `node-machine-account-info.priv.json` file in your `bootstrap` directory as shown above. ### Verify Machine Account Setup After finalizing your machine account setup, you should verify its correctness with the `check-machine-account` command: ```shell CheckMachineAccount $ ./boot-tools/bootstrap check-machine-account --access-address access.mainnet.nodes.onflow.org:9000 -o ./bootstrap DBG read machine account info from disk hash_algo=SHA3_256 key_index=0 machine_account_address=0x284463aa6e25877c machine_account_pub_key=f847b84051bad4512101640772bf5e05e8a49868d92eaf9ebed41030881d95485769afd28653c5c53216cdcda4554384bb3ff6396a2ac04842422d55f0562496ad8d952802038203e8 signing_algo=ECDSA_P256 DBG checking machine account configuration... machine_account_address=0x284463aa6e25877c role=consensus DBG machine account balance: 0.10000000 INF 🤖 machine account is configured correctly ``` This command will detect and provide information about common misconfigurations, or confirm that the machine account is configured correctly. --- # File: /md/protocol/node-ops/node-operation/monitoring-nodes.md --- title: Monitoring Node Health sidebar_label: Node Monitoring sidebar_position: 7 --- A Flow node generates logs and publishes metrics as it runs. These logs and metrics can be used to gain insights into the health of the node. ## Logs Logs are emitted to `stdout` as JSON formed strings. Where these logs are available on your system depends on how you launch your containers. On `systemd` systems for example, the logs will be sent to the system journal daemon `journald`. Other systems may log to `/var/log`. ## Metrics Flow nodes produce health metrics in the form of [Prometheus](https://prometheus.io) metrics, exposed from the node software on `/metrics`. If you wish to make use of these metrics, you'll need to set up a Prometheus server to scrape your Nodes. Alternatively, you can deploy the Prometheus Server on top of your current Flow node to see the metrics without creating an additional server. > The flow-go application doesn't expose any metrics from the underlying host such as CPU, network, or disk usages. It is recommended you collect these metrics in addition to the ones provided by flow using a tool like node exporter (https://github.com/prometheus/node_exporter) 1. Copy the following Prometheus configuration into your current flow node ```yaml global: scrape_interval: 15s # By default, scrape targets every 15 seconds. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: 'prometheus' # Override the global default and scrape targets from this job every 5 seconds. scrape_interval: 5s static_configs: - targets: ['localhost:8080'] ``` 2. Start Prometheus server ```shell docker run \ --network=host \ -p 9090:9090 \ -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus" ``` 3. (optional) Port forward to the node if you are not able to access port 9090 directly via the browser `ssh -L 9090:127.0.0.1:9090 YOUR_NODE` 4. Open your browser and go to the URL `http://localhost:9090/graph` to load the Prometheus Dashboard ### Key Metric Overview The following are some important metrics produced by the node. | Metric Name | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | go\_\* | Go runtime metrics | | consensus_compliance_finalized_height | Latest height finalized by this node; should increase at a constant rate. | | consensus_compliance_sealed_height | Latest height sealed by this node; should increase at a constant rate. | | consensus_hotstuff_cur_view | Current view of the HotStuff consensus algorith; Consensus/Collection only; should increase at a constant rate. | | consensus_hotstuff_timeout_seconds | How long it takes to timeout failed rounds; Consensus/Collection only; values consistently larger than 5s are abnormal. | ### Machine Account Collection and consensus nodes use a machine account that must be kept funded. See [here](../../staking/11-machine-account.md) for details. Nodes check their machine account's configuration and funding and produce metrics. | Metric Name | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | machine_account_balance | The current balance (FLOW) | | machine_account_recommended_min_balance | The recommended minimum balance (FLOW) | | machine_account_is_misconfigured | 0 if the node is configured correctly; 1 if the node is misconfigured | To be notified when your node's machine account needs to be refilled or has a configuration error, you can set up alerts. When the machine account balance needs to be refilled: ``` machine_account_balance < machine_account_recommended_min_balance ``` When the machine account has a configuration error: ``` machine_account_is_misconfigured > 0 ``` The metrics include the account address of the machine account (`acct_address` label) for convenience: ``` # HELP machine_account_balance the last observed balance of this node's machine account, in units of FLOW # TYPE machine_account_balance gauge machine_account_balance{acct_address="7b16b57ae0a3c6aa"} 9.99464935 ``` --- # File: /md/protocol/node-ops/node-operation/network-upgrade.md --- title: Network Upgrade (Spork) Process description: Steps to be carried out by node operators during a network upgrade. sidebar_position: 15 --- ## Overview A Network Upgrade (spork) is a coordinated network upgrade process where node operators upgrade their node software and re-initialize with a consolidated representation of the previous network upgrade's state. This enables rapid development on the Flow Protocol and minimizes the impact of breaking changes. Network upgrade are also referred to as Spork. Network Upgrades are approximately once every year. Upcoming network upgrades are announced in advance on the `#flow-validators-announcements` [Discord channel](https://discord.gg/flow) and on the [status](https://status.flow.com/) page. The `#flow-validators-announcements` channel is also used to coordinate during the upgrade process with all the node operators. > 📢 [Forte Upgrade](https://status.flow.com/incidents/x91d6t1x1qh4) on Wednesday, Oct 22nd, 2025 at 15:00 UTC This guide is for existing operators participating in a network upgrade. See [Node Bootstrap](./node-bootstrap.md) for a guide to joining the network for the first time. ## Step 1 - Cleaning Up Previous Spork State Once the spork start has been announced on Discord, stop your node and clear your database. The node should stay stopped for the duration of the spork. You can skip this step if it is your first time running a node on Flow. 1. Stop your Flow node 2. Clear the contents of your `data` directory that you have previously created. The default location is `/var/flow/data`. The `data` directory contains the Flow chain state. ## Step 2 - Start Your Node Once you receive an announcement that the spork process is complete (via [Discord server](https://discord.gg/flow)), you will need to fetch the genesis info, update your runtime configuration and then boot your Flow node up! If you had set the [dynamic bootstrap arguments](https://developers.flow.com/protocol/node-ops/node-operation/protocol-state-bootstrap) command line arguments (`--dynamic-startup-access-address`, `--dynamic-startup-access-publickey`, `--dynamic-startup-epoch-phase`) please remove them. 1. Run the transit script to fetch the new genesis info: Download the latest transit script - see instructions [here](./node-bootstrap.md#download-the-bootstrapping-kit) ``` ./boot-tools/transit pull -b ./bootstrap -t ${PULL_TOKEN} -r ${YOUR_NODE_TYPE} --concurrency 10 --timeout 50m ``` - `PULL_TOKEN` will be provided by the Flow team. - For `collection`, `consensus`, `verification` node type it will generally be `testnet-x` or `mainnet-x` where x is the latest number of respective network upgrade. e.g. `testnet-53`, `mainnet-27`. - For `execution` node type it will generally be `testnet-x-execution` or `mainnet-x-execution`. - For `access` node: - It will generally be `testnet-x` or `mainnet-x` if execution data indexing is not enabled. - It will generally be `testnet-x-execution` or `mainnet-x-execution` if execution data indexing is enabled. See [here](../access-nodes/access-node-configuration-options.md) to enable execution date indexing. - `YOUR_NODE_TYPE` should be one of `collection`, `consensus`, `execution`, `verification` based on the node(s) that you are running. - For access nodes however, if you have execution data index enabled use the role `execution` to ensure the execution state files (`root.checkpoint*`) are also downloaded. If you do not have execution data indexing enabled, specify the role as `access`. ```shell Example $ ./boot-tools/transit pull -b ./bootstrap -t mainnet-27 -r consensus Transit script Commit: 98a6ac408fdd86dba0011e698d40ebd71f4276fa 2020/11/25 01:02:53 Running pull 2020/11/25 01:02:53 Downloading bootstrap/public-root-information/node-infos.pub.json 2020/11/25 01:02:54 Downloading bootstrap/public-root-information/root-protocol-snapshot.json 2020/11/25 01:02:54 Downloading bootstrap/random-beacon.priv.json.39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a.enc 2020/11/25 01:02:54 SHA256 of the root block is: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 $ tree ./bootstrap/ ./bootstrap/ ├── private-root-information │ └── private-node-info_39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a │ └── node-info.priv.json ├── public-root-information │ ├── node-id │ ├── node-info.pub.39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a.json │ ├── node-infos.pub.json │ └── root-protocol-snapshot.json └── random-beacon.priv.json.39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a ``` 2. Update command line arguments For the Forte upgrade, remove the `pebble-dir` argument as it has been deprecated. This applies to all node types. The node will continue to use the `datadir` argument which points to the location of the node database. 3. Start your Flow node via `docker` or `systemd` The FlowFoundation team will share the new docker tag at the completion of the upgrade. Please use that docker tag to bring up the node. See [Node Bootstrap](./node-bootstrap.md) for detailed information on Docker/Systemd configuration. ## Common Issues ### Error: cannot create connection ```shell 20T18:34:21Z","message":"could not create connection"} {"level":"error","node_role":"consensus","node_id":"6d3fac8675a1df96f4bb7a27305ae531b6f4d0d2bc13a233e37bb07ab6b852dc","target":"QmVcSQaCdhmk1CMeMN7HTgGiUY1i2KqgVE2vvEmQXK4gAA","error":"failed to dial : all dials failed * [/ip4/155.138.151.101/tcp/3569] dial tcp4 155.138.151.101:3569: connect: connection refused","retry_attempt":2,"time":"2020-05-20T18:34:21Z","message":"could not create connection"} ``` This error is OK. Your fellow node operators have not turned on/joined the network yet. So no need to worry about it! --- # File: /md/protocol/node-ops/node-operation/node-bootstrap.md --- title: Node Bootstrap sidebar_label: Node Bootstrapping description: How to get started running a node on Flow sidebar_position: 8 --- This guide is for getting a new node staked and running on Flow other than a permissionless Access node. For running a permissionless Access node see [Access node setup](../access-nodes/access-node-setup.md). For sporking documentation for existing node operators, see [Spork Process](./network-upgrade). ## Timing New nodes are able to join the network each time a new epoch begins. In order to join the network at epoch N+1, the node must be registered with sufficient stake and authorized by the service account prior to the end of epoch N's Staking Auction Phase. Confirmation of a new node's inclusion in epoch N+1 is included in the [`EpochSetup` event](../../staking/05-epoch-scripts-events.md#flowepochepochsetup). Nodes registered for epoch N+1 are able to participate in network communication on a limited basis starting in the `Epoch Setup Phase` of epoch N. ![Flow Epoch Schedule](https://storage.googleapis.com/flow-resources/documentation-assets/epoch-startup-order.png) Once registered and confirmed to join the network at epoch N+1, the node must start up **before** epoch N+1 begins. - Verification & Access nodes may start up any time during the `Epoch Setup Phase`. - Consensus & Collection nodes must start up within the first **1000 views (~30mins)** of the `Epoch Setup Phase` to participate in the [Epoch Preparation Protocol](../../staking/04-epoch-preparation.md#phase-1-epoch-setup). ## Step 1 - Run Genesis Bootstrap :::info You will need to run this process for each node that you are operating ::: ### Download the Bootstrapping Kit :::warning If you have downloaded the bootstrapping kit previously, ensure you check the hash below still matches. If not, re-download to ensure you are using the most up-to-date version. ::: ```shell curl -sL -O storage.googleapis.com/flow-genesis-bootstrap/boot-tools.tar tar -xvf boot-tools.tar chmod +x ./boot-tools/bootstrap chmod +x ./boot-tools/transit ``` ```shell CheckSHA256 sha256sum ./boot-tools/bootstrapcmd 0c2ad1bdf27aaa449a2b54975324ecef7c78fa39512cf8c2448165fedf106eb4 ./boot-tools/bootstrap sha256sum ./boot-tools/transit dbf34e44baf622092bafdba73ab5f633e1d5db90a3a8fad780aec0eef7857722 ./boot-tools/transit ``` ### Generate Your Node Keys #### Network Address :::info Use a fully qualified domain name for the network address. Please also include the port number in the network address e.g. `example.com:3569` ::: :::warning Do not include in `http://` format. ::: :::info If you are running multiple nodes, please ensure you have different addresses for each node. ::: :::warning All your current keys and Flow genesis files should be in the `bootstrap` folder created earlier. Please take a back up of the entire folder. ::: ```shell ## Skip this section if this is your first time ## # If you joined our network previously, make sure to take a backup of your previously generated keys! cp -r /path/to/bootstrap /path/to/bootstrap.bak ######################################################### # Generate Keys $ mkdir ./bootstrap # YOUR_NODE_ADDRESS: FQDN associated to your instance (do NOT use an IP address, use a hostname) # YOUR_NODE_ROLE: The Flow nodes that you wish to run, it should be ONE of the following - [ access, collection, consensus, execution, verification ] $ ./boot-tools/bootstrap key --address \"YOUR_NODE_ADDRESS_GOES_HERE:3569\" --role YOUR_NODE_ROLE_GOES_HERE -o ./bootstrap ``` ```shell Example $./boot-tools/bootstrap key --address "consensus-001.nodes.flow.com:3569" --role consensus -o ./bootstrap DBG will generate networking key INF generated networking key DBG will generate staking key INF generated staking key DBG will generate db encryption key INF generated db encryption key DBG assembling node information address=consensus-001.nodes.flow.com:3569 DBG encoded public staking and network keys networkPubKey=7f31ae79017a2a58a5e59af9184f440d08885a16614b2c4e361019fa72a9a1a42bf85b4e3f9674782f12ca06afd9782e9ccf19496baed069139385b82f8f40f6 stakingPubKey=829d086b292d84de8e7938fd2fafa8f51a6e025f429291835c20e59d9e25665febf24fa59de12a4df08be7e82c5413180cc7b1c73e01f26e05344506aaca4fa9cc009dc1c33f8ba3d7c7509e86d3d3e7341b43b9bf80bb9fba56ae0b3135dd72 INF wrote file bootstrap/public-root-information/node-id INF wrote file bootstrap/private-root-information/private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9/node-info.priv.json INF wrote file bootstrap/private-root-information/private-node-info_5e44ad5598bb0acb44784f629e84000ffea34d5552427247d9008ccf147fb87f/secretsdb-key INF wrote file bootstrap/public-root-information/node-info.pub.ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9.json DBG will generate machine account key INF generated machine account key DBG assembling machine account information address=consensus-001.nodes.flow.com:3569 INF encoded machine account public key for entry to Flow Port machineAccountPubKey=f847b8406e8969b869014cd1684770a8db02d01621dd1846cdf42fc2bca3444d2d55fe7abf740c548639cc8451bcae0cd6a489e6ff59bb6b38c2cfb83e095e81035e507b02038203e8 INF wrote file bootstrap/private-root-information/private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9/node-machine-account-key.priv.json $tree ./bootstrap/ ./bootstrap ├── private-root-information │ └── private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9 │ ├── node-info.priv.json │ ├── node-machine-account-key.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9.json 3 directories, 4 files ``` :::info For `consensus` and `collection` node types an additional key will be created for the Machine Account. For all other node types this will not be needed. ::: #### Machine Account Creation If you are running a collection and consensus node, you will have an additional private key file (`node-machine-account-key.priv.json`) which contains the private key for your node's machine account. You can learn more about machine accounts [here](../../staking/11-machine-account.md). In Step 2 of this guide, when you submit a transaction to stake your node, you will need to provide the machine account public key, which can be found in the output of the previous `bootstrap key` command. ```shell MachineAccountPublicKey $./boot-tools/bootstrap key --address YOUR_NODE_ADDRESS_GOES_HERE --role YOUR_NODE_ROLE_GOES_HERE -o ./bootstrap ... DBG encoded public machine account key machineAccountPubKey=1b9c00e6f0930792c5738d3397169f8a592416f334cf11e84e6327b98691f2b72158b40886a4c3663696f96cd15bfb5a08730e529f62a00c78e2405013a6016d INF wrote file bootstrap/private-root-information/private-node-info_ab6e0b15837de7e5261777cb65665b318cf3f94492dde27c1ea13830e989bbf9/node-machine-account-key.priv.json ``` :::warning Copy the machine account public key somewhere safe. You will need it in a later step. ::: ## Step 2 - Stake Your Node Stake your node via [Flow Port](https://port.onflow.org/) The `node details` (`Node ID`, `Network Address`, `Networking Key` and `Staking Key`) that need to be submitted when staking the node on Flow Port, can be found in the file: `./bootstrap/public-root-information/node-info.pub..json`. ```shell Example $cat ./bootstrap/public-root-information/node-info.pub.39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a.json { "Role": "consensus", "Address": "consensus-001.nodes.flow.com:3569", "NodeID": "39fa54984b8eaa463e129919464f61c8cec3a4389478df79c44eb9bfbf30799a", "Weight": 0, "NetworkPubKey": "d92e3d5880abe233cf9fe9104db34bbb31251468a541454722b3870c04156a1b0504aef443bcaad124b997384b8fe7052847ce1e6189af1392d865e6be69835b", "StakingPubKey": "917826e018f056a00b778a58ae83054906957ffd4b6f1b7da083551f7a9f35e02b76ace50424ed7d2c9fc69207a59f0f08a031048f5641db94e77d0648b24d150dedd54bab7cd44b4aa60cfd54be418647b0b3965f8ae54c0bcb48ae9d705162", "StakingPoP": "99c879b963b45acd907aae0d7e968a24abf2d3dd28700eca81217b3f12c477334a531a56bcea0f86770614364ebca519" } ``` If you are running a collection or consensus node, you will need to provide an additional field `Machine Account Public Key`. This value is found in the output of the `bootstrap key` command from Step 1. Staking a collection or consensus node will also create a machine account for the node. The machine account will be mentioned in the output of the staking transaction displayed by Flow Port. Please save the machine account for the next step. :::info Please let us know your node id via discord or email. ::: ### Finalize Machine Account Setup :::warning If you are not running a collection or consensus node, you can skip this step. ::: You will now need to use the `bootstrap` utility to run `machine-account` with the created address to finalize the set up of your Machine account. ```shell $ ./boot-tools/bootstrap machine-account --address YOUR_MACHINE_ACCOUNT_ADDRESS_GOES_HERE -o ./bootstrap ``` ```shell Example $ ./boot-tools/bootstrap machine-account --address 0x1de23de44985c7e7 -o ./bootstrap INF read machine account private key json DBG encoded public machine account key machineAccountPubKey=2743786d1ff1bf7d7026d693a774210eaa54728343859baab62e2df7f71a370651f4c7fd239d07af170e484eedd4f3c2df47103f6c39baf2eb2a50f67bbcba6a INF wrote file bootstrap/private-root-information/private-node-info_6f6e98c983dbd9aa69320452949b81abeab2ac591a247f55f19f4dbf0b477d26/node-machine-account-info.priv.json $tree ./bootstrap/ ./bootstrap ├── private-root-information │ └── private-node-info_d60bd55ee616c5c297cae1d5cfb7f65e7e04014d9c4abe595af2fd83f3cfe160 │ ├── node-info.priv.json │ ├── node-machine-account-info.priv.json │ ├── node-machine-account-key.priv.json │ └── secretsdb-key └── public-root-information ├── node-id └── node-info.pub.d60bd55ee616c5c297cae1d5cfb7f65e7e04014d9c4abe595af2fd83f3cfe160.json 3 directories, 5 files ``` After running this step, you should see the `node-machine-account-info.priv.json` file in your `bootstrap` directory as shown above. ### Verify Machine Account Setup After finalizing your machine account setup, you should verify its correctness with the `check-machine-account` command: ```shell CheckMachineAccount $ ./boot-tools/bootstrap check-machine-account --access-address access.mainnet.nodes.onflow.org:9000 -o ./bootstrap DBG read machine account info from disk hash_algo=SHA3_256 key_index=0 machine_account_address=0x284463aa6e25877c machine_account_pub_key=f847b84051bad4512101640772bf5e05e8a49868d92eaf9ebed41030881d95485769afd28653c5c53216cdcda4554384bb3ff6396a2ac04842422d55f0562496ad8d952802038203e8 signing_algo=ECDSA_P256 DBG checking machine account configuration... machine_account_address=0x284463aa6e25877c role=consensus DBG machine account balance: 0.10000000 INF 🤖 machine account is configured correctly ``` This command will detect and provide information about common misconfigurations, or confirm that the machine account is configured correctly. ### Push transit keys (consensus node only) If you are running a consensus node, run the following command to generate the transit keys. ```shell transit $ ./boot-tools/transit prepare -b ./bootstrap -r consensus INF running prepare INF generating key pair INF completed preparation role=consensus ``` This will generate the public and private transit keys under the bootstrap folder. The transit keys are used to transfer the DKG keys after a network upgrade. Please share the **public** transit key with the Flow Foundation via [discord](https://discord.gg/flow) or [email](mailto::governance@flow.com). ## Step 3 - Start Your Flow Node Ensure you have configured your node using the [Node Setup guide](./node-setup.md). ### Confirming authorization You can confirm your node's successful registration and authorization by executing a Cadence script to query the [Staking Contract](../../../build/cadence/core-contracts/06-staking-contract-reference.md#contract). At the end of the `Staking Auction Phase`, the members of the Proposed Identity Table are confirmed as authorized participants in the next epoch. Therefore, if your node ID appears in the Proposed Identity Table during the `Staking Auction Phase`, your node will be a participant in the next epoch. You can read the current Proposed Identity Table using the [getProposedTable script](https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_proposed_table.cdc). You can read the current epoch phase using the [getEpochPhase script](https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_epoch_phase.cdc). (A return value of `0` indicates the `Staking Auction Phase`.) ### Trusted Root Snapshot Once your node has been registered and authorized by the service account, it will be able to participate in the next epoch. ![Flow Epoch Schedule](https://storage.googleapis.com/flow-resources/documentation-assets/epoch-startup-order.png) A new node must bootstrap with a trusted root snapshot of the protocol state, where the node is a confirmed participant. Since new nodes are confirmed at the end of the `Staking Auction Phase`, this means that, if the node is registered to join at epoch `N+1`, it must use a root snapshot from within the `Epoch Setup Phase` of epoch `N`. ### Dynamic Startup Flow provides a mechanism called Dynamic Startup to simplify the process of obtaining the root snapshot. When using Dynamic Startup, the node can be started **at any time during the `Staking Auction Phase`**. The node will wait for the `Epoch Setup Phase` to begin, retrieve a valid root snapshot from a trusted Access Node, then bootstrap its state and join the network. This is the recommended way to start your node for the first time. 1. Remove any `root-protocol-state-snapshot.json` file from your `bootstrap` folder. (If this file is present the node will attempt to bootstrap with it rather than Dynamic Startup.) 2. Select a trusted Access Node to provide the root snapshot. You will need this node's **secure GRPC server address** and **Networking Public Key**. 3. Configure Dynamic Startup by adding flags: ```shell ExampleDynamicStartupFlags ... \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae ``` 4. Start your node (see [guide](./node-setup#start-the-node)) :::info Once the node has bootstrapped, these flags will be ignored and may be removed. ::: ### Manually Provisioned Root Snapshot You can also provision the root snapshot file manually, then start the node without configuring Dynamic Startup. See [here](./protocol-state-bootstrap.md) for the available options to provision a Root Snapshot. :::warning The snapshot must be within the `Epoch Setup Phase`. ::: :::warning Since Collection and Consensus Nodes must start up in the first ~30mins of the `Epoch Setup Phase` (see [Timing](./node-bootstrap.md#timing)), the snapshot must be provisioned within this time window. ::: Once a valid root snapshot file is downloaded to the node's bootstrap folder, it can be started (see [guide](./node-setup.md#start-the-node)) --- # File: /md/protocol/node-ops/node-operation/node-economics.md --- title: Node Economics sidebar_label: Node Economics description: Node Operator Economics - An illustration sidebar_position: 8 --- Node operators play a crucial role in securing the Flow network. Here’s a simple example to illustrate what node operators can expect in terms of node economics. ## Node Operator Economics: An illustration :::warning This illustration is strictly to serve as an example. Actual numbers will vary based on several factors. For real-time numbers, please refer to the [block explorer](https://www.flowscan.io/tokenomics). ::: | # | Parameter | Value | Explanation | | -------- | ------------- | ------------- | ------------- | | A| Node Operator’s Stake | 500,000 FLOW | Assuming minimum staking requirements for a consensus node. Remember there’s no upper cap on how much FLOW can be staked to a Flow node. | | B| Delegation to node | 1,000,000 FLOW | Funds that individual/ institutional delegators delegate to your node. Assuming 1M FLOW for this example. | | C | APY | 10% | Subject to change based on total ecosystem stake in each epoch. Remember APY = R / S, where S = Total FLOW Staked / Total FLOW Supply and R = 5% (”reward rate”) | | D | Delegation Rate | 8% | Fee taken by the node operator from delegator rewards to cover their operating expenses, currently set at 8% of the rewards received by delegators. Note that the 8% fee is only applied to the staking reward, not to the tokens delegated. | | E | Annual Staking Rewards | 50,000 FLOW | Product of A x C; the number shown is annualized but is paid each epoch (week). | | F | Annual Delegator Fee | 8,000 FLOW | Product of B x C x D; ; the number shown is annualized but is paid each epoch (week). | | G | Annual (Gross) Rewards | 58,000 FLOW | Sum of E and F | | H | COGS | 4,190 FLOW | Assumed costs of running a consensus node in FLOW assuming 1US$/FLOW. The actual cost will vary depending on several factors such as self-hosted vs cloud, bare metal vs VM, the type of node, the FLOW exchange rate. | | J | Net Annual Rewards | 53,810 FLOW | G less H | ## Note 1. Each year, 5% of the total Flow supply is distributed as rewards to incentivize validators and delegators. While the total rewards for each epoch are fixed, the rewards for individual stakers vary depending on the amount they stake and the total funds delegated to their node. 2. All Flow node types follow the same economic principles, with the only difference being their minimum staking requirements. For details on the minimum stakes needed for each node type, see [here](https://flow.com/flow-tokenomics/technical-overview). --- # File: /md/protocol/node-ops/node-operation/node-migration.md --- title: Node Migration description: How to migrate a Flow node from one machine to another sidebar_position: 9 --- There are a few different methods to migrate a running Flow node from one machine to the other. Choose the method depending upon what part of the staking data of the node is changing. ### Method 1 - No change to the node staking data If there is no change to the network address or the staking and networking keys and only the hardware the node is running needs to be changed then do the following: 1. Stop the Flow node. 2. Copy over the bootstrap data (typically under `/var/flow/bootstrap`) which contains the node private key to the new machine. 3. Copy over the data folder (typically under `/var/flow/data`) which contains the state data. 4. Start the new node on the same network address as the old one. :::warning Please ensure that there is minimal downtime during this migration. ::: :::warning The network address is currently part of the staking data that was submitted for the node. It is how other nodes in the network discover this node. Hence, the network address of the node must stay the same between epochs otherwise the node will become unreachable for the other nodes and stop functioning. ::: ### Method 2 - Network address change A change to the node network address (IP or a hostname) can only be done during the spork process. To change the networking address: 1. A day before the upcoming mainnet spork, change the network address for the nodes in Flow Port (using the update network address feature). The change will not take effect till an epoch transition happens. 2. Change the addresses in the `/var/flow/bootstrap/private-root-information/private-node-info_/node-info.priv.json` json file on the node. 3. A spork also causes an epoch transition, and the new addresses will take effect after the spork immediately. ### Method 3 - Staking or networking key change If the node after migration will be using new staking or networking keys then it needs to be unstaked and then re-staked with the new keys. 1. Unstake the node via Flow Port. 2. Register the new node via Flow Port with the new staking information. 3. Run the new node with the new keys and network address. It should be able to join the network at the next epoch (see [timing](./node-bootstrap.md#timing)) :::warning Unstaking a node will result in the node [not earning rewards](../../staking/06-technical-overview.md#staking-operations-available-to-all-stakers) for the next epoch. Delegators to the old node will have their tokens unstaked automatically. They will also stop earning rewards unless they withdraw their unstaked tokens and delegate them to a different node. ::: --- # File: /md/protocol/node-ops/node-operation/node-providers.md --- sidebar_position: 18 description: | Easy access to Flow's blockchain. Providers handle the technical work, letting you use Flow's features without managing nodes yourself. sidebar_custom_props: icon: 📚 --- # Node Providers ## Quick Node [QuickNode](https://www.quicknode.com/chains/flow) offers convenient access to Flow's blockchain infrastructure, allowing developers and businesses to utilize Flow's capabilities without the complexities of managing nodes themselves. It offers reliable and fast connectivity to blockchain networks, sparing users from the resource-intensive task of running their own full nodes. ### Supported Networks - Testnet - Mainnet ## Tatum [Tatum](https://tatum.io/) provides a comprehensive platform that simplifies the process of building, testing, and deploying blockchain applications. With Tatum, users can access infrastructure, an SDK, and a unified API to develop blockchain apps without the need to handle individual blockchain node configuration or maintenance. ### Supported Networks - Testnet - Mainnet --- # File: /md/protocol/node-ops/node-operation/node-provisioning.md --- title: Provisioning a Flow node sidebar_label: Node Provisioning description: Hardware, networking and Operating system setup for a Flow node sidebar_position: 10 --- ## Hardware Requirements The hardware your Node will need varies depending on the role your Node will play in the Flow network. For an overview of the differences see the [Node Roles Overview](./node-roles.md). | Node Type | CPU | Memory | Disk | Example GCP Instance | Example AWS Instance | |:----------------:|:--------------------------------:|:---------------------------:|:-----------------------------------:|:--------------------:|:--------------------:| | **Collection** | 2 cores | 8 GB | 200 GB | n2-standard-2 | m5.large | | **Consensus** | 2 cores | 8 GB | 200 GB | n2-standard-2 | m5.large | | **Execution** | 80 cores 🆕
    (was 128 cores) | 640 GB 🆕
    (was 864 GB) | 6 TB1 🆕
    (was 9 TB) | n2-highmem-80 | | | **Verification** | 2 cores | 8 GB | 200 GB | n2-standard-2 | m5.large | | **Access** | 8 cores | 32 GB | 750 GB | n2-standard-8 | m5.2xlarge | | **Observer** | 2 cores | 4 GB | 300 GB | n2-standard-4 | m6i.xlarge | | **EVM Gateway** | 2 cores | 32 GB | 30 GB | n2-highmem-4 | r6i.xlarge | 1: Recommended with maintenance see: [pruning chunk data pack](https://forum.flow.com/t/execution-node-upgrade-to-v0-31-15-and-managing-disk-space-usage/5167), 30 TB without maintenance. _Note: The above numbers represent our current best estimate for the state of the network. These will be actively updated as we continue benchmarking the network's performance._ _Note: If you are running your node on bare metal, we recommend provisioning a machine with higher CPU and memory than the minimum requirements. Unlike cloud instances, bare metal servers cannot be easily scaled up, and over-provisioning upfront helps avoid the need for disruptive hardware upgrades later._ ## Networking Requirements Most of the load on your nodes will be messages sent back and forth between other nodes on the network. Make sure you have a sufficiently fast connection; we recommend at _least_ 1Gbps, and 5Gbps is better. Each node will require a fixed DNS name and we will refer to this more generally as your 'Node Address' from here on out. Your Node Address must be a publicly routable valid DNS name that points to your node. This is how other nodes in the network will communicate with you. Your firewalls must expose **TCP/3569** for both, ingress and egress. If you are running an Access Node, you must also expose the GRPC port **9000** to your internal network traffic. Port 9000 is not required for external ingress/egress. ![Flow Architecture](flow-architecture.png) ## Operating System Requirements The Flow node code is distributed as a Linux container image, so your node must be running an OS with a container runtime like [docker](https://docker.com) or [containerd](https://containerd.io). The bootstrapping scripts we'll use later are compiled binaries targeting an `amd64` architecture, so your system must be 64-bit. Some of these scripts are bash based hence a shell interpreter that is bash compatible will also be needed. Flow also provides `systemd` service and unit files as a template for installation, though `systemd` is not required to run Flow. Flow is distributed in such a way that makes it very system agnostic. You are free to build your own orchestration around how you run your nodes and manage your keys. For the remainder of this guide, we cover the most simple case, a single node being hand deployed. This should give you a good sense of what's needed, and you can modify to suit your needs from there. The Flow team has tested running nodes on Ubuntu 18.04 and GCP's Container Optimized OS, which is based on Chromium OS. If you are unsure where to start, those are good choices. ## Time synchronization You should also ensure you run **time synchronization** on the machine hosting the container, to avoid clock drift. In practice, this means configuring a client for the NTP protocol, and making sure it runs as a daemon. `ntpd` is one recommended example. To configure it, you just have to point it to an NTP server to query periodically. A default from your Linux distribution or cloud operator may already be set, and in the interest of decentralization, our recommendation would be to use it unless you have a specific reason to do otherwise. - **Leap-smearing**: Leap-smearing time servers and non-leap-smearing time servers are both acceptable for the magnitude of our time precision requirements - though considering very few providers offer leap smearing time servers, a "regular" time server helps ensure our pool of time providers is more diverse. - **Why not do it in the container itself? Why do we need to do this?**: Without special privileges and in all major container runtimes, a container will not run with the `CAP_SYS_TIME` capability. For Flow, this means that the node software itself cannot change the time of the host machine, making the in-container use of standard time synchronization protocols ineffective. - **Why does time matter in Flow?**: Time information comes up in consensus and in smart contracts. The consensus algorithm of Flow allows nodes to exit the influence of a corrupt or ineffective "leader" node by collectively deciding to switch to the next "phase" of the protocol at the right time. The smart contract language also allows developer access to block time stamps, which provide an approximation of time. To resist manipulation in each case, honest nodes must compute timing values from an aggregate of the information provided by all nodes. That approach, though resilient, is still sensitive to inaccurate time information. In other words, a node subject to clock drift but otherwise honest will not stop the consensus, but might make it slower. ## Setup Data Directories & Disks Flow stores protocol state on disk, as well as execution state in the case of execution nodes. Where the data is stored is up to you. By default, the `systemd` files that ship with Flow use `/var/flow/data`. This is where the vast majority of Flow's disk usage comes from, so you may wish to mount this directory on a separate disk from the OS. The performance of this disk IO is also a major bottleneck for certain node types. While all nodes need to make use of this disk, if you are running an execution node, you should make sure this is a high performing SSD. As a rough benchmark for planning storage capacity, each Flow block will grow the data directory by 3-5KiB. ### Confidential Data & Files Flow stores dynamically generated confidential data in a separate database. We strongly recommend enabling encryption for this database - see [this guide](./db-encryption-existing-operator.md) for instructions. Confidential information used by Flow is stored in the `private-root-information` subtree of the `bootstrap` folder. In particular: * the staking private key (`node-info.priv.json`) * the networking private key (`node-info.priv.json`) * the encryption key for the secrets database (`secretsdb-key`) * (if applicable) the initial random beacon private key (`random-beacon.priv.json`) These files contain confidential data, and must be stored and accessed securely. --- # File: /md/protocol/node-ops/node-operation/node-roles.md --- title: Node Roles sidebar_position: 11 --- Unlike most blockchains, not all Flow nodes are equal. Flow nodes all specialize and fulfill a specific role in the operation of the network. Collection, consensus, execution, verification and access nodes are all staked nodes while the observer node is not staked. ## Collection Collection nodes are bandwidth-optimized nodes divided by the protocol into several cooperating Clusters. Their first task is managing the transaction pool and collecting well-formed transactions to propose to Consensus nodes. Transactions are assigned to a cluster pseudorandomly by transaction hash. A well-formed transaction must include credentials from the guarantor of the transaction. When a Collection Node sees a well-formed transaction, it hashes the text of that transaction and signs the transaction to indicate two things: first, that it is well-formed; and second, that it will commit to storing the transaction text until the Execution nodes have finished processing it. Each cluster collects transactions, assembles them into Collections and submits a Collection Guarantee signed by a super-majority of the cluster to the Consensus nodes. Collection nodes are required to stake a minimum of 250,000 FLOW to be a confirmed node operator. ## Consensus Consensus nodes form and propose blocks in a manner similar to traditionally-structured proof-of-stake blockchains, using the HotStuff consensus algorithm to create a globally consistent chain of blocks. Consensus nodes validate that the signed collection hashes submitted to them by Collection nodes were, in fact, signed by the required majority of Collection nodes. Thereafter, the Consensus nodes assemble the transactions into blocks and finalize them through voting. The more participants there are in this process, the more decentralized the network. However, consensus algorithms typically bottleneck the limit to the number of participants. The Flow protocol chose the HotStuff algorithm because it is flexible enough to add participants and currently supports about 255 operators. Adding more than 255 participants to the protocol by adapting HotStuff will continue to be an area of active development. Consensus nodes act as checkpoints against other Collection nodes. They are responsible for checking that a critical number of Collection nodes reviewed and signed for the transaction. Collection nodes are held accountable by Consensus nodes. A common concern with proof-of-work- and proof-of-stake based systems is that a small subset of the population of nodes can control important resources such as the mining or stake needed to produce and vote on blocks, which is a degradation of the security of the system. By lowering the requirements to participate, Flow makes it extremely difficult and expensive to coordinate a Byzantine majority of Consensus nodes. Consensus nodes have minimal bandwidth and computation requirements, allowing even a modest computing device (any consumer-grade hardware) to participate in the voting process and ensure the safety of the network. Many networks claim open participation, yet substantial resources — stake, computation, or otherwise — are needed to partake. Maintaining such barriers to entry undermines the security of the network. Lowering the participation requirements preserves the security of the network by providing a high degree of byzantine fault tolerance since it becomes exceedingly difficult for a subset of bad actors to subvert the network. Consensus nodes are required to stake a minimum of 500,000 FLOW to be a confirmed node operator. ## Execution Execution nodes are the most resource-intensive nodes on the Flow network, responsible for executing transactions and maintaining the Execution State — a cryptographically-verifiable data store for all user accounts and smart contract states — as well as responding to queries related to it. Execution nodes compute the outputs of the blocks they are provided. They then ask the Collection nodes for the collections which contain transactions waiting to be executed. With this data they are able to compute the output, which is later verified by Verification nodes to ensure honesty (allocation of Verification nodes is via a sortition algorithm). The Execution nodes are primarily responsible for Flow's improvements in scale and efficiency because only a very small number of these powerful compute resources are required to compute and store the historical state. Execution nodes give the Flow network its performance characteristics: highly scalable within a single shared state environment (i.e., no sharding). However, the significant hardware requirements make them the least accessible option for participation as a Validator. Because the revenue pool splits between relatively few nodes, the revenue per-node should more than compensate for the high capital costs of operating this node. An Execution Node presents a hashed commitment once it has computed the output. The output is only revealed once its co-executors have also submitted their outputs. This is important to ensure nodes aren't spoofing each other's work. Once they've all submitted their answers, the output is revealed and subjected to random queries and checks run by Verification nodes. The Execution nodes have relatively low byzantine fault tolerance. However, this does not compromise the overall security of the system because the process they perform is deterministic -- any bad actor will easily be detected and punished by Verification nodes. This relatively small group of nodes has the most substantial technical requirements for processor speed and bandwidth because they are tasked with all the computations necessary to determine the output of the network. Allowing for this degree of specialization can reduce computation costs by at least one thousand times, and possibly much more, when compared to Ethereum. Execution nodes are required to stake a minimum of 1,250,000 FLOW to be a confirmed node operator. ## Verification Verification nodes are responsible for confirming the correctness of the work done by Execution nodes. Individual Verification nodes only check a small amount of the total computation, but collectively they check every computation many times in parallel. Verification nodes verify Execution Receipts provided by Execution nodes and issue Result Approvals. A sortition algorithm determines which chunks of the Execution Receipt from the Execution nodes the Verification Node must query to check they were computed correctly. Ultimately, these nodes keep the Execution nodes honest; this balance of power maintains the access, security, and verifiability criteria of decentralization. It is highly byzantine fault tolerant because even if there is a substantial number of byzantine errors in the Verification Node pool, the Consensus nodes are still required to approve that transactions they signed were reviewed by a critical amount of the network. Verification nodes are required to stake a minimum of 135,000 FLOW to be a confirmed node operator. ## Access Access nodes act as a proxy to the Flow network. The Access node routes transactions to the correct collection node and routes state queries to execution nodes (while likely caching state to answer queries in a timely manner in the future). Clients submit their transactions to any Access node or run their own if they can't find a service provider they're happy with. Access nodes are required to stake 100 FLOW to be a confirmed node operator. However, since an access node does not participate in block production, it does not receive any staking rewards. ## Observer An observer node provides locally accessible, continuously updated, verified copy of the block data. It serves the Access API but unlike an access node, an observer node does not need to be staked, and **anyone** can run it without being added to the approved list of nodes. [Get started running an observer node](../light-nodes/observer-node.md) ## Here is a comparison of the different node roles, | Role | Staked | Recives Rewards | Permissioned | | :---------------|:---------------:| :---------------:|:---------------:| | Collection | Yes | Yes | Yes | | Consensus | Yes | Yes | Yes | | Execution | Yes | Yes | Yes | | Verification | Yes | Yes | Yes | | Access | Yes | No | No 🆕 | | Observer | No | No | No | --- # File: /md/protocol/node-ops/node-operation/node-setup.md --- title: Setting Up a Flow Node sidebar_label: Node Setup description: How to run a Collection, Consensus, Verification and Execution node sidebar_position: 12 --- This guide is for running a Collection, Consensus, Verification and Execution node. If you are planning to run an Access node then refer to [access node setup](../access-nodes/access-node-setup.md). First you'll need to provision a machine or virtual machine to run your node software. Please see follow the [node-provisioning](./node-provisioning.md) guide for it. ## Pull the Flow Images The `flow-go` binaries are distributed as container images, and need to be pulled down to your host with your image management tool of choice. Replace `$ROLE` with the node type you are planning to run. Valid options are: - collection - consensus - execution - verification - access ```shell # Docker docker pull gcr.io/flow-container-registry/${ROLE}:alpha-v0.0.1 # Containerd ctr images pull gcr.io/flow-container-registry/${ROLE}:alpha-v0.0.1", ``` ## Prepare Your Node to Start Your nodes will need to boot at startup, and restart if they crash. If you are running `systemd` you can use the service files provided by `flow-go`. Find them in the [Flow Go](https://github.com/onflow/flow-go/tree/master/deploy). If you are using some other system besides Systemd, you need to ensure that the Flow container is started, the appropriate key directories are mounted into the container, and that the container will automatically restart following a crash. The `systemd` files pull runtime settings from `/etc/flow/runtime-config.env` and any `.env` files under `/etc/flow/conf.d`. Examples of these files are also available in the github repo. You will need to modify the runtime config file later. ### Systemd If you are not using Systemd, you can skip this step 1. Ensure that you pulled the latest changes from [flow-go repository](https://github.com/onflow/flow-go) via `git` ```shell ## Clone the repo if you haven't already done so git clone https://github.com/onflow/flow-go ## Get latest changes cd flow-go git pull origin master ``` 2. Copy your respective [systemd unit file](https://github.com/onflow/flow-go/tree/master/deploy/systemd-docker) to: `/etc/systemd/system` 3. Create directory `sudo mkdir /etc/flow` 4. Copy the [runtime-conf.env](https://github.com/onflow/flow-go/blob/master/deploy/systemd-docker/runtime-conf.env) file to: `/etc/flow/` 5. Enable your service `sudo systemctl enable flow-$ROLE.service` (replace `$ROLE` with your node role - eg. `collection`) ### Docker Configuration If you are not using Systemd, sample commands for running each Docker container are below. Be sure to replace `/path/to/data` and `/path/to/bootstrap` with the appropriate paths you are using. :::warning Do not run your node using `docker run` command directly without a mechanism for the node to automatically restart following a crash. ::: :::info The actual Docker image tag can be found [here](./past-upgrades) for appropriate spork. ::: ### System Configuration Flow nodes create connections to other nodes on the network, which are represented as file descriptors by the OS. Depending on the default limits for your machine, you may need to increase the soft limit available to the node software. Make sure the soft limit is at least `8192`. You can configure the ulimit for the node's docker container. See the [Docker documentation](https://docs.docker.com/engine/protocol/commandline/run/#ulimit) for more details. ### Admin Server Each node can be configured with an admin server, which allows you to control some of the node's configuration, as well as view some of its internal state. You can find a few of the commands in the Admin Server [README](https://github.com/onflow/flow-go/blob/master/admin/README.md). Two commands to highlight are: - `list-commands`: which returns a list of all of the available commands for your node - `set-log-level`: which allows you to change the log level of your node at runtime You can enable the admin server by passing the `--admin-addr` flag with an interface and port. > ⚠️ _IMPORANT: The admin server can modify your node's configuration. DO NOT allow access to untrusted clients._ ### Access ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ --ulimit nofile=8192 \ gcr.io/flow-container-registry/access: \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --execution-data-dir=/data/execution_data \ --rpc-addr=0.0.0.0:9000 \ --http-addr=0.0.0.0:8000 \ --admin-addr=0.0.0.0:9002 \ --collection-ingress-port=9000 \ --script-addr=${FLOW_NETWORK_EXECUTION_NODE} \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ### Collection ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ --ulimit nofile=8192 \ gcr.io/flow-container-registry/collection: \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --ingress-addr=0.0.0.0:9000 \ --admin-addr=0.0.0.0:9002 \ --bind 0.0.0.0:3569 \ --access-node-ids=4e17496619df8bb4dcd579c252d9fb026e54995db0dc6825bdcd27bd3288a990,c0710209b76bd840f0f4c1fb66f6863a712581f39b5b09b96c91e50a3f206c0e,1f46b4d23c7557e7b3c21f85aebddcb7bcda7cc4d589732ce53087cb1b5faf8e \ --gossipsub-peer-scoring-enabled=false \ --gossipsub-peer-gater-enabled=true \ --loglevel=error ``` ### Consensus ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ --ulimit nofile=8192 \ gcr.io/flow-container-registry/consensus: \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --admin-addr=0.0.0.0:9002 \ --bind 0.0.0.0:3569 \ --access-node-ids=4e17496619df8bb4dcd579c252d9fb026e54995db0dc6825bdcd27bd3288a990,c0710209b76bd840f0f4c1fb66f6863a712581f39b5b09b96c91e50a3f206c0e,1f46b4d23c7557e7b3c21f85aebddcb7bcda7cc4d589732ce53087cb1b5faf8e \ --gossipsub-peer-scoring-enabled=false \ --gossipsub-peer-gater-enabled=true \ --loglevel=error ``` ### Execution ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ --ulimit nofile=500000 \ gcr.io/flow-container-registry/execution: \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --triedir=/data/execution \ --execution-data-dir=/data/execution_data \ --rpc-addr=0.0.0.0:9000 \ --admin-addr=0.0.0.0:9002 \ --bind 0.0.0.0:3569 \ --loglevel=error ``` For execution nodes, it is recommend to increase the open files limit in your operating system. To do that, add the following to your `/etc/security/limits.conf` or the equivalent `limits.conf` for your distribution: ``` * hard nofile 500000 * soft nofile 500000 root hard nofile 500000 root soft nofile 500000 ``` Restart your machine to apply these changes. To verify that the new limits have been applied, run: ``` ulimit -n ``` ### Verification ```shell docker run --rm \ -v /path/to/bootstrap:/bootstrap:ro \ -v /path/to/data:/data:rw \ --name flow-go \ --network host \ --ulimit nofile=8192 \ gcr.io/flow-container-registry/verification: \ --nodeid=${FLOW_GO_NODE_ID} \ --bootstrapdir=/bootstrap \ --datadir=/data/protocol \ --secretsdir=/data/secrets \ --admin-addr=0.0.0.0:9002 \ --bind 0.0.0.0:3569 \ --loglevel=error ``` ## Start the Node Now that your node is provisioned and configured, it can be started. Before starting your node, ensure it is [registered](./node-bootstrap.md#step-2---stake-your-node) and [authorized](./node-bootstrap.md#confirming-authorization). Ensure you start your node at the appropriate time. See [Spork Process](./network-upgrade) for when to start up a node following a spork. See [Node Bootstrap](./node-bootstrap.md#timing) for when to start up a newly registered node. ### Systemd 1. Check that your `runtime-conf.env` is at `/etc/flow/runtime-conf.env` 2. Update your environment variables: `source /etc/flow/runtime-conf.env` 3. Start your service: `sudo systemctl start flow` ## Verify your Node is Running Here are a few handy commands that you can use to check if your Flow node is up and running ### Systemd - To get Flow logs: `sudo journalctl -u flow-YOUR_ROLE` - To get the status: `sudo systemctl status flow` ```shell ● flow-verification.service - Flow Access Node running with Docker Loaded: loaded (/etc/systemd/system/flow-verification.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 18:18:13 UTC; 1 day 6h ago Process: 3207 ExecStartPre=/usr/bin/docker pull gcr.io/flow-container-registry/verification:${FLOW_GO_NODE_VERSION} (code=exited, status=0/SUCCESS) Main PID: 3228 (docker) Tasks: 10 (limit: 4915) Memory: 33.0M CGroup: /system.slice/flow-verification.service └─3228 /usr/bin/docker run --rm -v /var/flow/bootstrap:/bootstrap:ro -v /var/flow/data:/data:rw --rm --name flow-go --network host gcr.io/flow-container-registry/verification:candidate8 --nodeid=489f8a4513d5bd8b8b093108fec00327b683db545b37b4ea9153f61b2c0c49dc --bootstrapdir=/bootstrap --datadir=/data/protocol --alpha=1 --bind 0.0.0.0:3569 --loglevel=error ``` ### Docker - To get Flow logs: `sudo docker logs flow-go` - To get the status: `sudo docker ps` ```shell $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1dc5d43385b6 gcr.io/flow-container-registry/verification:candidate8 \"/bin/app --nodeid=4…\" 30 hours ago Up 30 hours flow-go ``` ## Monitoring and Metrics This is intended for operators who would like to see what their Flow nodes are currently doing. Head over to [Monitoring Node Health](./monitoring-nodes.md) to get setup. ### Node Status The metrics for the node should be able to provide a good overview of the status of the node. If we want to get a quick snapshot of the status of the node, and if it's properly participating in the network, you can check the `consensus_compliance_finalized_height` or `consensus_compliance_sealed_height` metric, and ensure that it is not zero and strictly increasing. ```shell curl localhost:8080/metrics | grep consensus_compliance_sealed_height # HELP consensus_compliance_sealed_height the last sealed height # TYPE consensus_compliance_sealed_height gauge consensus_compliance_sealed_height 1.132054e+06 ``` --- # File: /md/protocol/node-ops/node-operation/past-upgrades.md --- title: Past Network Upgrades description: Information about Flow Mainnet and Testnet network upgrades. sidebar_position: 14 --- A JSON version of the information below can be found in [onflow/flow/sporks.json](https://github.com/onflow/flow/blob/master/sporks.json) ## Mainnet Upgrades > Currently only includes HCUs from 2025. ## Height Coordinated Upgrade 12 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Aug 7, 2025 | | **Block Height**: 122174655 | | **Git Commit**: 5ad9dd5706ae11656b7367c289bb2384a350c88d | | **Branch/Tag**: v0.42.3 | | **Docker Image Tag**: v0.42.3 or v0.42.1-rc.3 | | **Release Notes**: [v0.42.3](https://github.com/onflow/flow-go/releases/tag/v0.42.3) | ## Height Coordinated Upgrade 11 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: June 17, 2025 | | **Block Height**: 116720200 | | **Git Commit**: 5fe65350f5bffad0c6602787952a8d922861e754 | | **Branch/Tag**: v0.42.1 | | **Docker Image Tag**: v0.42.1 | | **Release Notes**: [v0.42.1](https://github.com/onflow/flow-go/releases/tag/v0.42.1) | ## Height Coordinated Upgrade 10 | HCU Info | |:-----------------------------------------------------------------------------------------------| | **Date**: June 3, 2025 | | **Block Height**: 115211511 | | **Git Commit**: 5a5a11d628a4c0e8d19592dc69b3e869aea71241 | | **Branch/Tag**: v0.42.0-rc.2 | | **Docker Image Tag**: v0.42.0-rc.2 | | **Release Notes**: [v0.42.0-rc.2](https://github.com/onflow/flow-go/releases/tag/v0.42.0-rc.2) | ## Height Coordinated Upgrade 9 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: May 23, 2025 | | **Block Height**: 114045444 | | **Git Commit**: b120bc4855ee4863fcfcb0d1f26a6e9eef990c90 | | **Branch/Tag**: v0.41.4 | | **Docker Image Tag**: v0.41.4 | | **Release Notes**: [v0.41.4](https://github.com/onflow/flow-go/releases/tag/v0.41.4) | ## Height Coordinated Upgrade 8 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: May 15, 2025 | | **Block Height**: 113162222 | | **Git Commit**: 0000b9b745b51db1f6f114ec1b903a265fa7a08f | | **Branch/Tag**: v0.41.0 | | **Docker Image Tag**: v0.41.0 | | **Release Notes**: [v0.41.0](https://github.com/onflow/flow-go/releases/tag/v0.41.0) | ## Height Coordinated Upgrade 7 | HCU Info | |:--------------------------------------------------------------------------------------| | **Date**: April 10, 2025 | | **Block Height**: 109384800 | | **Git Commit**: 86c40b1ff20b3b7f17c4017bde083d26303c508d | | **Branch/Tag**: v0.40.0 | | **Docker Image Tag**: v0.40.0 | | **Release Notes**: [v0.40.0](https://github.com/onflow/flow-go/releases/tag/v0.40.0) | ## Rolling Upgrade 1 | RU Info | |:-------------------------------------------------------------------------------------| | **Date**: April 3, 2025 | | **Block Height**: 108630638 | | **Block View **: 20504725 | | **Git Commit**: ad1076f0a36bb2d20cc36295d3573989edc15c6c | | **Branch/Tag**: v0.39.0 | | **Docker Image Tag**: v0.39.0 | | **Release Notes**: [v0.39.0](https://github.com/onflow/flow-go/releases/tag/v0.39.0) | ## Height Coordinated Upgrade 6 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Feb 18, 2025 | | **Block Height**: 103983000 | | **Git Commit**: 4e7e56b3a92e5772279f1304d88dd445c0ea5016 | | **Branch/Tag**: v0.38.3 | | **Docker Image Tag**: v0.38.3 | | **Release Notes**: [v0.38.3](https://github.com/onflow/flow-go/releases/tag/v0.38.3) | ## Height Coordinated Upgrade 5 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Feb 18, 2025 | | **Block Height**: 103957477 | | **Git Commit**: bcb8a2264fcde9dcd4c997f6b28d8184af19160b | | **Branch/Tag**: v0.38.2 | | **Docker Image Tag**: v0.38.2 | | **Release Notes**: [v0.38.2](https://github.com/onflow/flow-go/releases/tag/v0.38.2) | ## Height Coordinated Upgrade 4 | HCU Info | |:---------------------------------------------------------------------------------------| | **Date**: Jan 27, 2025 | | **Block Height**: 101584244 | | **Git Commit**: 5f6b25bd02257e3239341c4be0134b007f3deb49 | | **Branch/Tag**: v0.37.26 | | **Docker Image Tag**: v0.37.26 | | **Release Notes**: [v0.37.26](https://github.com/onflow/flow-go/releases/tag/v0.37.26) | ## Mainnet 28 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access.mainnet.nodes.onflow.org:9000 | | **Date**: Dec 28, 2025 | | **Root Height**: 137390146 | | **Root Parent ID**: ee69349b5044c4c1cb959ac5beccbe3df896b370fb098808fb9a6eada57b3f7f | | **Root State Commit**: d0a13c09f930322c5f053113354211acbf4d8c5eeb524c7004eb7c9d052c4bbe | | **Git Commit**: dd73c65fe83822e5fc2846879fb93f8a15b1b25c | | **Branch/Tag**: [v0.44.3](https://github.com/onflow/flow-go/releases/tag/v0.44.3) | | **Docker Image Tag**: v0.44.3 | ## Mainnet 27 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access.mainnet.nodes.onflow.org:9000 | | **Date**: Oct 22, 2025 | | **Root Height**: 130290659 | | **Root Parent ID**: 28d657bc8c6b0608e4c7caef3fb37b5b05f58c7bfedd1b8e556bd0940735e9cd | | **Root State Commit**: 46c078553c221a7265bea83ff9aa178ec6cbe4865a6ae55219ecd0db81357a67 | | **Git Commit**: fabd1242ca2266a2c0f53fa31a1636ea664bd45d | | **Branch/Tag**: [v0.43.3](https://github.com/onflow/flow-go/releases/tag/v0.43.2) | | **Docker Image Tag**: v0.43.3 | ## Mainnet 26 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access-001.mainnet26.nodes.onflow.org:9000 | | **Date**: Sep 25, 2024 | | **Root Height**: 88226267 | | **Root Parent ID**: 71052eb6e774ae5065b31c604a85af47fa16d5c53e54b9a992581c1ecf0ecfac | | **Root State Commit**: 3bba639062a723af1b44b1dfe07e795d158482f02f807f1df0b7c39edd6a8cca | | **Git Commit**: 25d9c2a9b89bac8fa003ca67928eb79b1427ea17 | | **Branch/Tag**: v0.37.16-patch.1 | | **Docker Image Tag**: v0.37.16-patch.1 | ## Mainnet 25 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access-001.mainnet25.nodes.onflow.org:9000 | | **Date**: Sep 4, 2024 | | **Root Height**: 85981135 | | **Root Parent ID**: bc16d37060cb408163a04afe406b9c9398a31663c839de35b92e3c3b10bcf834 | | **Root State Commit**: dead79e8f86d20ea3214735d4247b7fac1b4408e93e9b092fe0566cf40cecb9e | | **Git Commit**: feabd3a4f9baaa5d7770a312e5b8dc1bd94b1edb | | **Branch/Tag**: [v0.37.10](https://github.com/onflow/flow-go/releases/tag/v0.37.10) | | **Docker Image Tag**: v0.37.10 | ## Mainnet 24 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access-001.mainnet24.nodes.onflow.org:9000 | | **Date**: Nov 8, 2023 | | **Root Height**: 65264619 | | **Root Parent ID**: aace2d9b6e66067989d0f71c2efff38fe30d88da61e3d80946c7e7b4ee2bbc2f | | **Root State Commit**: 709530929e4968daff19c303ef1fc5f0a7649b3a1ce7d5ee5202056969524c94 | | **Git Commit**: e63117642e34b215993d14d36622d45df249016c | | **Branch/Tag**: [v0.32.7](https://github.com/onflow/flow-go/releases/tag/v0.32.7) | | **Docker Image Tag**: v0.32.7 | ## Mainnet 23 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet23.nodes.onflow.org:9000 | | **Date**: Jun 20, 2023 | | **Root Height**: 55114467 | | **Root Parent ID**: dcaa1469c6cd67154942c70b594bdff407ea28eda1fc9c4a81a814f02dc2efc9 | | **Root State Commit**: 5586f6b9af7c0d8efa7b403dbd3a894d71a18faad5a1abda48d3dfb7dcd4b017 | | **Git Commit**: 0f6ea38efc91b7d27736b8b2c94091076c624796 | | **Branch/Tag**: [v0.31.9](https://github.com/onflow/flow-go/releases/tag/v0.31.9) | | **Docker Image Tag**: v0.31.9 | ## Mainnet 22 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet22.nodes.onflow.org:9000 | | **Date**: Feb 22, 2023 | | **Root Height**: 47169687 | | **Root Parent ID**: 60a976d4cc36d0a5150d3f81ede85809916d4af9eb791d9190af0a12c1fd8a17 | | **Root State Commit**: c9c9d3da3fe059a616b13768da2374275bd1a35f94d753ee8e41c538a3cc92d8 | | **Git Commit**: e644427a8e83e8cd2a307c40e4c8fd3066008cae | | **Branch/Tag**: [v0.29.13](https://github.com/onflow/flow-go/releases/tag/v0.29.13) | | **Docker Image Tag**: v0.29.13 | ## Mainnet 21 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet21.nodes.onflow.org:9000 | | **Date**: Jan 18, 2023 | | **Root Height**: 44950207 | | **Root Parent ID**: 52004adfa7854b1a515d0d905dd5317dc7f77a8dbb56058e04dde01e53e80a92 | | **Root State Commit**: 905c5e9a576ba2cbe49b5fe5f64ae84c2dee1bc26a3e81932e573e06a722d08a | | **Git Commit**: 7f02a642bb437b45326c4ace54a7f033b32832f8 | | **Branch/Tag**: [v0.29.6](https://github.com/onflow/flow-go/releases/tag/v0.29.6) | | **Docker Image Tag**: v0.29.6 | ## Mainnet 20 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet20.nodes.onflow.org:9000 | | **Date**: Nov 2, 2022 | | **Root Height**: 40171634 | | **Root Parent ID**: f66302ac43623a87d29dfdeb08fce5d19e3af7be2e4283d468f74ee10468f248 | | **Root State Commit**: ec1e1cd34bb05b5abb3c4701a7f365d1dde46d9d908dc57420bde8b4a53d940a | | **Git Commit**: b9b941db9a3949db0e299a40264d852980d35ddd | | **Branch/Tag**: [v0.28.6](https://github.com/onflow/flow-go/releases/tag/v0.28.6) | | **Docker Image Tag**: v0.28.6 | ## Mainnet 19 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet19.nodes.onflow.org:9000 | | **Date**: Aug 24, 2022 | | **Root Height**: 35858811 | | **Root Parent ID**: 5c030d4125f8ace0e0ab8255880143190d58aca4ddb7c4720c28eaf497adcae1 | | **Root State Commit**: 96ff0d9a2b7d8264ea8727d4a104d7372efcfe18dc0c9111aff5c46b688eff04 | | **Git Commit**: b6e9a988514d13e1e1ecd0802d7e02f9e9b1415b | | **Branch/Tag**: [v0.27.4](https://github.com/onflow/flow-go/releases/tag/v0.27.4) | | **Docker Image Tag**: v0.27.4 | ## Mainnet 18 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet18.nodes.onflow.org:9000 | | **Date**: Jun 15, 2022 | | **Root Height**: 31735955 | | **Root Parent ID**: 716d54edbb3d9b2ad290709a516f1ffe4290c7b7b33a49bd0480e0b193a45884 | | **Root State Commit**: 5bdae9f5fb4cc5d63075547df5502e2bc3cb20707452389fb89ebbe71ecf7b68 | | **Git Commit**: fdc732183233f1c577a9e529da6b453364431301 | | **Branch/Tag**: [v0.26.9](https://github.com/onflow/flow-go/releases/tag/v0.26.9) | | **Docker Image Tag**: v0.26.9 | ## Mainnet 17 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet17.nodes.onflow.org:9000 | | **Date**: Apr 6, 2022 | | **Root Height**: 27341470 | | **Root Parent ID**: dd7ed04e14559ed47ecc92896a5cb3dbb6b234065d9be7f816d99602238762aa | | **Root State Commit**: 113e752ec0619b63187630b4fae308ec5405a00f56f25c2de0b139e283a95b14 | | **Git Commit**: 5226c35eb14890db024b9193793b0c49d1b5ad04 | | **Branch/Tag**: [v0.25.7](https://github.com/onflow/flow-go/releases/tag/v0.25.7) | | **Docker Image Tag**: v0.25.7 | ## Mainnet 16 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet16.nodes.onflow.org:9000 | | **Date**: Feb 9, 2022 | | **Root Height**: 23830813 | | **Root Parent ID**: b0e7a891682dce277a41e87e2cef52af344ac614bf70c82a5a3a801e63960e88 | | **Root State Commit**: 8964d1f74c2bed2d0fbb4a366fff1fd3c39b71653e9de4d4512090798631e5f8 | | **Git Commit**: c78cac3573e0548611f29df7cfa2db92203554c1 | | **Branch/Tag**: [v0.24.4](https://github.com/onflow/flow-go/releases/tag/v0.24.4) | | **Docker Image Tag**: v0.24.4 | ## Mainnet 15 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet15.nodes.onflow.org:9000 | | **Date**: Dec 8, 2021 | | **Root Height**: 21291692 | | **Root Parent ID**: 8a28ddc6653a8991435dfbc95103d6be1a3b653cda3d664681215ee93112a203 | | **Root State Commit**: 84d9b325d3f48f7f075eea0db6c246cb5adc759a2dd8561e461477a7ca1f7f22 | | **Git Commit**: bfde54ae3324db5d18ebeaa22c70b5574a114f2e | | **Branch/Tag**: [v0.23.4](https://github.com/onflow/flow-go/releases/tag/v0.23.4) | | **Docker Image Tag**: v0.23.4 | ## Mainnet 14 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet14.nodes.onflow.org:9000 | | **Date**: Oct 6, 2021 | | **Root Height**: 19050753 | | **Root Parent ID**: ac4dbf344ce96e39e15081f1dc3fbbf6dc80532e402de9a57af847d3b35df596 | | **Root State Commit**: 641eb088e3ce1a01ff56df2d3a14372c65a7fef44c08799eb92cd7759d1d1d2a | | **Git Commit**: f019c1dbd778ce9f92dea61349ca36003678a9ad | | **Branch/Tag**: [v0.22.9-patch-1-epoch-view-check-hotfix](https://github.com/onflow/flow-go/releases/tag/v0.22.9-patch-1-epoch-view-check-hotfix) | | **Docker Image Tag**: v0.22.9-patch-1-epoch-view-check-hotfix | ## Mainnet 13 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet13.nodes.onflow.org:9000 | | **Date**: Sept 15, 2021 | | **Root Height**: 18587478 | | **Root Parent ID**: 2f708745fff4f66db88fac8f2f41d496edd341a2837d3e990e87679266e9bdb8 | | **Root State Commit**: 51e3098d327df22fd005d960cb73167c83cb438c53e6c4363c07d8611ae44528 | | **Git Commit**: 9535540110a4452231d044aceabab0e60f67708c | | **Branch/Tag**: [v0.21.3](https://github.com/onflow/flow-go/releases/tag/v0.21.3) | | **Docker Image Tag**: v0.21.3 | ## Mainnet 12 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet12.nodes.onflow.org:9000 | | **Date**: Aug 18, 2021 | | **Root Height**: 17544523 | | **Root Parent ID**: f6ca04ba5c6fa6ba690a77202a9fad8d3ec30c67762ae065f0f0f53e8fed84d0 | | **Root State Commit**: b5cf1977b12de699d0e777af5be25095653735a153d1993f97ff07804b070917 | | **Git Commit**: 4a0c10d74f1bcaadfdfec8c325efa411acd1a084 | | **Branch/Tag**: [v0.20.5](https://github.com/onflow/flow-go/releases/tag/v0.20.5) | | **Docker Image Tag**: v0.20.5 | ## Mainnet 11 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet11.nodes.onflow.org:9000 | | **Date**: July 21, 2021 | | **Root Height**: 16755602 | | **Root Parent ID**: de15461988000eddc6e507dc7b159dcd192ee3aa72f3bd3b0e31ae9c6538399f | | **Root State Commit**: 4eb91bf34cb65b7537a6a95806f444f409308b2eaaa0ad28d1924b6cb8afa140 | | **Git Commit**: 2644560c0562412a3c2209820be07f8f3f8b1846 | | **Branch/Tag**: [v0.19.2](https://github.com/onflow/flow-go/releases/tag/v0.19.2) | | **Docker Image Tag**: v0.19.2 | ## Mainnet 10 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet10.nodes.onflow.org:9000 | | **Date**: June 23, 2021 | | **Root Height**: 15791891 | | **Root Parent ID**: 1d4109bbb364d5cdd94640546dd1c423d792d962284665233d541e4ade921726 | | **Root State Commit**: 9b6a1a5ab52fd3d5a19ea22f09cb607bba63671311f157b3e604dd265efb851a | | **Git Commit**: 01f53ebd7df3101e337d9212736cff6ab1e0056d | | **Branch/Tag**: [v0.18.4](https://github.com/onflow/flow-go/releases/tag/v0.18.4) | | **Docker Image Tag**: v0.18.4 | ## Mainnet 9 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet9.nodes.onflow.org:9000 | | **Date**: May 26, 2021 | | **Root Height**: 14892104 | | **Root Parent ID**: 265d10ba3e36ed6539fd4d7f4322735aad4997c0378d75783e471437dd83ef33 | | **Root State Commit**: fcc30a6664337ef534ad544ad7b17c5cc3b5470a8ef0d93f18573fddf6b25c4a | | **Git Commit**: 2d81520c49a8865fa686c32c508d2261155c86bc | | **Branch/Tag**: [v0.17.4](https://github.com/onflow/flow-go/releases/tag/v0.17.4) | | **Docker Image Tag**: v0.17.4 | ## Mainnet 8 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet8.nodes.onflow.org:9000 | | **Date**: April 28, 2021 | | **Root Height**: 13950742 | | **Root Parent ID**: faa2a3a996c6efcc3ef562fe03d797e4b19dbe00f6beab082d6d37a447044abd | | **Root State Commit**: 259836c7f74e6bbb803c9cfb516044bc701d99c2840e9b9f89609464867e7f0f | | **Git Commit**: 4e733ba2038512e9d80bcd955e67e88ba6e3ecf2 | | **Branch/Tag**: v0.16.2 | | **Docker Image Tag**: v0.16.2 | ## Mainnet 7 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet7.nodes.onflow.org:9000 | | **Date**: April 7, 2021 | | **Root Height**: 13404174 | | **Root Parent ID**: 8b969d0babbb7d2043957b3d55a811f2c13344faa76565096d4ad901a466ecaa | | **Root State Commit**: 1247d74449a0252ccfe4fd0f8c6dd98e049417b3bffc3554646d92f810e11542 | | **Git Commit**: b6f47fd23ffe31e2fe714c6bff0b17d901e210b4 | | **Branch/Tag**: v0.15.3-patch.4 | | **Docker Image Tag**: v0.15.3-patch.4 | Releases compatible with Mainnet 7: No change from Mainnet 6 ## Mainnet 6 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet6.nodes.onflow.org:9000 | | **Date**: Mar 10, 2021 | | **Root Height**: 12609237 | | **Root Parent ID**: c68e63ca5b6f7ff61ef2b28d7da528c5b677b0f81f2782f067679c108d77932b | | **Root State Commit**: ddfedbfaa2d858e6a8e3b142381a91b289f50e45622f5b5a86ac5c00ce61bf11 | | **Git Commit**: c887bd343ca7db6351690007b87bec40d39d7b86 | | **Branch/Tag**: v0.14.9 | | **Docker Image Tag**: v0.14.9 | Releases compatible with Mainnet 6: ### Network Implementations Flow Go: https://github.com/onflow/flow-go/releases/tag/v0.14.9
    Emulator: https://github.com/onflow/flow-emulator/releases/tag/v0.16.1
    Emulator v0.16.1 is distributed in Flow CLI v0.15.0: https://github.com/onflow/flow-cli/releases/tag/v0.15.0 ### SDK Compatibility #### Flow Go SDK Minimum version: https://github.com/onflow/flow-go-sdk/releases/tag/v0.16.1
    Recommended version: https://github.com/onflow/flow-go-sdk/releases/tag/v0.16.1
    #### FCL (Flow Client Library) Minimum version: [v0.0.66](https://github.com/onflow/fcl-js/blob/master/packages/fcl/CHANGELOG.md#0066---2020-11-09)
    Recommended version: [v0.0.67](https://github.com/onflow/fcl-js/blob/master/packages/fcl/CHANGELOG.md#0067-alpha0---2020-11-17) While FCL v0.0.67 is not strictly necessary to use Mainnet 6, we strongly recommend upgrading in order to adapt to wallet improvements that were introduced in v0.0.67. --- ## Mainnet 5 | Spork Info | | :--------------------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet5.nodes.onflow.org:9000 | | **Date**: Feb 17, 2021 | | **Root Height**: 12020337 | | **Root Parent ID**: 9131733835702b0d6321088bddb4642a4964bb5c630440ccb0de47bdbc371d1a | | **Root State Commit**: 54bef048a6c5574ef4eb452dd2698aeb2fe5eca6edd536aca6d0bc631c2daaa9 | | **Git Commit**: 027569a9d76e41b1140b189fa1b9187c711ab241 | | **Branch/Tag**: v0.14.1 for Access, Verification, Collection and v0.14.2 for Consensus and Execution | | **Docker Image Tag**: v0.14.1 / v0.14.2 | --- ## Mainnet 4 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet4.nodes.onflow.org:9000 | | **Date**: Dec 16, 2020 | | **Root Height**: 9992020 | | **Root Parent ID**: 691e35a4ac4d0d47e1be1ec81512ac9f6cdd04545b908fad1d6ceea58c76b560 | | **Root State Commit**: 0011fda57f2f3aaa8e6bcc1e1deea9778a9543252f8b65bbd4ebca3687789420 | | **Git Commit**: f4a73c7f20109209e9e2e999cf50fcf1ec41241b | | **Branch/Tag**: v0.13.1 | | **Docker Image Tag**: v0.13.1 | --- ## Mainnet 3 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet3.nodes.onflow.org:9000 | | **Date**: Dec 9, 2020 | | **Root Height**: 9737133 | | **Root Parent ID**: 116751c904a7f868cd6e8c90522fdbd70fe826db6886b830338c68c6339df3e7 | | **Root State Commit**: 1d2c91e801d0560024848a0c981e03120efc372436ada5f7909c4d44d4600f04 | | **Git Commit**: badd5887512b955e7aa18b4e73dae980ca72fa22 | | **Branch/Tag**: v0.12.6 | | **Docker Image Tag**: v0.12.6 | --- ## Mainnet 2 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet2.nodes.onflow.org:9000 | | **Date**: Nov 13, 2020 | | **Root Height**: 8742959 | | **Root Parent ID**: b35fdb189d21a95df7f19f941786f748d9854a8b93b1e555b51cda7d9f53a6e1 | | **Root State Commit**: d6a25be552ed93213df0ffc2e8c7f39f6401c04cbf22bac7a4b84d3c9493f005 | | **Git Commit**: 4ef68efb935c0e3393ae3966752ece5e7739bab4 | | **Branch/Tag**: v0.11.1 | | **Docker Image Tag**: v0.11.1 | --- ## Mainnet 1 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.mainnet1.nodes.onflow.org:9000 | | **Date**: Oct 13, 2020 | | **Root Height**: 7601063 | | **Root Parent ID**: ab1ee18b6e1c0ee11cc021c26a17c694c627699a576e85f7013cd743bdbc7877 | | **Root State Commit**: 6e1adf15689eaf5ea6859bcdd0b510f5eb4c34dac878d8577b3f65bc20c3f312 | | **Git Commit**: 114d45436e7d9052e910c98a1e40f730e3fd12d7 | | **Branch/Tag**: v0.10.1 | | **Docker Image Tag**: v0.10.1 | --- ## Candidate 9 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.candidate9.nodes.onflow.org:9000 | | **Date**: Sep 25, 2020 | | **Root Height**: 6483246 | | **Root Parent ID**: 9131733835702b0d6321088bddb4642a4964bb5c630440ccb0de47bdbc371d1a | | **Root State Commit**: 90c6f406f5d21880d525ad4702cb249509b85e7f745db2de67e9fe541a56da4c | | **Git Commit**: v0.9.3 | | **Branch/Tag**: v0.9.3 | | **Docker Image Tag**: | --- ## Candidate 8 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.candidate8.nodes.onflow.org:9000 | | **Date**: Sep 9, 2020 | | **Root Height**: 4972987 | | **Root Parent ID**: 5bc2b0900a5138e39d9209a8fe32e14b3e5c884bd36d2a645620f746b7c8bd47 | | **Root State Commit**: 6b9161a225b087a461ec95e710fdf4e73f6d6c9401ebf066207a021dced4ce5e | | **Git Commit**: | | **Branch/Tag**: | | **Docker Image Tag**: | --- ## Candidate 7 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.candidate7.nodes.onflow.org:9000 | | **Date**: Aug 24, 2020 | | **Root Height**: 4132133 | | **Root Parent ID**: 28f4f495aad016b519acf27fc9d9a328f6a4009807480e36e2df780eeccd99bc | | **Root State Commit**: 001d173bfcf9c7f71684da89bff72b3ee582b39a69c7929360230faf73735c17| | **Git Commit**: f811f8cd49369ae2bc559e0fbb781aff129484f5 | | **Branch/Tag**: candidate7 | | **Docker Image Tag**: v0.7.2 | --- ## Candidate 6 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.candidate6.nodes.onflow.org:9000 | | **Date**: Aug 18, 2020 | | **Root Height**: 3187931 | | **Root Parent ID**: 2ff5f7424a448943a153001d2f0869d4fac330906ecb8e17b7ef7fe50e4c7b36 | | **Root State Commit**: bd7f16dc5ef5eced849ab5f437547c14c1907059e1ecf89a942d0521166c5cbb | | **Git Commit**: b30c48008c0ec1cc8ecb750aeb9ff9f3d712681d | | **Branch/Tag**: | | **Docker Image Tag**: | --- ## Candidate 5 | Spork Info | | :------------------------------------------------------------------------------------- | | **Access Node**: access-001.candidate5.nodes.onflow.org:9000 | | **Date**: Jul 28, 2020 | | **Root Height**: 2033592 | | **Root Parent ID**: a0efffb2beb1500419ae4f7c6e49bfbbe3a4d1d1c201bf925ccaec467ea30e91 | | **Root State Commit**: 0190d417a26b9870f5bb2cf408ad31985b3aa7e57f6ababa6e543f0f90b99dcd | | **Git Commit**: cd876653d20b398952af4002701a0ae2800fd5f2 | | **Branch/Tag**: | | **Docker Image Tag**: | --- ## Candidate 4 | Spork Info | |:----------------------------------------------------------------------------------------| | **Access Node**: access-001.candidate4.nodes.onflow.org:9000 | | **Date**: Jul 14, 2020 | | **Root Height**: 1065711 | | **Root Parent ID**: 68c2bbe68524b50f5d689bc2ac7ad2dd70e88ed7dd15ad6c3cdf6ea314cb1aa3 | | **Root State Commit**: c05086e4d1d428d3b9af5bd8b81d8780054f783ef4eec3ca28b491202e9ac696 | | **Git Commit**: b9b197280d6590576f1ef183bc3d04d41d6be587 | | **Branch/Tag**: | | **Docker Image Tag**: | --- ## Testnet Upgrades > Currently only includes HCUs from 2025. ## Devnet 54 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Feb 12th, 2026 | | **Root Height**: 305778201 | | **Root Parent ID**: c26d0ebae62f0c39f8a8a2d94d49622b5fb46ec6f44888de2090e0c37783e440 | | **Root State Commit**: 1c886e1717c638506d55cc446d1b0e7fb8f189222f0b74212b2d810143b9bfe6 | | **Git Commit**: ae8fb8a4337cf99b119946c7906eee49075513b6 | | **Branch/Tag**: v0.46.0 | | **Docker Image Tag**: v0.46.0 | ## Height Coordinated Upgrade 14 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Aug 6, 2025 | | **Block Height**: 273012710 | | **Git Commit**: 5ad9dd5706ae11656b7367c289bb2384a350c88d | | **Branch/Tag**: v0.42.3 | | **Docker Image Tag**: v0.42.3 or v0.42.1-rc.3 | | **Release Notes**: [v0.42.3](https://github.com/onflow/flow-go/releases/tag/v0.42.3) | ## Height Coordinated Upgrade 13 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: June 16, 2025 | | **Block Height**: 264201500 | | **Git Commit**: 5fe65350f5bffad0c6602787952a8d922861e754 | | **Branch/Tag**: v0.42.1 | | **Docker Image Tag**: v0.42.1 | | **Release Notes**: [v0.42.1](https://github.com/onflow/flow-go/releases/tag/v0.42.1) | ## Height Coordinated Upgrade 12 | HCU Info | |:------------------------------------------------------------------------------------------------------| | **Date**: June 2, 2025 | | **Block Height**: 261783000 | | **Git Commit**: 5a5a11d628a4c0e8d19592dc69b3e869aea71241 | | **Branch/Tag**: v0.42.0-rc.2 | | **Docker Image Tag**: v0.42.0-rc.2 | | **Release Notes**: [v0.42.0-rc.2](https://github.com/onflow/flow-go/releases/tag/v0.42.0-rc.2) | ## Height Coordinated Upgrade 11 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: May 23, 2025 | | **Block Height**: 260085200 | | **Git Commit**: b120bc4855ee4863fcfcb0d1f26a6e9eef990c90 | | **Branch/Tag**: v0.41.4 | | **Docker Image Tag**: v0.41.4 | | **Release Notes**: [v0.41.4](https://github.com/onflow/flow-go/releases/tag/v0.41.4) | ## Height Coordinated Upgrade 10 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: May 8, 2025 | | **Block Height**: 257463367 | | **Git Commit**: 0000b9b745b51db1f6f114ec1b903a265fa7a08f | | **Branch/Tag**: v0.41.0 | | **Docker Image Tag**: v0.41.0 | | **Release Notes**: [v0.41.0](https://github.com/onflow/flow-go/releases/tag/v0.41.0) | ## Height Coordinated Upgrade 9 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: April 9, 2025 | | **Block Height**: 252457666 | | **Git Commit**: 86c40b1ff20b3b7f17c4017bde083d26303c508d | | **Branch/Tag**: v0.40.0 | | **Docker Image Tag**: v0.40.0 | | **Release Notes**: [v0.40.0](https://github.com/onflow/flow-go/releases/tag/v0.40.0) | ## Height Coordinated Upgrade 8 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Feb 18, 2025 | | **Block Height**: 243615144 | | **Git Commit**: 4e7e56b3a92e5772279f1304d88dd445c0ea5016 | | **Branch/Tag**: v0.38.3 | | **Docker Image Tag**: v0.38.3 | | **Release Notes**: [v0.38.3](https://github.com/onflow/flow-go/releases/tag/v0.38.3) | ## Height Coordinated Upgrade 7 | HCU Info | |:-------------------------------------------------------------------------------------| | **Date**: Feb 14, 2025 | | **Block Height**: 242883902 | | **Git Commit**: bcb8a2264fcde9dcd4c997f6b28d8184af19160b | | **Branch/Tag**: v0.38.2 | | **Docker Image Tag**: v0.38.2 | | **Release Notes**: [v0.38.2](https://github.com/onflow/flow-go/releases/tag/v0.38.2) | ## Height Coordinated Upgrade 6 | HCU Info | |:---------------------------------------------------------| | **Date**: Feb 11, 2025 | | **Block Height**: 242365900 | | **Git Commit**: bcb8a2264fcde9dcd4c997f6b28d8184af19160b | | **Branch/Tag**: v0.38.1 | | **Docker Image Tag**: v0.38.1 | | **Release Notes**: N/A | ## Height Coordinated Upgrade 5 | HCU Info | |:---------------------------------------------------------------------------------------| | **Date**: Jan 24, 2025 | | **Block Height**: 239255500 | | **Git Commit**: 5f6b25bd02257e3239341c4be0134b007f3deb49 | | **Branch/Tag**: v0.37.26 | | **Docker Image Tag**: v0.37.26 | | **Release Notes**: [v0.37.26](https://github.com/onflow/flow-go/releases/tag/v0.37.26) | ## Devnet 53 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Sept 17th, 2025 | | **Root Height**: 280224020 | | **Root Parent ID**: ab2646b3d5261f0fc522359249898a19015865a5a9c5c7742ebb1eb9e84b54f1 | | **Root State Commit**: 49c9d4fc51377802ee140cff610aed96bdf96fdf5e11acdd9c1fd05d927e5cbf | | **Git Commit**: 78746577a608a48745c841f6058ea418497cc910 | | **Branch/Tag**: v0.43.0 | | **Docker Image Tag**: v0.43.0 | ## Devnet 52 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Sept 24th, 2024 | | **Root Height**: 218215349 | | **Root Parent ID**: 20dd925a750399493cf7455f199c32c952e8010a6c0b4424dba00a193fa18e44 | | **Root State Commit**: b0498700398cdc8c0c9368cc2f82fde62e8fe4b06e9c8af6c9bb619ab499e6c3 | | **Git Commit**: 25d9c2a9b89bac8fa003ca67928eb79b1427ea17 | | **Branch/Tag**: v0.37.16-patch.1 | | **Docker Image Tag**: v0.37.16-patch.1 | ## Devnet 51 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Aug 14th, 2024 | | **Root Height**: 211176670 | | **Root Parent ID**: c92e07e5d4fbb3a64e0091085a190a4a1119bfc628c71efe513e373dc0482f5a | | **Root State Commit**: c3af77992f253f4dcfeac808912ff68e6f10923aa3fc4541a2e39eb9786c9eb3 | | **Git Commit**: eeac47931cd6837ec6e29c4c0480609238959ccd | | **Branch/Tag**: v0.37.1 | | **Docker Image Tag**: v0.37.1 | ## Devnet 50 | Spork Info | |:----------------------------------------------------------------------------------------------------------------------------| | **Date**: May 20th, 2024 | | **Root Height**: 185185854 | | **Root Parent ID**: cc4800bf44bc07864d156f829cfda2ae1964b5e103de7b9fa1bd879f9e92c10d | | **Root State Commit**: 6a0ae7bf43660e813ee9c2d654f00476ac1bdc357ff47ad11f0e52fc1700d62f | | **Git Commit**: 0585789483c4f5ea423bb11afcfe862c9a99711e | | **Branch/Tag**: v0.33.23-failure-mode-revert-patch | | **Docker Image Tag**: v0.33.23-failure-mode-revert-patch | ## Devnet 49 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Nov 2nd, 2023 | | **Root Height**: 129578013 | | **Root Parent ID**: 91b039c1a5caf25776948270a6355017b8841bfb329c87460bfc3cf5189eba6f | | **Root State Commit**: e10d3c53608a1f195b7969fbc06763285281f64595be491630a1e1bdfbe69161 | | **Git Commit**: fce4ae31c8c90c6a21de9856a319c379bb797fc5 | | **Branch/Tag**: v0.32.6-patch.1 | | **Docker Image Tag**: v0.32.6-patch.1 | ## Devnet 48 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Aug 4th, 2023 | | **Root Height**: 127720466 | | **Root Parent ID**: 2b30e75bd857f898456dcb296bea4b8bc8001cab7062eeee9e47876411b36d76 | | **Root State Commit**: f2e5ebfca2fd519e49f7bd85bea81e92eeaa85a705b3376d8534e9c9649da710 | | **Git Commit**: 692969b1718b3d21f95ee7f66e5061623d99e599 | | **Branch/Tag**: v0.32.3 | | **Docker Image Tag**: v0.32.3 | ## Devnet 47 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Aug 4th, 2023 | | **Root Height**: 113167876 | | **Root Parent ID**: dbd59a00503707c8bc3c5fb3bcc7bd243da4bf8e24c86b6a496505e275b85311 | | **Root State Commit**: 42006aedbfbfa9fcd949de8347b55166df77d8742bf5f273266a8dfcdf2b836b | | **Git Commit**: db1c2584d6d8359b7ccf733c16bd5e1b9385c9bc | | **Branch/Tag**: v0.31.13 | | **Docker Image Tag**: v0.31.13 | ## Devnet 46 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Jun 8th, 2023 | | **Root Height**: 105155067 | | **Root Parent ID**: daced0cdeed95cf143320db50ac904ed17dabe04898e988264af2a71e0d1ca48 | | **Root State Commit**: e8c39b7a1672cb3f5f70da6f1d71a3a0322d3d6c3d7ebf8092ae2ae40d12c30b | | **Git Commit**: 3d4159e93c92cc6331e69708b8c3270d40c09c5f | | **Branch/Tag**: v0.31.4 | | **Docker Image Tag**: v0.31.4 | ## Devnet 45 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Jun 7th, 2023 | | **Root Height**: 105032150 | | **Root Parent ID**: 71df56106ee1492c055a60e2d951a6a8d1b7d1483b903f10146cec912e793e82 | | **Root State Commit**: 0e2e053ca4436a4881f65acc031e12820b5260ef616446950650a1bc8fc9be2f | | **Git Commit**: 8d32e5ea087fcceb0d1aa923c56be1d5a2d6538a | | **Branch/Tag**: v0.31.2 | | **Docker Image Tag**: v0.31.2 | ## Devnet 44 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Apr 24th, 2023 | | **Root Height**: 100675451 | | **Root Parent ID**: 298854580771edc6ec2e2bbc8da3990ff7f746e2fc5cfe16e550d577bdd4bc5b | | **Root State Commit**: 2a062531331b8214de4e900a26ff0f76c578481b4ce22b4b5d8e55bd9535abda | | **Git Commit**: b5b65c9d43bf6bd12766eef06e870990c3963b7c | | **Branch/Tag**: v0.30.6 | | **Docker Image Tag**: v0.30.6 | ## Devnet 43 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Apr 12th, 2023 | | **Root Height**: 99452067 | | **Root Parent ID**: 0e6bae31b2f34ffb0d64d4e1a33c2fbcebd80c936d77d70d2f5dd9321dc92393 | | **Root State Commit**: 7cd69bb0a4448566dce42808efa8b1d03f322135f0bfce6f18037fca1294984c | | **Git Commit**: d55ca8f48167e9669bdb1dc3173253936863e31e | | **Branch/Tag**: v0.30.3 | | **Docker Image Tag**: v0.30.3 | ## Devnet 42 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Apr 12th, 2023 | | **Root Height**: 99444465 | | **Root Parent ID**: a3788d5c0ff1c45db38bca2625bf18bfc562e1bfdc9ebb7dc83ef080be31e697 | | **Root State Commit**: a6e5bfc16a39109ef86caad4dab77ec3752680ef3813c3449f239d84fddc5aa1 | | **Git Commit**: d55ca8f48167e9669bdb1dc3173253936863e31e | | **Branch/Tag**: v0.30.3 | | **Docker Image Tag**: v0.30.3 | ## Devnet 41 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Jan 30th, 2023 | | **Root Height**: 93156994 | | **Root Parent ID**: 26ff2f7f2948a05c63e723eb42946565809b47dbef87079a8f0bae0cc36a0478 | | **Root State Commit**: 08d712b4f7ed838d53b8699e2fd94d0ad010c4b1fa45735b6e80083ff8ef08ff | | **Git Commit**: a7f2cd0ddd9fc7e1e56187327a84fec9efdc3c9d | | **Branch/Tag**: v0.29.8 | | **Docker Image Tag**: v0.29.8 | ## Devnet 40 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Jan 23rd, 2023 | | **Root Height**: 92473965 | | **Root Parent ID**: 7c9812f414d9e9795c1cd7dbd27fc45baf880391452e0e948aaa80ba86dfc77d | | **Root State Commit**: 0068a04843d2c8e007086abaaee90c8c8cae8aa78f240048cd4d43aeb0376d0b | | **Git Commit**: 7f02a642bb437b45326c4ace54a7f033b32832f8 | | **Branch/Tag**: v0.29.6 | | **Docker Image Tag**: v0.29.6 | ## Devnet 39 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Jan 4th, 2023 | | **Root Height**: 90595736 | | **Root Parent ID**: 3d09b9703019b40a065787fff3dd62e28eafa5efcfb69efbc2d713d73034cf38 | | **Root State Commit**: afd47ff2a8e73efd51f0cbe7f572eefda25a66e77f1e6b9c4f3f4e7cdc46568f | | **Git Commit**: e1c172aaee7da9e33828429757b44f51e59368a2 | | **Branch/Tag**: v0.29.3 | | **Docker Image Tag**: v0.29.3 | ## Devnet 38 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Oct 19th, 2022 | | **Root Height**: 83007730 | | **Root Parent ID**: 1b914363c9a34c46b93974adeefb10c546578d7f3f4ac9291e01d746d2c84226 | | **Root State Commit**: 79df428bc27f22d38c233777610d93d33e180f23cfbc640a95d144a508e0f080 | | **Git Commit**: 3aae289f8f390f58ac481fd694254ea0e48960a8 | | **Branch/Tag**: v0.28.4 | | **Docker Image Tag**: v0.28.4 | ## Devnet 37 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: Aug 10th, 2022 | | **Root Height**: 76159167 | | **Root Parent ID**: 8f379a8a86f28c7adef276890874b17786cecf5efb9e71734b0a780bd38660a0 | | **Root State Commit**: 7d1fe692ea2f857568dec54ddece094a68a9ba8ff8dd0de0664e6f73abb90dd8 | | **Git Commit**: 959911cabd50e1a11be45e89726952a90f1a9c22 | | **Branch/Tag**: v0.27.2 | | **Docker Image Tag**: v0.27.2 | ## Devnet 36 | Spork Info | |:----------------------------------------------------------------------------------------| | **Date**: July 27th, 2022 | | **Root Height**: 74786360 | | **Root Parent ID**: 9d9113ca8daa4f3be72e949ea9ead2d4b11db222b2ccd0dc897186ee5f8703ab | | **Root State Commit**: aa2e12033129f27b5320fb973cf109f562db3ad7ddaf50dbfe9d8cc195a7ac0f | | **Git Commit**: 13970fcae812bc04487422922f73eab39f53935c | | **Branch/Tag**: v0.26.17 | | **Docker Image Tag**: v0.26.17 | ## Devnet 35 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: June 9th, 2022 | | **Root Height**: 70072575 | | **Root Parent ID**: cd76fd9d5ceb1b98b5f30b0108f40836593c533ffe32eef0c6f3ed6d50bf645b | | **Root State Commit**: d933d73f48c9371f0a00ab7ffc1ed0daf5ba9e520d2d539e6b9494920c5ffd91 | | **Git Commit**: 07057c8d5fe2f09bbe5a9a3f8de6209346d910d0 | | **Branch/Tag**: v0.26.6 | | **Docker Image Tag**: v0.26.6 | ## Devnet 34 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Apr 4th, 2022 | | **Root Height**: 64904846 | | **Root Parent ID**: 0cbb13d9e7ed7092b5f20d0a20a79d1cae96fd796fb2bc569b27ce49cc97d97e | | **Root State Commit**: f2e77e16628543e285be8bac677db06e17b37f49e53a107af1e6bf99fbc17b30 | | **Git Commit**: 5226c35eb14890db024b9193793b0c49d1b5ad04 | | **Branch/Tag**: v0.25.7 | | **Docker Image Tag**: v0.25.7 | ## Devnet 33 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Feb 7th, 2022 | | **Root Height**: 59558934 | | **Root Parent ID**: 099fbb7b645c3441ea830746ed67059bcc1091c88ff3fd78b331137cf917d15f | | **Root State Commit**: 5b5578cb4ef6c34818ce2cfd9969720c7f17681f41dffa3a9e361935140d7c8e | | **Git Commit**: bd3dca7bf20914f5c019a325b6939bbb662aa131 | | **Branch/Tag**: v0.24.3 | | **Docker Image Tag**: v0.24.3 | ## Devnet 32 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Dec 6th, 2021 | | **Root Height**: 53376277 | | **Root Parent ID**: f900db2ccff33f3bf353c8bf28ece0a9d2650f2805b23ddb7893e296774a5457 | | **Root State Commit**: 843ea0b5498342dcf960376585eeafee8ebe008df3b03325351408a100cb830c | | **Git Commit**: be20371fa8c5044a4e25e5629bbca91f1ed19731 | | **Branch/Tag**: v0.23.3 | | **Docker Image Tag**: v0.23.3 | ## Devnet 31 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Nov 5th, 2021 | | **Root Height**: 50540412 | | **Root Parent ID**: cfebaa6b8c19ec48a27eaa84c3469b23255350532b5ea4c7e4c42313386c07b6 | | **Root State Commit**: 31a2b1eb05a6acb91560970e46b8f3f3171747245eae1ad2bf40290ce773806e | | **Git Commit**: 3d060b90264d59ccd38b4500ae0cd6d72036cfe4 | | **Branch/Tag**: v0.23-testnet | | **Docker Image Tag**: v0.23.1 | ## Devnet 30 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Oct 6th, 2021 | | **Root Height**: 47330085 | | **Root Parent ID**: 0ec9172a21f84cbae15761ae4d59ab4a701f9d4fd15cd0632715befc8cf205cf | | **Root State Commit**: e280f972c72c6b379ec3d4a7173953e596704d8d72f384ef1637d2f4f01ff901 | | **Git Commit**: f019c1dbd778ce9f92dea61349ca36003678a9ad | | **Branch/Tag**: v0.22.8-patch-1-scripts-and-errors | | **Docker Image Tag**: v0.22.8-patch-1-scripts-and-errors | ## Devnet 29 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Oct 5th, 2021 | | **Root Height**: 47242826 | | **Root Parent ID**: 00f745576222a1e7e15ee79974b4b3eaddd760fb4f56e846cfaef3cb5ea59d50 | | **Root State Commit**: 3c5bfb88a3fa184c9981e7677d1f2a2cd0a4eaa581bb2a7867b7b023ae015f38 | | **Git Commit**: e1659aebc39b4a15c68112227fc8c32788a798b6 | | **Branch/Tag**: v0.22.8 | | **Docker Image Tag**: v0.22.8 | ## Devnet 28 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Sept 22, 2021 | | **Root Height**: 45889254 | | **Root Parent ID**: 1518c15f7078cb8fd4c636b9fc15ee847ac231a8631ed59a0b8c9d4a182fb5b2 | | **Root State Commit**: 296001ee05ce3e6c6616ad9bcdc20e6d93d02c91354e57fff6054cff44c5afa3 | | **Git Commit**: a979f4d25a79630581f6b350ad26730d4012cad4 | | **Branch/Tag**: v0.21.6 | | **Docker Image Tag**: v0.21.6 | ## Devnet 27 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Sept 14, 2021 | | **Root Height**: 45051578 | | **Root Parent ID**: 8525ac24717b5f42e29172f881e9a7439235e4cd443a8a59494dbecf07b9376a | | **Root State Commit**: b3ef3b039f722130009b658e3f5faee43b3b9202fec2e976907012994a8fc9be | | **Git Commit**: 4f903f1d45e6f8c997a60de47de62a74ede3c2e4 | | **Branch/Tag**: v0.21.3 | | **Docker Image Tag**: v0.21.3 | ## Devnet 26 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Aug 11, 2021 | | **Root Height**: 41567027 | | **Root Parent ID**: ea465f33266be26b21c82ec728c75cc0dcbb022405d83c44ed0082b0df9aa81d | | **Root State Commit**: 9459485a2a640b1bdc3066916a9a46dc20bf2528a03f06ddc541d34444c3264c | | **Git Commit**: 781ec414b892e2ccf7034aa263b5b19d97f82031 | | **Branch/Tag**: v0.20.4 | | **Docker Image Tag**: v0.20.4 | ## Devnet 25 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: July 20, 2021 | | **Root Height**: 39272449 | | **Root Parent ID**: 2824828e7c87e1a89bc94daca9497625f1a35c8f9fc555f52d1f8b475179e125 | | **Root State Commit**: bd50708e7808be0d43725a9eae6039558b32b679c5a584a2e6103bb027dad7eb | | **Git Commit**: 2644560c0562412a3c2209820be07f8f3f8b1846 | | **Branch/Tag**: v0.19.2 | | **Docker Image Tag**: v0.19.2 | ## Devnet 24 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: June 22, 2021 | | **Root Height**: 36290422 | | **Root Parent ID**: 8d6fad91536ec750d1bb44ebb59e030af2dca49d41c104e45f4f82433184e663 | | **Root State Commit**: fdfd7a9fff481256972095ffc2c6cba300128f41d8c6aa971985de29427eb39d | | **Git Commit**: 37d9ae7a309bf7d21063f57fce008d04828d4840 | | **Branch/Tag**: v0.18.3 | | **Docker Image Tag**: v0.18.3 | ## Devnet 23 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: May 25, 2021 | | **Root Height**: 33257098 | | **Root Parent ID**: dafa3e7a9a93de8ceb0a3acd09b2cce4ad0e5d7ea8fe4237f27c4503e5ad416c | | **Root State Commit**: decf340a722ba136c94ece029e9333d9fbcf216482cd1c81a274365e2abd6688 | | **Git Commit**: fef838147fa70c94a254183e23e7e79f0d412ef6 | | **Branch/Tag**: v0.17.3 | | **Docker Image Tag**: v0.17.3 | ## Devnet 22 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Apr 27, 2021 | | **Root Height**: 30171528 | | **Root Parent ID**: 46c7c675f2ed413532009a6a6ecaa566d360a806e033693520d7add147ca89ea | | **Root State Commit**: d88edf1b2a07dba9ef48214dbffb743c0b2e01f7c74e3e14d828ac076d30f1a6 | | **Git Commit**: 8331b78d99eddea405076e3b6a7839ec5f6ea209 | | **Branch/Tag**: v0.16.2 | | **Docker Image Tag**: v0.16.2 | ## Devnet 21 | Spork Info | | :-------------------------------------------------------------------------------------- | | **Date**: Mar 30, 2021 | | **Root Height**: 26935025 | | **Root Parent ID**: 3f92949a68c577b6d7d17267f00fc47304d1c44052f0c9a078b63fd344f636dd | | **Root State Commit**: 21f48748d35178bd6ad89f6324321fa9d123ee7a07b07826acbce887260b2c70 | | **Git Commit**: 8331b78d99eddea405076e3b6a7839ec5f6ea209 | | **Branch/Tag**: v0.15.3-patch.1 | | **Docker Image Tag**: v0.15.3-patch.1 | ## Devnet 20 | Spork Info | | :------------------------------------------------------------------------------------- | | **Date**: Mar 9, 2021 | | **Root Height**: 25450390 | | **Root Parent ID**: febe212117a83f7f70ed6a5af285ff03332f81a1120ab2c306560c4cb42672f7 | | **Root State Commit**: 828259e9cb895f7f8a7306debbe6de524500db65cb6b80e27b2db9040513b04e | | **Git Commit**: b9b197280d6590576f1ef183bc3d04d41d6be587 | | **Branch/Tag**: v0.14.19 | | **Docker Image Tag**: v0.14.19 | --- ## Devnet 19 | Spork Info | | :------------------------------------------------------------------------------------- | | **Date**: Feb 3, 2021 | | **Root Height**: 16483518 | | **Root Parent ID**: 4220911048404bd3a7733ab6219531a5945dc20020f86869bcd6422c3a6e3f76 | | **Root State Commit**: f6eea9b652a7df433b80d8d647ba414aabcfa03153dd1cba33048c27ce888097 | | **Git Commit**: eb11ae095df6db6e856b1a8e824f03ce4c713b19 | | **Branch/Tag**: v0.14.0 | | **Docker Image Tag**: v0.14.0 | --- ## Devnet 18 | Spork Info | | :------------------------------------------------------------------------------------- | | **Date**: Dec 11, 2020 | | **Root Height**: 17756122 | | **Root Parent ID**: 52ace0d0c8d4e574213fe98e19a5043f215bee992659bd1ef35c5758acf54d1b | | **Root State Commit**: b1412c9d453b1c10d3ace6111e00ac804d564ba3d3295002bedb077685c7da73 | | **Git Commit**: 115ccee9cae920ee5bc309537cf43a9b152a1cf9 | | **Branch/Tag**: v0.13.0 | | **Docker Image Tag**: v0.13.0 | --- ## Devnet 17 | Spork Info | | :------------------------------------------------------------------------------------- | | **Date**: Nov 27, 2020 | | **Root Height**: 16483518 | | **Root Parent ID**: 96d452f9d2a15fafab720f1809fc490b019b23e42c885590e62085e48b8c2b6b | | **Root State Commit**: 0b45f6055a1f09b413d098997cc0e7c9a0ef19eac8b4d294085b0e2f436c6bff | | **Git Commit**: ef54713595d0fe1e0bb4b14e9469c4a64dfaf6e7 | | **Branch/Tag**: v0.12.1 | | **Docker Image Tag**: v0.12.1 | --- # File: /md/protocol/node-ops/node-operation/protocol-state-bootstrap.md --- title: Protocol State Bootstrapping description: How to bootstrap a new or existing node --- When a node joins the network, it bootstraps its local database using a trusted initialization file, called a Root Snapshot. Most node operators will use the `Spork Root Snapshot` file distributed during the [spork process](./network-upgrade). This page will explain how the bootstrapping process works and how to use it in general. For guides covering specific bootstrapping workflows, see: - [Node Bootstrap](./node-bootstrap.md) for bootstrapping a newly joined node. - [Reclaim Disk](./reclaim-disk.md) for bootstrapping from a recent snapshot to recover disk space. This page covers only Protocol State bootstrapping and applies to Access, Collection, Consensus, & Verification Nodes. Execution Nodes also need to bootstrap an Execution State database, which is not covered here. ## Node Startup When a node starts up, it will first check its database status. If its local database is already bootstrapped, it will start up and begin operating. If its local database is not already bootstrapped, it will attempt to bootstrap using a Root Snapshot. There are two sources for a non-bootstrapped node to obtain a Root Snapshot: 1. Root Snapshot file in the `bootstrap` folder 2. Dynamic Startup flags, which will cause the node to download a Root Snapshot from a specified Access Node The node software requires that only one of the above options is provided. ## Using a Root Snapshot File If your node already has a bootstrapped database, the Root Snapshot file will be ignored. If both a Root Snapshot and Dynamic Startup flags are present, the node will not startup. Using a Root Snapshot file is more flexible but more involved for operators compared to Dynamic Startup. A file in `$BOOTDIR/public-root-information` named `root-protocol-state-snapshot.json` will be read and used as the Root Snapshot for bootstrapping the database. ### Instructions 1. Obtain a Root Snapshot file (see below for options) 2. Ensure your node is stopped and does not already have a bootstrapped database. 3. Move the Root Snapshot file to `$BOOTDIR/public-root-information/root-protocol-state-snapshot.json`, where `$BOOTDIR` is the value passed to the `--bootstrapdir` flag. 4. Start your node. ### Obtain Root Snapshot File using Flow CLI [Flow CLI](../../../build/tools/flow-cli/index.md) supports downloading the most recently sealed Root Snapshot from an Access Node using the [`flow snapshot save`](../../../build/tools/flow-cli/utils/snapshot-save.md) command. When using this method: - ensure you connect to an Access Node you operate or trust - ensure you use the [`--network-key`](../../../build/tools/flow-cli/utils/snapshot-save#network-key) flag so the connection is encrypted ### Obtain Root Snapshot File from Protocol database If you have an existing node actively participating in the network, you can obtain a Root Snapshot using its database. 1. Obtain a copy of the Flow `util` tool and ensure it is in your `$PATH`. This tool is distributed during sporks, or you can build a copy from [here](https://github.com/onflow/flow-go/tree/master/cmd/util). 2. Stop the existing node. 3. Construct a Root Snapshot using the `util` tool. The tool will print the JSON representation to STDOUT, so you can redirect the output to a file. Replace `$DATADIR` with the value passed to the `--datadir` flag. You can specify the desired reference block for the snapshot. Retrieve the snapshot for the latest finalized block: ```sh util read-protocol-state snapshot -d $DATADIR --final > latest-finalized-snapshot.json ``` Retrieve the snapshot for a specific finalized block height: ```sh util read-protocol-state snapshot -d $DATADIR --height 12345 > specific-height-snapshot.json ``` ## Using Dynamic Startup Dynamic Startup is a startup configuration where your node will download a Root Snapshot and use it to bootstrap its local database. Dynamic Startup is designed for nodes which are newly joining the network and need to [bootstrap from within a specific epoch phase](./node-bootstrap#timing), but can be used for other use-cases. If your node already has a bootstrapped database, Dynamic Startup flags will be ignored. If both a Root Snapshot and Dynamic Startup flags are present, the node will not startup. When using Dynamic Startup, we specify: 1. An Access Node to retrieve the snapshot from. 2. A target epoch counter and phase to wait for. After startup, your node will periodically download a candidate Root Snapshot from the specified Access Node. If the Root Snapshot's reference block is either **within or after** the specified epoch phase, the node will bootstrap using that snapshot. Otherwise the node will continue polling until it receives a valid Root Snapshot. See the [Epochs Schedule](./../../staking/03-schedule.md) for additional context on epoch phases. ### Specifying an Access Node Two flags are used to specify which Access Node to connect to: - `--dynamic-startup-access-address` - the Access Node's secure GRPC server address - `--dynamic-startup-access-publickey` - the Access Node's networking public key Select an Access Node you operate or trust to provide the Root Snapshot, and populate these two flags. For example, to use the Access Node maintained by the Flow Foundation for Dynamic Startup, specify the following flags: ```shell ExampleDynamicStartupFlags ... \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae ``` ### Specifying an Epoch Phase Two flags are used to specify when to bootstrap: - `--dynamic-startup-epoch-phase` - the epoch phase to start up in (default `EpochPhaseSetup`) - `--dynamic-startup-epoch` - the epoch counter to start up in (default `current`) > You can check the current epoch phase of the network by running [this](https://github.com/onflow/flow-core-contracts/blob/master/transactions/epoch/scripts/get_epoch_phase.cdc) script. Alternatively, you can also check the current epoch phase [here](https://dashboard.flow.com/) under Epoch Phase. #### Bootstrapping Immediately If you would like to bootstrap immediately, using the first Root Snapshot you receive, then specify a past epoch counter: ```shell ExampleDynamicStartupFlags ... \ --dynamic-startup-epoch-phase=1 ``` You may omit the `--dynamic-startup-epoch-phase` flag. ### Instructions #### Example 1 Use Dynamic Startup to bootstrap your node at the `Epoch Setup Phase` of the current epoch (desired behaviour for newly joining nodes): 1. Ensure your database is not already bootstrapped, and no Root Snapshot file is present in the `$BOOTSTRAPDIR` folder. 2. Add necessary flags to node startup command. For example, using the Flow Foundation Access Node: ```sh ... \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae ``` 3. Start your node. #### Example 2 Use Dynamic Startup to bootstrap your node immediately, using the most recent Root Snapshot: 1. Ensure your database is not already bootstrapped, and no Root Snapshot file is present in the `$BOOTSTRAPDIR` folder. 2. Add necessary flags to node startup command. For example, using the Flow Foundation Access Node: ```sh ... \ --dynamic-startup-access-address=secure.mainnet.nodes.onflow.org:9001 \ --dynamic-startup-access-publickey=28a0d9edd0de3f15866dfe4aea1560c4504fe313fc6ca3f63a63e4f98d0e295144692a58ebe7f7894349198613f65b2d960abf99ec2625e247b1c78ba5bf2eae \ --dynamic-startup-epoch=1 ``` 3. Start your node. --- # File: /md/protocol/node-ops/node-operation/reclaim-disk.md --- title: Managing disk space description: How to manage the node disk space --- As the chain advances, nodes receive chain data and store it on disk. Hence, the disk usage of a node keeps increasing gradually over time. In addition to this, currently nodes also experience an intermittent 30-35% spike in disk usage caused by the compaction process of the Badger database used by the node software. > The spikes will be eliminated once the Badger database is replaced by the Pebble database in the future. Hence, as a node operator, please make sure to do the following: 1. Provision enough disk space as per the node role (see: [node-provisioning](./node-provisioning.md)) 2. Setup disk usage monitoring and ensure that the node has enough room to grow and to also accommodate those intermittent spikes. 3. If needed, please add more disk space to the node from time to time. > It highly recommended to setup alerting around disk usage to facilitate timely action and avoid any downtime and subsequent reward slashing for the node. ## Reclaiming disk space ### Access, Collection, Consensus and Verification node If you are running any node other than an execution node and the node is close to running out of disk space or has already exhausted all of its disk, you can re-bootstrap the node's database. This frees up disk space by discarding historical data past a certain threshold. 1. Stop the node. 2. Back up the data folder to a tmp folder in case it is required to revert this change. The default location of the data folder is `/var/flow/data` unless overridden by the `--datadir` flag. ```sh mv /var/flow/data /var/flow/data_backup ``` 3. Configure the node to bootstrap from a new, more recent Root Snapshot. You may use either of the two methods described [here](./protocol-state-bootstrap.md) to configure your node. 4. Start the node. The node should now recreate the data folder and start fetching blocks. 5. If the node is up and running OK, delete the `data_backup` folder created in step 2. ```sh rm -rf /var/flow/data_backup ``` #### Limitation for Access Node Re-bootstrapping allows the node to be restarted at a particular block height by deleting all the previous state. For an **Access Node**, this results in the node not being able to serve any API request before the height at which the node was re-bootstrapped. _Hence, if you require the access node to serve data from the start of the last network upgrade (spork), do not use this method of reclaiming disk space. Instead provision more disk for the node._ ### Execution node For an execution node, the chunk data directory is the one that takes up most of the space. To reclaim space on an execution, do the following: 1. Stop the Execution Node. 2. Remove the Chunk Data Pack Directory. The default is `/var/flow/data/chunk_data_pack` unless overridden by the `chunk-data-pack-dir` parameter. Do **not** delete the bootstrap folder. ``` rm -rf /var/flow/data/chunk_data_pack``` 3. Start the Execution Node. Upon restart, the chunk data pack directory will be automatically recreated. > Note: Always exercise caution when performing system operations, and make sure you have a backup of important data before making any changes. --- # File: /md/protocol/node-ops/node-operation/slashing.md --- title: Slashing Conditions sidebar_position: 17 --- ## Introduction Flow is a proof-of-stake system, which means holders of FLOW can earn inflationary rewards by staking their FLOW tokens to secure and operate the network. A node can participate in the Flow network by depositing a specific amount of stake (based on role types) thereby making a bonded pledge to participate in the Flow protocol during the upcoming epoch. (An epoch is a finite amount of time defined by the protocol, approximately one week, during which the nodes participate to run the protocol and are responsible for their operations.) See the [Staking and Epochs section of the documentation](../../staking/index.md) to learn more about the design and functionality of this part of the protocol. Flow nodes follow the procedures defined in the protocol (based on their role) in order to receive rewards. Any deviation (see Slashing Challenges below) from the protocol can result in decreased reward payments or punishments. Severe infractions, which undermine the safety of the network, can lead to “slashing”, where some or all of the staked tokens are confiscated from the offending node(s). This reward and punishment structure is designed to guarantee the security of the protocol and optimize performance over time. This document outlines the most severe infractions against the protocol which result in some portion of a node’s stake being taken from them (“slashing conditions”). Enforcing these slashing conditions is critical to ensure the cryptoeconomic security of the protocol. Future documents will describe an incentive structure that encourages system-wide efficiency and speed, by providing bonuses to the most performant nodes and withholding payments to nodes that are unresponsive. This document assumes a working understanding of the high-level architecture of the Flow blockchain. Readers who are new to Flow or those looking for a refresher are encouraged to read the Protocol Summary [here](../node-operation/node-roles.md) and the staking documentation. ## Slashing Conditions Any violation of the Flow protocol that could result in staked tokens being seized from the offending nodes is called **Slashable Behaviour.** In order for the tokens to be seized, the data necessary to prove the occurrence of Slashable Behaviour must be combined with the data necessary to attribute the behaviour to the node(s) responsible into a **Slashing Witness**. (A reduction of rewards, e.g. due to lack of active participation, is not formally included in our definition of slashing.) The Flow protocol considers only server threats to safety and liveness to be slashable conditions and as such, there are no performance related slashing penalties. The one exception is in the case of missing Collections (see the section on MCC below), where a widespread failure to respond by a large number of nodes is presumed to be coordinated and therefore punishable with slashing. Most Slashable Behaviour in Flow can be detected and attributed to the offender by a single honest node observing that behaviour. (In other words, one node can generate a Slashing Witness without coordinating with other nodes.) However, some Slashable Behaviour can only be detected and attributed by combining information from multiple nodes. In those situations, the node that first detects the potential infraction raises a **Slashing Challenge**. When a challenge is raised, other nodes are expected to provide additional information which can be combined with the original challenge into a definitive Slashing Witness that is used to adjudicate the challenge. Each type of Slashing Challenge depends on different information provided from a different subset of nodes, the details of which are provided below. Flow adheres to a number of principles in the design of its slashing rules: - Only Consensus Nodes can perform slashing, and only by following the BFT consensus mechanism defined in the protocol. As such, a super-majority of Consensus Nodes must inspect and confirm a Slashing Witness before any punishment is levied. - All Slashing Witnesses are objectively decidable. Given the current protocol state (maintained by the Consensus Nodes) and a well-formed Slashing Witness, all non-Byzantine Consensus Nodes will deterministically come to the same conclusion as to which node or nodes should be slashed (if any) and the amount of stake to be seized. - All Slashing Behaviour in Flow requires active malfeasance on the part of the offending node. In other words, a node will only be slashed if it takes an action against the rules of the protocol, and it will not be slashed if it fails to take an action prescribed by the protocol. (“If your machine is crashed, you won’t get slashed.”) The one exception is in the case of missing Collections (see the section on MCC below), where a widespread failure to respond by a large number of nodes is presumed to be coordinated and therefore punishable with slashing. - Flow makes no attempt to detect and punish liveness failures within the protocol. A liveness failure across the network functionally slashes the stake of any participants excluded from participating in the reboot (since their stake is locked in a non-functional network). Community analysis can determine which nodes were responsible for the failure and exclude those Byzantine actors from the new instance. - Any staked node of Flow can submit a Slashing Witness for any Slashable Behaviour, regardless of its role. (For example, a Collection Node could submit a Slashing Witness for an invalid execution receipt, even though the protocol doesn’t require Collection Nodes to verify execution receipts.) - Submitting an invalid Slashing Witness is Slashable Behaviour. We treat the invalid Slashing Witness itself as the Slashing Witness for that case. ## Stages of Slashing Transitioning to a rigorous staking protocol in which all slashable conditions are checked, enforced, and punished will take place over three phases. The Slashing Challenges section below outlines the various challenges which may be submitted against an offending node but these challenges will not be fully enforced until Phase 3 of the network. ### Phase 1: Beta - In the beta phase of the network, the expectation is that nodes are running error detection and logging but not submitting formal challenges. Any errors found may be submitted to the Flow team for additional testing and security improvements. ### Phase 2: Testing Slashing Mechanics - At this time the slashing mechanisms will be implemented and require testing. Formal challenges should be raised and the protocol will follow the complete, formal mechanics for arbitrating challenges and slashing perpetrators, but no real slashing will take place. ### Phase 3: BFT - By now, the network has been security-hardened and tested and valid challenges result in real slashing of the offending node. ## Slashing Challenges ### 0. All Nodes **Invalid Report Witness (IRW): **if any nodes report an invalid/inaccurate witness, an invalid report witness will be reported by the Consensus Nodes, and the node(s) reporting the witness get slashed. ### 1. Collection Nodes **1.1 Missing Collection Challenge (MCC): ** Collection nodes are responsible for storing collection content (all transactions) for any collection which they guarantee during the current epoch and the first 1000 blocks of the next epoch. During this time they have to respond to any collection request from staked execution, verification and Consensus Nodes and should respond in a timely manner (specific timeout). If an Execution Node or a Verification Node doesn't receive the response from any of the collection guarantors (Collection Nodes who signed a collection), they can raise a Missing Collection Challenge and broadcast it to the Consensus Nodes to evaluate. **Adjudication: **Consensus nodes randomly contact some of the guarantors. If Collection Nodes don't respond, a portion of their stakes will be seized. If the amount of their stake goes to less than half, they will be fully slashed. Then the Consensus Nodes notify all the Execution Nodes to skip that collection. If any of the Collection Nodes respond, Consensus Nodes redirect the collection content to the Execution Nodes but will also set small penalties both for all the guarantors and that Execution Node (according to their revenue ratio). **1.2 Invalid Collection Witness (ICW):** Collection nodes are responsible for responding to collection content queries by collection hash from any staked nodes. The collection hash is the hash of an ordered list of transaction hashes. If a collection content sent by the Collection Node turns out to be invalid, any staked node can report an Invalid Collection Witness. This includes cases where: - the content is malformed or incomplete, - there exists an invalid transaction inside the collection, or - the collection hash doesn't match (inside collection guarantee). **Adjudication:** Consensus nodes evaluate the content of the collection, if the collection is found invalid, the Collection Node who signed the content is slashed. **1.3 Double Collection Proposal Witness (DCPW):** Collection nodes of a cluster run a mini consensus inside the cluster to decide on a collection, which requires Collection nodes to propose the collection and aggregate votes from others. During the collection consensus, if a Collection Node proposes more than one proposal, any other Collection Node inside the cluster can report a Double Collection Proposal Witness (including both proposals). **Adjudication: **Consensus nodes evaluate the content and signatures of these two proposals, and if the witness turns out to be valid, the Collection Node who proposed two collections will get slashed. **1.4 Double Collection Voting Witness (DCVW):** Collection nodes of a cluster run a mini consensus inside the cluster to decide on a collection, which requires Collection nodes to propose the collection and aggregate votes from others. During the collection consensus, if a Collection Node votes for more than one collection proposal with identical collection number and size, any other Collection Node inside the cluster can report a Double Collection Voting Witness (including both votes). **Adjudication: **Consensus nodes evaluate the signatures of these two votes and evaluate them, and if the witness turns out to be valid, the Collection Node who voted two times will get slashed. ### 2. Consensus Nodes **2.1 Double Block Proposal Witness (DBPW):** Consensus nodes run the consensus (HotStuff algorithm) over blocks. During these consensus steps, if a Consensus Node proposes more than one variation of a block proposal, any other Consensus Node can report a Double Block Proposal Witness (including both proposals). This report will be broadcasted to all other Consensus Nodes. **Adjudication: **Consensus nodes evaluate content and signatures of both proposals. If the witness turns out to be valid, the Consensus Node who submitted both proposals will get slashed. **2.2 Double Block Voting Witness (DBVW): ** Consensus nodes run the consensus (HotStuff algorithm) over blocks. During the consensus steps, if a Consensus Node votes for more than one block proposal with the same height, any other Consensus Node can report a Double Block Voting Witness (including both votes). This report will be broadcasted to all other Consensus Nodes. **Adjudication: **Consensus nodes evaluate content and signatures of both votes and If the witness turns out to be valid, the Consensus Node who submitted both votes will get slashed. **2.3 Invalid Block Vote Witness (IBVW):** If a Consensus Node votes for an invalid block or the content of the vote itself is invalid (e.g. vote for non-existing block), any other Consensus Nodes can report an Invalid Block Vote Witness. **Adjudication: **Consensus nodes evaluate the vote content and signature. If the witness turns out to be valid, the Consensus Node who submitted the faulty vote will get slashed. **2.4 Invalid Block Proposal Witness (IBPW):** If a Consensus Node proposes an invalid block proposal (e.g. quorum certificate without 2/3 vote), any other Consensus Nodes can raise an Invalid Block Proposal Witness. **Adjudication: **Consensus nodes evaluate the proposal content and signature, If the witness turns out to be valid, the Consensus Node who submitted the invalid proposal will get slashed. **2.5 Invalid Block Witness (IBW):** If the block contents returned by any Consensus Node is invalid, any node can raise the Invalid Block Witness: - It is malformed or incomplete - It doesn't match the payload hash provided by the block header **Adjudication: **Consensus nodes evaluate the block content and signatures. If the witness turns out to be valid, the Consensus Node who signed the block content will get slashed. **2.6 Invalid Random Beacon Signature Witness (IRBSW):** If any participant of the random beacon returns an invalid signature, an Invalid Random Beacon Signature Witness can be reported by other Consensus Nodes. **Adjudication:** Consensus nodes evaluate the random beacon signature. If the witness turns out to be valid, the Consensus Node who signed the invalid random beacon part will get slashed. ### 3. Execution Nodes **3.1 Faulty Computation Challenge (FCC): ** If any of the Verification Nodes find a fault in the execution of transactions by an Execution Node it can raise an FCC challenge. An FCC challenge includes a faulty chunk and all the evidence. **Adjudication: **Consensus nodes evaluate the challenge, by sending requests for collection contents and chunk data needed to run the faulty chunk and comparing the results against the expected state commitment. If Consensus Nodes detect any fault in the execution of that chunk, the Execution Node(s) who signed the faulty execution receipts will get slashed. If no fault is found, the Verification Node who raised the challenge will get slashed. **3.2 Conflicting Execution Results Challenge (CERC): ** If two or more variations of execution results are reported by Execution Nodes for a given block. Since only one can be valid, Consensus Nodes raise a conflicting execution results challenge. **Adjudication: **As soon as this challenge is raised, all the Verification Nodes go into full check mode (checks all the chunks). The first execution result that receives result approval from at least 2/3 of Verification Nodes is the accurate one, and the other execution results will be considered faulty and Execution Nodes generating those will get slashed. If none of the execution results receive majority approval from Verification Nodes after a very long timeout, all the Consensus Nodes start executing chunks to determine the correct output. **3.3 Invalid Chunk Data Package Witness (ICDPW):** If the contents of a chunk data package doesn't match the hash provided inside the execution result, or the contents is invalid, the Verification Nodes can report an Invalid Chunk Data Package Witness. **Adjudication: **Consensus nodes evaluate the content of the chunk data package. If the witness turns out to be valid, the Execution Node(s) who signed the faulty chunk data package will get slashed. **3.4 Missing Chunk Data Package Challenge (MCDPC):** If an Execution Node doesn't respond to the chunk data package request by any staked Verification Node, a Missing Chunk Data Package Challenge can be raised by the Verification Node. **Adjudication: **When this challenge is received by the Consensus Nodes, they contact Execution Nodes and ask for the chunk data package. If none of the Execution Nodes respond after a long timeout, all of them get slashed. If any of the Execution Nodes responds with a valid chunk data package, Consensus Nodes redirect the chunk data package to the Verification Nodes but will also set small penalties both for all the Execution Nodes and the challenge raiser (Verification Node) according to their revenue ratio. **3.5 Execution Results Timeout Challenge (ERTC):** If no execution receipt received in X number of blocks after the submission of each block, the liveness of the system is compromised and Consensus Nodes can raise an Execution Results Timeout Challenge for all the Execution Nodes. **Adjudication: **When this challenge is received by the Consensus Nodes, they contact Execution Nodes and ask for an update. If none of the Execution Nodes respond after a long timeout, all of them get slashed. If any of the Execution Nodes return the execution receipt, the case is dismissed. **3.6 Invalid Execution Receipt Witness (IERW):** If an Execution Node provides an execution receipt that is not valid, the Consensus Nodes can report an Invalid Execution Receipt Witness. **Adjudication: **Consensus nodes evaluate the content of the execution receipt. If the witness turns out to be valid, the Execution Node(s) who signed the invalid execution receipt will get slashed. **3.7 Non-Matching SPoCKs Challenge (NMSC): ** If the SPoCKs provided by the Execution Node don't match the ones provided by the Verification Node, the Consensus Nodes can raise a Non-Matching SPoCKs challenge. **Adjudication: **Consensus nodes have to re-execute the chunk to be able to compute the accurate SPoCKs secret to be able to adjudicate the challenge. This requires requesting the collection and all other data needed for execution from other nodes. Any node which provided invalid SPoCKs will be slashed. ### 4. Verification Nodes **4.1 Non-Matching SPoCKs Challenge (NMSC):** If the SPoCKs provided by the Execution Node don't match the ones provided by the Verification Node, the Consensus Nodes can raise a Non-Matching SPoCKs challenge. **Adjudication: **Consensus nodes have to re-execute the chunk to determine the accurate SPoCKs secret which is needed to adjudicate the challenge. This requires requesting the collection and all other data needed for execution from the other nodes. Any node which provided invalid SPoCKs will be slashed. **4.2 Invalid Result Approval Witness (IRAW):** If a Verification Node provides an invalid result approval, the Consensus Nodes can report this witness. This includes cases that a Verification Node sends a result approval for a chunk that was not assigned to the Verification Node (excluding full check mode) or if the SPoCK’s signature doesn't match the public key of the Verification Node. **Adjudication: **Consensus nodes evaluate the content and signatures of the result approval. If the witness turns out to be valid, the Verification Node who signed that result approval be slashed. --- # File: /md/protocol/staking.md --- title: Epochs, Staking & Delegating on Flow sidebar_label: Staking and Epochs sidebar_position: 1 description: Introduction to how staking works on Flow --- This document provides an introduction to staking FLOW tokens on the Flow network for token holders and node operators. Staking is an important part of the security protocol of a proof-of-stake (PoS) blockchain. Running nodes and staking tokens contributes to the blockchain's security and is rewarded accordingly. ## What is Staking? Flow is a global network of computers working together to maintain the security and integrity of its users' data. This global network is made up of many individual nodes: software applications run by people. Every node in the network shares a small part of the responsibility to keep the network running smoothly and to ensure that other nodes are doing the same. This shared responsibility is a core premise of decentralization, because no single central node is solely responsible for the security and integrity of the network and the data it contains. Node operators are what we call the people who run nodes. In order to connect their software applications as nodes on the network, a node operator must first purchase tokens. Every node operator has to temporarily give (or ‘stake’) a large number of their tokens to the network as a promise that they will not modify their node to do something that is against the rules of the network, like steal funds from users' accounts. This process of temporarily giving up tokens is called staking. If a node ever breaks the rules defined by the network, a number of the node operator's staked tokens will be taken from them as a punishment. This process is automatic. Every node knows the rules defined by the network and automatically watches other nodes and reports them if they misbehave. Meanwhile, the network pays the node operator a reward from a mixture of transaction fees and newly minted tokens on a regular basis provided their node does not break the rules. If a node operator breaks the rules, they lose the tokens they've staked. If they operate their node with integrity, they get rewarded with more tokens! This is the basic incentive that enables a decentralized proof-of-stake network, like Flow. ## How Does Staking Work on Flow? The Flow protocol maintains a list of node operators. The list contains important information about each node, like their public keys, node address, and what kind of node they are running. (Collection, Consensus, Execution, Verification, or Access) A node operator registers a node by submitting a transaction containing their node information, a cryptographic proof that they control their node info, and the FLOW they wish to stake. If they meet the requirements to run a node, then will be accepted to join the network! Once a node is staking and operating properly, it will receive periodic reward payments, assuming it stays online and actively participates in the protocol without committing any actions that would harm the network, which we call slashable offenses. Once nodes have registered, they are required to operate for a protocol-specified timeframe. This timeframe is otherwise known as an **Epoch.** ## Epochs An **Epoch** is a roughly week-long period that the network uses to manage list of nodes and pay rewards. - Only a pre-determined set of nodes is authorized to participate in the protocol. The set of authorized nodes is known to all network participants. This set is referred to as the **Identity Table**. - An **Epoch** is defined as a period of time, where the set of authorized nodes is constant (or can only shrink due to ejection of malicious nodes). Every epoch, a list of committed nodes are chosen to be the staked nodes of the network. This list is called the **Identity Table (ID Table)**. The node's staked tokens are locked in and cannot change for the duration of the epoch. At the end of the epoch, rewards are paid to each staked node based on how many tokens they had staked for that epoch and how well they performed during the epoch. Nodes can choose to join or leave, but changes to the Identity Table can only happen at end of an epoch, which is also the beginning of a new epoch. This process repeats itself indefinitely, as long as the network remains functioning. To determine the list of nodes that are included as officially staked nodes in the next epoch, the protocol looks at the records of all the nodes that have committed tokens. It checks to make sure each node's information is correct and that the node is running properly. Each node also has to have committed tokens above the minimum stake required for their node role and be authorized by the service account. If any of these checks are insufficient, the node is not included in the next epoch. Every epoch, some nodes also have to perform certain processes to initialize the state and communication with other nodes for the next epoch. These processes are called **Cluster Quorum Certificate Generation (QC)**, and **Distributed Key Generation (DKG)**. If any node does not perform this initialization properly, it is not included in the next epoch's Identity Table. If a node passes all the checks and initializations, it is approved and included as an official node for the next epoch. Nodes (and users who delegate to them) do not have to continue to submit staking registration transactions every epoch in order to remain staked. As long as they continue to run their node properly, their tokens will remain staked. A node operator only needs to take action if they want to stake more tokens or if they want to unstake their staked tokens. If a node operator or delegator decides to stake or unstake tokens, their requests are not carried out until the end of the current epoch. In the case of unstaking requests, they also must wait an additional epoch before their unstaked tokens are available to withdraw. This allows the protocol to deal with any slashable offenses that may have happened in the previous epoch. See the [Epochs](./04-epoch-preparation.md) section of the documentation for in-depth explanations of the identity table, epoch schedule, QC, and DKG. ## Rewards Please see the [schedule](./03-schedule.md) section of the documentation for information about reward calculations and schedule and what you can do with the rewards you earn by staking a node! ## Delegation Any account in the network may also participate in staking by delegating their tokens to a node operator. Every node operator in the network is eligible to receive delegations, there is no opting out. To delegate to a node, a user simply specifies the ID of the node they want to delegate to and the amount of tokens they want to delegate. The tokens are committed and managed in the exact same way that normal staked tokens are managed. Rewards for delegators are also calculated in the exact same way that rewards for node operators are calculated, with one difference in that 8% of the calculated amount is given to the delegatee (the node being delegated to). The remaining 92% is awarded to the delegator. ## How Do I Stake? So you have decided you want to be a part of the Flow network? Welcome! You are joining a group of people from all around the world that are a part of a movement that is bringing decentralization and transparency into the world. ### Staking using Flow Port [Flow Port](https://port.onflow.org/) is a simple browser-based app for the Flow blockchain that provides functionality for sending, receiving, and staking tokens. Any wallet that uses the [Flow Client Library](../../build/tools/clients/fcl-js/index.md) is compatible with Flow Port. If you created your account using [Flow Port](https://port.onflow.org/), you can also stake and earn rewards using the Flow Port. Follow this [step-by-step guide](../../protocol/flow-port/staking-guide.md) to stake using Flow Port. Flow Port currently supports staking as a node, delegating, and reward withdrawal using **Flow Reference Wallet**, **Ledger**, **Shadow**, **NuFi**, and any other FCL compatible accounts / wallets. ### Staking via a Custody Provider If you are using a custody provider who controls your account and private keys for you, such as Kraken, Finoa, or Coinlist, they all have different policies and processes for what you need to do to stake your tokens, the rewards you receive, and the fees that they take from your staking rewards. ### Manual Staking or Building your own Staking Integration If you are self-custodying your Flow account and keys, or you want to build a staking service for customers, you will need to learn more about how staking works, the various methods for staking, and how you can participate safely and reliably. See the [staking technical overview](./06-technical-overview.md) first for information about technical integration. --- # File: /md/protocol/staking/02-epoch-terminology.md --- title: Epoch and Staking Terminology sidebar_label: Epoch and Staking Terminology description: Important Definitions for Epochs --- If you haven't read the staking introduction, please read that first. That document provides a non-technical overview of staking on Flow for all users and is a necessary prerequisite to this document. This document assumes you have some technical knowledge about the Flow blockchain and programming environment. ## Terminology If any of the definitions are confusing, you can find more detail in the other sections of the technical docs. **Staker:** Any user who has staked tokens for the Flow network. A node operator is a staker, and a delegator is a staker as well. **Node Operator:** A user who operates a node on the Flow network. Each node operator has a unique node resource object they store in their account to perform staking operations. **Node Operator Metadata:** This information is tracked for each node operator in the Flow network. - **Node ID:** 32 byte identifier for the node. Usually a hash of the node public key. - **Role:** Indicates what role the node operator is. (Collection, Consensus, Execution, Verification, Access) - **Networking Address:** The address that the node operator uses for networking. Using a hostname is highly encouraged. - **Networking Key:** The 64 byte ECDSA-P256 node operator public key for networking. - **Staking Key:** The 96 byte BLS12-381 public key for the node. Used to sign node messages and votes for Quorum Certificate generation. - **Proof of Possession:** A 48 byte (96 hex characters) string that acts as cryptographic proof of ownership of the node's staking key. **Delegator:** A user who delegates tokens to a node operator and receives rewards for their staked tokens, minus a fee taken by the node operator. Each delegator stores a unique delegator resource object in their account that allows them to perform staking operations. - **Delegator Metadata:** This information is tracked for all delegators in the network. - **id:** The ID associated with a delegator. These IDs are assigned to delegators automatically by the staking contract and are only unique within an individual node operators' record. - **nodeID:** The ID of the node operator a user delegates to. **Node Identity Table:** The record of all the nodes in the network, and their delegators. The identity table keeps separate lists for the info about node operators and delegators. NOTE: The staking smart contract does not associate a node or delegator with an account address. It associates it with the assigned resource object that corresponds to that entry in the contract. There can be any number of these objects stored in the same account, and they can be moved to different accounts if the owner chooses. **Epoch:** The period of time between changes in the identity table and reward payments. (Initially a week, measured in consensus views) At the end of every epoch, insufficiently staked node operators are refunded their stake, rewards are paid to those who are currently staked, committed tokens are marked as staked, unstaking tokens are marked as unstaked, and unstaking requests are changed from staked to unstaking. **Consensus View:** A internal detail that the Flow consensus algorithm, HotStuff, uses to measure time. Views count the number of rounds in the consensus algorithm. Each round/view the counter is incremented and a new block may be proposed. **Seat/Slot:** The right to participate in the network as a node of a certain type for a specific Epoch. There are a limited number of seats/slots for each node type per epoch. However, these can be updated by the service committee to accommodate more nodes. Current Slot Limits can be queried from the chain. e.g. The [get_role_counts.cdc](https://github.com/onflow/flow-core-contracts/blob/master/transactions/idTableStaking/scripts/get_role_counts.cdc) returns the current count for all node types. ``` $ flow scripts execute get_role_counts.cdc -n mainnet Result: {1: 102, 2: 85, 5: 206, 4: 69, 3: 7} ``` **Candidate:** A node that has committed tokens for the next epoch but has not been accepted yet. There is a limited number of node slots per epoch and candidate nodes are selected randomly, so there is a chance that a candidate node will not be chosen to participate in the next epoch because there aren't enough slots even if they meet all the other regular requirements **Staking Auction Phase:** The period of time when nodes and delegators are able to submit staking operations in preparation for the upcoming epoch. This phase is expected to take over 90% of the time of an epoch. **Epoch Setup Phase:** The period of time after the staking auction, where nodes have to perform certain processes to initialize the state and communication with other nodes for the next epoch. These processes are called **Cluster Quorum Certificate Generation (QC)**, and **Distributed Key Generation (DKG)**. If any node does not perform this initialization properly, it is not included in the next epoch's Identity Table. This phase is expected to take less than 10% of the time of an epoch, near the end. **Cluster Quorum Certificate Generation (QC):** A process by which nodes using the HotStuff consensus algorithm submit signed messages in order to generate a certificate for bootstrapping HotStuff. Each collector cluster runs a mini-version of HotStuff, and since clusters are randomized each epoch, a new quorum certificate is required for each cluster each epoch. **Distributed Key Generation (DKG):** Process for generating a shared public key to initialize the random beacon. Consensus nodes use a shared whiteboard to communicate and submit final key vectors to generate a shared key. **Epoch Commit Phase:** The final phase of an epoch, after the Epoch Setup Phase. In this phase, the identity table has been finalized for the next epoch, all setup has been completed, and the network is simply waiting for the next epoch to start. **Service Event:** Special messages that are generated by the epoch smart contracts and included in execution results. They enable communication between system smart contracts and the Flow protocol. In other words, they serve as a communication mechanism between the execution state and the protocol state. Service events are not any different that other Cadence events, except in the fact that Flow nodes treat them differently because they are being emitted by the service account. **Node and Delegator Staked Token Tracking Terms:** - **Tokens Committed:** The tokens that a user has committed to stake in the next epoch, but that aren't currently staked. - **Tokens Staked:** The tokens that a user has staked in the current epoch. - **Tokens Requested to Unstake:** The amount of tokens that a user has requested to be unstaked at the end of the current epoch (to be removed from the **tokens staked** pool). - **Tokens Unstaking:** The tokens that were unstaked at the beginning of the current epoch and are being held for an additional epoch holding period before being released. - **Tokens Unstaked:** Tokens that used to be committed or staked and have been unstaked. - **Tokens Rewarded:** Tokens that the user has received via staking rewards. **Delegation Rewards Cut:** The percentage of a delegator's rewards that the node operators take. Initially set to 8%. **Epoch Payout:** The total amount of tokens paid in rewards at the end of an epoch. This value will change as the supply of FLOW changes. See the [rewards page](./03-schedule.md) for more details. **Minimum Stake Requirement:** Each node type AND delegator has a requirement for the minimum number of FLOW they have to commit to stake to be considered a valid staker and receive rewards. If a node operator or delegator does not meet the minimum stake, they will not be included in the next epoch and will not receive any rewards. - Access Nodes: 100 FLOW - Collection Nodes: 250,000 FLOW - Consensus Nodes: 500,000 FLOW - Execution Nodes: 1,250,000 FLOW - Verification Nodes: 135,000 FLOW - Delegators: 50 FLOW There is no maximum stake limit. --- # File: /md/protocol/staking/03-schedule.md --- title: Epoch and Reward Schedule sidebar_label: Epoch and Reward Schedule description: How the Flow protocol manages the schedule of an epoch and rewards payments --- This information covers the current state of how epoch phases are ran and how rewards are calculated and distributed to node operators and delegators. All of the information in the document is still being designed and is subject to change based on research and discussion by the Flow core team and community. If any changes are proposed, the Flow community will be notified in advance. # Schedule ![Flow Epoch Schedule](./epoch-phase-diagram.png) An Epoch is a period of time when the node operators in the network are constant. At epoch boundaries, newly staked node operators are able to join the network and existing node operators which have unstaked may exit the network. Each epoch lasts approximately a week, and the **Epoch Switchover** is defined as the point in time when one epoch ends and the next epoch begins, marking a possible change in the list of valid nodes. The exact timing of each epoch end is influenced by the number of blocks proposed during the epoch. Therefore, epoch phase timing can very and will likely drift over time. **All quoted epoch end times are estimates and subject to some variance (up to several hours)**. See [Epoch Preparation Protocol](./04-epoch-preparation.md#epoch-length) for details. **Staking Operations are disabled for approximately the last 6-12 hours of an epoch**, typically around 00:00 US Pacific Daylight Time (07:00 UTC) on Wednesday every week until around 12:00 US Pacific Daylight Time (19:00 UTC). See [Epoch Setup](./04-epoch-preparation.md#phase-1-epoch-setup) for more information on this phase. **Epoch Switchovers will happen around 12:00 pm PT on Wednesday (7:00 pm UTC)** every week. Please note exact epoch ending time vary based on the performance of the network & all staking operations that interact with staked tokens will be processed by the protocol at the start of each epoch. ## Rewards **Rewards are usually paid around 12 pm PT on Wednesday (7:00 pm UTC)**, every week, to all users that have tokens staked. This is close to the same time as the Epoch Switchover. See the Rewards Distribution section below for more information about rewards calculation and schedule. ## Staking Auction The first, and longest phase of an epoch is the [staking auction](./04-epoch-preparation.md#phase-0-staking-auction). This phase is when nodes and delegators can register to stake and perform other staking operations such as staking more tokens or unstaking their existing tokens. None of these operations are fully executed until the **Epoch Switchover** though. **The Staking Auction lasts for at least the first 90% of the length of an Epoch** ## Epoch Setup and Epoch Commit The [Epoch Setup](./04-epoch-preparation.md#phase-1-epoch-setup) and [Epoch Commit](./04-epoch-preparation.md#phase-2-epoch-committed) phases are the final phases of the epoch, when node operators who have been included in the next epoch perform important setup functionality to prepare for the next epoch. **The Epoch Setup and Epoch Committed phases usually last less than 10% of the time of an epoch.** **Staking Operations will be rejected during the Epoch Setup and Epoch Commit phases.** This is because the staking information has been finalized in preparation for the next epoch and cannot be changed because these final phases rely on the staking information being constant. **The Staking Auction Ends every Wednesday near 00:00 PDT (07:00 UTC).** **This means that staking operations will be disabled for ALL users** **in the period between the end of the staking auction and the beginning of the next epoch, currently 6-12hrs.** ## Rewards Distribution The rewards distribution schedule has been designed to ensure there is enough liquid supply of FLOW available in the ecosystem to empower a wide variety of use cases and promote fair and diverse participation in the Flow ecosystem. The numbers in this table represent the total amount of tokens that are paid as staking rewards at each epoch to the entire pool of participants in the Flow network. While the total staking reward amount is known and fixed per epoch, rewards that individual stakers receive are variable depending on many factors. The total rewards for each epoch are fixed for that epoch, but where those rewards come from can change. When the protocol pays rewards, it first pulls from the central pool of all the transaction fees that have been paid by every user in the network since the last rewards payment. Once that pool has been depleted, the protocol mints new tokens that are used as rewards. Please see the next section on how to calculate an individual staking reward. | | Dec 22, 2020 | Dec 29, Jan 5, 12, 19, 26 (2021) | Feb 2, 2021 weekly on Wednesdays indefinitely | | ----------------------------- | ------------------- | -------------------------------- | --------------------------------------------- | | Total Rewards % (Annual) | 5% | 20% | 5% | | Total Rewards Amount Per Week | ~1.2M FLOW | ~4.4M FLOW | ~1.3M FLOW | ## Individual Calculation Each user gets a percentage of the total rewards during each epoch that is proportional to their percentage of all the tokens that are staked by all participants. The full reward calculation on a per-user basis is equal to: ``` New Reward(user) = Tr * (Sn / St) ``` where: - `Tr` = Total staking rewards to be paid out during the current epoch. (See table above) - `Sn` = Amount of FLOW Staked by the target user for the current Epoch. (Different for each staker) - `St` = Sum of all the FLOW staked by all the participants in the network. (Changes every epoch) Rewards for delegators are also calculated in the exact same way that rewards for node operators are calculated, with one difference in that 8% of the calculated reward amount is given to the node operator being delegated to (effected as a protocol layer fee, which is the same for all node operators). The remaining 92% is awarded to the delegator. Note: the 8% fee is only applied to the staking reward, not to the tokens delegated. With this calculation, the node you choose to run or delegate to DOES NOT affect the amount of rewards you receive every week. The only variable that you can control is the number of tokens you have staked. The more tokens you stake, the more rewards you will receive. Because of the variable nature of the rewards calculation, we cannot provide an expected weekly/yearly return for a single staker. You can plug your own numbers into the formula to see some sample calculations, but you won't be able to know exactly what you will earn until the beginning of the epoch in which you are participating in staking or delegation. ## Rewards History For the first two years of its existence, the staking rewards payments were handled with manual transactions. You can find the history of those transactions including their arguments and IDs in the [Pay Rewards Section](https://github.com/onflow/service-account/tree/main/transactions/pay-rewards) of the Flow Service Account Repo. The dates correspond to the date when the rewards were paid at the end of an epoch and the network transitioned to a new epoch. Future rewards payments and epoch switchovers happen automatically via a system chunk transaction, which does not create regular transaction IDs. --- # File: /md/protocol/staking/04-epoch-preparation.md --- title: Epoch Preparation Protocol sidebar_label: Epoch Preparation Protocol description: Technical Overview of the Flow Epoch Protocol --- If you haven't read the staking introduction, please read that first. That document provides a non-technical overview of staking on Flow for all users and is a necessary prerequisite to this document. This document assumes you have some technical knowledge about the Flow blockchain and programming environment. # Epochs The epoch preparation protocol defines how information about the next epoch is determined and propagated to the protocol state. There are two primary actors in this protocol, the Epoch Smart Contracts, and the Consensus Committee: - [`Epoch Smart Contracts`](https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs) - the smart contracts that manage epochs: - `FlowClusterQC` : Manages the quorum certificate generation for bootstrapping the hotstuff consensus algorithm for each collector cluster. - `FlowDKG` : Manages the Distributed Key Generation that consensus nodes participate in to initialize the random beacon for each epoch. - `FlowIDTableStaking` : Manages the source of truth for the identity table, and enforces rules related to staking FLOW, delegating, paying rewards, and allocating token movements between epochs. - `FlowEpoch` : Ties all of the previously mentioned contracts together to manage the high level epoch lifecycle. `FlowEpoch` acts as a state machine that transitions between different epoch phases when specific conditions from the other contracts are met and triggers important operations in the other smart contracts when phase changes happen. - `Consensus Committee` - the committee of consensus nodes for the current epoch This document describes the communication protocol between these two actors and the impact on the protocol state. It gives an overview of the process of epochs, the staking auction, and the epoch setup and commit phases. It is an important prerequisite to understand before proceeding with any other technical integration or interaction with the Flow Protocol, but does not provide step-by-step instructions for how to perform specific actions. The transactions described in this document are contained in the [`flow-core-contracts/transactions/epoch/`](https://github.com/onflow/flow-core-contracts/tree/master/transactions/epoch) directory. You can see the text of all the transactions used to interact with the smart contracts there. ## Epochs Overview Only a pre-determined set of nodes is authorized to participate in the protocol at any given time. The set of authorized nodes is a-priori known to all network participants. This set is referred to as the **Identity Table**. An **Epoch** is defined as a period of time where the set of authorized nodes is constant (or can only shrink due to ejection of malicious nodes). At an Epoch switchover, which is the time when the network transitions from one epoch to the next, the set of authorized nodes can change. For each of Flow's node roles, the Flow protocol admits a protocol-determined number of nodes. For each Epoch, there is a [Staking Auction](./06-technical-overview.md) in which new potential node operators may submit Staking Commitments. All this is completely smart-contract based and handled through conventional transactions. After the Staking Auction is over, the protocol determines which commitments to accept and which to reject. The node operators whose staking commitments were accepted are added to the Identity Table for the next epoch, and become authorized participants at the next epoch switchover. Staked Nodes also can submit other operations to modify their existing stake, which are all carried out at the end of the current epoch. The smart contract that determines the nodes for the next Epoch has special privileges. Specifically, it is allowed to emit [Service Events](./05-epoch-scripts-events.md#monitor-epoch-service-events), which are how the execution state updates the consensus node-based protocol state. At the end of the staking auction, the epoch smart contracts conclude that they have now determined the set of nodes which will be running the network for the next Epoch, and the amount of FLOW that all the nodes have staked. The smart contract then emits a service event with this information. When processing the block with seat assignment, all network nodes (including future ones which are supposed to monitor the chain in anticipation) are thereby informed about the upcoming change. Note: At this point in the epoch (end of the staking auction), there is no change in participating nodes. The change in participating nodes happens at the end of the epoch. After the staking auction, there is an interim period of time until the new Epoch starts for the following tasks to be completed: - The epoch smart contract runs the cluster assignment algorithm for all the collector nodes and each collector node will vote for the root block of their respective clusters - The Random Beacon Committee for the next Epoch (currently all consensus nodes) will run the Distributed Key Generation (DKG), - When completing the QC generation and DKG, the smart contracts will emit a service event. After consensus nodes have collected all relevant information (public keys for the random beacon and cluster quorum certificates), they can update the identity table to include the information for the next Epoch. If preparation for the next Epoch is not completed before the current Epoch ends, the network goes into epoch fallback mode (EFM) and a special transaction, sometimes including a spork, is required to transition to the next Epoch. ## Epoch Length The length of an Epoch is measured in terms of consensus views. The number of views in an epoch and in the various epoch phases are determined before the Epoch begins and stored as a field in the main epoch smart contract (`FlowEpoch`). Generally, there is not a block for every view, so the view number will not change at the same rate as the block height. Because the length of a consensus view can vary depending on many different factors, the wall-clock time of an epoch is expected to vary from week to week. Under typical network conditions we expect the variance in epoch length to be less than 2 hours for a 1-week epoch (~1%). Under adverse network conditions the variance in epoch length will increase (typically this will result in longer epochs). As the average view rate changes over time, the Service Account can change the epoch length to target a 1 week wall-clock epoch length. # Phases The preparation for the next epoch is separated into distinct phases. Each phase occurs completely within the current epoch. ![Flow Epoch Schedule](https://storage.googleapis.com/flow-resources/documentation-assets/epoch-phase-diagram.png) The Epoch Smart Contract acts as a state machine. The smart contract keeps a record of the current phase, the number of views in the current phase, and the conditions that need to be met in order to advance to the next phase, or next epoch. A special `Heartbeat` resource is used to call the `advanceBlock()` method during every single new block in Flow. During these regular method calls, if all of the conditions are met to advance to the next phase, the smart contract performs any relevant retrieval and storage of information, emits a Service Event, and transitions to the next phase, which often involves setting certain metadata or enabling one of the connected smart contracts to begin its work. From the perspective of the consensus committee, the phase transitions within epochs occur as a result of including a service event in a block, thus the phase transition only applies to the fork containing the block with the service event. At the end of Phase 0 and beginning of Phase 1, the `EpochSetup` service event is emitted that contains the identity table and other initial metadata for the upcoming epoch. At the end of Phase 1 and beginning of Phase 2, the `EpochCommit` service event is emitted that contains the results of the Epoch Setup phase. The start of a new epoch is the first block with its view > the last view of the previous epoch, and its parent view ≤ the last view of the last epoch. ## Phase Transitions The **Consensus Committee** triggers the **phase transition coinciding with the Epoch switchover** by publishing the block of the next Epoch. This block's execution state will also detect the the end view of an epoch has arrived and trigger the start of the new epoch. The transition to a new epoch is also marked by the emission of [an event](https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowEpoch.cdc#L62) (`EpochStart`) from the epoch smart contract. - The state of the smart contracts reflect the latest epoch's new identity table and metadata. For the **Epoch-*internal* Phase transitions**, meaning the phase transitions within an epoch, the **Epoch Smart Contract** provides the trigger by emitting a respective service event: - The `EpochSetup` service event triggers the phase transition `Staking Auction Phase` → `Epoch Setup Phase` - The `EpochCommit` service event triggers the phase transition `Epoch Setup Phase` → `Epoch Committed Phase` Only one of each service event may be emitted each epoch, for a given fork. `EpochCommit` may only be emitted after `EpochSetup` has been generated in the respective given fork. The `FlowEpoch` contract manages all of these phases, the `FlowIDTableStaking` contract manages the identity table and staking auction, the `FlowClusterQC` contract manages the Quorum Certificate generation for collector clusters, and the `FlowDKG` contract manages the Distributed Key Generation protocol for the consensus nodes. Initially, control of these phases and contracts will be managed manually by the Flow Token Admin, but control will eventually be completely decentralized and managed by the node software, smart contracts, and democratically by all the stakers in the network. ## Phase 0: Staking Auction **Purpose:** During the staking auction phase, operators can put up stake in exchange for being a part of the upcoming epoch. All voluntary commitments to register a new node, increase, or decrease stake for the next epoch must occur before the end of this phase. **Duration:** The staking auction phase begins with the first block of the current Epoch Its last block is the block in which the `EpochSetup` service event is emitted. ### **Protocol Directives:** Epoch Smart Contract - The `FlowEpoch` Smart Contract is responsible for ensuring that staking, un-staking, and stake-modification transactions for the next epoch are are only executed during the staking auction and fail otherwise. The contract enforces this by setting a `stakingEnabled` field in the staking contract. Every staking method checks to see if this is set before executing. - The `FlowEpoch` Smart Contract must ensure that the subsequent phases are sufficiently long to perform all required tasks before the epoch ends. - As part of the execution result for the last block of the staking auction, the `Epoch Smart Contract` computes the seat assignment information for the next epoch, and emits a specialized service event, the `EpochSetup` event, with the timing and identity table information about the next epoch. See the [Epoch Setup Event Documentation](./05-epoch-scripts-events.md#flowepochepochsetup) for a detailed breakdown of the epoch setup event. ## Phase 1: Epoch Setup **Purpose:** During the epoch setup phase, all nodes participating in the upcoming epoch must perform setup tasks in preparation for the upcoming epoch. **Duration:** The epoch setup phase begins right after the `EpochSetup` service event is emitted. It ends with the block where `EpochCommit` service event is emitted. ### **Protocol Directives:** Consensus: - When a primary constructs a block that seals the `EpochSetup` service event, the primary includes an update to the protocol state in the block. Specifically, it adds the nodes for the `PendingEpoch` to the list of authorized nodes. When this block is propagated, all staked nodes will know about the participants in the next epoch and can communicate with them. - Based on the `RandSeed` field in the `EpochSetup` event, all nodes compute: - The seed to initialize the consensus node's primary selection algorithm for the next epoch - The seeds to initialize the collector clusters' primary selection algorithm for the next epoch - The collector nodes generate the root block for their respective clusters in the next Epoch and submit a vote for the root block to a specialized smart contract, `FlowClusterQC`. - The Random Beacon Committee for the next Epoch (currently all consensus nodes) will run the DKG through a specialized smart contract, `FlowDKG`. Epoch Smart Contract: - The `FlowEpoch` Smart Contract is responsible for ensuring that Epoch Setup transactions are only executed during the Epoch Setup phase and fail otherwise. The contract enforces this by setting an `enabled` field in the `FlowClusterQC` and `FlowDKG` contracts. Every state-changing method from these contracts checks to see if this is set before executing. - The `FlowEpoch` Smart Contract must ensure that the subsequent phase is sufficiently long to perform all required tasks before the epoch ends. - As part of the execution of the last block of the Epoch Setup phase, the `FlowEpoch` Smart Contract computes the public key shares generated by the DKG and the `QC`s for the collector clusters and publishes these as `EpochCommit` service event. The `FlowEpoch` Smart Contract should emit this event as soon as the artifacts are determined. See the [Epoch Commit Event Documentation](./05-epoch-scripts-events.md#flowepochepochcommit) for a detailed breakdown of the epoch commit event. ## Phase 2: Epoch Committed **Purpose:** When the epoch committed phase starts, the precise role of each node is fully specified. From a protocol-perspective, all information is available for each node to start its operation for the next Epoch. This phase provides some time for nodes to establish the communication channels and synchronize with the network to seamlessly switch over to the next epoch. **Duration:** The epoch committed phase begins right *after* the `EpochCommit` service event has been emitted. It ends when the epoch ends. ### **Protocol Directives:** Consensus - When a primary constructs a block that seals the `EpochCommit` service event, the primary includes an update to the protocol state in the block. Specifically, it: - adds the information generated in the setup phase to the Protocol State and - marks the updated Protocol State as `committed` in this respective fork. # Query Information from the Epoch Contract See the [epoch scripts and events document](./05-epoch-scripts-events.md#introduction) for detailed documentation about you can use scripts events to learn information about the state of the epoch contracts. --- # File: /md/protocol/staking/04-stake-slashing.md --- title: Stake Slashing sidebar_position: 4 description: How Flow enforces honest node behaviour --- Flow slashes nodes only for acts that directly impact the security and integrity of the network and its shared execution state. Nodes are not slashed for liveness infractions. The protocol reserves slashing for maintaining the security of the protocol rather than its liveness. You can find more details on the conditions under which a node is slashed in the [Flow whitepapers](https://www.onflow.org/technical-paper). Direct stake slashing is not currently enforced by the protocol and staking contract. It will be handled on a case-by-case basis for the foreseeable future to ensure network participants have time to participate in the testing and rollout of slashing. There is a very basic form of slashing that is currently used, where nodes who have liveness issues during an epoch may have their rewards and their delegators' rewards reduced by a pre-determinded amount based on the severity of the liveness infractions. This amount is often 50% and is only taken from the stakers' rewards for a given epoch. Their staked FLOW is not touched at all. When slashing is enforced, slashable protocol violations must be adjudicated by a supermajority of more than 2/3 of the staked consensus nodes in order to take effect. If a node is found guilty of committing a slashable protocol violation, the consensus nodes directly deduct a fine from the node's stake. It is still TBD where the slashed tokens will be deposited. The remaining un-slashed stake is deposited back into node's unstaked pool at the end of the unstaking period. --- # File: /md/protocol/staking/05-epoch-scripts-events.md --- title: Query Epoch Info with Scripts or Events sidebar_label: Epoch Scripts and Events toc_max_heading_level: 4 --- ## Introduction The epoch contract stores a lot of different state, and the state is constantly changing. As an external party, there are two ways to keep track of these state changes. You can either use Cadence scripts to query the state of the contract at any given time, or you can monitor events that are emitted by the epoch contract to be notified of any important occurrences. ## Monitor Epoch Service Events These events can be queried using the Go or JavaScript SDKs to extract useful notifications and information about the state of the epoch preparation protocol. ### What is a Service Event? Service events are special messages that are generated by smart contracts and included in execution results. They enable communication between system smart contracts and the Flow protocol. In other words, they serve as a communication mechanism between the execution state and the protocol state. Concretely, service events are defined and emitted as events like any other in Cadence. An event is considered a service event when it is: - emitted within the service chunk - emitted from a smart contract deployed to the service account - conformant to an event allowlist Each block contains a system chunk. For each system chunk, all service events emitted are included in the corresponding execution result. When verifying the system chunk, verifier nodes will only produce result approvals when the system chunks included in the execution result are correct. Thus, the security of this communication mechanism is enforced by the verification system. When sealing a block containing a service event, the consensus committee will update the protocol state accordingly, depending on the semantics of the event. For example, a service event may indicate that a node's stake has diminished to the point where they should be ejected, in which case the consensus committee would mark that node as ejected in the protocol state. Service events are fundamentally asynchronous, due the lag between block execution and sealing. Consequently they are handled slightly differently than other protocol state updates. The diagram below illustrates the steps each service event goes through to be included in the protocol state. ![Flow Service Event Diagram](./epoch-service-event-diagram.png) For conciseness, we say a service event is `sealed` when the block in which it was emitted is sealed, and we say a service event is `finalized` when the block containing the seal is finalized. ### Event Descriptions #### `FlowEpoch.EpochStart` The Epoch Start service event is emitted by `FlowEpoch.startNewEpoch()` when the epoch commit phase ends and the Epoch Smart Contracts transition to the staking auction phase. It contains the relevant metadata for the new epoch that was generated during the last epoch: ```cadence access(all) event EpochStart ( /// The counter for the current epoch that is beginning counter: UInt64, /// The first view (inclusive) of the current epoch. firstView: UInt64, /// The last view (inclusive) of the current epoch's staking auction. stakingAuctionEndView: UInt64, /// The last view (inclusive) of the current epoch. finalView: UInt64, /// Total FLOW staked by all nodes and delegators for the current epoch. totalStaked: UFix64, /// Total supply of all FLOW for the current epoch /// Includes the rewards that will be paid for the previous epoch totalFlowSupply: UFix64, /// The total rewards that will be paid out at the end of the current epoch. totalRewards: UFix64, ) ``` #### `FlowEpoch.EpochSetup` The Epoch Setup service event is emitted by `FlowEpoch.startEpochSetup()` when the staking auction phase ends and the Epoch Smart Contracts transition to the Epoch Setup phase. It contains the finalized identity table for the upcoming epoch, as well as timing information for phase changes. ```cadence access(all) event EpochSetup ( /// The counter for the upcoming epoch. Must be one greater than the /// counter for the current epoch. counter: UInt64, /// Identity table for the upcoming epoch with all node information. /// Includes: /// nodeID, staking key, networking key, networking address, role, /// staking information, weight, and more. nodeInfo: [FlowIDTableStaking.NodeInfo], /// The first view (inclusive) of the upcoming epoch. firstView: UInt64, /// The last view (inclusive) of the upcoming epoch. finalView: UInt64, /// The cluster assignment for the upcoming epoch. Each element in the list /// represents one cluster and contains all the node IDs assigned to that /// cluster, with their weights and votes collectorClusters: [FlowClusterQC.Cluster], /// The source of randomness to seed the leader selection algorithm with /// for the upcoming epoch. randomSource: String, /// The deadlines of each phase in the DKG protocol to be completed in the upcoming /// EpochSetup phase. Deadlines are specified in terms of a consensus view number. /// When a DKG participant observes a finalized and sealed block with view greater /// than the given deadline, it can safely transition to the next phase. DKGPhase1FinalView: UInt64, DKGPhase2FinalView: UInt64, DKGPhase3FinalView: UInt64 ) ``` #### `FlowEpoch.EpochCommit` The `EpochCommit` service event is emitted when the Epoch Smart Contracts transition from the Epoch Setup phase to the Epoch Commit phase. It is emitted only when all preparation for the upcoming epoch (QC and DKG) has been completed. ```cadence access(all) event EpochCommit ( /// The counter for the upcoming epoch. Must be equal to the counter in the /// previous EpochSetup event. counter: UInt64, /// The result of the QC aggregation process. Each element contains /// all the nodes and votes received for a particular cluster /// QC stands for quorum certificate that each cluster generates. clusterQCs: [FlowClusterQC.ClusterQC], /// The resulting public keys from the DKG process, encoded as by the flow-go /// crypto library, then hex-encoded. /// Group public key is the first element, followed by the individual keys dkgPubKeys: [String], ) ``` ## Query Information with Scripts The `FlowEpoch` smart contract stores important metadata about the current, proposed, and previous epochs. Metadata for all historical epochs is stored permanently in the Epoch Smart Contract's storage. ```cadence access(all) struct EpochMetadata { /// The identifier for the epoch access(all) let counter: UInt64 /// The seed used for generating the epoch setup access(all) let seed: String /// The first view of this epoch access(all) let startView: UInt64 /// The last view of this epoch access(all) let endView: UInt64 /// The last view of the staking auction access(all) let stakingEndView: UInt64 /// The total rewards that are paid out for the epoch access(all) var totalRewards: UFix64 /// The reward amounts that are paid to each individual node and its delegators access(all) var rewardAmounts: [FlowIDTableStaking.RewardsBreakdown] /// Tracks if rewards have been paid for this epoch access(all) var rewardsPaid: Bool /// The organization of collector node IDs into clusters /// determined by a round robin sorting algorithm access(all) let collectorClusters: [FlowClusterQC.Cluster] /// The Quorum Certificates from the ClusterQC contract access(all) var clusterQCs: [FlowClusterQC.ClusterQC] /// The public keys associated with the Distributed Key Generation /// process that consensus nodes participate in /// Group key is the last element at index: length - 1 access(all) var dkgKeys: [String] } ``` #### Get Epoch Metadata The `FlowEpoch` smart contract provides a public function, `FlowEpoch.getEpochMetadata()` to query the metadata for a particular epoch. You can use the **Get Epoch Metadata**([EP.01](../../build/cadence/core-contracts/07-epoch-contract-reference.md#getting-epoch-info)) script with the following arguments: | Argument | Type | Description | | ---------------- | -------- | --------------------------------------------- | | **epochCounter** | `UInt64` | The counter of the epoch to get metadata for. | #### Get Configurable Metadata The `FlowEpoch` smart contract also has a set of metadata that is configurable by the admin for phase lengths, number of collector clusters, and inflation percentage. ```cadence access(all) struct Config { /// The number of views in an entire epoch access(all) var numViewsInEpoch: UInt64 /// The number of views in the staking auction access(all) var numViewsInStakingAuction: UInt64 /// The number of views in each dkg phase access(all) var numViewsInDKGPhase: UInt64 /// The number of collector clusters in each epoch access(all) var numCollectorClusters: UInt16 /// Tracks the annualized percentage of FLOW total supply that is minted as rewards at the end of an epoch /// Calculation for a single epoch would be (totalSupply * FLOWsupplyIncreasePercentage) / 52 access(all) var FLOWsupplyIncreasePercentage: UFix64 } ``` You can use the **Get Configurable Metadata**([EP.02](../../build/cadence/core-contracts/07-epoch-contract-reference.md#getting-epoch-info)) script to get the list of configurable metadata: This script does not require any arguments. #### Get Epoch Counter The `FlowEpoch` smart contract always tracks the counter of the current epoch. You can use the **Get Epoch Counter**([EP.03](../../build/cadence/core-contracts/07-epoch-contract-reference.md#getting-epoch-info)) script to get the current epoch counter. This script does not require any arguments. #### Get Epoch Phase The `FlowEpoch` smart contract always tracks the active phase of the current epoch. ```cadence access(all) enum EpochPhase: UInt8 { access(all) case STAKINGAUCTION access(all) case EPOCHSETUP access(all) case EPOCHCOMMIT } ``` You can use the **Get Epoch Phase**([EP.04](../../build/cadence/core-contracts/07-epoch-contract-reference.md#getting-epoch-info)) script to get the current epoch phase. This script does not require any arguments. --- # File: /md/protocol/staking/06-technical-overview.md --- title: Staking Technical Overview sidebar_label: Staking Technical Overview description: Technical Overview of the Flow Staking Auction Phase --- If you haven't read the Introduction, please read that first. That document provides a non-technical overview of staking on Flow for all users and is a necessary prerequisite to this document. This document assumes you have some technical knowledge about the Flow blockchain and programming environment. # Staking This document describes the functionality of the [core identity table and staking smart contract](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc). It gives an overview of the process of epochs, staking as a node, and delegation. It is an important prerequisite to understand before proceeding with any other technical integration or interaction with the Flow Protocol, but does not provide step-by-step instructions for how to perform specific actions. See the [Staking Collection Docs for instructions](./14-staking-collection.md) This document also describes how to read public staking data from the contract. Anyone can read public data from the staking smart contract with these instructions. The transactions described in this document are contained in the [`flow-core-contracts/transactions/idTableStaking/`](https://github.com/onflow/flow-core-contracts/tree/master/transactions/idTableStaking) directory. You can see the text of all the transactions used to interact with the smart contract there. ## Smart Contract Summary The Flow staking smart contract manages a record of stakers who have staked tokens for the network. Users who want to stake can register with the staking contract at any time during the staking auction, and their tokens will be locked for staking until they request to unstake them. You should already understand from reading the [epoch documentation](./04-epoch-preparation.md) that an epoch lasts roughly a week. The `FlowIDTableStaking` contract focuses on the identity table and staking part of the epoch schedule. Epoch Schedule from the perspective of the `FlowIDTableStaking` contract: 1. **Start of Epoch:** Generic metadata about the current epoch is updated and shared and the staking auction is enabled. 2. **Staking Auction:** Stakers can perform any action they want to manage their stake, like initially registering, staking new tokens, unstaking tokens, or withdrawing rewards. This phase takes up the vast majority of time in the epoch. 3. **End Staking Auction:** Stakers cannot perform any more staking actions until the start of the next epoch/staking auction. 4. **Remove Insufficiently Staked Nodes:** All node operators who don't meet the minimum or are not operating their node properly will be removed. 5. **Randomly Assign Nodes to New Slots:** Each node type has a configurable number of nodes that can operate during any given epoch. The contract will randomly select nodes from the list of newly staked and approved nodes to add them to the ID table. Once all the slots have been filled, the remaining nodes are refunded and can apply again for the next epoch if there are slots available. 6. **Rewards Calculation:** Calculate rewards for all the node operators staked in the current epoch. 7. **Move tokens between pools.** (See the token pools section for the order of movements) 8. **End Epoch:** Set the reward payout for the upcoming epoch and go to the top of this list. 9. **Rewards Payout:** Pay rewards to all the node operators staked from the previous epoch using the calculation from earlier in the epoch. The `FlowIDTableStaking` contract manages the identity table, and all of these phases. Control of these phases is controlled by the `FlowIDTableStaking.Admin` resource object stored in the Flow Epoch account storage. The `FlowEpoch` smart contract uses this resource to autonomously manage the functioning of the network. It is decentralized and managed by the node software, smart contracts, and democratically by all the stakers in the network. ## Staking as a Node Operator For a node to stake, node operators first need to generate their staking key, staking key proof-of-possesion, networking address, and networking key. The [node operation guide](../node-ops/index.md) describes how to run a node and generate node information. To generate a node ID, simply hash the staking key. Node operators need to determine the role of node they will be running (Collection, Consensus, Execution, Verification, or Access). NOTE: Access Nodes are eligible to stake and have a staking minimum of 100 FLOW, but will not receive rewards for their stake. Please register as a different node type if you would like to receive rewards. Once the info has been determined: - Node role: `UInt8` (1 = Collection, 2 = Consensus, 3 = Execution, 4 = Verification, 5 = Access) - Node ID: 32 byte `String` (64 hex characters) - Networking Address: `String` (Length must be less than 510 characters and be a properly formatted IP address or hostname) - Networking Key: 64 byte `String` (128 hex characters, must be a valid ECDSA-P256 Key) - Staking Key: 96 byte `String` (192 hex characters, must be a valid BLS key) - Staking Key Proof of Possesion: (48 byte (96 hex characters) string) The node operator is ready to register their node. NOTE: The staking smart contract validates that the strings for the keys are valid public keys. The staking admin and node software also checks the keys and networking address to make sure they are valid and if they are not, the registered node will not be eligible to stake. To register a node, the node operator calls the [`addNodeRecord` function](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L1552) on the staking contract, providing all the node info and the tokens that they want to immediately stake, if any. This registers the node in the Flow node identity table and commits the specified tokens to stake during the next epoch. This also returns a special node operator object that is stored in the node operator's account. This object is used for staking, unstaking, and withdrawing rewards. Consensus and Collection nodes also need to create a separate machine account for use in the DKG and QC processes, respectively. This machine account creation is handled automatically by the staking collection smart contract. More information is in the [machine account documentation](./11-machine-account.md#creation). The register node transaction only needs to be submitted once per node. A node does not need to register every epoch. A registration cannot be used to manage multiple nodes. Multiple nodes need to be registered separately (with the Staking Collection). Once a node operator has registered their node and its metadata, the metadata cannot be modified. The only exception is the networking address, which can me modified with the Update Networking Address transaction. If a node operator wants to update any of their other metadata such as ID, keys, or role, they need to unstake, withdraw their tokens, and register a completely new node. Once node operators have registered and have the special node object, they will be able to perform any of the valid staking options with it, assuming that they have the required amount of tokens to perform each operation. When the staking auction ends, if a node operator has committed less than the minimum stake required, [or if their node information is invalid and they haven't been approved by the network,](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L788) their committed tokens are moved to their unstaked pool, which they can withdraw from at any time. Nodes who did have enough tokens committed and are approved will have their [committed tokens moved to the staked state](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L923-L927) at the end of the epoch if they are selected as a node operator by the random node slot filling algorithm. There is a configurable cap on the number of nodes of each type, so if the number of selected nodes equals the cap, than newly registered nodes will not be added to the network until the cap is raised or other nodes unstake. If a node operator has users delegating to them, they cannot withdraw their own tokens such that their own staked tokens would fall below the minimum requirement for that node type. If they have delegators and try to submit [an unstaking transaction](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L510-L514) that would put their stake below the minimum, it will fail. If they want to unstake below the minimum, they must unstake all of their tokens using the special [`unstakeAll` method,](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L538) which also unstakes all of the tokens that have been delegated to them. Consequently, a node operator cannot accept delegation unless [their own stake is above the minimum.](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L1066) ## Staking as a Delegator Every staked non-access node in the Flow network is eligible for delegation by any other user. The user only needs to know the node ID of the node they want to delegate to. To register as a delegator, the delegator submits a **Register Delegator** transaction that calls the [`registerNewDelegator function`](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L1590), providing the ID of the node operator they want to delegate to. This transaction should store the `NodeDelegator` object in the user's account, which is what they use to perform staking operations. Users are able to get a list of possible node IDs to delegate to via onchain scripts. This information will also be provided off-chain, directly from the node operators or via third-party services. [Available node IDs are listed in a public repo.](https://github.com/onflow/flow/blob/master/nodeoperators/NodeOperatorList.md) The fee that node operators take from the rewards their delegators receive is 8%. A node operator cannot be delegated to unless the total tokens they have committed to stake are above the minimum requirement for their node types. The delegation logic keeps track of the amount of tokens each delegator has delegated for the node operator. When rewards are paid, the protocol [automatically takes the 8% cut](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L888-L898) of the delegator's rewards for the node operator and the delegator's rewards are deposited in the delegator's reward pool. ## Staking Operations Available to All Stakers Regardless of whether they are a node operator or delegator, a staker has access to all the same staking operations, outlined below. Specific implementations of these transactions are detailed in the [Staking Collection Docs](./14-staking-collection.md) ### Stake More Tokens A staker can commit more tokens to stake for the next epoch at any time during the staking auction, and there are three different ways to do it. 1. They can commit new tokens to stake by submitting a **stake_new_tokens** transaction, which withdraws tokens from their account's flow token vault and commits them. 2. They can commit tokens that are in their unstaked token pool, which holds the tokens that they have unstaked. Submit a **stake_unstaked_tokens** transaction to move the tokens from the unstaked pool to the committed pool. 3. They can commit tokens that are in their rewarded token pool, which holds the tokens they have been awarded. They submit a **stake_rewarded_tokens** transaction to move the tokens from the rewards pool to the committed pool. ### Cancel Committed Stake / Unstake Tokens At any time during the staking auction, a staker can submit a request to unstake tokens with a **request_unstaking** transaction. If there are tokens that have been committed but are not staked yet, they are moved to the unstaked pool and are available to withdraw. If the requested tokens are in the staked pool, it marks the specified amount of tokens to be unstaked at the end of the epoch. At the end of the epoch, the tokens are moved to the unstaking pool. They will sit in this pool for one (1) additional epoch, at which point they will be moved to the unstaked tokens pool. ### Cancel an Unstake Request Unstaking requests are not fulfilled until the end of the epoch where they are submitted, so a staker can cancel the unstaking request before it is carried out. A staker can do this by submitting a **stake_unstaked_tokens** transaction, specifying the number of tokens of their unstake request they would like to cancel. If the specified number of tokens have been requested to unstake, the request will be canceled. ### Withdraw Unstaked Tokens At any time, stakers are able to freely withdraw from their unstaked tokens pool with the **withdraw_unstaked** transaction. ### Withdraw Rewarded Tokens Staking rewards are paid out at the end of every epoch based on how many tokens are in a users `tokensStaked` pool. Every staker's rewards are deposited into their rewarded tokens pool. Rewards can be withdrawn at any time by submitting a **withdraw_reward_tokens** transaction. These tokens are unlocked and can be transferred onchain if desired, or re-staked. The source code for the staking contract and more transactions can be found in the [Flow Core Contracts GitHub Repository](https://github.com/onflow/flow-core-contracts). # Monitor Events from the Identity Table and Staking Contract See the [staking events document](./07-staking-scripts-events.md) for information about the events that can be emitted by the staking contract. # Appendix ## Token Pools Each node operator has five token pools allocated to them: - **Committed Tokens:** Tokens that are committed for the next epoch. They are automatically moved to the staked pool when the next epoch starts. - **Staked Tokens:** Tokens that are staked by the node operator for the current epoch. They are only moved at the end of an epoch and if the staker has submitted an unstaking request. - **Unstaking Tokens:** Tokens that have been unstaked, but are not free to withdraw until the following epoch. - **Unstaked Tokens:** Tokens that are freely available to withdraw or re-stake. Unstaked tokens go to this pool. - **Rewarded Tokens:** Tokens that are freely available to withdraw or re-stake. Rewards are paid and deposited to the rewarded Pool after each epoch. At the end of every epoch, tokens are moved between pools in this order: 1. All committed tokens will get moved either to the staked tokens pool, or to the unstaked tokens pool (depending on if the registered node has met the minimum stake requirements). 2. All committed tokens get moved to staked tokens pool. 3. All unstaking tokens get moved to the unstaked tokens pool. 4. All requested unstaking tokens get moved from the staked pool to the unstaking pool. --- # File: /md/protocol/staking/07-staking-scripts-events.md --- title: Query Staking Info with Scripts or Events sidebar_label: Staking Scripts and Events --- # Introduction The staking contract stores a lot of different state, and the state is constantly changing. As an external party, there are two ways to keep track of these state changes. You can either use Cadence scripts to query the state of the contract at any given time, or you can monitor events that are emitted by the staking contract to be notified of any important occurances. # Query Information with Scripts ## Get the list of proposed nodes for the next epoch: `FlowIDTableStaking.getProposedNodeIDs()`: Returns an array of node IDs for proposed nodes. Proposed nodes are nodes that have enough staked and committed for the next epoch to be above the minimum requirement and have been selected to participate in the next epoch. This means that new access nodes that have not been selected with the random slot selection algorithm will not be included in this list. You can use the **Get Proposed Table**([SC.05](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script for retrieving this info. This script requires no arguments. ## Get the list of all nodes that are currently staked: `FlowIDTableStaking.getStakedNodeIDs()` and ``FlowIDTableStaking.getParticipantNodeList()`: Returns an array of nodeIDs that are currently staked. Staked nodes are nodes that are staked and participating in the current epoch. You can use the **Get Current Table**([SC.04](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script for retrieving this info. This script requires no arguments. ## Get the list of all Candidate Nodes `getCandidateNodeList(): {UInt8: {String: Bool}}`: Returns a dictionary of nodes that are candidates to stake in the next epoch but are not staked in the current epoch. You can use the [**Get Candidate Node List**](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L1762) script for retrieving this info. This script requires no arguments. ## Get all of the info associated with a single node staker: `FlowIDTableStaking.NodeInfo(nodeID: String)`: Returns a `NodeInfo` struct with all of the metadata associated with the specified node ID. You can see the `NodeInfo` definition in the [FlowIDTableStaking smart contract.](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L254) You can use the **Get Node Info**([SC.08](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script with the following arguments: | Argument | Type | Description | | ---------- | -------- | -------------------------------------- | | **nodeID** | `String` | The node ID of the node to search for. | You can also query the info from an address that uses the staking collection by using the **Get Node Info From Address**([SCO.15](../../build/cadence/core-contracts/11-staking-collection.md#scripts)) script with the following arguments: | Argument | Type | Description | | ----------- | --------- | -------------------------------------------------- | | **address** | `Address` | The address of the account that manages the nodes. | ## Get the total committed balance of a node (with delegators): `FlowIDTableStaking.NodeInfo(_ nodeID: String).totalCommittedWithDelegators()`: Returns the total committed balance for a node, which is their total tokens staked + committed, plus all of the staked + committed tokens of all their delegators. You can use the **Get Node Total Commitment**([SC.09](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script with the following argument: | Argument | Type | Description | | ---------- | -------- | -------------------------------------- | | **nodeID** | `String` | The node ID of the node to search for. | ## Get the total committed balance of a node (without delegators): `FlowIDTableStaking.NodeInfo(_ nodeID: String).totalCommittedWithoutDelegators()`: Returns the total committed balance for a node, which is their total tokens staked + committed, plus all of the staked + committed tokens of all their delegators. You can use the **Get Only Node Total Commitment**([SC.11](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script with the following argument: | Argument | Type | Description | | ---------- | -------- | -------------------------------------- | | **nodeID** | `String` | The node ID of the node to search for. | ## Get all the info associated with a single delegator: `FlowIDTableStaking.DelegatorInfo(nodeID: String, delegatorID: UInt32)`: Returns a `DelegatorInfo` struct with all of the metadata associated with the specified node ID and delegator ID. You can see the `DelegatorInfo` definition in the [FlowIDTableStaking smart contract.](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L375) You can use the **Get Delegator Info**([SC.10](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script with the following arguments: | Argument | Type | Description | | --------------- | -------- | -------------------------------------------- | | **nodeID** | `String` | The node ID that the delegator delegates to. | | **delegatorID** | `String` | The ID of the delegator to search for. | You can also query the info from an address by using the **Get Delegator Info From Address**([SCO.16](../../build/cadence/core-contracts/11-staking-collection.md#scripts)) script with the following arguments: | Argument | Type | Description | | ----------- | --------- | ------------------------------------------------------ | | **address** | `Address` | The address of the account that manages the delegator. | ## Get the delegation cut percentage: `FlowIDTableStaking.getRewardCutPercentage(): UFix64`: Returns a `UFix64` number for the cut of delegator rewards that each node operator takes. You can use the **Get Cut Percentage**([SC.01](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script to retrieve this info. This script requires no arguments. ## Get the minimum stake requirements: `FlowIDTableStaking.getMinimumStakeRequirements(): {UInt8: UFix64}`: Returns a mapping for the stake requirements for each node type. You can use the **Get stake requirements**([SC.02](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script to retrieve this info. This script requires no arguments. ## Get the total weekly reward payout: `FlowIDTableStaking.getEpochTokenPayout(): UFix64`: Returns a `UFix64` value for the total number of FLOW paid out each epoch (week). You can use the **Get weekly payout**([SC.03](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script to retrieve this info. This script requires no arguments. ## Get the total FLOW staked: You can use the **Get total FLOW staked**([SC.06](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script to retrieve this info. This script requires no arguments. ## Get the total FLOW staked by all the nodes of a single node role: You can use the **Get total FLOW staked by node type**([SC.07](../../build/cadence/core-contracts/06-staking-contract-reference.md#getting-staking-info-with-scripts)) script with the following arguments: | Argument | Type | Description | | ------------ | ------- | ------------------------------- | | **nodeType** | `UInt8` | The type of node to search for. | # Staking Events Staking events can be queried using the Go or JavaScript SDKs to extract useful notifications and information about the state of the staking process. ## Global Staking and Epoch Events ### NewEpoch ```cadence access(all) event NewEpoch(totalStaked: UFix64, totalRewardPayout: UFix64, newEpochCounter: UInt64) ``` | Field | Type | Description | | ----------------- | ------ | ---------------------------------------------------------------------- | | totalStaked | UFix64 | The total number of tokens staked for the new Epoch | | totalRewardPayout | UFix64 | The total number of tokens that will be paid as rewards for this epoch | | newEpochCounter | UInt64 | The epoch counter for this new epoch | Emitted by `FlowIDTableStaking.Admin.moveTokens()` when the tokens are moved between pools, which signals a new epoch. ### NewWeeklyPayout ```cadence access(all) event NewWeeklyPayout(newPayout: UFix64) ``` | Field | Type | Description | | --------- | ------ | ------------------------------------------------------------------ | | newPayout | UFix64 | The new number of tokens that will be paid at the end of the epoch | Emitted by `FlowIDTableStaking.Admin.setEpochTokenPayout()` when the Admin changes the total tokens paid at the end of the epoch. After this event the `epochTokenPayout` is equal to the new value. ## Node Events These are events that concern the operation of a node. ### NewNodeCreated ```cadence access(all) event NewNodeCreated(nodeID: String, role: UInt8, amountCommitted: UFix64) ``` | Field | Type | Description | | --------------- | ------ | ---------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. Usually the hash of the node's public key. | | role | UInt8 | The node's role type. From 1 to 5 inclusive. | | amountCommitted | UFix64 | The amount of FLOW tokens staked to register the node. This is determined by the `role`. | Emitted by `FlowIDTableStaking.NodeRecord.init()` when a new node is successfully created. After this event is emitted for your node, you can begin to perform staking transactions using it. ### NodeRemovedAndRefunded ```cadence access(all) event NodeRemovedAndRefunded(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of FLOW tokens returned to the node. | Emitted by `FlowIDTableStaking.Admin.endStakingAuction()` if the node is being removed from the next epoch due to a failure to meet the minimum requirements of committed tokens for the next epoch. After this event, the refunded FLOW tokens will be part of the node's `tokensUnstaked` balance. ## Token Events These are events that concern the direct usage of FLOW tokens - staking or unstaking locked tokens, withdrawing rewards, etc. Events emitted when using delegation are described in the next section. ### TokensCommitted ```cadence access(all) event TokensCommitted(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of additional FLOW tokens committed to the node. | Emitted whenever additional tokens are staked on the node for the following epoch. Specifically: 1. By `FlowIDTableStaking.NodeStaker.stakeNewTokens()` when new tokens (tokens that have not previously been staked) are added to the system to stake on the node during the next epoch. 2. By `FlowIDTableStaking.NodeStaker.stakeUnstakedTokens()` when unstaked tokens (tokens that were previously staked and then unstaked) are staked again with the node for the next epoch. 3. By `FlowIDTableStaking.NodeStaker.stakeRewardedTokens()` when reward tokens (tokens paid in return for previous staking) are staked with the node for the next epoch. After this event, the FLOW tokens will be part of the node's `tokensCommitted` balance. ### TokensStaked ```cadence access(all) event TokensStaked(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of FLOW tokens staked to the node. | Emitted by `FlowIDTableStaking.Admin.moveTokens()` at the end of an epoch if committed tokens are being added to the node's tokensStaked balance. After this event, the tokens will be part of the node's staked balance. ### TokensUnstaking ```cadence access(all) event TokensUnstaking(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of FLOW tokens unstaked from the node. | Emitted by `FlowIDTableStaking.Admin.moveTokens()` at the end of an epoch if a node operator's staked tokens are being unstaked in response to a request from the node operator. After this event, the tokens will be a part of the node operator's `tokensUnstaking` balance, where they are held for a whole epoch "unstaking period" with no rewards. ### TokensUnstaked ```cadence access(all) event TokensUnstaked(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of FLOW tokens unstaked from the node. | Emitted by `FlowIDTableStaking.NodeStaker.requestUnstaking()` and `FlowIDTableStaking.Admin.moveTokens()` when tokens are deposited into the `tokensUnstaked` pool: ### RewardsPaid ```cadence access(all) event RewardsPaid(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of FLOW tokens paid to the node this epoch as a reward. | Emitted by `FlowIDTableStaking.Admin.payRewards()` at the end of the epoch to pay rewards to node operators based on the tokens that they have staked. After this event, the reward tokens will be part of the node's tokensRewarded balance. The Delegator rewards are paid at the same time, see `DelegatorRewardsPaid` below. ### UnstakedTokensWithdrawn ```cadence access(all) event UnstakedTokensWithdrawn(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of unstaked FLOW tokens that the node operator is withdrawing. | Emitted by `FlowIDTableStaking.NodeStaker.withdrawUnstakedTokens()` when the node operator calls that function to withdraw part or all of their unstaked tokens balance. After this event, the FLOW tokens will be withdrawn to a newly created `FungibleToken.Vault` which the caller can deposit to the vault of their choice. ### RewardTokensWithdrawn ```cadence access(all) event RewardTokensWithdrawn(nodeID: String, amount: UFix64) ``` | Field | Type | Description | | ------ | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | amount | UFix64 | The amount of rewarded FLOW tokens that the node operator is withdrawing. | Emitted by `FlowIDTableStaking.NodeStaker.withdrawRewardedTokens()` when the node operator calls that function to withdraw part or all of their reward tokens balance. After this event, the FLOW tokens will be withdrawn to a newly created `FungibleToken.Vault` which the caller can deposit to the vault of their choice. ## Delegator Events These are events that concern FLOW token delegation. ### NewDelegatorCreated ```cadence access(all) event NewDelegatorCreated(nodeID: String, delegatorID: UInt32) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UFix64 | The ID for the new delegator. Unique within the node but not globally. | Emitted by `FlowIDTableStaking.Admin.registerNewDelegator()` when the node operator registers a new delegator for the node. Note that the delegatorID is unique within the node but is not globally unique. After this event, the new delegator is registered with the node. ### DelegatorTokensCommitted ```cadence access(all) event DelegatorTokensCommitted(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UInt32 | The ID for the delegator. | | amount | UFix64 | The amount of additional FLOW tokens committed to the node. | Emitted whenever additional tokens are committed for a delegator for the following epoch. Specifically: 1. By `FlowIDTableStaking.NodeDelegator.delegateNewTokens()` when new tokens (tokens that have not previously been staked) are added to the system to stake with the delegator during the next epoch. 2. By `FlowIDTableStaking.NodeDelegator.delegateUnstakedTokens()` when unstaked tokens (tokens that were previously staked and then unstaked) are staked again with the delegator for the next epoch. 3. By `FlowIDTableStaking.NodeDelegator.delegateRewardedTokens()` when reward tokens (tokens paid in return for previous staking) are staked with the delegator for the next epoch. After this event, the FLOW tokens will be part of the delegator's `tokensCommitted` balance. ### DelegatorTokensStaked ```cadence access(all) event DelegatorTokensStaked(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UInt32 | The ID for the delegator. | | amount | UFix64 | The amount of FLOW tokens staked to the node. | Emitted by `FlowIDTableStaking.Admin.moveTokens()` at the end of an epoch if committed tokens are being added to the delegator's tokensStaked balance. After this event, the tokens will be part of the delegator's staked balance. ### DelegatorTokensUnstaking ```cadence access(all) event DelegatorTokensUnstaking(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UInt32 | The ID for the delegator. | | amount | UFix64 | The amount of FLOW tokens unstaked from the node. | Emitted by `FlowIDTableStaking.Admin.moveTokens()` at the end of an epoch if a delegator's staked tokens are being unstaked in response to a request from the delegator. After this event, the tokens will be a part of the delegator's `tokensUnstaking` balance, where they are held for a whole epoch "unstaking period" with no rewards. ### DelegatorTokensUnstaked ```cadence access(all) event DelegatorTokensUnstaked(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UInt32 | The ID for the delegator. | | amount | UFix64 | The amount of FLOW tokens unstaked from the node. | Emitted by `FlowIDTableStaking.NodeDelegator.requestUnstaking()` and `FlowIDTableStaking.Admin.moveTokens()` when tokens are deposited into the delegator's `tokensUnstaked` pool: ### DelegatorRewardsPaid ```cadence access(all) event DelegatorRewardsPaid(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UFix64 | The ID for the delegator. Unique within the node but not globally. | | amount | UFix64 | The amount of rewarded FLOW tokens that the delegator is paid. | Emitted by `FlowIDTableStaking.Admin.payRewards()` at the end of an epoch when rewards are being paid. After this event is emitted, the reward tokens will be part of the delegator's tokensRewarded balance. The Node rewards are paid at the same time, see `RewardsPaid` above. ### DelegatorUnstakedTokensWithdrawn ```cadence access(all) event DelegatorUnstakedTokensWithdrawn(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UFix64 | The ID for the delegator. Unique within the node but not globally. | | amount | UFix64 | The amount of unstaked FLOW tokens that the delegator is withdrawing. | Emitted by `FlowIDTableStaking.NodeDelegator.withdrawUnstakedTokens()` when the delegator calls that function to withdraw part or all of their unstaked tokens balance. After this event, the FLOW tokens will be withdrawn to a newly created `FungibleToken.Vault` which the caller can deposit to the vault of their choice. ### DelegatorRewardTokensWithdrawn ```cadence access(all) event DelegatorRewardTokensWithdrawn(nodeID: String, delegatorID: UInt32, amount: UFix64) ``` | Field | Type | Description | | ----------- | ------ | --------------------------------------------------------------------------------------------------------------- | | nodeID | String | The unique ID string for the node. 32 bytes. The same value emitted in the `NewNodeCreated` event for the node. | | delegatorID | UFix64 | The ID for the delegator. Unique within the node but not globally. | | amount | UFix64 | The amount of rewarded FLOW tokens that the delegator is withdrawing. | Emitted by `FlowIDTableStaking.NodeDelegator.withdrawRewardedTokens()` when the delegator calls that function to withdraw part or all of their unstaked tokens balance. After this event, the FLOW tokens will be withdrawn to a newly created `FungibleToken.Vault` which the caller can deposit to the vault of their choice. --- # File: /md/protocol/staking/08-staking-rewards.md --- title: Staking and Delegation rewards sidebar_label: How to Query Staking rewards description: How to check the staking and delegation rewards --- ## Current method to check staking rewards Rewards payout happens automatically after the end of the epoch and without the need of an explicit transaction being submitted by the service account. Instead of a separate reward payout transaction, the reward payout events will be recorded in the system chunk in the block that is produced at the time of the epoch transition without creating a regular transaction ID. The rewards payout can be queried by querying the block which contains the system chunk that contains the reward payout events. ``` flow events get A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid --start --end -n mainnet where block height is the height of the block containing the rewards payout events ``` Example ``` $ flow events get A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid --start 51753836 --end 51753836 -n mainnet Events Block #51753836: Index 6 Type A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "a3075cf9280cab4fa0b7b1e639b675bdae3e8874557d98ee78963f0799338a5f" - amount (UFix64): 1660.21200000 Index 9 Type A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "cf0ff514b6aa659914b99ab1d17743edb2b69fbb338ab01945a08530a98c97d4" - amount (UFix64): 3762.20370347 Index 12 Type A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "de988efc8cb79d02876b7beffd404fc24b61c287ebeede567f90056f0eece90f" - amount (UFix64): 939.85630919 Index 27 Type A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "fa5f24a66c2f177ebc09b8b51429e9f157037880290e7858f4336479e57dc26b" - amount (UFix64): 1660.21200000 Index 30 Type A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "581525fa93d8fe4b334c179698c6e72baccb802593e55e40da61d24e589d85be" - amount (UFix64): 1937.24727662 ... ... ... ... Index 50115 Type A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "95ffacf0c05757cff71a4ee49e025d5a6d1103a3aa7d91253079e1bfb7c22458" - delegatorID (UInt32): 23 - amount (UFix64): 0.10424555 Index 50118 Type A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid Tx ID f31815934bff124e332b3c8be5e1c7a949532707251a9f2f81def8cc9f3d1458 Values - nodeID (String): "95ffacf0c05757cff71a4ee49e025d5a6d1103a3aa7d91253079e1bfb7c22458" - delegatorID (UInt32): 18 - amount (UFix64): 17.31047712 ``` Example using [Flow Go SDK](../../build/tools/clients/flow-go-sdk/index.md) ``` package main import ( "context" "fmt" client "github.com/onflow/flow-go-sdk/access/grpc" ) func main() { // the Flow testnet community Access node API endpoint accessNodeAddress := "access.mainnet.nodes.onflow.org:9000" // create a gRPC client for the Access node accessNodeClient, err := client.NewClient(accessNodeAddress) if err != nil { fmt.Println("err:", err.Error()) panic(err) } ctx := context.Background() blockEvents, err := accessNodeClient.GetEventsForHeightRange(ctx, "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid", 51753836, 51753836) if err != nil { panic(err) } for _, blockEvent := range blockEvents { fmt.Println("Block: " + blockEvent.BlockID.String()) for _, event := range blockEvent.Events { fmt.Println("\tEvent type: " + event.Type) fmt.Println("\tEvent: " + event.Value.String()) fmt.Println("\tEvent payload: " + string(event.Payload)) } } } ``` ## Check staking rewards before May 2023 Before May 2023, rewards payouts were done manually by the Flow governance committee. When the transactions executed, they generated events for the rewards paid to each node and delegator. To check the staking and delegation rewards, those transactions should be queried directly. Example using [Flow cli](../../build/tools/flow-cli/index.md) ``` $ flow transactions get 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 -n mainnet Status ✅ SEALED ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Payer e467b9dd11fa00df Authorizers [e467b9dd11fa00df] Proposal Key: Address e467b9dd11fa00df Index 11 Sequence 118 No Payload Signatures Envelope Signature 0: e467b9dd11fa00df Envelope Signature 1: e467b9dd11fa00df Envelope Signature 2: e467b9dd11fa00df Envelope Signature 3: e467b9dd11fa00df Envelope Signature 4: e467b9dd11fa00df Signatures (minimized, use --include signatures) Events: Index 0 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 64.59694884 - from (Address?): 0xf919ee77447b7497 Index 1 Type A.f919ee77447b7497.FlowFees.TokensWithdrawn Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 64.59694884 Index 2 Type A.1654653399040a61.FlowToken.TokensMinted Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 1326397.40305116 Index 3 Type A.1654653399040a61.FlowToken.TokensDeposited Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 1326397.40305116 - to (Never?): nil Index 4 Type A.1654653399040a61.FlowToken.TokensWithdrawn Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 1004.16460872 - from (Never?): nil Index 5 Type A.1654653399040a61.FlowToken.TokensDeposited Tx ID 84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84 Values - amount (UFix64): 1004.16460872 - to (Address?): 0x8624b52f9ddcd04a ... ... ``` Example using [Flow Go SDK](../../build/tools/clients/flow-go-sdk/index.md) ``` package main import ( "context" "fmt" "github.com/onflow/flow-go-sdk" client "github.com/onflow/flow-go-sdk/access/grpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) func main() { // the Flow mainnet community Access node API endpoint accessNodeAddress := "access.mainnet.nodes.onflow.org:9000" maxGRPCMessageSize := 1024 * 1024 * 20 // to accommodate for the large transaction payload // create a gRPC client for the Access node accessNodeClient, err := client.NewClient(accessNodeAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxGRPCMessageSize))) if err != nil { fmt.Println("err:", err.Error()) panic(err) } ctx := context.Background() txID := flow.HexToID("84eca4ff612ef70047d60510710cca872c8a17c1bd9f63686e74852b6382cc84") rewardsTxResult, err := accessNodeClient.GetTransactionResult(ctx, txID) if err != nil { panic(err) } for _, event := range rewardsTxResult.Events { fmt.Println("Event type: " + event.Type) fmt.Println("Event: " + event.Value.String()) fmt.Println("Event payload: " + string(event.Payload)) } } ``` --- # File: /md/protocol/staking/09-qc-dkg.md --- title: Quorum Certificate and Distributed Key Generation sidebar_label: QC and DKG description: How the Flow protocol manages the Epoch Setup Phase toc_max_heading_level: 4 --- :::warning If you haven't read the Intro to Flow Staking document and the Epoch protocol document, please read that first. Those documents provide an overview of epochs on Flow for all users and are necessary prerequisites to this document. ::: :::warning This document assumes you have some technical knowledge about the Flow blockchain and programming environment. ::: ## Epoch Setup Phase **Purpose:** During the epoch setup phase, all nodes participating in the upcoming epoch must perform setup tasks in preparation for the upcoming epoch, including the Collector Cluster Quorum Certificate Generation and Consensus Committe Distributed Key Generation. **Duration:** The epoch setup phase begins right after the `EpochSetup` service event is emitted. It ends with the block where `EpochCommit` service emitted. ## Machine Accounts The processes described in this document are fully automated. They are expected to be performed entirely by the node software with no manual interaction required by the node operator after the node has been set up and registered. To facilitate this, we recommend that node operators use a secondary "machine account" that only stores the `FlowClusterQC.Voter` or `FlowDKG.Participant` resource objects in addition to FLOW to pay for transaction fees. You can connect your node to this account to participate in the Epoch Setup Phase without having to do the actions manually. If you are using the [Staking Collection](./14-staking-collection.md) for your node, this functionality is built-in. When you register a node with the staking collection, you also have to provide a public key or keys for your machine account for the node. If you have a node without a machine account (if you were operating a node from the time before epochs and staking collection were enabled, for example) the staking collection also provides a method to create a machine account for an existing node. See the [Staking Collection Docs](./14-staking-collection.md#machine-account-support) for more information. ## Collector Cluster Quorum Certificate Generation Protocol The collector nodes are organized into clusters and must bootstrap the Hotstuff consensus algorithm for each cluster. To do this, they generate the root block for their respective clusters and submit a vote for the root block to a specialized smart contract, `FlowClusterQC`. If 2/3 of the collectors in a cluster have voted with the same unique vote, then the cluster is considered complete. Once all clusters are complete, the QC is complete. ### `FlowClusterQC` Transactions #### Create QC Voter Object A node uses the [`getClusterQCVoter()`](https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowEpoch.cdc#L905) function in the `FlowEpoch` contract to create their Voter object and needs to provide a reference to their `FlowIDTableStaking.NodeStaker` object to prove they are the node owner. When registering a node with the staking collection, this process is handled by [the transaction to register.](./14-staking-collection.md#register-a-new-staked-node) It also creates a machine account for the QC object. If a user already has a registered node with the staking collection, but hasn't created their QC Voter object yet, they can use the [`create_machine_account.cdc` transaction.](./14-staking-collection.md#create-a-machine-account-for-an-existing-node) If a user is not using the staking collection, they can use the **Create QC Voter** ([QC.01](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) transaction. This will only store the QC Voter object in the account that stores the `NodeStaker` object. It does not create a machine account or store it elsewhere, so it is not recommended to use. We encourage to use the staking collection instead. #### Submit Vote During the Epoch Setup Phase, the node software should submit the votes for the QC generation automatically using the **Submit QC Vote** ([QC.02](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) transaction with the following arguments. | Argument | Type | Description | | ----------------- | -------- | ------------------------------------------------------- | | **voteSignature** | `String` | The signed message (signed with the node's staking key) | | **voteMessage** | `String` | The raw message itself. | ## Consensus Committee Distributed Key Generation Protocol (DKG) The Random Beacon Committee for the next Epoch (currently all consensus nodes) will run the DKG through a specialized smart contract, `FlowDKG`. To do this, they post a series of messages to a public "whiteboard" to collectively generate a shared key array. When each node has enough information to generate their array of keys, they send the generated array to the smart contract as their final submission. If `(# of consensus nodes-1)/2` consensus nodes submit the same key array, the DKG is considered to be complete. ### `FlowDKG` Transactions #### Create DKG Participant Object A node uses the [`getDKGParticipant()`](https://github.com/onflow/flow-core-contracts/blob/master/contracts/epochs/FlowEpoch.cdc#L919) function in the `FlowEpoch` contract to create their Voter object and needs to provide a reference to their `FlowIDTableStaking.NodeStaker` object to prove they are the node owner. When registering a node with the staking collection, this process is handled by [the transaction to register.](./14-staking-collection.md#register-a-new-staked-node) It also creates a machine account for the DKG Object. If a user already has a registered node with the staking collection, but hasn't created their DKG Participant object yet, they can use the [`create_machine_account.cdc` transaction.](./14-staking-collection.md#create-a-machine-account-for-an-existing-node) If a user is not using the staking collection, they can use the **Create DKG Participant** ([DKG.01](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) transaction. This will only store the DKG Participant object in the account that stores the `NodeStaker` object. It does not create a machine account or store it elsewhere, so it is not recommended to use. The staking collection is the recommended method. #### Post Whiteboard Message During the Epoch Setup Phase, the node software should post whiteboard messages to the DKG automatically using the **Post Whiteboard Message** ([DKG.02](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) transaction with the following arguments. | Argument | Type | Description | | ----------- | -------- | ------------------------------------- | | **content** | `String` | The content of the whiteboard message | #### Send Final Submission During the Epoch Setup Phase, the node software should send its final submission for the DKG automatically using the **Send Final Submission** ([DKG.03](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) transaction with the following arguments. | Argument | Type | Description | | -------------- | ----------- | -------------------------------------------- | | **submission** | `[String?]` | The final key vector submission for the DKG. | ## Monitor Events and Query State from the QC and DKG Contracts See the [QC and DKG events and scripts document](./10-qc-dkg-scripts-events.md) for information about the events that can be emitted by these contracts and scripts you can use to query information. --- # File: /md/protocol/staking/10-qc-dkg-scripts-events.md --- title: Query QC/DKG Info with Scripts or Events sidebar_label: QC/DKG Scripts and Events --- # Introduction The Cluster Quorum Certificate (QC) and Distributed Key Generation (DKG) protocol smart contracts store a lot of different state, and the state is constantly changing. As an external party, there are two ways to keep track of these state changes. You can either use Cadence scripts to query the state of the contract at any given time, or you can monitor events that are emitted by the contracts to be notified of any important occurrences. # Query Information with Scripts These events can be queried using the Go or JavaScript SDKs to extract useful notifications and information about the state of these processes. ## QC Scripts These scripts allow anyone to query information about the state of the QC contract. ### Get Clusters To return a struct representing the information associated with a collector cluster, can use the **Get Cluster** ([QC.03](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) script with the following argument: | Argument | Type | Description | | ---------------- | -------- | --------------------------------- | | **clusterIndex** | `UInt16` | The index of the cluster to query | ### Get QC Enabled To return a boolean representing if the QC is enabled, can use the **Get QC Enabled** ([QC.04](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) script with no arguments. ### Get Node Has Voted To return a boolean representing if a node has voted for the current QC, you can use the **Get Node Has Voted** ([QC.05](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) script with the following argument: | Argument | Type | Description | | ---------- | -------- | ------------------------ | | **nodeID** | `String` | The node ID to check for | ### Get Voting Complete To return a boolean representing if the voting for the QC phase is complete, can use the **Get Voting Complete** ([QC.06](../../build/cadence/core-contracts/07-epoch-contract-reference.md#quorum-certificate-transactions-and-scripts)) script with no arguments. ## DKG Scripts ### Get DKG Enabled To return a boolean representing if the DKG is enabled, you can use the **Get DKG Enabled** ([DKG.04](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) script with no arguments. ### Get DKG Completed To return a boolean representing if the dkg is complete, you can use the **Get DKG Complete** ([DKG.05](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) script with no arguments. ### Get Whiteboard Messages To return an array of structs representing all the whiteboard messages, you can use the **Get DKG Whiteboard Messages** ([DKG.06](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) script with no arguments. ### Get Final Submissions To return an array of key vectors for the nodes' final submissions, you can use the **Get Final Submissions** ([DKG.07](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) script with no arguments. ### Get Node Has Submitted To return a boolean representing if a node has sent their final submission for the DKG, you can use the **Get Node Has Submitted** ([DKG.08](../../build/cadence/core-contracts/07-epoch-contract-reference.md#dkg-transactions-and-scripts)) script with the following argument: | Argument | Type | Description | | ---------- | -------- | ------------------------ | | **nodeID** | `String` | The node ID to check for | ## DKG Events ```cadence /// Emitted when the admin enables the DKG access(all) event StartDKG() /// Emitted when the admin ends the DKG after enough submissions have been recorded access(all) event EndDKG(finalSubmission: [String?]?) /// Emitted when a consensus node has posted a message to the DKG whiteboard access(all) event BroadcastMessage(nodeID: String, content: String) ``` --- # File: /md/protocol/staking/11-machine-account.md --- title: Machine Account sidebar_label: Machine Account description: Usage and Purpose of the Machine Account --- ### What is a Machine Account? A Machine Account is a Flow account which is used autonomously by a node to interact with system smart contracts. Machine Accounts contain Cadence resources granted to network participants which are used to participate in smart-contract-mediated protocols. Currently, Machine Accounts are used in the [Epoch Preparation Protocol](./04-epoch-preparation.md), which prepares the network for the next epoch. Your Machine Account is distinct from the account you use for staking your node (your Staking Account). The Machine Account is intended for use by node software and does not have access to your staked tokens or staking rewards. Currently Machine Accounts are required only for `collection` and `consensus` nodes. #### Creation Machine Accounts are created during the [staking process](../../protocol/flow-port/staking-guide.md) in Flow Port. #### Funding Machine Accounts must maintain a balance of liquid FLOW tokens to pay fees on transactions they submit to system smart contracts. Typically very few transactions will be sent (about 1-5 per week) however more may be required under certain circumstances and network conditions. Because some transactions sent by the Machine Account are system critical, we recommend maintaining a balance sufficient to accommodate worst-case transaction submission numbers at all times. **See [here](./../node-ops/node-operation/monitoring-nodes.md#machine-account) for how to monitor.** When creating a new machine account, we recommend initially funding with **0.75 FLOW for collection nodes** and **6 FLOW for consensus nodes**. Machine account balances must be [monitored](./../node-ops/node-operation/monitoring-nodes.md#machine-account) and periodically refilled to ensure they have sufficient funds. We recommend a minimum balance at all times of 0.25 FLOW for collection nodes and 2 FLOW for consensus nodes. FLOW deposited to a machine account can be withdrawn at any time by the node operator. --- # File: /md/protocol/staking/12-faq.md --- title: Staking FAQ sidebar_label: FAQs description: Frequently Asked Questions --- ### Where will users receive their staking reward for each staking option? Staking rewards are not deposited directly into a user's account. They are deposited to the user's rewards pool in their connected staking object and can be withdrawn or restaked at any time. If you staked using [Flow Port](https://port.flow.com), then you can see your staking rewards there. You can also withdraw the rewards or manually re-stake them through Flow Port. If you staked using a staking provider such as Kraken, Blocto or Finoa, please ask them how they manage staking rewards. ### Will staking rewards be automatically re-staked? There will be _no_ automatic re-staking of staking rewards with Flow Port (i.e. using Ledger or Blocto). If you want to re-stake your rewards, you must manually do so yourself. If you staked using a staking provider such as Kraken, Blocto or Finoa, please ask them what their policies are. DeFi liquid staking strategies such as offered by [incrementFi](https://app.increment.fi/staking) are not managed by the protocol or nodes, but are more sophisticated ways to manage your staking. ### Does it make a difference as to what TYPE of node we delegate to in terms of rewards? No, rewards are calculated the same for every node type. ### Can a validator change its fees? The network enforces a delegation fee of 8% which cannot be directly changed. Any different fees that nodes claim they have are rebates that they offer using their own methods and are not enforced by the protocol. ### Can a token holder stake to multiple nodes? If yes, how is the stake split between them? A token holder can delegate to multiple nodes from a single account if they use the [Staking Collection](./14-staking-collection.md). The staking collection is enabled by default on Flow port, and many custody providers also support it. ### Is the wallet key transferred to the staked node? No - The keys on the node are different from the wallet keys. The wallet keys always stay in the wallet. A node operator generates the staking and networking keys separately which will be used on the node. ### Can I stake through multiple accounts to meet the minimum FLOW required for staking a node? No, the minimum stake must come from a single account for all node types. ### Is rewards payout another spork? No, rewards payout is not a spork but is an automatic transaction that happens at the beginning of every new epoch. ### Can I query an account address of a node ID or delegator ID? The staking smart contract does not directly associate a node or delegator with an account address. It associates it with the assigned resource object that corresponds to that entry in the contract. There can be any number of these objects stored in the same account, and they can be moved to different accounts if the owner chooses. It is possible to query the information about a node that an address runs though, by using the `get_node_info_from_address.cdc` script. --- # File: /md/protocol/staking/13-staking-options.md --- title: Options for Building Staking Integrations sidebar_label: Technical Staking Options --- This document describes two different methods for staking at a high level. We highly recommended you use the Staking Collection paradigm, as this will be the most supported method for staking with any set up. # Staking Collection A Staking Collection is a resource that allows its owner to manage multiple staking objects in a single account via a single storage path, and perform staking actions using Flow. It also supports machine accounts, a necessary feature for Flow epoch node operation. The staking collection paradigm is the most flexible of the three choices and will receive the most support in the future. It is the set-up that Flow Port and many other staking providers use. The staking collection setup and guide is detailed in the [staking collection guide.](./14-staking-collection.md) --- # File: /md/protocol/staking/14-staking-collection.md --- title: Manage a Staking Collection sidebar_label: Staking Collection Guide --- This document outlines the steps a token holder can take to stake using [the `FlowIDTableStaking` contract](../../build/cadence/core-contracts/06-staking-contract-reference.md) and [the `FlowStakingCollection` contract.](../../build/cadence/core-contracts/11-staking-collection.md) This is the recommended and most supported way to stake FLOW. It supports any number of nodes and delegators per account, supports locked and unlocked FLOW, and supports easily interaction with a node's machine account for collector and consensus nodes. # Staking Collection Overview A Staking Collection is a resource that allows its owner to manage multiple staking objects in a single account via a single storage path, and perform staking actions using both locked and unlocked Flow. When an account is set up to use a staking collection, the staking collection recognizes the existing locked account capabilities (if they exist) and unlocked account staking objects, and incorporates their functionality so any user can stake for a node or stake as a delegator through a single common interface, regardless of if they have a brand new account, or have been staking through the locked account or unlocked account before. The staking collection also easily allows a user to transfer their node or delegator objects to other accounts without interrupting the staking process! ## Staker Object Fields The staking collection resource has two main fields, ```cadence access(self) var nodeStakers: @{String: FlowIDTableStaking.NodeStaker} access(self) var nodeDelegators: @{String: FlowIDTableStaking.NodeDelegator} ``` These dictionaries store the staking objects that are managed by the staking collection. Access to these dictionaries are mediated by the staking methods. When a user wants to perform a staking operation, they specify the nodeID and/or delegatorID they want to stake for, and the function routes the function call to the correct staking object and performs the specified operation. ## Vault Capability Fields The staking collection also has a field that stores a capability for the unlocked FLOW Vault and locked FLOW vault (if applicable) ```cadence /// unlocked vault access(self) var unlockedVault: Capability<&FlowToken.Vault> /// locked vault /// will be nil if the account has no corresponding locked account access(self) var lockedVault: Capability<&FlowToken.Vault>? ``` When a user performs staking operations like staking new tokens, the staking collection tracks the number of unlocked tokens and locked tokens (if applicable) that are used by the staking objects in the collection. The collection will always try to stake any available locked tokens first. Once all locked tokens are staked, if the user requested to stake more than the locked token balance, the collection will then dip into the unlocked balance for the remaining tokens. If the user has no locked tokens, the staking collection will simply ignore the locked tokens part of the functionality and only use unlocked tokens. When a user withdraws tokens from a staking object, the collection will always try to withdraw unlocked tokens first. Any unlocked tokens are then deposited directly into the vault on the unlocked account, and remaining locked tokens are deposited to the vault in the locked account. ## Machine Account Support The staking collection also supports an important feature for epochs, machine accounts. Machine accounts are where node operators store important resource objects that are critical to the functionality of the epoch preparation protocol. Every collector and consensus node should have an associated machine account that stores these objects, and the staking collection helps the user create and manage these accounts. When a user registers a new collector or consensus node, the staking collection also creates a machine account for them and registers the required object that needs to go in the machine account. The node operator is then responsible for adding keys to the account. (the **Register Node** transaction includes this step). Once the machine account is created and set up, the node operator just has to connect it to their node software and make sure the account has enough FLOW to pay for transaction fees, which can be handled simply by submitting a regular FLOW transfer to the machine account's address ## Staking Collection Public Getter Methods The staking collection also defines many getter methods to query information about an account's staking collection. You can simply call one of these methods on the contract, providing the account address, and the contract will retrieve the relevant info for you, like so: ```cadence import FlowStakingCollection from 0xSTAKINGCOLLECTIONADDRESS import FlowIDTableStaking from 0xIDENTITYTABLEADDRESS /// Gets an array of all the delegator metadata for delegators stored in the staking collection access(all) fun main(address: Address): [FlowIDTableStaking.DelegatorInfo] { return FlowStakingCollection.getAllDelegatorInfo(address: address) } ``` Remember: A Staking Collection does not require an account to have a secondary locked account or locked FLOW. However, if an account does have an associated locked account, when the Staking Collection is initialized, it will connect to that locked account's node and delegator objects as well as it's locked token vault allowing it to perform staking actions with locked and unlocked FLOW. Staking Collection is backwards compatible with other methods of staking on Flow. Existing accounts with associated locked accounts will still be able to stake in the same way as before, but they will also be able to use the staking collection, if desired. # How to use the Staking Collection There is a standard set of transactions provided with the staking collection. ## Setup ### Setup a Staking Collection To set up a Staking Collection, you must run the **Setup Staking Collection** ([SCO.01](../../build/cadence/core-contracts/11-staking-collection.md)) transaction. This transaction requires no arguments and will perform the following actions: 1. Create private capabilities for the unlocked vault and locked vault (if applicable). 2. Create a new staking collection resource object, initializing it with the unlocked and locked vault capabilities. 3. Store the staking collection at a pre-defined storage path. 4. Create a public link to the staking collection so others can query metadata about it. 5. If there are any node or delegator objects in the unlocked account, the transaction stores those in the staking collection so they can be used through the same interface as usual. **No arguments** are required for the **Setup Staking Collection** transaction. Once this transaction is complete, your existing staking objects (if any) from your unlocked account and locked account will be available via the staking collection and you can use all the transactions described below to access them. ### Create a Machine Account for an existing Node Many nodes will have been created before the staking collection was set up and before epochs were enabled, meaning that they don't already have an associated machine account. These nodes need a new transaction to create the machine account for the node and save it to the staking collection. To create a machine account for a node that doesn't already have one, you must submit the **Create Machine Account** ([SCO.03](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | -------------- | ---------- | ---------------------------------------------- | | **nodeID** | `String` | The ID of the node. | | **publicKeys** | `[String]` | The public keys to add to the machine account. | If the node is a collector or consensus node, this transaction creates the associated machine account, registers the QC or DKG object, stores it in the machine account, and adds the provided public key(s) to the machine account. If no public keys are provided, the transaction will fail. ## Register Stakers ### Register a New Staked Node To register a new staked node, you must submit the **Register Node** ([SCO.03](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **id** | `String` | The ID of the new node. It must be a 32 byte `String`. The operator is free to choose this value, but it must be unique across all nodes. A recommended process to generate this is to hash the staking public key. | | **role** | `UInt8` | The role of the new node. (1: collection, 2: consensus, 3: execution, 4: verification, 5: access) | | **networkingAddress** | `String` | The IP address of the new node. | | **networkingKey** | `String` | The networking public key as a hex-encoded string. | | **stakingKey** | `String` | The staking public key as a hex-encoded string. | | **stakingKeyPoP** | `String` | The staking key Proof-of-Possesion as a hex-encoded string. | | **amount** | `UFix64` | The number of FLOW tokens to stake. | | **publicKeys** | `[String]?` | The public keys to add to the machine account. `nil` if no machine account | This transaction registers the account as a staker with the specified node information and attaches a `NodeStaker` resource to the `Staking Collection`. This `NodeStaker` resourece can then later be used to perform staking actions via the staking collection staking methods. If the node is a collector or consensus node, it also creates the associated machine account, registers the QC or DKG object, stores it in the machine account, and adds the provided public key(s) to the machine account. If the node requires a machine account and no public keys are provided, the transaction will fail. Once the account has registered their node using their Staking Collection, their tokens and node information are committed to the central staking contract for the next epoch. At this point, the Staking Collection now has access to various staking operations that they can perform, assuming they have the correct number of tokens to perform the action. ### Register a New Staked Delegator To register a new delegator, you must submit the **Register Delegator** ([SCO.02](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | ---------- | -------- | -------------------------------------- | | **id** | `String` | The ID of the node to delegate to. | | **amount** | `UFix64` | The number of FLOW tokens to delegate. | This transaction registers the account as a delegator to the node identified by the supplied node id. It also attaches a `NodeDelegator` resource to the `Staking Collection`. This `NodeDelegator` resourece can then later be used to perform delegation actions. Once the account has registered their new delegator using their Staking Collection, their tokens are committed to the central staking contract for the next epoch. At this point, the Staking Collection now has access to various delegator operations that they can perform, assuming they have the correct number of tokens to perform the action. ## Staking Operations These transactions perform actions that directly interact with the staking contract. Most of them will only succeed during the Staking Auction phase of the epoch. ### Stake New Tokens The Staking Collection can stake additional tokens for any Node or Delegator managed by it at any time. The owner of a Staking Collection can use the **Stake New Tokens** ([SCO.06](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to stake new tokens to. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to stake. | To stake new tokens for an active node, leave the delegatorID argument as nil. If staking for a delegator, delegatorID should be the delegator ID you are staking for. The amount may be any number of tokens up to the sum of an accounts locked and unlocked FLOW. ### Re-stake Unstaked Tokens After tokens become unstaked, the owner of a Staking Collection can choose to re-stake the unstaked tokens to the same Node or Delegator. The owner of a Staking Collection can use the **Stake Unstaked Tokens** ([SCO.08](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to stake the unstaked tokens to. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to restake. | To stake unstaked tokens for an active node, leave the delegatorID argument as nil. If staking for a delegator, delegatorID should be the delegator ID you are staking for. ### Re-stake Rewarded Tokens After earning rewards from staking, the owner of a Staking Collection can choose to re-stake the rewarded tokens to the same node or delegator. The owner of a Staking Collection can use the **Stake Unstaked Tokens** ([SCO.07](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to stake the rewarded tokens to. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to restake. | To stake rewarded tokens for an active node, leave the delegatorID argument as nil. ### Request to Unstake Tokens at the end of the Epoch The owner of a Staking Collection can submit a request to unstake their tokens at any time for any Node or Delegator in their collection. If the tokens aren't staked yet, they will be uncommitted and available to withdraw. _Note: unstaked tokens will be held by the central staking contract until the end of the following epoch._ _Once the tokens are released (unstaked), they can be claimed via the [Withdraw Unstaked Tokens](#withdraw-unstaked-tokens) action below._ The owner of a Staking Collection can use the **Unstake Tokens** ([SCO.05](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the chosen node. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to restake. | To unstake tokens from an active node, leave the delegatorID argument as nil. ### Unstake All Tokens The owner of a Staking Collection can use the **Unstake All** ([SCO.09](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to unstake all tokens from. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | ### Withdraw Unstaked Tokens After tokens for an active Node or Delegator become unstaked, the ownder of Staking Collection can withdraw them from the central staking contract. The owner of a Staking Collection can use the **Withdraw Unstaked Tokens** ([SCO.11](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to withdraw the unstaked tokens from. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to withdraw. | To withdraw unstaked tokens from an active node, leave the delegatorID argument as nil. ### Withdraw Rewarded Tokens After earning rewards from staking, the token holder can withdraw them from the central staking contract. The owner of a Staking Collection can use the **Withdraw Rewarded Tokens** ([SCO.10](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to withdraw the rewarded tokens from. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | | **amount** | `UFix64` | The number of FLOW tokens to withdraw. | To withdraw rewarded tokens from an active node, leave the delegatorID argument as nil. ## Staking Collection Modification ### Close a Node or Delegator Once a Node or Delegator has no tokens staked, comitted or in an unstaking state, it is eligible to be closed. Closing a Node or Delegator first returns any unstaked or rewarded tokens to the account for which the Staking Collection is stored in. It then destroys the NodeStaker or NodeDelegator object from within the Staking Collection. _Note: Once a Node or Delegator has been closed, it cannot be accessed again,_ _and no staking or delegation actions can be further preformed on it._ The owner of a Staking Collection can use the **Close Stake** ([SCO.12](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | -------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to close. | | **delegatorID** | `Optional(UInt32)` | `nil` if staking for a node. If staking for a delegator, the delegator ID. | To close an active node, leave the delegatorID argument as nil. ### Transfer a Node A user may transfer an existing Node to another another account's Staking Collection. The account to transfer the Node to must have a valid Staking Collection set up. Transferring a Node will remove it from the authorizer's Staking Collection and deposit it to the receiver's Staking Collection. _Note: Once a Node or Delegator has been transferred, it cannot be accessed again by the sender._ _As well, all staked tokens will be considered staked by the receiver's Staking Collection._ Transferring a Node will result in loss of custody of any Staked tokens for the sender. The owner of a Staking Collection can use the **Transfer Node** ([SCO.13](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | ---------- | --------- | ----------------------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the node to transfer. | | **to** | `Address` | The address of the account which contains the Staking Collection to transfer the Node to. | ### Transfer a Delegator A user may transfer an existing Delegator to another another account's Staking Collection. The account to transfer the Delegator to must have a valid Staking Collection set up. Transferring a Delegator will remove it from the authorizer's Staking Collection and deposit it to the receiver's Staking Collection. _Note: Once a Node or Delegator has been transferred, it cannot be accessed again by the sender._ _As well, all staked tokens will be considered staked by the receiver's Staking Collection._ Transferring a Delegator will result in loss of custody of any Staked tokens for the sender. The owner of a Staking Collection can use the **Transfer Delegator** ([SCO.14](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | --------------- | --------- | ---------------------------------------------------------------------------------------------- | | **nodeID** | `String` | The nodeID of the delegator to transfer. | | **delegatorID** | `UInt32` | The delegatorID of the delegator to transfer. | | **to** | `Address` | The address of the account which contains the Staking Collection to transfer the Delegator to. | ### Update A Node's Networking Address A user may update their node's networking address if it has become inconsistent with the protocol state. This operation can only be performed in the staking auction phase of an epoch. _Note: Currently, if a node updates its networking address and the new address does not match_ _what is stored in the protocol state for the node, the node will not be able to participate in the upcoming epoch_ _Only update your networking address if you have already confirmed with the Flow team that you can._ _This restriction will be removed once fully automated epochs are completely implemented_ The owner of a Staking Collection can use the **Update Networking Address** ([SCO.22](../../build/cadence/core-contracts/11-staking-collection.md)) transaction with the following arguments: | Argument | Type | Description | | -------------- | -------- | --------------------------------- | | **nodeID** | `String` | The nodeID of the node to update. | | **newAddress** | `String` | The new networking address | # Staking Collection Scripts These scripts allow anyone to query information about an account's staking collection ### Get All Node Info To return an array of structs representing the information associated with each node managed by an account's Staking Collection, anyone can use the **Get All Node Info** ([SCO.15](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns an array of `FlowIDTableStaking.NodeInfo` [structs](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L264) representing the nodes managed by an accounts Staking Collection. ### Get All Delegator Info To return an array of structs representing the information associated with each delegator managed by an account's Staking Collection, anyone can use the **Get All Delegator Info** ([SCO.16](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns an array of `FlowIDTableStaking.DelegatorInfo` [structs](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowIDTableStaking.cdc#L264) representing the delegators managed by an accounts Staking Collection. ### Get All Node Ids To return an array of Strings representing the ids associated with each node managed by an account's Staking Collection, anyone can use the **Get All Node Ids** ([SCO.17](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns an array of `String` representing each id of each node managed by an accounts Staking Collection. ### Get All Delegator Ids To return an array of structs representing the delegator ids associated with each delegation managed by an account's Staking Collection, anyone can use the **Get All Delegator Ids** ([SCO.22](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns an array of `FlowStakingCollection.DelegatorIDs` [structs](https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowStakingCollection.cdc#L40) representing the delegator Ids of each delegator managed by an accounts Staking Collection. ### Get Locked Tokens Used To query how many Locked FLOW tokens an account has staked using their Staking Collection, anyone can use the **Get Locked Tokens Used** ([SCO.19](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns a `UFix64` representing the number of Locked FLOW tokens staked using an accounts Staking Collection. Note: This number does not include Locked FLOW tokens staked not through an accounts Staking Collection. ### Get Unlocked Tokens Used To query how many Unlocked FLOW tokens an account has staked using their Staking Collection, anyone can use the **Get Unlocked Tokens Used** ([SCO.20](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns a `UFix64` representing the number of Unlocked FLOW tokens staked using an accounts Staking Collection. Note: This number does not include Unlocked FLOW tokens staked not through an accounts Staking Collection. ### Get Does Node Exist To query if a Node or Delegator is managed by an accounts Staking Collection, anyone can use the **Get Does Node Exist** ([SCO.21](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | --------------- | ------------------ | ---------------------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | | **nodeID** | `String` | The nodeID of the node to check, or the nodeID of the node delegating to to check. | | **delegatorID** | `Optional(UInt32)` | The delegatorID of the delegator to check, if checking for a delegator. | This script returns a `Bool`. To query if a Node is managed by an accounts Staking Collection, leave the delegatorID argument as nil. Otherwise, fill it in with the delegatorID of the Delegator. ### Get Machine Account Info To query the machine account information for an account's staking collection, anyone can use the **Get Machine Account Info** ([SCO.21](../../build/cadence/core-contracts/11-staking-collection.md)) script with the following arguments: | Argument | Type | Description | | ----------- | ---------- | ----------------------------------------------------------------------- | | **address** | `Addresss` | The Address of the account holding the Staking Collection to query from | This script returns a `{String: FlowStakingCollection.MachineAccountInfo}`, which is a mapping of nodeIDs to the `FlowStakingCollection.MachineAccountInfo` struct. --- # File: /md/templates/tutorial.md --- title: SEO-Rich Title That Matches Document Name and Path description: A one sentence description. sidebar_label: Title (match title if possible; can shorten if more than 2 lines, prefer 1 line) sidebar_position: 5 keywords: - keywords - describing - the main topics - cursor is great at this --- # Complete Title (Must Match Front Matter Title) 1-2 Paragraphs describing what the tutorial will teach, why someone might learn it, and if possible, a link to a live version of the app demoing the techniques and content taught. **Important:** Do not add an H2 "Overview" heading. The overview text goes directly after the H1 title. ## Objectives Now that you've completed this tutorial, you'll be able to: - 3-5 high-level Bloom's taxonomy verb learning objectives (terminal objectives, not enabling objectives) - Don't wordsmith these - clarity and directness are more important than variety - It's okay if they're repetitive (e.g., "Construct X, Construct Y, Construct Z" is fine) - Can use lower-level verbs if it makes sense for the learning goal ## Prerequisites **Note:** Use canonical prerequisite text from the templates folder. When the import system is available, import prerequisites rather than copying and pasting. ### Next.js and Modern Frontend Development This tutorial uses [Next.js]. You don't need to be an expert, but it's helpful to be comfortable with development using a current React framework. You'll be on your own to select and use a package manager, manage Node versions, and other frontend environment tasks. If you don't have your own preference, you can just follow along with us and use [Yarn]. ### (Additional Prerequisites) This doesn't need to be exhaustive, but it should be comprehensive. It's a good place to outline what you're specifically **not** going to teach in this tutorial. ## Descriptive H2 Heading (Not "Part 1", "Part 2", etc.) Text can go here. Usually it will be either an introduction to a long section with subsections, or a short section with no subsections that doesn't fit under a higher level. ### Document Length Guidelines - Target size: ~500 lines (with each paragraph as a line, no manual line breaks) - Maximum: 1000 lines - Minimum: 200-300 lines (unless standalone like a simple configuration guide) - If a tutorial exceeds 1000 lines, consider splitting into separate documents at logical breakpoints ### Subsection (H3) Divide each section into appropriate categories. **Recommended document structure:** Most documents should have 2-3 H2 sections with 2-5 H3 subsections in each. **Avoid H4 and above** - If you're getting into H4, you're cutting the content into too small pieces. ## Another Descriptive H2 Heading More text goes here. Use explicit, descriptive headings that tell the reader what they'll be doing in that section. ### Subsection (H3) Continue as appropriate ## Tutorial Approach **We are opinionated and provide one golden path.** We tell people one and only one specific way to do things in tutorials. This doesn't mean we can't teach multiple things - we can take learners through a journey - but we don't overwhelm them with choices when they're not informed enough to make decisions. **Example:** In the getting started tutorial, we don't say "you could deploy this on testnet or emulator or mainnet." Instead, we say "first you are going to deploy this on emulator, here's how. Next you are going to deploy this on testnet, here's how." **Why?** Learners coming to a tutorial don't know what they're doing - that's why they're at the tutorial. Cognitive friction from making decisions while learning prevents effective learning. If they have more experience, they can adapt our approach to their needs. **Our opinionated choices:** - Frontend: Next.js and Tailwind - EVM: Wagmi - Cadence: React SDK **Rationale:** If they're a non-professional frontend developer writing smart contracts, they need exact instructions. If they're a professional frontend developer, they'll adapt it anyway and know how to do that. ## Style Guide This section is a **guide** for the style and tone we use writing tutorials at Flow. It is **not** rigid or inflexible, but should be generally adhered to without specific reasons for exceptions. Our standard is to achieve approximately 85% adherence or better before publication. It does not need to be perfect and we can iterate later. ### Tone, Format, Language, and General Guidance - Write content in a friendly and approachable tone, think business casual. - Tutorials are less formal, docs are more formal, articles are in the middle. - **Subheadings use sentence case** (not title case) - **Use proper heading levels (H2, H3) for subheadings - do not use bold text alone as a heading** - Use H2 for main sections - Use H3 for subsections (can be used freely) - **Recommended structure:** Most documents should have 2-3 H2 sections with 2-5 H3 subsections in each - Avoid H4 and above - Bold text is for emphasis within content, not for creating headings - Avoid passive voice - **Target eighth grade reading level** - We're teaching difficult and complicated things to people from many different backgrounds with many different first languages. We do not need to show off our vocabulary. - Use **you** and **we** to speak directly to the learner - Use **you** when giving specific instructions to the learner or things they need to be aware of. - Next, you'll need to implement a function that does X, Y, and Z. - You might find that it's useful to index this property, but there is a performance cost. - Use **we** when speaking as Flow Foundation (not "Flow Foundation" in third person - we're people talking to people). - We recommend this approach. - We found that a setting of 50 works best, but it's more of an art than a science. - **Exception:** Only use "Flow Foundation" in third person for legal disclaimers when required by counsel. - Avoid gerunds (-ing) when possible, but prioritize technical accuracy and clarity. If avoiding a gerund makes the text passive, uses 12 words instead of 5, or reduces technical accuracy, use the gerund. - We do not use profanity. - Released content should be free of spelling and grammar errors. - We use US English spellings. - We do **not** use manual line breaks. - We can be tactfully critical of other networks, but we must back up criticism with facts and evidence. We are not harsh or insulting. - We use the Oxford comma. - We use one space after periods. - Use an ESLint plugin for markdown that auto-formats your document - The site will render the same with 1 space or 2, so it is a moot point ### Organization - If you move a file, you **must** add a permanent redirect to `vercel.json`. - File and folder names are kebab case. - `docs/build/cadence/guides/account-linking/account-linking-with-dapper.md` - Images can be stored either: - Flat in the same folder as the document: `docs/build/cadence/guides/account-linking/example-app.png` - In an `images` folder within the document's directory: `docs/build/cadence/guides/account-linking/images/example-app.png` - Either approach is acceptable. Using an images folder is better organization, but having images flat in the folder is more convenient and encourages more image usage. ### Emphasis - We use two asterisks to **bold** text for emphasis. - When stating a negative, always emphasize the part where you say that you should **not** do something. - We use one underscore to highlight, introduce, or emphasize _technical terms_. - Every instance of the term should **not** be highlighted. Only the first, the first in awhile, or when special attention must be called. - Inline code samples or references, filenames, or interactive elements are surrounded by `backticks`. - Next, call the `approve()` function. - After filling out the form, click Submit (prefer "click Submit" over "click the `Submit` button" - more declarative and cleaner). - UI interface elements can be in backticks to make them look like buttons: click the `Submit` button. - The `.borrow()` function is a property used to... - Create a file called `providers.ts` in the `app` folder. - Function names must include the parentheses after the name but should **not** include parameters. ### Code Blocks and Admonitions - [Code Blocks] should be used for any segment of code longer than 15 characters. - Supply the appropriate language - Cadence is supported - Always use `tsx` and `jsx` for TypeScript or JavaScript (they render TS/JS correctly too) - Use `zsh` for terminal text (not `bash`) to match MacBook defaults - [Admonitions] are used to highlight important sections of text. - **Important:** You must have a blank new line before and after the admonition text, otherwise the auto-formatter will break them. - `tip` is used to share a tip or suggestion, or to remind the learner of something. **Use `tip`, not `note`** (note makes the box white and doesn't call enough attention). - `reminder` is used to remind the learner of something. - `info` is used to highlight unusual, confusing, or particularly important information. - `warning` is used to highlight cases in which a mistake can cause confusion, frustration, or cost developer time. - `danger` is used for anything in which a mistake can cause account compromise, loss of funds, key exposure, or any other type of permanent harm for developers or their users. Use sparingly but appropriately - we work in a high-stakes environment where mistakes can cost millions of dollars. ### Links - We use reference-style links using the text itself to identify the link. - See lines 11 and 17 in this [markdown reference links] document. - This makes the raw file more readable for editing and allows you to repeat links easily. - Link the first reference to a function, property, tool, library, etc. and the first reference in the section in which it is used. - This is critically important for links both within docs and to external sites - Don't link every instance - it quickly gets overwhelming and can hurt SEO if you have too many links. - Avoid linking to other networks' sites unless: - Citing a source or specific piece of information (e.g., "their own documents say that there is one single sequencer...") - Making a critical point backed by facts - Otherwise, don't make it easier for people to go to competitor information - We strongly cross-link with products that work with our stuff (e.g., Wagmi docs, RainbowKit docs) and encourage them to cross-link with us. - Internal links are relative and link to the **file name** (not the URL). Docusaurus will sort itself at build time, and adding the `.md` or `.mdx` makes the link clickable in the editor. - `[link example]: ../up-one-folder/other-document.md` - This only gets tricky when pages have names like `cadence-index.md` that don't match the URL, but the build will break and you'll know to fix it. ### Lists - Unordered lists are defined using a dash `-`. The auto-formatter will do this for you. - Ordered lists are defined with the number `1.` for every item. These will render the correct number automatically. - Using `1.` for every item makes it much easier to maintain - if you need to add an item in the middle, you don't need to renumber everything. 1. One 1. Two 1. Three 1. Four - Sentences in bulleted lists end in a period. - When a bulleted list is introduced by a colon, each bullet effectively completes that first sentence, so periods are grammatically correct. - Lists of 1-3 word items, such as product names, do not need periods. ## Canonical Spelling and Capitalization - For other brands, we are diligent to follow their spelling and capitalization - Flow - Flow Foundation - Flow Cadence - Flow Cadence Mainnet - Flow Cadence Testnet - Flow EVM - Flow EVM Mainnet - Flow EVM Testnet - **onchain** (one word, never "on-chain" or "on chain") - This matches how Base talks about it - **web3**, **blockchain**, **crypto** (lowercase, used frequently for SEO reasons) - **app** (not "mobile app" - in 2025, you just say "app") - onchain app - Only when needed to distinguish from web apps, mobile apps, etc. - **Never dapp, Dapp, or dApp** (DAP is banned) - frontend - backend - DevRel ## Conclusion **Always include a conclusion.** The conclusion repeats the learning objectives verbatim. After you complete this tutorial, you'll be able to: - Copy/paste the Objectives from above here (repeat them exactly) (OPTIONAL) Now that you've completed this tutorial, you're ready to... **Note:** Both the overview (after H1) and conclusion can be written with Cursor/AI assistance, but you'll almost always need to edit them afterward. [Cadence]: https://cadence-lang.org/docs [Next.js]: https://nextjs.org/docs/app/getting-started/installation [Yarn]: https://yarnpkg.com [Code Blocks]: https://docusaurus.io/docs/markdown-features/code-blocks [Admonitions]: https://docusaurus.io/docs/markdown-features/admonitions [markdown reference links]: https://gist.github.com/emedinaa/28ed71b450243aba48accd634679f805 ---