Smart Contract

FlowEVMBridgeCustomAssociationTypes

A.1e4aa0b87d10b141.FlowEVMBridgeCustomAssociationTypes

Valid From

114,568,552

Deployed

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

Dependents

0 imports
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}