extern crate alloc;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use miden_core::Felt;
use miden_protocol::account::AccountId;
use miden_protocol::crypto::rand::FeltRng;
use miden_protocol::errors::NoteError;
use miden_protocol::note::{
Note,
NoteAssets,
NoteAttachment,
NoteAttachments,
NoteRecipient,
NoteScript,
NoteScriptRoot,
NoteStorage,
NoteType,
PartialNoteMetadata,
};
use miden_standards::interop::eth::EthAddress;
use miden_standards::note::costs::NoteConsumptionCost;
use miden_standards::note::{NetworkAccountTarget, NoteExecutionHint};
use miden_utils_sync::LazyLock;
use crate::costs::CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES;
use crate::{MetadataHash, note_script};
const CONFIG_AGG_BRIDGE_SCRIPT_PATH: &str = "::agglayer::notes::config_agg_bridge::main";
static CONFIG_AGG_BRIDGE_SCRIPT: LazyLock<NoteScript> =
LazyLock::new(|| note_script(CONFIG_AGG_BRIDGE_SCRIPT_PATH));
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConversionMetadata {
pub faucet_account_id: AccountId,
pub origin_token_address: EthAddress,
pub scale: u8,
pub origin_network: u32,
pub is_native: bool,
pub metadata_hash: MetadataHash,
}
impl ConversionMetadata {
pub fn to_elements(&self) -> Vec<Felt> {
let mut v = Vec::with_capacity(ConfigAggBridgeNote::NUM_STORAGE_ITEMS);
v.extend(self.origin_token_address.to_elements());
v.push(self.faucet_account_id.suffix());
v.push(self.faucet_account_id.prefix().as_felt());
v.push(Felt::from(self.scale));
v.push(Felt::from(self.origin_network));
v.push(Felt::from(u8::from(self.is_native)));
v.extend(self.metadata_hash.to_elements());
v
}
}
pub struct ConfigAggBridgeNote;
impl ConfigAggBridgeNote {
pub const NUM_STORAGE_ITEMS: usize = 18;
pub fn script() -> NoteScript {
CONFIG_AGG_BRIDGE_SCRIPT.clone()
}
pub fn script_root() -> NoteScriptRoot {
CONFIG_AGG_BRIDGE_SCRIPT.root()
}
pub fn create<R: FeltRng>(
metadata: ConversionMetadata,
sender_account_id: AccountId,
target_account_id: AccountId,
rng: &mut R,
) -> Result<Note, NoteError> {
let storage_values = metadata.to_elements();
debug_assert_eq!(
storage_values.len(),
Self::NUM_STORAGE_ITEMS,
"CONFIG_AGG_BRIDGE storage must have exactly {} felts",
Self::NUM_STORAGE_ITEMS
);
let note_storage = NoteStorage::new(storage_values)?;
let serial_num = rng.draw_word();
let recipient = NoteRecipient::new(serial_num, Self::script(), note_storage);
let attachment = NetworkAccountTarget::new(target_account_id, NoteExecutionHint::Always)
.map_err(|e| NoteError::other(e.to_string()))?;
let attachments = NoteAttachments::from(NoteAttachment::from(attachment));
let metadata = PartialNoteMetadata::new(sender_account_id, NoteType::Public);
let assets = NoteAssets::new(vec![])?;
Ok(Note::with_attachments(assets, metadata, recipient, attachments))
}
}
impl NoteConsumptionCost for ConfigAggBridgeNote {
fn consumption_cycles() -> u32 {
CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES
}
}
#[cfg(test)]
mod tests {
use miden_protocol::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET;
use super::*;
#[test]
fn to_elements_layout_matches_masm_storage_indices() {
let faucet = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)
.expect("valid faucet account id");
let origin_token_address =
EthAddress::from_hex("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
let metadata_hash = MetadataHash::from_token_info("USD Coin", "USDC", 6);
let metadata = ConversionMetadata {
faucet_account_id: faucet,
origin_token_address,
scale: 6,
origin_network: 42,
is_native: true,
metadata_hash,
};
let elements = metadata.to_elements();
assert_eq!(elements.len(), ConfigAggBridgeNote::NUM_STORAGE_ITEMS);
assert_eq!(&elements[0..5], origin_token_address.to_elements().as_slice());
assert_eq!(elements[5], faucet.suffix());
assert_eq!(elements[6], faucet.prefix().as_felt());
assert_eq!(elements[7], Felt::from(6_u8));
assert_eq!(elements[8], Felt::from(42_u32));
assert_eq!(elements[9], Felt::from(1_u8));
assert_eq!(&elements[10..18], metadata_hash.to_elements().as_slice());
}
}