Smart Contract

FlowEVMBridgeConfig

A.1e4aa0b87d10b141.FlowEVMBridgeConfig

Valid From

114,568,775

Deployed

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

Dependents

12721 imports
1import EVM from 0xe467b9dd11fa00df
2import NonFungibleToken from 0x1d7e57aa55817448
3
4import FlowEVMBridgeHandlerInterfaces from 0x1e4aa0b87d10b141
5import FlowEVMBridgeCustomAssociations from 0x1e4aa0b87d10b141
6
7/// This contract is used to store configuration information shared by FlowEVMBridge contracts
8///
9access(all)
10contract FlowEVMBridgeConfig {
11
12    /******************
13        Entitlements
14    *******************/
15
16    access(all) entitlement Gas
17    access(all) entitlement Fee
18    access(all) entitlement Pause
19    access(all) entitlement Blocklist
20
21    /*************
22        Fields
23    **************/
24
25    /// Amount of FLOW paid to onboard a Type or EVMAddress to the bridge
26    access(all)
27    var onboardFee: UFix64
28    /// Flat rate fee for all bridge requests
29    access(all)
30    var baseFee: UFix64
31    /// Default ERC20.decimals() value
32    access(all)
33    let defaultDecimals: UInt8
34    /// The gas limit for all EVM calls related to bridge operations
35    access(all)
36    var gasLimit: UInt64
37    /// Flag enabling pausing of bridge operations
38    access(self)
39    var paused: Bool
40    /// Mapping of Type to its associated EVMAddress. The contained struct values also store the operational status of
41    /// the association, allowing for pausing of operations by Type
42    access(self) let registeredTypes: {Type: TypeEVMAssociation}
43    /// Reverse mapping of registeredTypes. Note the EVMAddress is stored as a hex string since the EVMAddress type
44    /// as of contract development is not a hashable or equatable type and making it so is not supported by Cadence
45    access(self)
46    let evmAddressHexToType: {String: Type}
47    /// Mapping of Type to its associated EVMAddress as relevant to the bridge
48    access(self)
49    let typeToTokenHandlers: @{Type: {FlowEVMBridgeHandlerInterfaces.TokenHandler}}
50
51    /********************
52        Path Constants
53    *********************/
54
55    /// StoragePath where bridge Cadence Owned Account is stored
56    access(all)
57    let coaStoragePath: StoragePath
58    /// StoragePath where bridge config Admin is stored
59    access(all)
60    let adminStoragePath: StoragePath
61    /// PublicPath where a public Capability on the bridge config Admin is exposed
62    access(all)
63    let adminPublicPath: PublicPath
64    /// StoragePath to store the Provider capability used as a bridge fee Provider
65    access(all)
66    let providerCapabilityStoragePath: StoragePath
67
68    /*************
69        Events
70    **************/
71
72    /// Emitted whenever the onboarding fee is updated
73    ///
74    access(all)
75    event BridgeFeeUpdated(old: UFix64, new: UFix64, isOnboarding: Bool)
76    /// Emitted whenever a TokenHandler is configured
77    ///
78    access(all)
79    event HandlerConfigured(targetType: String, targetEVMAddress: String?, isEnabled: Bool)
80    /// Emitted whenever the bridge is paused or unpaused globally - true for paused, false for unpaused
81    ///
82    access(all)
83    event BridgePauseStatusUpdated(paused: Bool)
84    /// Emitted whenever a specific asset is paused or unpaused - true for paused, false for unpaused
85    ///
86    access(all)
87    event AssetPauseStatusUpdated(paused: Bool, type: String, evmAddress: String)
88    /// Emitted whenever an association is updated
89    ///
90    access(all)
91    event AssociationUpdated(type: String, evmAddress: String)
92
93    /*************
94        Getters
95     *************/
96
97    /// Returns whether all bridge operations are currently paused or active
98    ///
99    access(all)
100    view fun isPaused(): Bool {
101        return self.paused
102    }
103
104    /// Returns whether operations for a given Type are paused. A return value of nil indicates the Type is not yet
105    /// onboarded to the bridge.
106    ///
107    access(all)
108    view fun isTypePaused(_ type: Type): Bool? {
109        // Paused if the type has a token handler & it's disabled, a custom config has been paused or the bridge config has been paused
110        return !(self.borrowTokenHandler(type)?.isEnabled() ?? true)
111            || FlowEVMBridgeCustomAssociations.isCustomConfigPaused(forType: type) ?? false
112            || self.registeredTypes[type]?.isPaused == true
113    }
114
115    /// Retrieves the EVMAddress associated with a given Type if it has been onboarded to the bridge
116    ///
117    access(all)
118    view fun getEVMAddressAssociated(with type: Type): EVM.EVMAddress? {
119        if self.typeHasTokenHandler(type) {
120            return self.borrowTokenHandler(type)!.getTargetEVMAddress()
121        }
122        let customAssociation = FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type)
123        return customAssociation ?? self.registeredTypes[type]?.evmAddress
124    }
125
126    /// Retrieves the type associated with a given EVMAddress if it has been onboarded to the bridge
127    ///
128    access(all)
129    view fun getTypeAssociated(with evmAddress: EVM.EVMAddress): Type? {
130        let evmAddressHex = evmAddress.toString()
131        let customAssociation = FlowEVMBridgeCustomAssociations.getTypeAssociated(with: evmAddress)
132        return customAssociation ?? self.evmAddressHexToType[evmAddressHex]
133    }
134
135    /// Returns whether the given EVMAddress is currently blocked from onboarding to the bridge
136    ///
137    access(all)
138    view fun isEVMAddressBlocked(_ evmAddress: EVM.EVMAddress): Bool {
139        return self.borrowEVMBlocklist().isBlocked(evmAddress)
140    }
141
142    /// Returns whether the given Cadence Type is currently blocked from onboarding to the bridge
143    ///
144    access(all)
145    view fun isCadenceTypeBlocked(_ type: Type): Bool {
146        return self.borrowCadenceBlocklist().isBlocked(type)
147    }
148
149    /// Returns the project-defined Type has been registered as a replacement for the originally bridge-defined asset
150    /// type. This would arise in the event an EVM-native project onboarded to the bridge via permissionless onboarding
151    /// & later registered their own Cadence NFT contract as associated with their ERC721 per FLIP-318 mechanisms.
152    /// If there is not a related custom cross-VM Type registered with the bridge, `nil` is returned.
153    ///
154    access(all)
155    view fun getUpdatedCustomCrossVMTypeForLegacyType(_ type: Type): Type? {
156        if !type.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) || type.address! != self.account.address {
157            // only bridge-defined NFT Types can have an updated custom cross-VM implementation
158            return nil
159        }
160        if let legacyEVMAssoc = self.getLegacyEVMAddressAssociated(with: type) {
161            // return the new Type associated with the originally associated EVM contract address
162            return FlowEVMBridgeCustomAssociations.getTypeAssociated(with: legacyEVMAssoc)
163        }
164        return nil
165    }
166
167    /// Returns the bridge-defined Type that was originally associated with the related EVM contract given some
168    /// externally defined contract. This would arise in the event an EVM-native project onboarded to the bridge via
169    /// permissionless onboarding & later registered their own Cadence NFT contract as associated with their ERC721 per
170    /// FLIP-318 mechanisms. If there is not a related bridge-defined Type registered with the bridge, `nil` is returned.
171    ///
172    access(all)
173    view fun getLegacyTypeForCustomCrossVMType(_ type: Type): Type? {
174        if !type.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) || type.address! == self.account.address {
175            // only externally-defined NFT Types can have an updated custom cross-VM implementation
176            return nil
177        }
178        if let customEVMAssoc = FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type ) {
179            // return the original bridged NFT Type associated with the custom cross-VM EVM contract address
180            return self.evmAddressHexToType[customEVMAssoc.toString()]
181        }
182        return nil
183    }
184
185    /// Returns the project-defined EVM contract address has been registered as a replacement for the originally bridge-
186    /// defined asset EVM contract. This would arise in the event an Cadence-native project onboarded to the bridge via
187    /// permissionless onboarding & later registered their own EVM contract as associated with their Cadence NFT per
188    /// FLIP-318 mechanisms. If there is not a related custom cross-VM EVM contract registered with the bridge, `nil` is
189    /// returned.
190    ///
191    access(all)
192    view fun getUpdatedCustomCrossVMEVMAddressForLegacyEVMAddress(_ evmAddress: EVM.EVMAddress): EVM.EVMAddress? {
193        if let legacyType = self.getLegacyTypeAssociated(with: evmAddress) {
194            // return the new EVM address associated with the originally associated Type
195            return FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: legacyType)
196        }
197        return nil
198    }
199
200    /// Returns the bridge-defined EVM contract address that was originally associated with the related Cadence NFT
201    /// given some externally defined contract. This would arise in the event a Cadence-native project onboarded to the
202    /// bridge via permissionless onboarding & later registered their own EVM contract as associated with their
203    /// Cadence NFT per FLIP-318 mechanisms. If there is not a related bridge-defined EVM contract registered with the
204    /// bridge, `nil` is returned.
205    ///
206    access(all)
207    view fun getLegacyEVMAddressForCustomCrossVMAddress(_ evmAddress: EVM.EVMAddress): EVM.EVMAddress? {
208        if let customType = FlowEVMBridgeCustomAssociations.getTypeAssociated(with: evmAddress) {
209            // return the original bridged NFT Type associated with the custom cross-VM EVM contract address
210            return self.registeredTypes[customType]?.evmAddress
211        }
212        return nil
213    }
214
215    /****************************
216        Bridge Account Methods
217     ****************************/
218
219    /// Returns whether the given Type has a TokenHandler configured
220    ///
221    access(account)
222    view fun typeHasTokenHandler(_ type: Type): Bool {
223        return self.typeToTokenHandlers[type] != nil
224    }
225
226    /// Returns whether the given EVMAddress has a TokenHandler configured
227    ///
228    access(account)
229    view fun evmAddressHasTokenHandler(_ evmAddress: EVM.EVMAddress): Bool {
230        let associatedType = self.getTypeAssociated(with: evmAddress)
231        return associatedType != nil ? self.typeHasTokenHandler(associatedType!) : false
232    }
233
234    /// Returns the Type associated with the provided EVM contract address if the association was established via
235    /// the permissionless onboarding path
236    ///
237    access(account)
238    view fun getLegacyTypeAssociated(with evmAddress: EVM.EVMAddress): Type? {
239        return self.evmAddressHexToType[evmAddress.toString()] ?? nil
240    }
241
242    /// Returns the EVM contract address associated with the provided Type if the association was established via
243    /// the permissionless onboarding path
244    ///
245    access(account)
246    view fun getLegacyEVMAddressAssociated(with type: Type): EVM.EVMAddress? {
247        if self.typeHasTokenHandler(type) {
248            return self.borrowTokenHandler(type)!.getTargetEVMAddress()
249        }
250        return self.registeredTypes[type]?.evmAddress ?? nil
251    }
252
253    /// Enables bridge contracts to add new associations between types and EVM addresses
254    ///
255    access(account)
256    fun associateType(_ type: Type, with evmAddress: EVM.EVMAddress) {
257        pre {
258            self.getEVMAddressAssociated(with: type) == nil:
259            "Type ".concat(type.identifier).concat(" already associated with an EVMAddress ")
260                .concat(self.registeredTypes[type]!.evmAddress.toString())
261            self.getTypeAssociated(with: evmAddress) == nil:
262            "EVMAddress ".concat(evmAddress.toString()).concat(" already associated with Type ")
263                .concat(self.evmAddressHexToType[evmAddress.toString()]!.identifier)
264        }
265        self.registeredTypes[type] = TypeEVMAssociation(associated: evmAddress)
266        let evmAddressHex = evmAddress.toString()
267        self.evmAddressHexToType[evmAddressHex] = type
268
269        emit AssociationUpdated(type: type.identifier, evmAddress: evmAddressHex)
270    }
271
272    /// Adds a TokenHandler to the bridge configuration
273    ///
274    access(account)
275    fun addTokenHandler(_ handler: @{FlowEVMBridgeHandlerInterfaces.TokenHandler}) {
276        pre {
277            handler.getTargetType() != nil: "Cannot configure Handler without a target Cadence Type set"
278            self.getEVMAddressAssociated(with: handler.getTargetType()!) == nil:
279                "Cannot configure Handler for Type that has already been onboarded to the bridge"
280            self.borrowTokenHandler(handler.getTargetType()!) == nil:
281                "Cannot configure Handler for Type that already has a Handler configured"
282        }
283        let type = handler.getTargetType()!
284        var targetEVMAddressHex: String? = nil
285        if let targetEVMAddress = handler.getTargetEVMAddress() {
286            targetEVMAddressHex = targetEVMAddress.toString()
287
288            let associatedType = self.getTypeAssociated(with: targetEVMAddress)
289            assert(
290                associatedType == nil,
291                message: "Handler target EVMAddress is already associated with a different Type"
292            )
293            self.associateType(type, with: targetEVMAddress)
294        }
295
296        emit HandlerConfigured(
297            targetType: type.identifier,
298            targetEVMAddress: targetEVMAddressHex,
299            isEnabled: handler.isEnabled()
300        )
301
302        self.typeToTokenHandlers[type] <-! handler
303    }
304
305    /// Returns an unentitled reference to the TokenHandler associated with the given Type
306    ///
307    access(account)
308    view fun borrowTokenHandler(
309        _ type: Type
310    ): &{FlowEVMBridgeHandlerInterfaces.TokenHandler}? {
311        return &self.typeToTokenHandlers[type]
312    }
313
314    /// Returns an entitled reference to the TokenHandler associated with the given Type
315    ///
316    access(self)
317    view fun borrowTokenHandlerAdmin(
318        _ type: Type
319    ): auth(FlowEVMBridgeHandlerInterfaces.Admin) &{FlowEVMBridgeHandlerInterfaces.TokenHandler}? {
320        return &self.typeToTokenHandlers[type]
321    }
322
323    /// Returns an entitled reference to the bridge EVMBlocklist
324    ///
325    access(self)
326    view fun borrowEVMBlocklist(): auth(Blocklist) &EVMBlocklist {
327        return self.account.storage.borrow<auth(Blocklist) &EVMBlocklist>(from: /storage/evmBlocklist)
328            ?? panic("Missing or mis-typed EVMBlocklist in storage")
329    }
330
331    /// Returns an entitled reference to the bridge CadenceBlocklist
332    ///
333    access(self)
334    view fun borrowCadenceBlocklist(): auth(Blocklist) &CadenceBlocklist {
335        return self.account.storage.borrow<auth(Blocklist) &CadenceBlocklist>(from: /storage/cadenceBlocklist)
336            ?? panic("Missing or mis-typed CadenceBlocklist in storage")
337    }
338
339    /// Sets the pause status of a given type, reverting if the type has no associated EVM address as either bridge-
340    /// defined or registered as a custom cross-VM association
341    ///
342    access(self)
343    fun updatePauseStatus(_ type: Type, pause: Bool) {
344        var evmAddress = ""
345        var updated = false
346        if let customAssoc = FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type) {
347            updated = FlowEVMBridgeCustomAssociations.isCustomConfigPaused(forType: type)! != pause
348            // Called methods no-op internally, so check for status update is skipped here
349            pause ? FlowEVMBridgeCustomAssociations.pauseCustomConfig(forType: type)
350                : FlowEVMBridgeCustomAssociations.unpauseCustomConfig(forType: type)
351            // Assign the EVM address based on the CustomConfig value
352            evmAddress = customAssoc.toString()
353        }
354        if let bridgedAssoc = &FlowEVMBridgeConfig.registeredTypes[type] as &TypeEVMAssociation? {
355            if evmAddress.length == 0 {
356                // Assign as bridge association only if custom association does not exist
357                evmAddress = bridgedAssoc.evmAddress.toString()
358            }
359            // No-op if already meets pause status, otherwise update as specified
360            if (pause && !bridgedAssoc.isPaused) || (!pause && bridgedAssoc.isPaused) {
361                updated = true
362                pause ? bridgedAssoc.pause() : bridgedAssoc.unpause()
363            }
364        }
365        assert(evmAddress.length > 0,
366            message: "There was no association found for type \(type.identifier). To block the type from onboarding, use the CadenceBlocklist.")
367        if updated { emit AssetPauseStatusUpdated(paused: pause, type: type.identifier, evmAddress: evmAddress) }
368    }
369
370    /*****************
371        Constructs
372     *****************/
373
374    /// Entry in the registeredTypes mapping, associating a Type with an EVMAddress and its operational status. Since
375    /// the registeredTypes mapping is indexed on Type, this struct does not additionally store the Type to reduce
376    /// redundant storage.
377    ///
378    access(all) struct TypeEVMAssociation {
379        /// The EVMAddress associated with the Type
380        access(all) let evmAddress: EVM.EVMAddress
381        /// Flag indicating whether operations for the associated Type are paused
382        access(all) var isPaused: Bool
383
384        init(associated evmAddress: EVM.EVMAddress) {
385            self.evmAddress = evmAddress
386            self.isPaused = false
387        }
388
389        /// Pauses operations for this association
390        ///
391        access(contract) fun pause() {
392            self.isPaused = true
393        }
394
395        /// Unpauses operations for this association
396        ///
397        access(contract) fun unpause() {
398            self.isPaused = false
399        }
400    }
401
402    /// EVMBlocklist resource stores a mapping of EVM addresses that are blocked from onboarding to the bridge
403    ///
404    access(all) resource EVMBlocklist {
405        /// Mapping of serialized EVM addresses to their blocked status
406        ///
407        access(all) let blocklist: {String: Bool}
408
409        init() {
410            self.blocklist = {}
411        }
412
413        /// Returns whether the given EVM address is blocked from onboarding to the bridge
414        ///
415        access(all) view fun isBlocked(_ evmAddress: EVM.EVMAddress): Bool {
416            return self.blocklist[evmAddress.toString()] ?? false
417        }
418
419        /// Blocks the given EVM address from onboarding to the bridge
420        ///
421        access(Blocklist) fun block(_ evmAddress: EVM.EVMAddress) {
422            self.blocklist[evmAddress.toString()] = true
423        }
424
425        /// Removes the given EVM address from the blocklist
426        ///
427        access(Blocklist) fun unblock(_ evmAddress: EVM.EVMAddress) {
428            self.blocklist.remove(key: evmAddress.toString())
429        }
430    }
431
432    /// CadenceBlocklist resource stores a mapping of Cadence Types that are blocked from onboarding to the bridge
433    ///
434    access(all) resource CadenceBlocklist {
435        /// Mapping of serialized Cadence Type to their blocked status
436        ///
437        access(all) let blocklist: {Type: Bool}
438
439        init() {
440            self.blocklist = {}
441        }
442
443        /// Returns whether the given Type is blocked from onboarding to the bridge
444        ///
445        access(all) view fun isBlocked(_ type: Type): Bool {
446            return self.blocklist[type] ?? false
447        }
448
449        /// Blocks the given Type from onboarding to the bridge
450        ///
451        access(Blocklist) fun block(_ type: Type) {
452            self.blocklist[type] = true
453        }
454
455        /// Removes the given type from the blocklist
456        ///
457        access(Blocklist) fun unblock(_ type: Type) {
458            self.blocklist.remove(key: type)
459        }
460    }
461
462    /*****************
463        Config Admin
464     *****************/
465
466    /// Admin resource enables updates to the bridge fees
467    ///
468    access(all)
469    resource Admin {
470
471        /// Sets the TokenMinter for the given Type. If a TokenHandler does not exist for the given Type, the operation
472        /// reverts. The provided minter must be of the expected type for the TokenHandler and the handler cannot have
473        /// a minter already set.
474        ///
475        /// @param targetType: Cadence type indexing the relevant TokenHandler
476        /// @param minter: TokenMinter minter to set for the TokenHandler
477        ///
478        access(all)
479        fun setTokenHandlerMinter(targetType: Type, minter: @{FlowEVMBridgeHandlerInterfaces.TokenMinter}) {
480            pre {
481                FlowEVMBridgeConfig.typeHasTokenHandler(targetType):
482                    "Cannot set minter for Type that does not have a TokenHandler configured"
483                FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType) != nil:
484                    "No handler found for target Type"
485                FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType)!.getExpectedMinterType() == minter.getType():
486                    "Invalid minter type"
487            }
488            FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType)!.setMinter(<-minter)
489        }
490
491        /// Sets the gas limit for all EVM calls related to bridge operations
492        ///
493        /// @param lim the new gas limit
494        ///
495        access(Gas)
496        fun setGasLimit(_ limit: UInt64) {
497            FlowEVMBridgeConfig.gasLimit = limit
498        }
499
500        /// Updates the onboarding fee
501        ///
502        /// @param new: UFix64 - new onboarding fee
503        ///
504        /// @emits BridgeFeeUpdated with the old and new rates and isOnboarding set to true
505        ///
506        access(Fee)
507        fun updateOnboardingFee(_ new: UFix64) {
508            emit BridgeFeeUpdated(old: FlowEVMBridgeConfig.onboardFee, new: new, isOnboarding: true)
509            FlowEVMBridgeConfig.onboardFee = new
510        }
511
512        /// Updates the base fee
513        ///
514        /// @param new: UFix64 - new base fee
515        ///
516        /// @emits BridgeFeeUpdated with the old and new rates and isOnboarding set to false
517        ///
518        access(Fee)
519        fun updateBaseFee(_ new: UFix64) {
520            emit BridgeFeeUpdated(old: FlowEVMBridgeConfig.baseFee, new: new, isOnboarding: false)
521            FlowEVMBridgeConfig.baseFee = new
522        }
523
524        /// Pauses the bridge, preventing all bridge operations
525        ///
526        /// @emits BridgePauseStatusUpdated with true
527        ///
528        access(Pause)
529        fun pauseBridge() {
530            if FlowEVMBridgeConfig.isPaused() {
531                return
532            }
533            FlowEVMBridgeConfig.paused = true
534            emit BridgePauseStatusUpdated(paused: true)
535        }
536
537        /// Unpauses the bridge, allowing bridge operations to resume
538        ///
539        /// @emits BridgePauseStatusUpdated with true
540        ///
541        access(Pause)
542        fun unpauseBridge() {
543            if !FlowEVMBridgeConfig.isPaused() {
544                return
545            }
546            FlowEVMBridgeConfig.paused = false
547            emit BridgePauseStatusUpdated(paused: false)
548        }
549
550        /// Pauses all operations for a given asset type
551        ///
552        /// @param type: The Type for which to pause bridge operations
553        ///
554        /// @emits AssetPauseStatusUpdated with the pause status and serialized type & associated EVM address
555        ///
556        access(Pause)
557        fun pauseType(_ type: Type) {
558            pre {
559                FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) != nil || FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type) != nil:
560                "Could not find a bridged or custom association for type \(type.identifier) - cannot pause a type without an association"
561            }
562            FlowEVMBridgeConfig.updatePauseStatus(type, pause: true)
563        }
564
565        /// Unpauses all operations for a given asset type
566        ///
567        /// @param type: The Type for which to unpause bridge operations
568        ///
569        /// @emits AssetPauseStatusUpdated with the pause status and serialized type & associated EVM address
570        ///
571        access(Pause)
572        fun unpauseType(_ type: Type) {
573            pre {
574                FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) != nil || FlowEVMBridgeCustomAssociations.getEVMAddressAssociated(with: type) != nil:
575                "Could not find a bridged or custom association for type \(type.identifier) - cannot unpause a type without an association"
576            }
577            FlowEVMBridgeConfig.updatePauseStatus(type, pause: false)
578        }
579
580        /// Sets the target EVM contract address on the handler for a given Type, associating the Cadence type with the
581        /// provided EVM address. If a TokenHandler does not exist for the given Type, the operation reverts.
582        ///
583        /// @param targetType: Cadence type to associate with the target EVM address
584        /// @param targetEVMAddress: target EVM address to associate with the Cadence type
585        ///
586        /// @emits HandlerConfigured with the target Type, target EVM address, and whether the handler is enabled
587        ///
588        access(FlowEVMBridgeHandlerInterfaces.Admin)
589        fun setHandlerTargetEVMAddress(targetType: Type, targetEVMAddress: EVM.EVMAddress) {
590            pre {
591                FlowEVMBridgeConfig.getEVMAddressAssociated(with: targetType) == nil:
592                    "Type already associated with an EVM Address"
593                FlowEVMBridgeConfig.getTypeAssociated(with: targetEVMAddress) == nil:
594                    "EVM Address already associated with another Type"
595            }
596            post {
597                FlowEVMBridgeConfig.getEVMAddressAssociated(with: targetType)!.equals(targetEVMAddress):
598                "Problem associating target Type and target EVM Address"
599            }
600            FlowEVMBridgeConfig.associateType(targetType, with: targetEVMAddress)
601
602            let handler = FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType)
603                ?? panic("No handler found for target Type")
604            handler.setTargetEVMAddress(targetEVMAddress)
605
606            emit HandlerConfigured(
607                targetType: targetType.identifier,
608                targetEVMAddress: targetEVMAddress.toString(),
609                isEnabled: handler.isEnabled()
610            )
611        }
612
613        /// Enables the TokenHandler for the given Type. If a TokenHandler does not exist for the given Type, the
614        /// operation reverts.
615        ///
616        /// @param targetType: Cadence type indexing the relevant TokenHandler
617        ///
618        /// @emits HandlerConfigured with the target Type, target EVM address, and whether the handler is enabled
619        ///
620        access(FlowEVMBridgeHandlerInterfaces.Admin)
621        fun enableHandler(targetType: Type) {
622            let handler = FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType)
623                ?? panic("No handler found for target Type ".concat(targetType.identifier))
624            handler.enableBridging()
625
626            let targetEVMAddressHex = handler.getTargetEVMAddress()?.toString()
627                ?? panic("Handler cannot be enabled without a target EVM Address")
628
629            emit HandlerConfigured(
630                targetType: handler.getTargetType()!.identifier,
631                targetEVMAddress: targetEVMAddressHex,
632                isEnabled: handler.isEnabled()
633            )
634        }
635
636        /// Disables the TokenHandler for the given Type. If a TokenHandler does not exist for the given Type, the
637        /// operation reverts.
638        ///
639        /// @param targetType: Cadence type indexing the relevant TokenHandler
640        ///
641        /// @emits HandlerConfigured with the target Type, target EVM address, and whether the handler is enabled
642        ///
643        access(FlowEVMBridgeHandlerInterfaces.Admin)
644        fun disableHandler(targetType: Type) {
645            let handler = FlowEVMBridgeConfig.borrowTokenHandlerAdmin(targetType)
646                ?? panic("No handler found for target Type".concat(targetType.identifier))
647            handler.disableBridging()
648
649            emit HandlerConfigured(
650                targetType: handler.getTargetType()!.identifier,
651                targetEVMAddress: handler.getTargetEVMAddress()?.toString(),
652                isEnabled: handler.isEnabled()
653            )
654        }
655    }
656
657    init() {
658        self.onboardFee = 0.0
659        self.baseFee = 0.0
660        self.defaultDecimals = 18
661        self.gasLimit = 15_000_000
662        self.paused = true
663
664        self.registeredTypes = {}
665        self.evmAddressHexToType = {}
666
667        self.typeToTokenHandlers <- {}
668
669        self.adminStoragePath = /storage/flowEVMBridgeConfigAdmin
670        self.adminPublicPath = /public/flowEVMBridgeConfigAdmin
671        self.coaStoragePath = /storage/evm
672        self.providerCapabilityStoragePath = /storage/bridgeFlowVaultProvider
673
674        // Create & save Admin, issuing a public unentitled Admin Capability
675        self.account.storage.save(<-create Admin(), to: self.adminStoragePath)
676        let adminCap = self.account.capabilities.storage.issue<&Admin>(self.adminStoragePath)
677        self.account.capabilities.publish(adminCap, at: self.adminPublicPath)
678
679        // Initialize the blocklists
680        self.account.storage.save(<-create EVMBlocklist(), to: /storage/evmBlocklist)
681        self.account.storage.save(<-create CadenceBlocklist(), to: /storage/cadenceBlocklist)
682    }
683}
684