From b7eb222ebfcf96d0b3a26618a7f8686f5aa57ab3 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 24 Nov 2025 11:00:10 -0800 Subject: [PATCH 1/6] Add Interactive Testing With Forked Emualator Docs --- .../cadence/emulator-fork-testing/index.md | 967 ++++++++++++++++++ .../smart-contracts/testing-strategy.md | 3 +- docs/build/tools/emulator/index.md | 5 + 3 files changed, 974 insertions(+), 1 deletion(-) create mode 100644 docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md new file mode 100644 index 0000000000..eb9955f2ff --- /dev/null +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -0,0 +1,967 @@ +--- +sidebar_position: 21 +sidebar_label: Emulator Fork Testing +title: Interactive Testing with Forked Emulator +description: Run your dapp, 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 + - dapp 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 + +This tutorial teaches you how to run your dapp, E2E tests, and manual explorations against a snapshot of Flow mainnet using `flow emulator --fork`. You'll learn how to connect your frontend to production-like state, test user flows with real contracts and data, and debug issues interactively—all without deploying to a live network. + +The forked emulator creates a local Flow network that mirrors mainnet or testnet state. It's perfect for manual testing, running E2E test suites, and exploring contract interactions in a production-like environment with full control. + +## 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 dapp 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. + +## What You'll Build + +You'll create a complete forked emulator setup that demonstrates: + +- Starting the emulator with forked mainnet state. +- A React dapp 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 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 dapp, 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—execute transactions as any mainnet account +- 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: + +- **E2E and frontend testing**: Run Cypress/Playwright tests against production-like state +- **Manual exploration**: Interact with your dapp 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]) +- Tests that need to be fast (<1s)—fork mode has network latency + +### Emulator Fork vs Test Framework Fork + +| Feature | `flow emulator --fork` | `flow test --fork` | +| --------------- | --------------------------------------- | ------------------------------ | +| **Use for** | Dapp 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 --yes + +# 2. Configure fork network (add to flow.json) +# Add this under "networks": +# "mainnet-fork": { +# "host": "127.0.0.1:3569", +# "fork": "mainnet" +# } + +# 3. Install FlowToken dependency +flow dependencies install +# Select FlowToken from the list + +# 4. Start forked emulator (in a separate terminal) +flow emulator --fork mainnet + +# 5. In another terminal, check the forked state +flow scripts execute cadence/scripts/get_flow_supply.cdc --network mainnet-fork +``` + +Create `cadence/scripts/get_flow_supply.cdc`: + +```cadence +import "FlowToken" + +access(all) fun main(): UFix64 { + return FlowToken.totalSupply +} +``` + +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 +``` + +The `--yes` flag accepts defaults non-interactively. + +## Configure Fork Network in flow.json + +Before starting the emulator, configure a fork network in your `flow.json`. This enables automatic contract alias inheritance from mainnet, so you don't need to manually duplicate aliases. + +Open `flow.json` and add a `mainnet-fork` network: + +```json +{ + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "testnet": "access.devnet.nodes.onflow.org:9000", + "mainnet-fork": { + "host": "127.0.0.1:3569", + "fork": "mainnet" + } + } +} +``` + +**What this does:** + +- `host`: Points to your local emulator +- `fork`: Tells the CLI to automatically inherit contract aliases from mainnet + +Now any contract with a `mainnet` alias will automatically work on `mainnet-fork` without manual configuration! + +:::tip + +**Why forking is powerful:** + +The emulator fork mode gives you access to **real production state**: + +- ✅ Test against actual deployed contracts (FT, NFT, DEXs, marketplaces) +- ✅ Read real account balances, storage, and capabilities +- ✅ Query production data without setting up test fixtures +- ✅ Catch integration issues with real-world contract implementations +- ✅ Debug with historical state by pinning block heights + +**Plus, fork networks simplify configuration:** + +- ✅ No need to duplicate 30+ contract aliases +- ✅ Automatic inheritance from source network +- ✅ Can override specific contracts if needed + +**Example of automatic inheritance:** + +```json +{ + "dependencies": { + "FlowToken": { + "aliases": { + "mainnet": "0x1654653399040a61" + // ✅ mainnet-fork automatically inherits this! + // No need for: "mainnet-fork": "0x1654653399040a61" + } + } + } +} +``` + +When you run commands with `--network mainnet-fork`, the CLI automatically resolves contract imports to their mainnet addresses. + +::: + +## 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) + +:::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. + +::: + +## Advanced: Override Specific Contracts (Optional) + +If you need to test against a modified version of a contract, you can override specific contracts while inheriting others: + +```json +{ + "dependencies": { + "MyModifiedContract": { + "source": "./contracts/MyModifiedContract.cdc", + "aliases": { + "mainnet": "0x1234567890abcdef", + "mainnet-fork": "0xf8d6e0586b0a20c7" // Override for testing + } + }, + "FlowToken": { + "aliases": { + "mainnet": "0x1654653399040a61" + // Still inherits mainnet address on fork + } + } + } +} +``` + +This is useful for: + +- Testing a modified/upgraded version of a contract +- Using mock contracts for specific scenarios +- Pointing to staging deployments + +For most use cases, inheritance alone is sufficient. + +## 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 +``` + +Select `FlowToken` and `FungibleToken` from the list (use space to select, enter to confirm). + +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. + +Create a directory for scripts: + +```bash +mkdir -p cadence/scripts +``` + +Create `cadence/scripts/get_flow_supply.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. + +In a **new terminal** (keep the emulator running), execute the script: + +```bash +flow scripts execute cadence/scripts/get_flow_supply.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 Dapp + +Create a React app with Flow integration: + +```bash +npx create-react-app flow-fork-app +cd flow-fork-app +npm install @onflow/react-sdk +``` + +Copy your project's `flow.json` into the React app's `src` directory: + +```bash +# From your flow-fork-app directory +cp ../flow.json src/ +``` + +This allows the `FlowProvider` to resolve contract imports. + +Replace `src/index.js` with: + +```javascript +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import { FlowProvider } from '@onflow/react-sdk'; +import flowJSON from './flow.json'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + + + , +); +``` + +Replace `src/App.js` with: + +```javascript +import { useState } from 'react'; +import { useFlowCurrentUser, useFlowQuery, Connect } from '@onflow/react-sdk'; + +function App() { + 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.message}

} + {flowSupply && ( +

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

+ )} +
+ +
+

Wallet Connection

+ + {user?.loggedIn && ( +

+ Connected: {user.addr} +

+ )} +
+
+ ); +} + +export default App; +``` + +### 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 dapp + +Start the React app: + +```bash +npm start +``` + +Your browser will open 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. Since `flowNetwork` is set to `"mainnet"`, the provider uses mainnet aliases (which are inherited by `mainnet-fork`). + +::: + +## Account Impersonation + +The forked emulator's superpower: you can execute transactions as **any mainnet account** because signature validation is disabled. + +### Read Account Balance + +Create `cadence/scripts/get_balance.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/get_balance.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 + +Create `cadence/transactions/transfer_tokens.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. + +:::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 dapp tests against production-like state. + +### Quick Example with Cypress + +```bash +npm install --save-dev cypress +``` + +Create `cypress/e2e/flow_fork.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 dapp connect to the forked emulator (`http://localhost:8888`) while your E2E framework tests the UI. + +::: + +## Common Use Cases + +### Testing Contract Upgrades + +Test a contract upgrade against real mainnet state: + +1. Start forked emulator +2. Deploy your upgraded contract to the test environment +3. Run scripts/transactions that interact with both old mainnet contracts and your new contract +4. Verify behavior with real user account states + +### 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 dapp 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 dapp 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 +``` + +The key: configure your SDK's access node URL to point to the local emulator endpoints (`http://localhost:8888` for REST or `localhost:3569` for gRPC). + +:::note + +For React applications, always use `@onflow/react-sdk` instead of raw FCL. + +::: + +## 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 12345678 +``` + +### 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 NFT collector accounts, whale wallets, or any address that has interesting 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 Latency + +Fork mode is slower than the local emulator because it fetches state over the network. First access to an account/contract is slowest; subsequent accesses are cached. + +**Impact:** E2E tests take longer (seconds instead of milliseconds). + +### 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 + +### Rate Limiting + +Public access nodes have rate limits. If you hit them: + +- Reduce test parallelism +- Use a pinned height (enables better caching) +- Consider running your own access node for heavy testing + +## 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:** Ensure your fork network is properly configured: + +```json +{ + "networks": { + "mainnet-fork": { + "host": "127.0.0.1:3569", + "fork": "mainnet" + } + } +} +``` + +And that you've installed dependencies with the mainnet alias: + +```bash +flow dependencies install +``` + +Verify the contract has a mainnet alias that the fork can inherit. + +### Dapp 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. + +### 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 dapp | `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 dapp 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 dapp 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 dapp before going live. + +### Next Steps + +- Add E2E tests to your CI/CD pipeline using pinned fork heights +- Test your dapp's upgrade flows against forked mainnet +- Explore [Flow React SDK] hooks and components (events, mutations, Cross-VM features) +- For Cadence contract testing, see [Fork Testing with Cadence] +- 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 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 diff --git a/docs/build/cadence/smart-contracts/testing-strategy.md b/docs/build/cadence/smart-contracts/testing-strategy.md index 420ca430d2..15e2684578 100644 --- a/docs/build/cadence/smart-contracts/testing-strategy.md +++ b/docs/build/cadence/smart-contracts/testing-strategy.md @@ -106,7 +106,7 @@ See also: [Fork Testing with Cadence], [Fork Testing Flags]. npx cypress run ``` -See also: [Flow Emulator]. +See also: [Interactive Testing with Forked Emulator], [Flow Emulator]. ### Staging — Testnet @@ -196,6 +196,7 @@ See also: [Flow Networks]. [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 diff --git a/docs/build/tools/emulator/index.md b/docs/build/tools/emulator/index.md index 88f078853a..7362fac9c9 100644 --- a/docs/build/tools/emulator/index.md +++ b/docs/build/tools/emulator/index.md @@ -174,6 +174,10 @@ flow emulator --help - **Debugging**: Use `#debugger()` pragma in Cadence code for breakpoints - **Fork mode note**: When using `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 for testing where you establish a beginning state, run tests and after revert back to the initial state. @@ -227,3 +231,4 @@ To learn more about using the Emulator, please have a look at the [public GitHub [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 From c53656b9543218c6fbc4905480ddf64d7ac08ca3 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 25 Nov 2025 09:21:31 -0800 Subject: [PATCH 2/6] cleanup doc --- .../cadence/emulator-fork-testing/index.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md index eb9955f2ff..e06fb12b71 100644 --- a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -128,7 +128,6 @@ Use `flow emulator --fork` for: **Don't use this for:** - Cadence unit/integration tests (use `flow test --fork` instead—see [Fork Testing with Cadence]) -- Tests that need to be fast (<1s)—fork mode has network latency ### Emulator Fork vs Test Framework Fork @@ -755,7 +754,7 @@ Start the forked emulator once and leave it running. Restart only when you need Test against testnet first to avoid mainnet access node rate limits: ```bash -flow emulator --fork testnet --fork-height 12345678 +flow emulator --fork testnet --fork-height ``` ### 4. Mock External Dependencies @@ -797,11 +796,9 @@ FORK_HEIGHT_LATEST= # Latest tested state ## Limitations and Considerations -### Network Latency +### Network State Fetching -Fork mode is slower than the local emulator because it fetches state over the network. First access to an account/contract is slowest; subsequent accesses are cached. - -**Impact:** E2E tests take longer (seconds instead of milliseconds). +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 From fe12ab93fa28dc7943f748d99920aa567c26db01 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 9 Dec 2025 09:10:02 -0800 Subject: [PATCH 3/6] touchup doc --- .../cadence/emulator-fork-testing/index.md | 112 ++++++++++++++---- .../smart-contracts/testing-strategy.md | 9 +- 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md index e06fb12b71..c07a3ecdab 100644 --- a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -432,8 +432,8 @@ root.render( @@ -888,6 +941,23 @@ Verify the contract has a mainnet alias that the fork can inherit. 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 fork network in flow.json:** Make sure your `flow.json` has the fork network configured: + + ```json + "networks": { + "mainnet-fork": { + "host": "127.0.0.1:3569", + "fork": "mainnet" + } + } + ``` + +3. **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 diff --git a/docs/build/cadence/smart-contracts/testing-strategy.md b/docs/build/cadence/smart-contracts/testing-strategy.md index 15e2684578..69aa574668 100644 --- a/docs/build/cadence/smart-contracts/testing-strategy.md +++ b/docs/build/cadence/smart-contracts/testing-strategy.md @@ -88,10 +88,17 @@ See also: [Fork Testing with Cadence], [Fork Testing Flags]. ```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 */} ); From 348fbb8d93db10167cf823d663248a233fd1b708 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 9 Dec 2025 09:24:09 -0800 Subject: [PATCH 4/6] update mocking docs --- .../cadence/emulator-fork-testing/index.md | 54 +++++++++++-------- .../cadence/fork-testing/index.md | 37 +++++++++++++ .../smart-contracts/testing-strategy.md | 4 +- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md index c07a3ecdab..845e3183e8 100644 --- a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -291,37 +291,48 @@ This ensures the forked state is consistent across runs—essential for E2E test ::: -## Advanced: Override Specific Contracts (Optional) +## Mocking Mainnet Contracts -If you need to test against a modified version of a contract, you can override specific contracts while inheriting others: +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. + +Configure the mock in `flow.json`, then deploy to the forked emulator. Your mock takes precedence while other contracts use real mainnet versions. + +### Example + +**1. Configure in `flow.json`:** ```json { - "dependencies": { - "MyModifiedContract": { - "source": "./contracts/MyModifiedContract.cdc", - "aliases": { - "mainnet": "0x1234567890abcdef", - "mainnet-fork": "0xf8d6e0586b0a20c7" // Override for testing - } - }, + "accounts": { + "flow-token-mainnet": { + "address": "0x1654653399040a61", + "key": "0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "contracts": { "FlowToken": { + "source": "./contracts/FlowTokenModified.cdc", "aliases": { "mainnet": "0x1654653399040a61" - // Still inherits mainnet address on fork } } + }, + "deployments": { + "mainnet-fork": { + "flow-token-mainnet": ["FlowToken"] + } } } ``` -This is useful for: +**2. Deploy the mock:** -- Testing a modified/upgraded version of a contract -- Using mock contracts for specific scenarios -- Pointing to staging deployments +```bash +flow emulator --fork mainnet +flow project deploy --network mainnet-fork --update +``` -For most use cases, inheritance alone is sufficient. +Your dapp now uses the mocked FlowToken while FungibleToken, USDC, and all other contracts use real mainnet versions. ## Install Dependencies @@ -729,12 +740,13 @@ Use the same approach with Playwright, Puppeteer, or any browser automation tool ### Testing Contract Upgrades -Test a contract upgrade against real mainnet state: +Test a contract upgrade against real mainnet state by mocking the contract with your upgraded version: -1. Start forked emulator -2. Deploy your upgraded contract to the test environment -3. Run scripts/transactions that interact with both old mainnet contracts and your new contract -4. Verify behavior with real user account states +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 dapp 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 diff --git a/docs/blockchain-development-tutorials/cadence/fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/fork-testing/index.md index 277f97855a..ed3f7309d5 100644 --- a/docs/blockchain-development-tutorials/cadence/fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/fork-testing/index.md @@ -560,6 +560,43 @@ Configuring fork tests in the file keeps the configuration with your test code, 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", 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: diff --git a/docs/build/cadence/smart-contracts/testing-strategy.md b/docs/build/cadence/smart-contracts/testing-strategy.md index 69aa574668..71b877a552 100644 --- a/docs/build/cadence/smart-contracts/testing-strategy.md +++ b/docs/build/cadence/smart-contracts/testing-strategy.md @@ -92,8 +92,8 @@ See also: [Fork Testing with Cadence], [Fork Testing Flags]. function App() { return ( - Date: Tue, 9 Dec 2025 13:22:49 -0500 Subject: [PATCH 5/6] Apply suggestion from @briandoyle81 --- .../cadence/emulator-fork-testing/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md index 845e3183e8..7d3ccec97e 100644 --- a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -110,7 +110,7 @@ The emulator's fork mode starts a local Flow blockchain that connects to a real - Full gRPC and REST API servers running locally - On-demand fetching of accounts, contracts, and state from the live network -- Disabled signature validation—execute transactions as any mainnet account +- 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 From cf287f48b9cd034258c5117a7877be4ee828d857 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 12 Dec 2025 10:41:59 -0800 Subject: [PATCH 6/6] address feedback --- .../cadence/emulator-fork-testing/index.md | 162 +++++++++--------- 1 file changed, 85 insertions(+), 77 deletions(-) diff --git a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md index 7d3ccec97e..d7a98e6732 100644 --- a/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md +++ b/docs/blockchain-development-tutorials/cadence/emulator-fork-testing/index.md @@ -2,12 +2,12 @@ sidebar_position: 21 sidebar_label: Emulator Fork Testing title: Interactive Testing with Forked Emulator -description: Run your dapp, 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. +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 - - dapp testing + - app testing - frontend testing - Cypress testing - Playwright testing @@ -30,7 +30,7 @@ keywords: # Interactive Testing with Forked Emulator -This tutorial teaches you how to run your dapp, E2E tests, and manual explorations against a snapshot of Flow mainnet using `flow emulator --fork`. You'll learn how to connect your frontend to production-like state, test user flows with real contracts and data, and debug issues interactively—all without deploying to a live network. +This tutorial teaches you how to run your app, E2E tests, and manual explorations against a snapshot of Flow mainnet using `flow emulator --fork`. You'll learn how to connect your frontend to production-like state, test user flows with real contracts and data, and debug issues interactively—all without deploying to a live network. The forked emulator creates a local Flow network that mirrors mainnet or testnet state. It's perfect for manual testing, running E2E test suites, and exploring contract interactions in a production-like environment with full control. @@ -39,7 +39,7 @@ The forked emulator creates a local Flow network that mirrors mainnet or testnet After you complete this tutorial, you'll be able to: - **Start the emulator in fork mode** with `flow emulator --fork`. -- **Connect your dapp frontend** to the forked emulator. +- **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. @@ -51,7 +51,7 @@ After you complete this tutorial, you'll be able to: You'll create a complete forked emulator setup that demonstrates: - Starting the emulator with forked mainnet state. -- A React dapp connected to the forked emulator reading real FlowToken data. +- 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. @@ -104,7 +104,7 @@ This tutorial covers `flow emulator --fork` (interactive testing with a forked e ### 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 dapp, scripts, and transactions run locally but can read from and interact with real network data. +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:** @@ -119,7 +119,7 @@ The emulator's fork mode starts a local Flow blockchain that connects to a real Use `flow emulator --fork` for: - **E2E and frontend testing**: Run Cypress/Playwright tests against production-like state -- **Manual exploration**: Interact with your dapp connected to forked mainnet +- **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 @@ -133,7 +133,7 @@ Use `flow emulator --fork` for: | Feature | `flow emulator --fork` | `flow test --fork` | | --------------- | --------------------------------------- | ------------------------------ | -| **Use for** | Dapp E2E, manual testing, debugging | Cadence unit/integration tests | +| **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 | @@ -147,27 +147,19 @@ Want to see it work immediately? Here's the fastest path: ```bash # 1. Initialize a Flow project -flow init --yes - -# 2. Configure fork network (add to flow.json) -# Add this under "networks": -# "mainnet-fork": { -# "host": "127.0.0.1:3569", -# "fork": "mainnet" -# } +flow init -# 3. Install FlowToken dependency -flow dependencies install -# Select FlowToken from the list +# 2. Install FlowToken dependency +flow dependencies install FlowToken FungibleToken -# 4. Start forked emulator (in a separate terminal) +# 3. Start forked emulator (in a separate terminal) flow emulator --fork mainnet -# 5. In another terminal, check the forked state -flow scripts execute cadence/scripts/get_flow_supply.cdc --network mainnet-fork +# 4. Create a script to check the forked state +flow generate script getFlowSupply ``` -Create `cadence/scripts/get_flow_supply.cdc`: +Add the following to `cadence/scripts/getFlowSupply.cdc`: ```cadence import "FlowToken" @@ -177,6 +169,12 @@ access(all) fun main(): UFix64 { } ``` +In another terminal, run the script: + +```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 @@ -332,18 +330,16 @@ flow emulator --fork mainnet flow project deploy --network mainnet-fork --update ``` -Your dapp now uses the mocked FlowToken while FungibleToken, USDC, and all other contracts use real mainnet versions. +Your app now uses the mocked FlowToken while FungibleToken, USDC, and all other contracts use real mainnet versions. ## 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 +flow dependencies install FlowToken FungibleToken ``` -Select `FlowToken` and `FungibleToken` from the list (use space to select, enter to confirm). - Your `flow.json` now includes: ```json @@ -375,13 +371,13 @@ Your `flow.json` now includes: Before connecting a frontend, verify the fork works with a simple script. -Create a directory for scripts: +Generate a script file using the Flow CLI: ```bash -mkdir -p cadence/scripts +flow generate script getFlowSupply ``` -Create `cadence/scripts/get_flow_supply.cdc`: +Add the following to `cadence/scripts/getFlowSupply.cdc`: ```cadence import "FlowToken" @@ -393,10 +389,16 @@ access(all) fun main(): UFix64 { 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. -In a **new terminal** (keep the emulator running), execute the script: +First, verify the script works against real mainnet: ```bash -flow scripts execute cadence/scripts/get_flow_supply.cdc --network mainnet-fork +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`). @@ -410,7 +412,7 @@ You should see the real mainnet FlowToken supply (e.g., `Result: 1523456789.0000 Now let's connect a frontend. -## Create a React Dapp +## Create a React App Create a React app with Flow integration: @@ -458,7 +460,7 @@ root.render( Replace `src/App.js` with: -```javascript +````javascript import { useState } from 'react'; import { useFlowCurrentUser, useFlowQuery, Connect } from '@onflow/react-sdk'; @@ -519,25 +521,21 @@ function App() { } export default App; -``` - -### Start the dev wallet (optional) - -For wallet authentication flows, start the FCL dev wallet in another terminal: - -```bash -flow dev-wallet -``` - +# 2. Configure fork network (add to flow.json) +# Add this in "networks": +# "mainnet-fork": { +# "host": "127.0.0.1:3569", +# "fork": "mainnet" +# } This starts the dev wallet at `http://localhost:8701`. -### Run your dapp +### Run your app Start the React app: ```bash npm start -``` +```` Your browser will open to `http://localhost:3000`. Click "Get FlowToken Supply" to see real mainnet data! @@ -576,7 +574,13 @@ The forked emulator's superpower: you can execute transactions as **any mainnet ### Read Account Balance -Create `cadence/scripts/get_balance.cdc`: +Generate a script to read account balances: + +```bash +flow generate script getBalance +``` + +Add the following to `cadence/scripts/getBalance.cdc`: ```cadence import "FlowToken" @@ -595,14 +599,20 @@ access(all) fun main(address: Address): UFix64 { Check the Flow service account balance (a real mainnet account): ```bash -flow scripts execute cadence/scripts/get_balance.cdc 0x1654653399040a61 --network mainnet-fork +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 -Create `cadence/transactions/transfer_tokens.cdc`: +Generate a transaction to transfer tokens: + +```bash +flow generate transaction transferTokens +``` + +Add the following to `cadence/transactions/transferTokens.cdc`: ```cadence import "FungibleToken" @@ -653,18 +663,18 @@ 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/transfer_tokens.cdc 100.0 0xRECIPIENT_ADDRESS \ +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/get_balance.cdc 0xRECIPIENT_ADDRESS \ +flow scripts execute cadence/scripts/getBalance.cdc 0xRECIPIENT_ADDRESS \ --network mainnet-fork ``` ### Dev Wallet Authentication with Impersonation -The most powerful feature: when connecting your dapp to the forked emulator with the dev wallet, **you can authenticate as ANY mainnet account** directly in the UI. +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: @@ -672,11 +682,11 @@ Start the dev wallet: flow dev-wallet ``` -In your dapp (running against the forked emulator), click the wallet connect button. In the dev wallet UI: +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, NFT collector, or protocol account) 2. Click "Authenticate" -3. Your dapp is now authenticated as that mainnet account with all its real balances, NFTs, and storage! +3. Your app is now authenticated as that mainnet account with all its real balances, NFTs, and storage! **Additional dev wallet features in fork mode:** @@ -686,7 +696,7 @@ In your dapp (running against the forked emulator), click the wallet connect but This lets you: -- Test your dapp as a user with specific assets or permissions +- 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 many NFTs - Test edge cases with real account states @@ -700,7 +710,7 @@ The forked emulator simply skips signature verification. You can specify any mai ## Automating with E2E Testing -The forked emulator works with any E2E testing framework (Cypress, Playwright, Puppeteer, etc.). This lets you automate your dapp tests against production-like state. +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 @@ -708,7 +718,7 @@ The forked emulator works with any E2E testing framework (Cypress, Playwright, P npm install --save-dev cypress ``` -Create `cypress/e2e/flow_fork.cy.js`: +Create `cypress/e2e/flowFork.cy.js`: ```javascript describe('Flow Fork Test', () => { @@ -732,7 +742,7 @@ Your tests now run against forked mainnet—**perfect for CI/CD pipelines** with :::tip -Use the same approach with Playwright, Puppeteer, or any browser automation tool. The key is having your dapp connect to the forked emulator (`http://localhost:8888`) while your E2E framework tests the UI. +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. ::: @@ -745,7 +755,7 @@ Test a contract upgrade against real mainnet state by mocking the contract with 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 dapp against the upgraded contract with all real mainnet state intact +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 @@ -756,14 +766,14 @@ Reproduce a bug at the exact block height it occurred: flow emulator --fork mainnet --fork-height ``` -Then manually interact with your dapp or run specific transactions to reproduce the issue. +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 dapp to authenticate +2. Use your app to authenticate 3. Sign transactions as real mainnet accounts (via impersonation) 4. Verify balance updates, event emissions, etc. @@ -914,26 +924,24 @@ flow emulator --fork-host access.mainnet.nodes.onflow.org:9000 **Solution:** Ensure your fork network is properly configured: -```json +````json { - "networks": { - "mainnet-fork": { - "host": "127.0.0.1:3569", - "fork": "mainnet" - } - } -} -``` +# 2. Configure fork network (add to flow.json) +# Add this in "networks": +# "mainnet-fork": { +# "host": "127.0.0.1:3569", +# "fork": "mainnet" +# } And that you've installed dependencies with the mainnet alias: ```bash flow dependencies install -``` +```` Verify the contract has a mainnet alias that the fork can inherit. -### Dapp Can't Connect +### App Can't Connect **Error:** Frontend can't reach the emulator @@ -1001,7 +1009,7 @@ Choose the right tool: | --------------------------------------------- | ---------------------- | | Cadence unit tests | `flow test` (no fork) | | Cadence integration tests with real contracts | `flow test --fork` | -| Manual testing with dapp | `flow emulator --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` | @@ -1010,24 +1018,24 @@ 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 dapp 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. +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 dapp frontend** to the forked emulator. +- **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 dapp before going live. +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 dapp's upgrade flows against forked mainnet +- Test your app's upgrade flows against forked mainnet - Explore [Flow React SDK] hooks and components (events, mutations, Cross-VM features) - For Cadence contract testing, see [Fork Testing with Cadence] - Review the [Testing Strategy] for the full testing approach