{"id":19610688,"url":"https://github.com/onflow/flow-evm-bridge","last_synced_at":"2026-02-21T20:03:22.932Z","repository":{"id":212378622,"uuid":"731358734","full_name":"onflow/flow-evm-bridge","owner":"onflow","description":"Repository for contracts supporting bridge between Flow \u003c\u003e EVM","archived":false,"fork":false,"pushed_at":"2025-05-19T18:44:38.000Z","size":5640,"stargazers_count":11,"open_issues_count":24,"forks_count":7,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-05-19T19:48:56.110Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Cadence","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/onflow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-13T22:50:31.000Z","updated_at":"2025-05-19T18:44:43.000Z","dependencies_parsed_at":"2023-12-13T23:41:52.435Z","dependency_job_id":"a8bb6b53-b6cf-4a57-9115-5b5170eca60c","html_url":"https://github.com/onflow/flow-evm-bridge","commit_stats":null,"previous_names":["onflow/flow-evm-bridge"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onflow%2Fflow-evm-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onflow%2Fflow-evm-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onflow%2Fflow-evm-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onflow%2Fflow-evm-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/onflow","download_url":"https://codeload.github.com/onflow/flow-evm-bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onflow%2Fflow-evm-bridge/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259256490,"owners_count":22829642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T10:31:59.525Z","updated_at":"2025-10-23T15:49:09.342Z","avatar_url":"https://github.com/onflow.png","language":"Cadence","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Tests](https://github.com/onflow/flow-evm-bridge/actions/workflows/cadence_test.yml/badge.svg)\n[![codecov](https://codecov.io/gh/onflow/flow-evm-bridge/graph/badge.svg?token=C1vCK0t88F)](https://codecov.io/gh/onflow/flow-evm-bridge)\n\n# Flow EVM Bridge\n\nThis repo contains contracts enabling bridging of fungible \u0026 non-fungible tokens between Cadence and EVM on Flow.\n\n## Deployments\n\nThe bridge contracts in this repo are deployed to the following addresses:\n\n|Contracts|Testnet|Mainnet|\n|---|---|---|\n|All Cadence Bridge contracts|[`0xdfc20aee650fcbdf`](https://contractbrowser.com/account/0xdfc20aee650fcbdf/contracts)|[`0x1e4aa0b87d10b141`](https://contractbrowser.com/account/0x1e4aa0b87d10b141/contracts)|\n|[`FlowEVMBridgeFactory.sol`](./solidity/src/FlowBridgeFactory.sol)|[`0xf8146b4aef631853f0eb98dbe28706d029e52c52`](https://evm-testnet.flowscan.io/address/0xF8146B4aEF631853F0eB98DBE28706d029e52c52)|[`0x1c6dea788ee774cf15bcd3d7a07ede892ef0be40`](https://evm.flowscan.io/address/0x1C6dEa788Ee774CF15bCd3d7A07ede892ef0bE40)|\n|[`FlowEVMBridgeDeploymentRegistry.sol`](./solidity/src/FlowEVMBridgeDeploymentRegistry.sol)|[`0x8781d15904d7e161f421400571dea24cc0db6938`](https://evm-testnet.flowscan.io/address/0x8781d15904d7e161f421400571dea24cc0db6938)|[`0x8fdec2058535a2cb25c2f8cec65e8e0d0691f7b0`](https://evm.flowscan.io/address/0x8FDEc2058535A2Cb25C2f8ceC65e8e0D0691f7B0)|\n|[`FlowEVMBridgedERC20Deployer.sol`](./solidity/src/FlowEVMBridgedERC20Deployer.sol)|[`0x4d45CaD104A71D19991DE3489ddC5C7B284cf263`](https://evm-testnet.flowscan.io/address/0x4d45CaD104A71D19991DE3489ddC5C7B284cf263)|[`0x49631Eac7e67c417D036a4d114AD9359c93491e7`](https://evm.flowscan.io/address/0x49631Eac7e67c417D036a4d114AD9359c93491e7)|\n|[`FlowEVMBridgedERC721Deployer.sol`](./solidity/src/FlowEVMBridgedERC721Deployer.sol)|[`0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56`](https://evm-testnet.flowscan.io/address/0x1B852d242F9c4C4E9Bb91115276f659D1D1f7c56)|[`0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df`](https://evm.flowscan.io/address/0xe7c2B80a9de81340AE375B3a53940E9aeEAd79Df)|\n\nAnd below are the bridge escrow's EVM addresses. These addresses are [`CadenceOwnedAccount`s (COA)](https://developers.flow.com/evm/cadence/interacting-with-coa#coa-interface) and they are stored stored in the same Flow account as you'll find the Cadence contracts (see above).\n\n|Network|Address|\n|---|---|\n|Testnet|[`0x0000000000000000000000023f946ffbc8829bfd`](https://evm-testnet.flowscan.io/address/0x0000000000000000000000023f946FFbc8829BFD)|\n|Mainnet|[`0x00000000000000000000000249250a5c27ecab3b`](https://evm.flowscan.io/address/0x00000000000000000000000249250a5C27Ecab3B)|\n\n## Interacting with the bridge\n\n\u003e :information_source: All bridging activity in either direction is orchestrated via Cadence on `CadenceOwnedAccount`\n\u003e (COA) resources. This means that all bridging activity must be initiated via a Cadence transaction, not an EVM\n\u003e transaction regardless of the directionality of the bridge request. For more information on the interplay between\n\u003e Cadence and EVM, see [EVM Integration FLIP #223](https://github.com/onflow/flips/pull/225/files)\n\n### Overview\n\nThe Flow EVM bridge allows both fungible and non-fungible tokens to move atomically between Cadence and EVM. In the\ncontext of EVM, fungible tokens are defined as ERC20 tokens, and non-fungible tokens as ERC721 tokens. In Cadence,\nfungible tokens are defined by contracts implementing FungibleToken and non-fungible tokens the NonFungibleToken\nstandard contract interfaces.\n\nLike all operations on Flow, there are native fees associated with both computation and storage. To prevent spam and\nsustain the bridge account's storage consumption, fees are charged for both onboarding assets and bridging assets. In\nthe case where storage consumption is expected, fees are charges based on the storage consumed at the current network\nrates. In all cases, there is a flat-rate fee in addition to any storage fees.\n\n### Onboarding\n\nSince a contract must define the asset in the target VM, an asset must be \"onboarded\" to the bridge before requests can\nbe fulfilled. Moving from Cadence to EVM, onboarding can occur on the fly, deploying a template contract in the same\ntransaction as the asset is bridged to EVM if the transaction so specifies. Moving from EVM to Cadence, however,\nrequires that onboarding occur in a separate transaction due to the fact that a Cadence contract is initialized at the\nend of a transaction and isn't available in the runtime until after the transaction has executed.\n\nBelow are transactions relevant to onboarding assets:\n- [`onboard_by_type.cdc`](./cadence/transactions/bridge/onboarding/onboard_by_type.cdc)\n- [`onboard_by_evm_address.cdc`](./cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc)\n\n\n### Bridging\n\nOnce an asset has been onboarded, either by its Cadence type or EVM contract address, it can be bridged in either\ndirection referred to by its Cadence type. For Cadence-native assets, this is simply its native type. For EVM-native\nassets, this is in most cases a templated Cadence contract deployed to the bridge account, the name of which is derived\nfrom the EVM contract address. For instance, an ERC721 contract at address `0x1234` would be onboarded to the bridge as\n`EVMVMBridgedNFT_0x1234`, making its type identifier `A.\u003cBRIDGE_ADDRESS\u003e.EVMVMBridgedNFT_0x1234.NFT`.\n\nHowever, the derivation of these identifiers can be abstracted within transactions. For example, calling applications \ncan provide the defining contract address and name of the bridged asset (see\n[`bridge_nft_to_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc)). Alternatively, the defining EVM\ncontract could be provided, etc - this flexibility is thanks to Cadence's scripted transactions.\n\n#### NFTs\n\nAny Cadence NFTs bridging to EVM are escrowed in the bridge account and either minted in a bridge-deployed ERC721\ncontract or transferred from escrow to the calling COA in EVM. On the return trip, NFTs are escrowed in EVM - owned by\nthe bridge's COA - and either unlocked from escrow if locked or minted from a bridge-owned NFT contract.\n\nBelow are transactions relevant to bridging NFTs:\n- [`bridge_nft_to_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc)\n- [`bridge_nft_from_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_from_evm.cdc)\n\n#### Fungible Tokens\n\nAny Cadence fungible tokens bridging to EVM are escrowed in the bridge account only if they are Cadence-native. If the\nbridge defines the tokens, they are burned. On the return trip the pattern is similar, with the bridge burning\nbridge-defined tokens or escrowing them if they are EVM-native. In all cases, if the bridge has authority to mint on one\nside, it must escrow on the other as the native VM contract is owned by an external party.\n\nWith fungible tokens in particular, there may be some cases where the Cadence contract is not deployed to the bridge\naccount, but the bridge still follows a mint/burn pattern in Cadence. These cases are handled via\n[`TokenHandler`](./cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc) implementations.\n\nAlso know that moving $FLOW to EVM is built into the `EVMAddress` object via `EVMAddress.deposit(from: @FlowToken.Vault)`.\nConversely, moving $FLOW from EVM is facilitated via the `CadenceOwnedAccount.withdraw(balance: Balance): @FlowToken.Vault`\nmethod. Given these existing interfaces, the bridge instead handles $FLOW as corresponding fungible token\nimplementations - `FungibleToken.Vault` in Cadence \u0026 ERC20 in EVM. Therefore, the bridge wraps $FLOW en route to EVM\n(depositing WFLOW to the recipient) and unwraps WFLOW when bridging when moving from EVM. In short, the cross-VM\nassociation for $FLOW as far as the bridge is concerned is `@FlowToken.Vault` \u003c\u003e WFLOW\n(`0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e` on\n[Testnet](https://evm-testnet.flowscan.io/address/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e) \u0026\n[Mainnet](https://evm.flowscan.io/address/0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e)).\n\nBelow are transactions relevant to bridging fungible tokens:\n- [`bridge_tokens_to_evm.cdc`](./cadence/transactions/bridge/tokens/bridge_tokens_to_evm.cdc)\n- [`bridge_tokens_from_evm.cdc`](./cadence/transactions/bridge/tokens/bridge_tokens_from_evm.cdc)\n\n\n## Prep Your Assets for Bridging\n\n### Context\n\nTo maximize utility to the ecosystem, this bridge is permissionless and open to any fungible or non-fungible token as\ndefined by the respective Cadence standards and limited to ERC20 and ERC721 Solidity standards. Ultimately, a project\ndoes not have to do anything for users to be able to bridge their assets between VMs. However, there are some\nconsiderations developers may take to enhance the representation of their assets in non-native VMs. These largely relate\nto asset metadata and ensuring that bridging does not compromise critical user assumptions about asset ownership.\n\n### EVMBridgedMetadata\n\nProposed in [@onflow/flow-nft/pull/203](https://github.com/onflow/flow-nft/pull/203), the `EVMBridgedMetadata` view\npresents a mechanism to both represent metadata from bridged EVM assets as well as enable Cadence-native projects to\nspecify the representation of their assets in EVM. Implementing this view is not required for assets to be bridged, but\nthe bridge does default to it when available as a way to provide projects greater control over their EVM asset\ndefinitions within the scope of ERC20 and ERC721 standards.\n\nThe interface for this view is as follows:\n\n```cadence\naccess(all) struct URI: MetadataViews.File {\n    /// The base URI prefix, if any. Not needed for all URIs, but helpful\n    /// for some use cases For example, updating a whole NFT collection's\n    /// image host easily\n    access(all) let baseURI: String?\n    /// The URI string value\n    /// NOTE: this is set on init as a concatenation of the baseURI and the\n    /// value if baseURI != nil\n    access(self) let value: String\n\n    access(all) view fun uri(): String\n        \n}\n\naccess(all) struct EVMBridgedMetadata {\n    access(all) let name: String\n    access(all) let symbol: String\n\n    access(all) let uri: {MetadataViews.File}\n}\n```\n\nThis uri value could be a pointer to some offchain metadata if you expect your metadata to be static. Or you could\ncouple the `uri()` method with the utility contract below to serialize the onchain metadata on the fly.\n\n### SerializeMetadata\n\nThe key consideration with respect to metadata is the distinct metadata storage patterns between ecosystem. It's\ncritical for NFT utility that the metadata be bridged in addition to the representation of the NFTs ownership. However,\nit's commonplace for Cadence NFTs to store metadata onchain while EVM NFTs often store an onchain pointer to metadata\nstored offchain. In order for Cadence NFTs to be properly represented in EVM platforms, the metadata must be bridged in\na format expected by those platforms and be done in a manner that also preserves the atomicity of bridge requests. The\npath forward on this was decided to be a commitment of serialized Cadence NFT metadata into formats popular in the EVM\necosystem.\n\nFor assets that do not implement `EVMBridgedMetadata`, the bridge will attempt to serialize the metadata of the asset as\na JSON data URL string. This is done via the [`SerializeMetadata`\ncontract](./cadence/contracts/utils/SerializeMetadata.cdc) which serializes metadata values into a JSON blob compatible\nwith the OpenSea metadata standard. The serialized metadata is then committed as the ERC721 `tokenURI` upon bridging\nCadence-native NFTs to EVM. Since Cadence NFTs can easily update onchain metadata either by field or by the ownership of\nsub-NFTs, this serialization pattern enables token URI updates on subsequent bridge requests.\n\n### Opting Out\n\nIt's also recognized that the logic of some use cases may actually be compromised by the act of bridging, particularly\nin such a unique runtime environment. These would be cases that do not maintain ownership assumptions implicit to\necosystem standards. For instance, an ERC721 implementation may reclaim a user's assets after a month of inactivity time\nperiod. In such a case, bridging that ERC721 to Cadence would decouple the representation of ownership of the bridged\nNFT from the actual ownership in the definining ERC721 contract after the token had been reclaimed - there would be no\nNFT in escrow for the bridge to transfer on fulfillment of the NFT back to EVM. In such cases, projects may choose to\nopt-out of bridging, but **importantly must do so before the asset has been onboarded to the bridge**.\n\nFor Solidity contracts, opting out is as simple as extending the [`BridgePermissions.sol` abstract\ncontract](./solidity/src/interfaces/BridgePermissions.sol) which defaults `allowsBridging()` to false. The bridge explicitly checks\nfor the implementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not\nopted out of bridging.\n\nSimilarly, Cadence contracts can implement the [`IBridgePermissions.cdc` contract\ninterface](./cadence/contracts/bridge/interfaces/IBridgePermissions.cdc). This contract has a single method\n`allowsBridging()` with a default implementation returning `false`. Again, the bridge explicitly checks for the\nimplementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not opted\nout of bridging. Should you later choose to enable bridging, you can simply override the default implementation and\nreturn true.\n\nIn both cases, `allowsBridging()` gates onboarding to the bridge. Once onboarded - **a permissionless operation anyone can\nexecute** - the value of `allowsBridging()` is irrelevant and assets can move between VMs permissionlessly.\n\n## Under the Hood\n\nFor an in-depth look at the high-level architecture of the bridge, see [FLIP\n#237](https://github.com/onflow/flips/blob/main/application/20231222-evm-vm-bridge.md)\n\n## Local Development\n\nThe contracts in this repo are not yet included in the Flow emulator. For local development against the bridge, follow\nthe steps below to stand up a local Flow emulator instance and deploy the bridge contracts:\n\n### Prerequisites\n\n- Install Flow CLI on your machine. For instructions, see the [Flow CLI documentation](https://developers.flow.com/tools/flow-cli/install).\n- Download and install Go. For instructions, see the [Go documentation](https://go.dev/doc/install).\n\nEnsure both are installed with:\n\n```sh\nflow version\n```\n\nand go with:\n\n```sh\ngo version\n```\n\n### Start your local emulator\n\nStart the Flow emulator with the following command:\n\n```sh\nflow emulator\n```\n\n### Run the deployment script\n\nIn a separate terminal window, run the deployment script to deploy the bridge contracts to your local emulator:\n\n```sh\ngo run main.go\n```\n\nIf all is successful, you should see a long flow of event and transaction logs in your terminal with a final line resulting in:\n\n```sh\nSETUP COMPLETE! Bridge is now unpaused and ready for use.\n```\n\n### Interact with the bridge\n\nYou're now ready to interact with the bridge!\n\n### Additional Resources\n\nFor the current state of Flow EVM across various task paths, see the following resources:\n\n- [Flow EVM Equivalence forum post](https://forum.flow.com/t/evm-equivalence-on-flow-proposal-and-path-forward/5478)\n- [EVM Integration FLIP #223](https://github.com/onflow/flips/pull/225/files)\n- [Gateway \u0026 JSON RPC FLIP #235](https://github.com/onflow/flips/pull/235)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonflow%2Fflow-evm-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonflow%2Fflow-evm-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonflow%2Fflow-evm-bridge/lists"}