Smart Contract
FlowEVMBridgeAccessor
A.1e4aa0b87d10b141.FlowEVMBridgeAccessor
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