Smart Contract

FlowEVMBridgeAccessor

A.1e4aa0b87d10b141.FlowEVMBridgeAccessor

Valid From

114,569,135

Deployed

1w ago
Feb 16, 2026, 08:14:21 PM UTC

Dependents

2 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4
5import EVM from 0xe467b9dd11fa00df
6
7import FlowEVMBridgeConfig from 0x1e4aa0b87d10b141
8import FlowEVMBridge from 0x1e4aa0b87d10b141
9
10/// This contract defines a mechanism for routing bridge requests from the EVM contract to the Flow-EVM bridge contract
11///
12access(all)
13contract FlowEVMBridgeAccessor {
14
15    access(all) let StoragePath: StoragePath
16
17    /// BridgeAccessor implementation used by the EVM contract to route bridge calls from COA resources
18    ///
19    access(all)
20    resource BridgeAccessor : EVM.BridgeAccessor {
21
22        /// Passes along the bridge request to dedicated bridge contract
23        ///
24        /// @param nft: The NFT to be bridged to EVM
25        /// @param to: The address of the EVM account to receive the bridged NFT
26        /// @param feeProvider: A reference to a FungibleToken Provider from which the bridging fee is withdrawn in $FLOW
27        ///
28        access(EVM.Bridge)
29        fun depositNFT(
30            nft: @{NonFungibleToken.NFT},
31            to: EVM.EVMAddress,
32            feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
33        ) {
34            FlowEVMBridge.bridgeNFTToEVM(token: <-nft, to: to, feeProvider: feeProvider)
35        }
36
37        /// Passes along the bridge request to the dedicated bridge contract, returning the bridged NFT
38        ///
39        /// @param caller: A reference to the COA which currently owns the NFT in EVM
40        /// @param type: The Cadence type of the NFT to be bridged from EVM
41        /// @param id: The ID of the NFT to be bridged from EVM
42        /// @param feeProvider: A reference to a FungibleToken Provider from which the bridging fee is withdrawn in $FLOW
43        ///
44        /// @return The bridged NFT
45        ///
46        access(EVM.Bridge)
47        fun withdrawNFT(
48            caller: auth(EVM.Call) &EVM.CadenceOwnedAccount,
49            type: Type,
50            id: UInt256,
51            feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
52        ): @{NonFungibleToken.NFT} {
53            // Define a callback function, enabling the bridge to act on the ephemeral COA reference in scope
54            var executed = false
55            fun callback(target: EVM.EVMAddress): EVM.Result {
56                pre {
57                    !executed: "Callback can only be executed once"
58                    FlowEVMBridge.getAssociatedEVMAddress(with: type) ?? FlowEVMBridgeConfig.getLegacyEVMAddressAssociated(with: type) != nil:
59                    "Could not find EVM association for NFT Type \(type.identifier) - ensure the NFT has been onboarded to the bridge & try again"
60                }
61                post {
62                    executed: "Callback must be executed"
63                }
64                // Ensure the call is to an EVM contract known to be associated with the NFT Type as registered with
65                // the VM Bridge
66                let callAllowed = FlowEVMBridgeAccessor.isValidEVMTarget(forType: type, target: target)
67                assert(callAllowed,
68                    message: "Target EVM contract \(target.toString()) is not association with NFT Type \(type.identifier) - COA `safeTransferFrom` callback rejected")
69
70                executed = true
71                return caller.call(
72                    to: target,
73                    data: EVM.encodeABIWithSignature(
74                        "safeTransferFrom(address,address,uint256)",
75                        [caller.address(), FlowEVMBridge.getBridgeCOAEVMAddress(), id]
76                    ),
77                    gasLimit: FlowEVMBridgeConfig.gasLimit,
78                    value: EVM.Balance(attoflow: 0)
79                )
80            }
81            // Execute the bridge request
82            return <- FlowEVMBridge.bridgeNFTFromEVM(
83                owner: caller.address(),
84                type: type,
85                id: id,
86                feeProvider: feeProvider,
87                protectedTransferCall: callback
88            )
89        }
90
91        /// Passes along the bridge request to dedicated bridge contract
92        ///
93        /// @param vault: The fungible token vault to be bridged to EVM
94        /// @param to: The address of the EVM account to receive the bridged tokens
95        /// @param feeProvider: A reference to a FungibleToken Provider from which the bridging fee is withdrawn in $FLOW
96        ///
97        access(EVM.Bridge)
98        fun depositTokens(
99            vault: @{FungibleToken.Vault},
100            to: EVM.EVMAddress,
101            feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
102        ) {
103            FlowEVMBridge.bridgeTokensToEVM(vault: <-vault, to: to, feeProvider: feeProvider)
104        }
105
106        /// Passes along the bridge request to the dedicated bridge contract, returning the bridged FungibleToken
107        ///
108        /// @param caller: A reference to the COA which currently owns the tokens in EVM
109        /// @param type: The Cadence type of the fungible token vault to be bridged from EVM
110        /// @param amount: The amount of tokens to be bridged
111        /// @param feeProvider: A reference to a FungibleToken Provider from which the bridging fee is withdrawn in $FLOW
112        ///
113        /// @return The bridged FungibleToken Vault
114        ///
115        access(EVM.Bridge)
116        fun withdrawTokens(
117            caller: auth(EVM.Call) &EVM.CadenceOwnedAccount,
118            type: Type,
119            amount: UInt256,
120            feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
121        ): @{FungibleToken.Vault} {
122            // Define a callback function, enabling the bridge to act on the ephemeral COA reference in scope
123            var executed = false
124            fun callback(): EVM.Result {
125                pre {
126                    !executed: "Callback can only be executed once"
127                }
128                post {
129                    executed: "Callback must be executed"
130                }
131                executed = true
132                return caller.call(
133                    to: FlowEVMBridge.getAssociatedEVMAddress(with: type)
134                        ?? panic("No EVM address associated with type"),
135                    data: EVM.encodeABIWithSignature(
136                        "transfer(address,uint256)",
137                        [FlowEVMBridge.getBridgeCOAEVMAddress(), amount]
138                    ),
139                    gasLimit: FlowEVMBridgeConfig.gasLimit,
140                    value: EVM.Balance(attoflow: 0)
141                )
142            }
143            // Execute the bridge request
144            return <- FlowEVMBridge.bridgeTokensFromEVM(
145                owner: caller.address(),
146                type: type,
147                amount: amount,
148                feeProvider: feeProvider,
149                protectedTransferCall: callback
150            )
151        }
152
153        /// Returns a BridgeRouter resource so a Capability on this BridgeAccessor can be stored in the BridgeRouter
154        ///
155        access(EVM.Bridge) fun createBridgeRouter(): @BridgeRouter {
156            return <-create BridgeRouter()
157        }
158    }
159
160    /// BridgeRouter implementation used by the EVM contract to capture a BridgeAccessor Capability and route bridge
161    /// calls from COA resources to the FlowEVMBridge contract
162    ///
163    access(all) resource BridgeRouter : EVM.BridgeRouter {
164        /// Capability to the BridgeAccessor resource, initialized to nil
165        access(self) var bridgeAccessorCap: Capability<auth(EVM.Bridge) &{EVM.BridgeAccessor}>?
166
167        init() {
168            self.bridgeAccessorCap = nil
169        }
170
171        /// Returns an EVM.Bridge entitled reference to the underlying BridgeAccessor resource
172        ///
173        access(EVM.Bridge) view fun borrowBridgeAccessor(): auth(EVM.Bridge) &{EVM.BridgeAccessor} {
174            let cap = self.bridgeAccessorCap ?? panic("BridgeAccessor Capabaility is not yet set")
175            return cap.borrow() ?? panic("Problem retrieving BridgeAccessor reference")
176        }
177
178        /// Sets the BridgeAccessor Capability in the BridgeRouter
179        access(EVM.Bridge) fun setBridgeAccessor(_ accessorCap: Capability<auth(EVM.Bridge) &{EVM.BridgeAccessor}>) {
180            self.bridgeAccessorCap = accessorCap
181        }
182    }
183
184    /// Assesses whether the EVM contract address is associated with the provided type based on bridge associations
185    ///
186    access(self)
187    fun isValidEVMTarget(forType: Type, target: EVM.EVMAddress): Bool {
188        let currentAssociation = FlowEVMBridge.getAssociatedEVMAddress(with: forType)
189        let bridgedAssociation = FlowEVMBridgeConfig.getLegacyEVMAddressAssociated(with: forType)
190        return currentAssociation?.equals(target) ?? false || bridgedAssociation?.equals(target) ?? false
191    }
192
193    init(publishToEVMAccount: Address) {
194        self.StoragePath = /storage/flowEVMBridgeAccessor
195        self.account.storage.save(
196            <-create BridgeAccessor(),
197            to: self.StoragePath
198        )
199        let cap = self.account.capabilities.storage.issue<auth(EVM.Bridge) &BridgeAccessor>(self.StoragePath)
200        self.account.inbox.publish(cap, name: "FlowEVMBridgeAccessor", recipient: publishToEVMAccount)
201    }
202}
203