Smart Contract
FlowEVMBridgeCustomAssociationTypes
A.1e4aa0b87d10b141.FlowEVMBridgeCustomAssociationTypes
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import CrossVMMetadataViews from 0x1d7e57aa55817448
4import EVM from 0xe467b9dd11fa00df
5
6/// This contract defines types required for custom cross-VM associations as used in FlowEVMBridgeCustomAssociation
7/// and in EVM-native cross-VM NFTs.
8///
9access(all) contract FlowEVMBridgeCustomAssociationTypes {
10
11 /// Emitted whenever an NFTFulfillmentMinter is used to fulfill an NFT from EVM
12 access(all) event FulfilledFromEVM(type: String, requestedID: UInt256, resultID: UInt64, uuid: UInt64)
13
14 access(all) entitlement FulfillFromEVM
15
16 /// Resource interface used by EVM-native NFT collections allowing for the fulfillment of NFTs from EVM into Cadence
17 ///
18 access(all) resource interface NFTFulfillmentMinter {
19 /// Getter for the type of NFT that's fulfilled by this implementation
20 ///
21 access(all) view fun getFulfilledType(): Type
22
23 /// Called by the VM bridge when moving NFTs from EVM into Cadence if the NFT is not in escrow. Since such NFTs
24 /// are EVM-native, they are distributed in EVM. On the Cadence side, those NFTs are handled by a mint & escrow
25 /// pattern. On moving to EVM, the NFTs are minted if not in escrow at the time of bridging.
26 ///
27 /// @param id: The id of the token being fulfilled from EVM
28 ///
29 /// @return The NFT fulfilled from EVM as its Cadence implementation
30 ///
31 access(FulfillFromEVM)
32 fun fulfillFromEVM(id: UInt256): @{NonFungibleToken.NFT} {
33 pre {
34 id <= UInt256(UInt64.max):
35 "The requested ID \(id.toString()) exceeds the maximum assignable Cadence NFT ID \(UInt64.max.toString())"
36 }
37 post {
38 UInt256(result.id) == id:
39 "Resulting NFT ID \(result.id.toString()) does not match requested ID \(id.toString())"
40 result.getType() == self.getFulfilledType():
41 "Expected \(self.getFulfilledType().identifier) but fulfilled \(result.getType().identifier)"
42 emit FulfilledFromEVM(type: result.getType().identifier, requestedID: id, resultID: result.id, uuid: result.uuid)
43 }
44 }
45 }
46
47 /// Data structure containing information about a registered CustomConfig
48 ///
49 access(all) struct CustomConfigInfo {
50 access(all) let updatedFromBridged: Bool
51 access(all) let isPaused: Bool
52 access(all) let fulfillmentMinterType: Type?
53 access(all) let evmPointer: CrossVMMetadataViews.EVMPointer
54
55 view init(
56 updatedFromBridged: Bool,
57 isPaused: Bool,
58 fulfillmentMinterType: Type?,
59 evmPointer: CrossVMMetadataViews.EVMPointer
60 ) {
61 self.updatedFromBridged = updatedFromBridged
62 self.isPaused = isPaused
63 self.fulfillmentMinterType = fulfillmentMinterType
64 self.evmPointer = evmPointer
65 }
66 }
67
68 /// Resource interface retrieving all relevant information for the VM bridge to fulfill bridge requests. The
69 /// interface allows for extensibility in the event future config types are added in the future for various asset
70 /// types.
71 ///
72 access(all) resource interface CustomConfig {
73 access(all) view fun getCadenceType(): Type
74 access(all) view fun getCadenceStandardInterfaceTypes(): {Type: Bool}
75 access(all) view fun getEVMContractAddress(): EVM.EVMAddress
76 access(all) view fun getNativeVM(): CrossVMMetadataViews.VM
77 access(all) view fun isUpdatedFromBridged(): Bool
78 access(all) view fun isPaused(): Bool
79 access(all) view fun checkFulfillmentMinter(): Bool?
80 access(account) fun setPauseStatus(_ paused: Bool)
81 }
82
83 /// Resource containing all relevant information for the VM bridge to fulfill NFT bridge requests. This is a resource
84 /// instead of a struct to ensure contained Capabilities cannot be copied
85 ///
86 access(all) resource NFTCustomConfig : CustomConfig {
87 /// The Cadence Type of the associated asset.
88 access(all) let type: Type
89 /// The EVM address defining the EVM implementation of the associated asset.
90 access(all) let evmContractAddress: EVM.EVMAddress
91 /// The VM in which the asset is distributed by the project. The bridge will mint/escrow in the non-native
92 /// VM environment.
93 access(all) let nativeVM: CrossVMMetadataViews.VM
94 /// Whether the asset was originally onboarded to the bridge via permissionless onboarding. In other words,
95 /// whether there was first a bridge-defined implementation of the underlying asset.
96 access(all) let updatedFromBridged: Bool
97 /// Flag determining if this config is paused from bridging - true: paused | false: unpaused
98 access(all) var pauseStatus: Bool
99 /// An authorized Capability allowing the bridge to fulfill bridge requests moving the underlying asset from
100 /// EVM. Required if the asset is EVM-native.
101 access(self) let fulfillmentMinter: Capability<auth(FulfillFromEVM) &{NFTFulfillmentMinter}>?
102
103 init(
104 type: Type,
105 evmContractAddress: EVM.EVMAddress,
106 nativeVM: CrossVMMetadataViews.VM,
107 updatedFromBridged: Bool,
108 fulfillmentMinter: Capability<auth(FulfillFromEVM) &{NFTFulfillmentMinter}>?
109 ) {
110 pre {
111 type.isSubtype(of: Type<@{NonFungibleToken.NFT}>()):
112 "The provided Type \(type.identifier) is not an NFT implementation - only NFTs are supported by NFTCustomConfig"
113 nativeVM == CrossVMMetadataViews.VM.EVM ? fulfillmentMinter != nil : true:
114 "EVM-native NFTs must provide an NFTFulfillmentMinter Capability."
115 fulfillmentMinter?.check() ?? true:
116 "Invalid NFTFulfillmentMinter Capability provided. Ensure the Capability is properly issued and active."
117 fulfillmentMinter != nil ? fulfillmentMinter!.borrow()!.getFulfilledType() == type : true:
118 "NFTFulfillmentMinter fulfills \(fulfillmentMinter!.borrow()!.getFulfilledType().identifier) but expected \(type.identifier)"
119 }
120 self.type = type
121 self.evmContractAddress = evmContractAddress
122 self.nativeVM = nativeVM
123 self.updatedFromBridged = updatedFromBridged
124 self.pauseStatus = false
125 self.fulfillmentMinter = fulfillmentMinter
126 }
127
128 /// Returns the Cadence Type related to this custom cross-VM NFT configuration
129 access(all) view fun getCadenceType(): Type {
130 return self.type
131 }
132
133 /// Returns the Cadence standard Types supported by this CustomConfig implementation
134 access(all) view fun getCadenceStandardInterfaceTypes(): {Type: Bool} {
135 return {Type<@{NonFungibleToken.NFT}>(): true}
136 }
137
138 /// Returns the EVM contract address related to this custom cross-VM NFT configuration
139 access(all) view fun getEVMContractAddress(): EVM.EVMAddress {
140 return self.evmContractAddress
141 }
142
143 /// Returns the VM in which the NFT is distributed
144 access(all) view fun getNativeVM(): CrossVMMetadataViews.VM {
145 return self.nativeVM
146 }
147
148 /// True if the NFT was originally onboarded to the bridge
149 access(all) view fun isUpdatedFromBridged(): Bool {
150 return self.updatedFromBridged
151 }
152
153 /// True if currently paused, false if unpaused
154 access(all) view fun isPaused(): Bool {
155 return self.pauseStatus
156 }
157
158 /// Returns true/false on the fulfillment minter Capability. If no Capability is stored, nil is returned
159 access(all) view fun checkFulfillmentMinter(): Bool? {
160 return self.fulfillmentMinter?.check() ?? nil
161 }
162
163 /// Returns a reference to the NFTFulfillmentMinter, allowing the bridge contracts to fulfill EVM-native NFTs
164 /// moving into Cadence
165 access(account)
166 view fun borrowFulfillmentMinter(): auth(FulfillFromEVM) &{NFTFulfillmentMinter} {
167 pre {
168 self.fulfillmentMinter != nil:
169 "CustomConfig for type \(self.type.identifier) was not assigned a NFTFulfillmentMinter."
170 }
171 return self.fulfillmentMinter!.borrow()
172 ?? panic("NFTFulfillmentMinter for type \(self.type.identifier) is now invalid.")
173 }
174
175 access(account) fun setPauseStatus(_ paused: Bool) {
176 self.pauseStatus = paused
177 }
178 }
179
180 /// Allows bridge contracts to create an NFTCustomConfig resource from the provided arguments
181 access(account)
182 fun createNFTCustomConfig(
183 type: Type,
184 evmContractAddress: EVM.EVMAddress,
185 nativeVM: CrossVMMetadataViews.VM,
186 updatedFromBridged: Bool,
187 fulfillmentMinter: Capability<auth(FulfillFromEVM) &{NFTFulfillmentMinter}>?
188 ): @NFTCustomConfig {
189 return <- create NFTCustomConfig(
190 type: type,
191 evmContractAddress: evmContractAddress,
192 nativeVM: nativeVM,
193 updatedFromBridged: updatedFromBridged,
194 fulfillmentMinter: fulfillmentMinter
195 )
196 }
197}