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 Account,
11 AccountComponent,
12 AccountId,
13 AccountProcedureRoot,
14 RoleSymbol,
15 StorageMapKey,
16 StorageSlot,
17 StorageSlotName,
18};
19use miden_protocol::crypto::hash::poseidon2::Poseidon2;
20use miden_protocol::crypto::rand::FeltRng;
21use miden_protocol::errors::NoteError;
22use miden_protocol::note::{Note, NoteScriptRoot};
23use miden_standards::account::access::{PausableManager, PausableStorage, RoleConfig};
24use miden_standards::account::auth::AuthNetworkAccount;
25use miden_standards::account::fees::ConstantFeeManager;
26use miden_standards::note::config::{
27 ConstantFeePolicyConfigNote,
28 PauseConfig,
29 PauseConfigNote,
30 RbacConfigNote,
31};
32use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError, NoteExecutionHint};
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});
154static FEE_MANAGER_ROLE: LazyLock<RoleSymbol> =
155 LazyLock::new(|| RoleSymbol::new("FEE_MNGR").expect("FEE_MNGR role symbol should be valid"));
156static PAUSER_ROLE: LazyLock<RoleSymbol> =
157 LazyLock::new(|| RoleSymbol::new("PAUSER").expect("PAUSER role symbol should be valid"));
158
159static BRIDGE_COMPONENT_CODE: LazyLock<AccountComponentCode> =
162 LazyLock::new(|| AccountComponentCode::from(agglayer_bridge_component_package()));
163
164procedure_root!(
165 REGISTER_FAUCET_ROOT,
166 AggLayerBridge::COMPONENT_NAMESPACE,
167 "register_faucet",
168 AggLayerBridge::code()
169);
170procedure_root!(
171 STORE_FAUCET_METADATA_HASH_ROOT,
172 AggLayerBridge::COMPONENT_NAMESPACE,
173 "store_faucet_metadata_hash",
174 AggLayerBridge::code()
175);
176procedure_root!(
177 UPDATE_GER_ROOT,
178 AggLayerBridge::COMPONENT_NAMESPACE,
179 "update_ger",
180 AggLayerBridge::code()
181);
182procedure_root!(
183 REMOVE_GER_ROOT,
184 AggLayerBridge::COMPONENT_NAMESPACE,
185 "remove_ger",
186 AggLayerBridge::code()
187);
188procedure_root!(
189 DEREGISTER_FAUCET_ROOT,
190 AggLayerBridge::COMPONENT_NAMESPACE,
191 "deregister_faucet",
192 AggLayerBridge::code()
193);
194
195#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct BridgeRoles {
209 roles: Vec<RoleConfig>,
210}
211
212impl BridgeRoles {
213 pub fn new(
222 faucet_managers: BTreeSet<AccountId>,
223 ger_injectors: BTreeSet<AccountId>,
224 ger_removers: BTreeSet<AccountId>,
225 fee_managers: BTreeSet<AccountId>,
226 pausers: BTreeSet<AccountId>,
227 ) -> Result<Self, AgglayerBridgeError> {
228 let mut roles = Vec::new();
229 for (role, members) in [
230 (AggLayerBridge::faucet_manager_role(), &faucet_managers),
231 (AggLayerBridge::ger_injector_role(), &ger_injectors),
232 (AggLayerBridge::ger_remover_role(), &ger_removers),
233 (AggLayerBridge::fee_manager_role(), &fee_managers),
234 (AggLayerBridge::pauser_role(), &pausers),
235 ] {
236 if members.is_empty() {
237 return Err(AgglayerBridgeError::EmptyBridgeRole(role));
238 }
239 roles.push(RoleConfig::new(role).with_members(members.iter().copied()));
240 }
241
242 Ok(Self { roles })
243 }
244}
245
246impl IntoIterator for BridgeRoles {
247 type Item = RoleConfig;
248 type IntoIter = alloc::vec::IntoIter<RoleConfig>;
249
250 fn into_iter(self) -> Self::IntoIter {
251 self.roles.into_iter()
252 }
253}
254
255#[derive(Debug, Clone, Copy)]
311pub struct AggLayerBridge {
312 network_id: u32,
313}
314
315impl AggLayerBridge {
316 const COMPONENT_NAMESPACE: &'static str = "agglayer::components::bridge";
323
324 pub fn new(network_id: u32) -> Self {
332 Self { network_id }
333 }
334
335 pub fn code() -> &'static AccountComponentCode {
340 &BRIDGE_COMPONENT_CODE
341 }
342
343 pub fn faucet_manager_role() -> RoleSymbol {
346 FAUCET_MANAGER_ROLE.clone()
347 }
348
349 pub fn ger_injector_role() -> RoleSymbol {
351 GER_INJECTOR_ROLE.clone()
352 }
353
354 pub fn ger_remover_role() -> RoleSymbol {
356 GER_REMOVER_ROLE.clone()
357 }
358
359 pub fn fee_manager_role() -> RoleSymbol {
361 FEE_MANAGER_ROLE.clone()
362 }
363
364 pub fn pauser_role() -> RoleSymbol {
366 PAUSER_ROLE.clone()
367 }
368
369 pub fn register_faucet_root() -> AccountProcedureRoot {
371 *REGISTER_FAUCET_ROOT
372 }
373
374 pub fn store_faucet_metadata_hash_root() -> AccountProcedureRoot {
376 *STORE_FAUCET_METADATA_HASH_ROOT
377 }
378
379 pub fn update_ger_root() -> AccountProcedureRoot {
381 *UPDATE_GER_ROOT
382 }
383
384 pub fn remove_ger_root() -> AccountProcedureRoot {
386 *REMOVE_GER_ROOT
387 }
388
389 pub fn deregister_faucet_root() -> AccountProcedureRoot {
391 *DEREGISTER_FAUCET_ROOT
392 }
393
394 pub fn procedure_roles() -> BTreeMap<AccountProcedureRoot, RoleSymbol> {
398 BTreeMap::from([
399 (Self::register_faucet_root(), Self::faucet_manager_role()),
400 (Self::store_faucet_metadata_hash_root(), Self::faucet_manager_role()),
401 (Self::deregister_faucet_root(), Self::faucet_manager_role()),
402 (Self::update_ger_root(), Self::ger_injector_role()),
403 (Self::remove_ger_root(), Self::ger_remover_role()),
404 (ConstantFeeManager::set_note_fee_root(), Self::fee_manager_role()),
405 (PausableManager::pause_root(), Self::pauser_role()),
406 ])
407 }
408
409 pub fn ger_map_slot_name() -> &'static StorageSlotName {
416 &GER_MAP_SLOT_NAME
417 }
418
419 pub fn removed_ger_hash_chain_lo_slot_name() -> &'static StorageSlotName {
421 &REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME
422 }
423
424 pub fn removed_ger_hash_chain_hi_slot_name() -> &'static StorageSlotName {
426 &REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME
427 }
428
429 pub fn faucet_registry_map_slot_name() -> &'static StorageSlotName {
431 &FAUCET_REGISTRY_MAP_SLOT_NAME
432 }
433
434 pub fn token_registry_map_slot_name() -> &'static StorageSlotName {
436 &TOKEN_REGISTRY_MAP_SLOT_NAME
437 }
438
439 pub fn faucet_metadata_map_slot_name() -> &'static StorageSlotName {
444 &FAUCET_METADATA_MAP_SLOT_NAME
445 }
446
447 pub fn network_id_slot_name() -> &'static StorageSlotName {
452 &NETWORK_ID_SLOT_NAME
453 }
454
455 pub fn claim_nullifiers_slot_name() -> &'static StorageSlotName {
459 &CLAIM_NULLIFIERS_SLOT_NAME
460 }
461
462 pub fn cgi_chain_hash_lo_slot_name() -> &'static StorageSlotName {
464 &CGI_CHAIN_HASH_LO_SLOT_NAME
465 }
466
467 pub fn cgi_chain_hash_hi_slot_name() -> &'static StorageSlotName {
469 &CGI_CHAIN_HASH_HI_SLOT_NAME
470 }
471
472 pub fn let_frontier_slot_name() -> &'static StorageSlotName {
476 &LET_FRONTIER_SLOT_NAME
477 }
478
479 pub fn let_root_lo_slot_name() -> &'static StorageSlotName {
481 &LET_ROOT_LO_SLOT_NAME
482 }
483
484 pub fn let_root_hi_slot_name() -> &'static StorageSlotName {
486 &LET_ROOT_HI_SLOT_NAME
487 }
488
489 pub fn let_num_leaves_slot_name() -> &'static StorageSlotName {
491 &LET_NUM_LEAVES_SLOT_NAME
492 }
493
494 pub fn allowed_notes() -> BTreeSet<NoteScriptRoot> {
502 let mut notes = BTreeSet::from([
503 ClaimNote::script_root(),
504 B2AggNote::script_root(),
505 ConfigAggBridgeNote::script_root(),
506 DeregisterAggFaucetNote::script_root(),
507 UpdateGerNote::script_root(),
508 RemoveGerNote::script_root(),
509 PauseConfigNote::script_root(),
510 RbacConfigNote::script_root(),
511 ConstantFeePolicyConfigNote::script_root(),
512 ]);
513 notes.extend(AuthNetworkAccount::default_allowed_note_scripts());
514 notes
515 }
516
517 pub fn pause_note<R: FeltRng>(
531 config: PauseConfig,
532 sender: AccountId,
533 bridge_id: AccountId,
534 rng: &mut R,
535 ) -> Result<Note, AgglayerBridgeError> {
536 let attachment = NetworkAccountTarget::new(bridge_id, NoteExecutionHint::Always)
537 .map_err(AgglayerBridgeError::NonPublicPauseNoteTarget)?;
538
539 PauseConfigNote::builder()
540 .sender(sender)
541 .target(bridge_id)
542 .config(config)
543 .attachment(attachment)
544 .generate_serial_number(rng)
545 .build()
546 .map(Into::into)
547 .map_err(AgglayerBridgeError::PauseNoteCreationFailed)
548 }
549
550 const REGISTERED_GER_MAP_VALUE: Word =
554 Word::new([Felt::ONE, Felt::ZERO, Felt::ZERO, Felt::ZERO]);
555
556 pub fn is_ger_registered(
564 ger: ExitRoot,
565 bridge_account: &Account,
566 ) -> Result<bool, AgglayerBridgeError> {
567 Self::assert_bridge_account(bridge_account)?;
569
570 let ger_lower: Word = ger.to_elements()[0..4].try_into().unwrap();
572 let ger_upper: Word = ger.to_elements()[4..8].try_into().unwrap();
573 let ger_hash = Poseidon2::merge(&[ger_lower, ger_upper]);
574
575 let stored_value = bridge_account
578 .storage()
579 .get_map_item(AggLayerBridge::ger_map_slot_name(), StorageMapKey::from_raw(ger_hash))
580 .expect("provided account should have AggLayer Bridge specific storage slots");
581
582 if stored_value == Self::REGISTERED_GER_MAP_VALUE {
583 Ok(true)
584 } else {
585 Ok(false)
586 }
587 }
588
589 pub fn read_local_exit_root(account: &Account) -> Result<Vec<Felt>, AgglayerBridgeError> {
603 Self::assert_bridge_account(account)?;
605
606 let root_lo_slot = AggLayerBridge::let_root_lo_slot_name();
607 let root_hi_slot = AggLayerBridge::let_root_hi_slot_name();
608
609 let root_lo = account
610 .storage()
611 .get_item(root_lo_slot)
612 .expect("should be able to read LET root lo");
613 let root_hi = account
614 .storage()
615 .get_item(root_hi_slot)
616 .expect("should be able to read LET root hi");
617
618 let mut root = Vec::with_capacity(8);
619 root.extend(root_lo.to_vec());
620 root.extend(root_hi.to_vec());
621
622 Ok(root)
623 }
624
625 pub fn network_id(account: &Account) -> Result<u32, AgglayerBridgeError> {
632 Self::assert_bridge_account(account)?;
634
635 let value = account
636 .storage()
637 .get_item(AggLayerBridge::network_id_slot_name())
638 .expect("should be able to read the network ID");
639 let network_id = u32::try_from(value.to_vec()[0].as_canonical_u64())
640 .map_err(|_| AgglayerBridgeError::InvalidNetworkId)?;
641
642 Ok(network_id)
643 }
644
645 pub fn read_let_num_leaves(account: &Account) -> u64 {
647 let num_leaves_slot = AggLayerBridge::let_num_leaves_slot_name();
648 let value = account
649 .storage()
650 .get_item(num_leaves_slot)
651 .expect("should be able to read LET num leaves");
652 value.to_vec()[0].as_canonical_u64()
653 }
654
655 pub fn cgi_chain_hash(
662 bridge_account: &Account,
663 ) -> Result<crate::claim_note::CgiChainHash, AgglayerBridgeError> {
664 Self::assert_bridge_account(bridge_account)?;
666
667 let cgi_chain_hash_lo = bridge_account
668 .storage()
669 .get_item(AggLayerBridge::cgi_chain_hash_lo_slot_name())
670 .expect("failed to get CGI hash chain lo slot");
671 let cgi_chain_hash_hi = bridge_account
672 .storage()
673 .get_item(AggLayerBridge::cgi_chain_hash_hi_slot_name())
674 .expect("failed to get CGI hash chain hi slot");
675
676 Ok(crate::claim_note::CgiChainHash::new(Self::chain_hash_bytes(
677 cgi_chain_hash_lo,
678 cgi_chain_hash_hi,
679 )))
680 }
681
682 pub fn removed_ger_hash_chain(
692 bridge_account: &Account,
693 ) -> Result<RemovedGerHashChain, AgglayerBridgeError> {
694 Self::assert_bridge_account(bridge_account)?;
696
697 let chain_lo = bridge_account
698 .storage()
699 .get_item(AggLayerBridge::removed_ger_hash_chain_lo_slot_name())
700 .expect("failed to get removed GER hash chain lo slot");
701 let chain_hi = bridge_account
702 .storage()
703 .get_item(AggLayerBridge::removed_ger_hash_chain_hi_slot_name())
704 .expect("failed to get removed GER hash chain hi slot");
705
706 Ok(RemovedGerHashChain::new(Self::chain_hash_bytes(chain_lo, chain_hi)))
707 }
708
709 fn chain_hash_bytes(lo: Word, hi: Word) -> [u8; 32] {
714 lo.iter()
715 .chain(hi.iter())
716 .flat_map(|felt| {
717 (u32::try_from(felt.as_canonical_u64()).expect("Felt value does not fit into u32"))
718 .to_le_bytes()
719 })
720 .collect::<Vec<u8>>()
721 .try_into()
722 .expect("keccak hash should consist of exactly 32 bytes")
723 }
724
725 fn assert_bridge_account(account: &Account) -> Result<(), AgglayerBridgeError> {
734 Self::assert_storage_slots(account)?;
736
737 Self::assert_code_commitment(account)?;
739
740 Ok(())
741 }
742
743 fn assert_storage_slots(account: &Account) -> Result<(), AgglayerBridgeError> {
750 let account_storage_slot_names: Vec<&StorageSlotName> = account
752 .storage()
753 .slots()
754 .iter()
755 .map(|storage_slot| storage_slot.name())
756 .collect::<Vec<&StorageSlotName>>();
757
758 let are_slots_present = Self::slot_names()
760 .iter()
761 .all(|slot_name| account_storage_slot_names.contains(slot_name));
762 if !are_slots_present {
763 return Err(AgglayerBridgeError::StorageSlotsMismatch);
764 }
765
766 Ok(())
767 }
768
769 fn assert_code_commitment(account: &Account) -> Result<(), AgglayerBridgeError> {
778 if BRIDGE_CODE_COMMITMENT != account.code().commitment() {
779 return Err(AgglayerBridgeError::CodeCommitmentMismatch);
780 }
781
782 Ok(())
783 }
784
785 fn slot_names() -> Vec<&'static StorageSlotName> {
792 vec![
793 &*GER_MAP_SLOT_NAME,
794 &*LET_FRONTIER_SLOT_NAME,
795 &*LET_ROOT_LO_SLOT_NAME,
796 &*LET_ROOT_HI_SLOT_NAME,
797 &*LET_NUM_LEAVES_SLOT_NAME,
798 &*FAUCET_REGISTRY_MAP_SLOT_NAME,
799 &*TOKEN_REGISTRY_MAP_SLOT_NAME,
800 &*FAUCET_METADATA_MAP_SLOT_NAME,
801 &*REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME,
802 &*REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME,
803 &*CGI_CHAIN_HASH_LO_SLOT_NAME,
804 &*CGI_CHAIN_HASH_HI_SLOT_NAME,
805 &*CLAIM_NULLIFIERS_SLOT_NAME,
806 &*NETWORK_ID_SLOT_NAME,
807 PausableStorage::is_paused_slot(),
808 ]
809 }
810}
811
812impl From<AggLayerBridge> for AccountComponent {
813 fn from(bridge: AggLayerBridge) -> Self {
814 let bridge_storage_slots = vec![
815 StorageSlot::with_empty_map(GER_MAP_SLOT_NAME.clone()),
816 StorageSlot::with_empty_map(LET_FRONTIER_SLOT_NAME.clone()),
817 StorageSlot::with_value(LET_ROOT_LO_SLOT_NAME.clone(), Word::empty()),
818 StorageSlot::with_value(LET_ROOT_HI_SLOT_NAME.clone(), Word::empty()),
819 StorageSlot::with_value(LET_NUM_LEAVES_SLOT_NAME.clone(), Word::empty()),
820 StorageSlot::with_empty_map(FAUCET_REGISTRY_MAP_SLOT_NAME.clone()),
821 StorageSlot::with_empty_map(TOKEN_REGISTRY_MAP_SLOT_NAME.clone()),
822 StorageSlot::with_empty_map(FAUCET_METADATA_MAP_SLOT_NAME.clone()),
823 StorageSlot::with_value(REMOVED_GER_HASH_CHAIN_LO_SLOT_NAME.clone(), Word::empty()),
824 StorageSlot::with_value(REMOVED_GER_HASH_CHAIN_HI_SLOT_NAME.clone(), Word::empty()),
825 StorageSlot::with_value(CGI_CHAIN_HASH_LO_SLOT_NAME.clone(), Word::empty()),
826 StorageSlot::with_value(CGI_CHAIN_HASH_HI_SLOT_NAME.clone(), Word::empty()),
827 StorageSlot::with_empty_map(CLAIM_NULLIFIERS_SLOT_NAME.clone()),
828 StorageSlot::with_value(
829 NETWORK_ID_SLOT_NAME.clone(),
830 Word::new([Felt::from(bridge.network_id), Felt::ZERO, Felt::ZERO, Felt::ZERO]),
831 ),
832 ];
833 bridge_component(bridge_storage_slots)
834 }
835}
836
837#[derive(Debug, Error)]
842pub enum AgglayerBridgeError {
843 #[error(
844 "provided account does not have storage slots required for the AggLayer Bridge account"
845 )]
846 StorageSlotsMismatch,
847 #[error(
848 "the code commitment of the provided account does not match the code commitment of the AggLayer Bridge account"
849 )]
850 CodeCommitmentMismatch,
851 #[error("bridge role {0} must have at least one initial holder")]
852 EmptyBridgeRole(RoleSymbol),
853 #[error("the network ID stored in the bridge account does not fit into a u32")]
854 InvalidNetworkId,
855 #[error("bridge account must be public to be named by a network account target")]
856 NonPublicPauseNoteTarget(#[source] NetworkAccountTargetError),
857 #[error("failed to create a PAUSE_CONFIG note for the bridge account")]
858 PauseNoteCreationFailed(#[source] NoteError),
859}
860
861fn bridge_component(storage_slots: Vec<StorageSlot>) -> AccountComponent {
866 let package = agglayer_bridge_component_package();
867 let metadata = AccountComponentMetadata::new("agglayer::bridge")
868 .with_description("Bridge component for AggLayer");
869
870 AccountComponent::new(package, storage_slots, metadata)
871 .expect("bridge component should satisfy the requirements of a valid account component")
872}