1extern crate alloc;
2
3use alloc::collections::{BTreeMap, BTreeSet};
4use alloc::vec;
5use alloc::vec::Vec;
6
7use miden_core::{Felt, Word};
8use miden_protocol::account::component::{AccountComponentCode, AccountComponentMetadata};
9use miden_protocol::account::{
10 AccountComponent,
11 AccountId,
12 AccountProcedureRoot,
13 RoleSymbol,
14 StorageSlot,
15 StorageSlotName,
16};
17use miden_protocol::crypto::rand::FeltRng;
18use miden_protocol::errors::NoteError;
19use miden_protocol::note::{Note, NoteScriptRoot};
20#[cfg(any(feature = "testing", test))]
21use miden_standards::account::access::PausableStorage;
22use miden_standards::account::access::RoleConfig;
23use miden_standards::account::auth::AuthNetworkAccount;
24use miden_standards::note::{
25 ConstantFeePolicyConfigNote,
26 NetworkAccountTarget,
27 NetworkAccountTargetError,
28 NoteExecutionHint,
29 PauseConfig,
30 PauseConfigNote,
31 RbacConfigNote,
32};
33use miden_standards::procedure_root;
34use miden_utils_sync::LazyLock;
35use thiserror::Error;
36
37use super::agglayer_bridge_component_package;
38use crate::utils::Keccak256Output;
39
40pub type RemovedGerHashChain = Keccak256Output;
42pub use miden_standards::interop::eth::{
43 EthAddress,
44 EthAmount,
45 EthAmountError,
46 EthEmbeddedAccountId,
47};
48
49pub use crate::{
50 B2AggNote,
51 ClaimNote,
52 ClaimNoteStorage,
53 ConfigAggBridgeNote,
54 DeregisterAggFaucetNote,
55 ExitRoot,
56 GlobalIndex,
57 GlobalIndexError,
58 LeafData,
59 MetadataHash,
60 ProofData,
61 RemoveGerNote,
62 SmtNode,
63 UpdateGerNote,
64};
65
66include!(concat!(env!("OUT_DIR"), "/agglayer_constants.rs"));
70
71static GER_MAP_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
78 StorageSlotName::new("agglayer::bridge::ger_map")
79 .expect("GER map storage slot name should be valid")
80});
81static REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
82 StorageSlotName::new("agglayer::bridge::removed_ger_hash_chain_lo")
83 .expect("removed GER hash chain lo storage slot name should be valid")
84});
85static REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
86 StorageSlotName::new("agglayer::bridge::removed_ger_hash_chain_hi")
87 .expect("removed GER hash chain hi storage slot name should be valid")
88});
89static FAUCET_REGISTRY_MAP_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
90 StorageSlotName::new("agglayer::bridge::faucet_registry_map")
91 .expect("faucet registry map storage slot name should be valid")
92});
93static TOKEN_REGISTRY_MAP_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
94 StorageSlotName::new("agglayer::bridge::token_registry_map")
95 .expect("token registry map storage slot name should be valid")
96});
97static FAUCET_METADATA_MAP_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
98 StorageSlotName::new("agglayer::bridge::faucet_metadata_map")
99 .expect("faucet metadata map storage slot name should be valid")
100});
101static NETWORK_ID_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
102 StorageSlotName::new("agglayer::bridge::network_id")
103 .expect("network ID storage slot name should be valid")
104});
105
106static CLAIM_NULLIFIERS_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
110 StorageSlotName::new("agglayer::bridge::claim_nullifiers")
111 .expect("claim nullifiers storage slot name should be valid")
112});
113static CGI_CHAIN_HASH_LO_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
114 StorageSlotName::new("agglayer::bridge::cgi_chain_hash_lo")
115 .expect("CGI chain hash_lo storage slot name should be valid")
116});
117static CGI_CHAIN_HASH_HI_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
118 StorageSlotName::new("agglayer::bridge::cgi_chain_hash_hi")
119 .expect("CGI chain hash_hi storage slot name should be valid")
120});
121
122static LET_FRONTIER_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
126 StorageSlotName::new("agglayer::bridge::let_frontier")
127 .expect("LET frontier storage slot name should be valid")
128});
129static LET_ROOT_LO_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
130 StorageSlotName::new("agglayer::bridge::let_root_lo")
131 .expect("LET root_lo storage slot name should be valid")
132});
133static LET_ROOT_HI_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
134 StorageSlotName::new("agglayer::bridge::let_root_hi")
135 .expect("LET root_hi storage slot name should be valid")
136});
137static LET_NUM_LEAVES_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
138 StorageSlotName::new("agglayer::bridge::let_num_leaves")
139 .expect("LET num_leaves storage slot name should be valid")
140});
141
142static FAUCET_MANAGER_ROLE: LazyLock<RoleSymbol> = LazyLock::new(|| {
146 RoleSymbol::new("FAUCET_MNGR").expect("FAUCET_MNGR role symbol should be valid")
147});
148static GER_INJECTOR_ROLE: LazyLock<RoleSymbol> = LazyLock::new(|| {
149 RoleSymbol::new("GER_INJECTOR").expect("GER_INJECTOR role symbol should be valid")
150});
151static GER_REMOVER_ROLE: LazyLock<RoleSymbol> = LazyLock::new(|| {
152 RoleSymbol::new("GER_REMOVER").expect("GER_REMOVER role symbol should be valid")
153});
154
155static BRIDGE_COMPONENT_CODE: LazyLock<AccountComponentCode> =
158 LazyLock::new(|| AccountComponentCode::from(agglayer_bridge_component_package()));
159
160procedure_root!(
161 REGISTER_FAUCET_ROOT,
162 AggLayerBridge::COMPONENT_NAMESPACE,
163 "register_faucet",
164 AggLayerBridge::code()
165);
166procedure_root!(
167 STORE_FAUCET_METADATA_HASH_ROOT,
168 AggLayerBridge::COMPONENT_NAMESPACE,
169 "store_faucet_metadata_hash",
170 AggLayerBridge::code()
171);
172procedure_root!(
173 UPDATE_GER_ROOT,
174 AggLayerBridge::COMPONENT_NAMESPACE,
175 "update_ger",
176 AggLayerBridge::code()
177);
178procedure_root!(
179 REMOVE_GER_ROOT,
180 AggLayerBridge::COMPONENT_NAMESPACE,
181 "remove_ger",
182 AggLayerBridge::code()
183);
184procedure_root!(
185 DEREGISTER_FAUCET_ROOT,
186 AggLayerBridge::COMPONENT_NAMESPACE,
187 "deregister_faucet",
188 AggLayerBridge::code()
189);
190
191#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct BridgeRoles {
203 roles: Vec<RoleConfig>,
204}
205
206impl BridgeRoles {
207 pub fn new(
216 faucet_managers: BTreeSet<AccountId>,
217 ger_injectors: BTreeSet<AccountId>,
218 ger_removers: BTreeSet<AccountId>,
219 ) -> Result<Self, AgglayerBridgeError> {
220 let mut roles = Vec::new();
221 for (role, members) in [
222 (AggLayerBridge::faucet_manager_role(), &faucet_managers),
223 (AggLayerBridge::ger_injector_role(), &ger_injectors),
224 (AggLayerBridge::ger_remover_role(), &ger_removers),
225 ] {
226 if members.is_empty() {
227 return Err(AgglayerBridgeError::EmptyBridgeRole(role));
228 }
229 roles.push(RoleConfig::new(role).with_members(members.iter().copied()));
230 }
231
232 Ok(Self { roles })
233 }
234}
235
236impl IntoIterator for BridgeRoles {
237 type Item = RoleConfig;
238 type IntoIter = alloc::vec::IntoIter<RoleConfig>;
239
240 fn into_iter(self) -> Self::IntoIter {
241 self.roles.into_iter()
242 }
243}
244
245#[derive(Debug, Clone, Copy)]
301pub struct AggLayerBridge {
302 network_id: u32,
303}
304
305impl AggLayerBridge {
306 const COMPONENT_NAMESPACE: &'static str = "agglayer::components::bridge";
313
314 pub fn new(network_id: u32) -> Self {
322 Self { network_id }
323 }
324
325 pub fn code() -> &'static AccountComponentCode {
330 &BRIDGE_COMPONENT_CODE
331 }
332
333 pub fn faucet_manager_role() -> RoleSymbol {
336 FAUCET_MANAGER_ROLE.clone()
337 }
338
339 pub fn ger_injector_role() -> RoleSymbol {
341 GER_INJECTOR_ROLE.clone()
342 }
343
344 pub fn ger_remover_role() -> RoleSymbol {
346 GER_REMOVER_ROLE.clone()
347 }
348
349 pub fn register_faucet_root() -> AccountProcedureRoot {
351 *REGISTER_FAUCET_ROOT
352 }
353
354 pub fn store_faucet_metadata_hash_root() -> AccountProcedureRoot {
356 *STORE_FAUCET_METADATA_HASH_ROOT
357 }
358
359 pub fn update_ger_root() -> AccountProcedureRoot {
361 *UPDATE_GER_ROOT
362 }
363
364 pub fn remove_ger_root() -> AccountProcedureRoot {
366 *REMOVE_GER_ROOT
367 }
368
369 pub fn deregister_faucet_root() -> AccountProcedureRoot {
371 *DEREGISTER_FAUCET_ROOT
372 }
373
374 pub fn procedure_roles() -> BTreeMap<AccountProcedureRoot, RoleSymbol> {
378 BTreeMap::from([
379 (Self::register_faucet_root(), Self::faucet_manager_role()),
380 (Self::store_faucet_metadata_hash_root(), Self::faucet_manager_role()),
381 (Self::deregister_faucet_root(), Self::faucet_manager_role()),
382 (Self::update_ger_root(), Self::ger_injector_role()),
383 (Self::remove_ger_root(), Self::ger_remover_role()),
384 ])
385 }
386
387 pub fn ger_map_slot_name() -> &'static StorageSlotName {
394 &GER_MAP_SLOT_NAME
395 }
396
397 pub fn removed_ger_hash_chain_lo_slot_name() -> &'static StorageSlotName {
399 &REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME
400 }
401
402 pub fn removed_ger_hash_chain_hi_slot_name() -> &'static StorageSlotName {
404 &REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME
405 }
406
407 pub fn faucet_registry_map_slot_name() -> &'static StorageSlotName {
409 &FAUCET_REGISTRY_MAP_SLOT_NAME
410 }
411
412 pub fn token_registry_map_slot_name() -> &'static StorageSlotName {
414 &TOKEN_REGISTRY_MAP_SLOT_NAME
415 }
416
417 pub fn faucet_metadata_map_slot_name() -> &'static StorageSlotName {
422 &FAUCET_METADATA_MAP_SLOT_NAME
423 }
424
425 pub fn network_id_slot_name() -> &'static StorageSlotName {
430 &NETWORK_ID_SLOT_NAME
431 }
432
433 pub fn claim_nullifiers_slot_name() -> &'static StorageSlotName {
437 &CLAIM_NULLIFIERS_SLOT_NAME
438 }
439
440 pub fn cgi_chain_hash_lo_slot_name() -> &'static StorageSlotName {
442 &CGI_CHAIN_HASH_LO_SLOT_NAME
443 }
444
445 pub fn cgi_chain_hash_hi_slot_name() -> &'static StorageSlotName {
447 &CGI_CHAIN_HASH_HI_SLOT_NAME
448 }
449
450 pub fn let_frontier_slot_name() -> &'static StorageSlotName {
454 &LET_FRONTIER_SLOT_NAME
455 }
456
457 pub fn let_root_lo_slot_name() -> &'static StorageSlotName {
459 &LET_ROOT_LO_SLOT_NAME
460 }
461
462 pub fn let_root_hi_slot_name() -> &'static StorageSlotName {
464 &LET_ROOT_HI_SLOT_NAME
465 }
466
467 pub fn let_num_leaves_slot_name() -> &'static StorageSlotName {
469 &LET_NUM_LEAVES_SLOT_NAME
470 }
471
472 pub fn allowed_notes() -> BTreeSet<NoteScriptRoot> {
480 let mut notes = BTreeSet::from([
481 ClaimNote::script_root(),
482 B2AggNote::script_root(),
483 ConfigAggBridgeNote::script_root(),
484 DeregisterAggFaucetNote::script_root(),
485 UpdateGerNote::script_root(),
486 RemoveGerNote::script_root(),
487 PauseConfigNote::script_root(),
488 RbacConfigNote::script_root(),
489 ConstantFeePolicyConfigNote::script_root(),
490 ]);
491 notes.extend(AuthNetworkAccount::default_allowed_note_scripts());
492 notes
493 }
494
495 pub fn pause_note<R: FeltRng>(
508 config: PauseConfig,
509 sender: AccountId,
510 bridge_id: AccountId,
511 rng: &mut R,
512 ) -> Result<Note, AgglayerBridgeError> {
513 let attachment = NetworkAccountTarget::new(bridge_id, NoteExecutionHint::Always)
514 .map_err(AgglayerBridgeError::NonPublicPauseNoteTarget)?;
515
516 PauseConfigNote::builder()
517 .sender(sender)
518 .target(bridge_id)
519 .config(config)
520 .attachment(attachment)
521 .generate_serial_number(rng)
522 .build()
523 .map(Into::into)
524 .map_err(AgglayerBridgeError::PauseNoteCreationFailed)
525 }
526}
527
528impl From<AggLayerBridge> for AccountComponent {
529 fn from(bridge: AggLayerBridge) -> Self {
530 let bridge_storage_slots = vec![
531 StorageSlot::with_empty_map(GER_MAP_SLOT_NAME.clone()),
532 StorageSlot::with_empty_map(LET_FRONTIER_SLOT_NAME.clone()),
533 StorageSlot::with_value(LET_ROOT_LO_SLOT_NAME.clone(), Word::empty()),
534 StorageSlot::with_value(LET_ROOT_HI_SLOT_NAME.clone(), Word::empty()),
535 StorageSlot::with_value(LET_NUM_LEAVES_SLOT_NAME.clone(), Word::empty()),
536 StorageSlot::with_empty_map(FAUCET_REGISTRY_MAP_SLOT_NAME.clone()),
537 StorageSlot::with_empty_map(TOKEN_REGISTRY_MAP_SLOT_NAME.clone()),
538 StorageSlot::with_empty_map(FAUCET_METADATA_MAP_SLOT_NAME.clone()),
539 StorageSlot::with_value(REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME.clone(), Word::empty()),
540 StorageSlot::with_value(REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME.clone(), Word::empty()),
541 StorageSlot::with_value(CGI_CHAIN_HASH_LO_SLOT_NAME.clone(), Word::empty()),
542 StorageSlot::with_value(CGI_CHAIN_HASH_HI_SLOT_NAME.clone(), Word::empty()),
543 StorageSlot::with_empty_map(CLAIM_NULLIFIERS_SLOT_NAME.clone()),
544 StorageSlot::with_value(
545 NETWORK_ID_SLOT_NAME.clone(),
546 Word::new([Felt::from(bridge.network_id), Felt::ZERO, Felt::ZERO, Felt::ZERO]),
547 ),
548 ];
549 bridge_component(bridge_storage_slots)
550 }
551}
552
553#[cfg(any(feature = "testing", test))]
557impl AggLayerBridge {
558 const REGISTERED_GER_MAP_VALUE: Word = Word::new([
559 miden_protocol::Felt::ONE,
560 miden_protocol::Felt::ZERO,
561 miden_protocol::Felt::ZERO,
562 miden_protocol::Felt::ZERO,
563 ]);
564
565 pub fn is_ger_registered(
573 ger: ExitRoot,
574 bridge_account: &miden_protocol::account::Account,
575 ) -> Result<bool, AgglayerBridgeError> {
576 use miden_protocol::account::StorageMapKey;
577 use miden_protocol::crypto::hash::poseidon2::Poseidon2;
578
579 Self::assert_bridge_account(bridge_account)?;
581
582 let ger_lower: Word = ger.to_elements()[0..4].try_into().unwrap();
584 let ger_upper: Word = ger.to_elements()[4..8].try_into().unwrap();
585 let ger_hash = Poseidon2::merge(&[ger_lower, ger_upper]);
586
587 let stored_value = bridge_account
590 .storage()
591 .get_map_item(AggLayerBridge::ger_map_slot_name(), StorageMapKey::from_raw(ger_hash))
592 .expect("provided account should have AggLayer Bridge specific storage slots");
593
594 if stored_value == Self::REGISTERED_GER_MAP_VALUE {
595 Ok(true)
596 } else {
597 Ok(false)
598 }
599 }
600
601 pub fn read_local_exit_root(
615 account: &miden_protocol::account::Account,
616 ) -> Result<Vec<miden_core::Felt>, AgglayerBridgeError> {
617 Self::assert_bridge_account(account)?;
619
620 let root_lo_slot = AggLayerBridge::let_root_lo_slot_name();
621 let root_hi_slot = AggLayerBridge::let_root_hi_slot_name();
622
623 let root_lo = account
624 .storage()
625 .get_item(root_lo_slot)
626 .expect("should be able to read LET root lo");
627 let root_hi = account
628 .storage()
629 .get_item(root_hi_slot)
630 .expect("should be able to read LET root hi");
631
632 let mut root = Vec::with_capacity(8);
633 root.extend(root_lo.to_vec());
634 root.extend(root_hi.to_vec());
635
636 Ok(root)
637 }
638
639 pub fn network_id(
646 account: &miden_protocol::account::Account,
647 ) -> Result<u32, AgglayerBridgeError> {
648 Self::assert_bridge_account(account)?;
650
651 let value = account
652 .storage()
653 .get_item(AggLayerBridge::network_id_slot_name())
654 .expect("should be able to read the network ID");
655 let network_id = u32::try_from(value.to_vec()[0].as_canonical_u64())
656 .map_err(|_| AgglayerBridgeError::InvalidNetworkId)?;
657
658 Ok(network_id)
659 }
660
661 pub fn read_let_num_leaves(account: &miden_protocol::account::Account) -> u64 {
663 let num_leaves_slot = AggLayerBridge::let_num_leaves_slot_name();
664 let value = account
665 .storage()
666 .get_item(num_leaves_slot)
667 .expect("should be able to read LET num leaves");
668 value.to_vec()[0].as_canonical_u64()
669 }
670
671 pub fn cgi_chain_hash(
678 bridge_account: &miden_protocol::account::Account,
679 ) -> Result<crate::claim_note::CgiChainHash, AgglayerBridgeError> {
680 Self::assert_bridge_account(bridge_account)?;
682
683 let cgi_chain_hash_lo = bridge_account
684 .storage()
685 .get_item(AggLayerBridge::cgi_chain_hash_lo_slot_name())
686 .expect("failed to get CGI hash chain lo slot");
687 let cgi_chain_hash_hi = bridge_account
688 .storage()
689 .get_item(AggLayerBridge::cgi_chain_hash_hi_slot_name())
690 .expect("failed to get CGI hash chain hi slot");
691
692 Ok(crate::claim_note::CgiChainHash::new(Self::chain_hash_bytes(
693 cgi_chain_hash_lo,
694 cgi_chain_hash_hi,
695 )))
696 }
697
698 pub fn removed_ger_hash_chain(
708 bridge_account: &miden_protocol::account::Account,
709 ) -> Result<RemovedGerHashChain, AgglayerBridgeError> {
710 Self::assert_bridge_account(bridge_account)?;
712
713 let chain_lo = bridge_account
714 .storage()
715 .get_item(AggLayerBridge::removed_ger_hash_chain_lo_slot_name())
716 .expect("failed to get removed GER hash chain lo slot");
717 let chain_hi = bridge_account
718 .storage()
719 .get_item(AggLayerBridge::removed_ger_hash_chain_hi_slot_name())
720 .expect("failed to get removed GER hash chain hi slot");
721
722 Ok(RemovedGerHashChain::new(Self::chain_hash_bytes(chain_lo, chain_hi)))
723 }
724
725 fn chain_hash_bytes(lo: Word, hi: Word) -> [u8; 32] {
730 lo.iter()
731 .chain(hi.iter())
732 .flat_map(|felt| {
733 (u32::try_from(felt.as_canonical_u64()).expect("Felt value does not fit into u32"))
734 .to_le_bytes()
735 })
736 .collect::<Vec<u8>>()
737 .try_into()
738 .expect("keccak hash should consist of exactly 32 bytes")
739 }
740
741 fn assert_bridge_account(
750 account: &miden_protocol::account::Account,
751 ) -> Result<(), AgglayerBridgeError> {
752 Self::assert_storage_slots(account)?;
754
755 Self::assert_code_commitment(account)?;
757
758 Ok(())
759 }
760
761 fn assert_storage_slots(
768 account: &miden_protocol::account::Account,
769 ) -> Result<(), AgglayerBridgeError> {
770 let account_storage_slot_names: Vec<&StorageSlotName> = account
772 .storage()
773 .slots()
774 .iter()
775 .map(|storage_slot| storage_slot.name())
776 .collect::<Vec<&StorageSlotName>>();
777
778 let are_slots_present = Self::slot_names()
780 .iter()
781 .all(|slot_name| account_storage_slot_names.contains(slot_name));
782 if !are_slots_present {
783 return Err(AgglayerBridgeError::StorageSlotsMismatch);
784 }
785
786 Ok(())
787 }
788
789 fn assert_code_commitment(
798 account: &miden_protocol::account::Account,
799 ) -> Result<(), AgglayerBridgeError> {
800 if BRIDGE_CODE_COMMITMENT != account.code().commitment() {
801 return Err(AgglayerBridgeError::CodeCommitmentMismatch);
802 }
803
804 Ok(())
805 }
806
807 fn slot_names() -> Vec<&'static StorageSlotName> {
814 vec![
815 &*GER_MAP_SLOT_NAME,
816 &*LET_FRONTIER_SLOT_NAME,
817 &*LET_ROOT_LO_SLOT_NAME,
818 &*LET_ROOT_HI_SLOT_NAME,
819 &*LET_NUM_LEAVES_SLOT_NAME,
820 &*FAUCET_REGISTRY_MAP_SLOT_NAME,
821 &*TOKEN_REGISTRY_MAP_SLOT_NAME,
822 &*FAUCET_METADATA_MAP_SLOT_NAME,
823 &*REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME,
824 &*REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME,
825 &*CGI_CHAIN_HASH_LO_SLOT_NAME,
826 &*CGI_CHAIN_HASH_HI_SLOT_NAME,
827 &*CLAIM_NULLIFIERS_SLOT_NAME,
828 &*NETWORK_ID_SLOT_NAME,
829 PausableStorage::is_paused_slot(),
830 ]
831 }
832}
833
834#[derive(Debug, Error)]
839pub enum AgglayerBridgeError {
840 #[error(
841 "provided account does not have storage slots required for the AggLayer Bridge account"
842 )]
843 StorageSlotsMismatch,
844 #[error(
845 "the code commitment of the provided account does not match the code commitment of the AggLayer Bridge account"
846 )]
847 CodeCommitmentMismatch,
848 #[error("bridge role {0} must have at least one initial holder")]
849 EmptyBridgeRole(RoleSymbol),
850 #[error("the network ID stored in the bridge account does not fit into a u32")]
851 InvalidNetworkId,
852 #[error("bridge account must be public to be named by a network account target")]
853 NonPublicPauseNoteTarget(#[source] NetworkAccountTargetError),
854 #[error("failed to create a PAUSE_CONFIG note for the bridge account")]
855 PauseNoteCreationFailed(#[source] NoteError),
856}
857
858fn bridge_component(storage_slots: Vec<StorageSlot>) -> AccountComponent {
863 let package = agglayer_bridge_component_package();
864 let metadata = AccountComponentMetadata::new("agglayer::bridge")
865 .with_description("Bridge component for AggLayer");
866
867 AccountComponent::new(package, storage_slots, metadata)
868 .expect("bridge component should satisfy the requirements of a valid account component")
869}