1#![no_std]
2
3extern crate alloc;
4
5use miden_assembly::Library;
6use miden_assembly::serde::Deserializable;
7use miden_core::{Felt, Word};
8use miden_protocol::account::{Account, AccountBuilder, AccountComponent, AccountId, AccountType};
9use miden_protocol::asset::TokenSymbol;
10use miden_standards::account::access::{Authority, Ownable2Step};
11use miden_standards::account::auth::AuthNetworkAccount;
12use miden_standards::account::policies::{
13 BurnAllowAll,
14 BurnPolicyConfig,
15 MintPolicyConfig,
16 PolicyRegistration,
17 TokenPolicyManager,
18 TransferPolicy,
19};
20use miden_utils_sync::LazyLock;
21
22pub mod b2agg_note;
23pub mod bridge;
24pub mod claim_note;
25pub mod config_note;
26pub mod errors;
27pub mod eth_types;
28pub mod faucet;
29#[cfg(feature = "testing")]
30pub mod testing;
31pub mod update_ger_note;
32pub mod utils;
33
34pub use b2agg_note::B2AggNote;
35pub use bridge::{AggLayerBridge, AgglayerBridgeError};
36pub use claim_note::{
37 CgiChainHash,
38 ClaimNote,
39 ClaimNoteStorage,
40 ExitRoot,
41 LeafData,
42 LeafValue,
43 ProofData,
44 SmtNode,
45};
46pub use config_note::{ConfigAggBridgeNote, ConversionMetadata};
47#[cfg(any(test, feature = "testing"))]
48pub use eth_types::GlobalIndexExt;
49pub use eth_types::{
50 EthAddress,
51 EthAmount,
52 EthAmountError,
53 EthEmbeddedAccountId,
54 GlobalIndex,
55 GlobalIndexError,
56 MetadataHash,
57};
58pub use faucet::{AggLayerFaucet, AgglayerFaucetError};
59pub use update_ger_note::UpdateGerNote;
60pub use utils::Keccak256Output;
61
62static AGGLAYER_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
66 let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/agglayer.masl"));
67 Library::read_from_bytes(bytes).expect("shipped AggLayer library is well-formed")
68});
69
70static BRIDGE_COMPONENT_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
71 let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/components/bridge.masl"));
72 Library::read_from_bytes(bytes).expect("shipped bridge component library is well-formed")
73});
74
75static FAUCET_COMPONENT_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
76 let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/components/faucet.masl"));
77 Library::read_from_bytes(bytes).expect("shipped faucet component library is well-formed")
78});
79
80pub fn agglayer_library() -> Library {
82 AGGLAYER_LIBRARY.clone()
83}
84
85fn agglayer_bridge_component_library() -> Library {
87 BRIDGE_COMPONENT_LIBRARY.clone()
88}
89
90fn agglayer_faucet_component_library() -> Library {
92 FAUCET_COMPONENT_LIBRARY.clone()
93}
94
95fn create_agglayer_faucet_component(
115 token_symbol: &str,
116 decimals: u8,
117 max_supply: Felt,
118 token_supply: Felt,
119) -> AccountComponent {
120 let symbol = TokenSymbol::new(token_symbol).expect("token symbol should be valid");
121 AggLayerFaucet::new(symbol, decimals, max_supply, token_supply)
122 .expect("agglayer faucet metadata should be valid")
123 .into()
124}
125
126fn create_bridge_account_builder(
134 seed: Word,
135 bridge_admin_id: AccountId,
136 ger_manager_id: AccountId,
137 network_id: u32,
138) -> AccountBuilder {
139 Account::builder(seed.into())
140 .account_type(AccountType::Public)
141 .with_component(AggLayerBridge::new(bridge_admin_id, ger_manager_id, network_id))
142 .with_auth_component(
143 AuthNetworkAccount::with_allowed_notes(AggLayerBridge::allowed_notes())
144 .expect("bridge note allowlist is non-empty"),
145 )
146}
147
148pub fn create_bridge_account(
152 seed: Word,
153 bridge_admin_id: AccountId,
154 ger_manager_id: AccountId,
155 network_id: u32,
156) -> Account {
157 create_bridge_account_builder(seed, bridge_admin_id, ger_manager_id, network_id)
158 .build()
159 .expect("bridge account should be valid")
160}
161
162#[cfg(any(feature = "testing", test))]
166pub fn create_existing_bridge_account(
167 seed: Word,
168 bridge_admin_id: AccountId,
169 ger_manager_id: AccountId,
170 network_id: u32,
171) -> Account {
172 create_bridge_account_builder(seed, bridge_admin_id, ger_manager_id, network_id)
173 .build_existing()
174 .expect("bridge account should be valid")
175}
176
177fn create_agglayer_faucet_builder(
191 seed: Word,
192 token_symbol: &str,
193 decimals: u8,
194 max_supply: Felt,
195 token_supply: Felt,
196 bridge_account_id: AccountId,
197) -> AccountBuilder {
198 let agglayer_component =
199 create_agglayer_faucet_component(token_symbol, decimals, max_supply, token_supply);
200
201 let token_policy_manager = TokenPolicyManager::new()
204 .with_mint_policy(MintPolicyConfig::OwnerOnly, PolicyRegistration::Active)
205 .expect("active mint policy is registered exactly once")
206 .with_burn_policy(BurnPolicyConfig::OwnerOnly, PolicyRegistration::Active)
207 .expect("active burn policy is registered exactly once")
208 .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Reserved)
209 .expect("reserved burn policy registration does not conflict")
210 .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)
211 .expect("active send policy is registered exactly once")
212 .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)
213 .expect("active receive policy is registered exactly once");
214
215 Account::builder(seed.into())
216 .account_type(AccountType::Public)
217 .with_component(agglayer_component)
218 .with_component(Ownable2Step::new(bridge_account_id))
219 .with_component(Authority::OwnerControlled)
220 .with_components(token_policy_manager)
221 .with_component(BurnAllowAll)
222 .with_auth_component(
223 AuthNetworkAccount::with_allowed_notes(AggLayerFaucet::allowed_notes())
224 .expect("faucet note allowlist is non-empty"),
225 )
226}
227
228pub fn create_agglayer_faucet(
232 seed: Word,
233 token_symbol: &str,
234 decimals: u8,
235 max_supply: Felt,
236 bridge_account_id: AccountId,
237) -> Account {
238 create_agglayer_faucet_builder(
239 seed,
240 token_symbol,
241 decimals,
242 max_supply,
243 Felt::ZERO,
244 bridge_account_id,
245 )
246 .build()
247 .expect("agglayer faucet account should be valid")
248}
249
250#[cfg(any(feature = "testing", test))]
254pub fn create_existing_agglayer_faucet(
255 seed: Word,
256 token_symbol: &str,
257 decimals: u8,
258 max_supply: Felt,
259 token_supply: Felt,
260 bridge_account_id: AccountId,
261) -> Account {
262 create_agglayer_faucet_builder(
263 seed,
264 token_symbol,
265 decimals,
266 max_supply,
267 token_supply,
268 bridge_account_id,
269 )
270 .build_existing()
271 .expect("agglayer faucet account should be valid")
272}