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