miden-objects 0.17.0-rc.3

Canonical Protobuf representations for Miden protocol objects
Documentation
use alloc::format;

use miden_protocol::asset::{Asset, AssetClass, AssetComposition, AssetId};

use super::{MessageDecodeExt, required};
use crate::{ConversionError, ConversionResultExt, proto};

impl From<&AssetClass> for proto::asset::AssetClass {
    fn from(asset_class: &AssetClass) -> Self {
        Self {
            suffix: Some(asset_class.suffix().into()),
            prefix: Some(asset_class.prefix().into()),
        }
    }
}

impl From<AssetClass> for proto::asset::AssetClass {
    fn from(asset_class: AssetClass) -> Self {
        Self::from(&asset_class)
    }
}

impl TryFrom<proto::asset::AssetClass> for AssetClass {
    type Error = ConversionError;

    fn try_from(message: proto::asset::AssetClass) -> Result<Self, Self::Error> {
        let decoder = message.decoder();
        let suffix = required!(decoder, message.suffix)?;
        let prefix = required!(decoder, message.prefix)?;

        Ok(Self::new(suffix, prefix))
    }
}

fn decode_asset_composition(composition: i32) -> Result<AssetComposition, ConversionError> {
    match proto::asset::AssetComposition::try_from(composition) {
        Ok(proto::asset::AssetComposition::None) => Ok(AssetComposition::None),
        Ok(proto::asset::AssetComposition::Fungible) => Ok(AssetComposition::Fungible),
        Ok(proto::asset::AssetComposition::Custom) => Ok(AssetComposition::Custom),
        Ok(proto::asset::AssetComposition::Unspecified) => {
            Err(ConversionError::message("asset composition is unspecified"))
        },
        Err(error) => Err(ConversionError::with_source(
            format!("unknown asset composition {composition}"),
            error,
        )),
    }
}

fn decode_asset_version(version: i32) -> Result<(), ConversionError> {
    match proto::asset::AssetVersion::try_from(version) {
        Ok(proto::asset::AssetVersion::V1) => Ok(()),
        Ok(proto::asset::AssetVersion::Unspecified) => {
            Err(ConversionError::message("asset id version is unspecified"))
        },
        Err(error) => Err(ConversionError::with_source(
            format!("unknown asset id version {version}"),
            error,
        )),
    }
}

fn encode_asset_composition(composition: AssetComposition) -> i32 {
    match composition {
        AssetComposition::None => proto::asset::AssetComposition::None as i32,
        AssetComposition::Fungible => proto::asset::AssetComposition::Fungible as i32,
        AssetComposition::Custom => proto::asset::AssetComposition::Custom as i32,
    }
}

impl From<&AssetId> for proto::asset::AssetId {
    fn from(asset_id: &AssetId) -> Self {
        Self {
            version: proto::asset::AssetVersion::V1 as i32,
            asset_class: Some(asset_id.asset_class().into()),
            composition: encode_asset_composition(asset_id.composition()),
            faucet_id: Some(asset_id.faucet_id().into()),
        }
    }
}

impl From<AssetId> for proto::asset::AssetId {
    fn from(asset_id: AssetId) -> Self {
        Self::from(&asset_id)
    }
}

impl TryFrom<proto::asset::AssetId> for AssetId {
    type Error = ConversionError;

    fn try_from(message: proto::asset::AssetId) -> Result<Self, Self::Error> {
        decode_asset_version(message.version).context("version")?;

        let decoder = message.decoder();
        let asset_class = required!(decoder, message.asset_class)?;
        let composition = decode_asset_composition(message.composition).context("composition")?;
        let faucet_id = required!(decoder, message.faucet_id)?;

        Self::new(asset_class, faucet_id, composition).map_err(ConversionError::new)
    }
}

impl From<&Asset> for proto::asset::Asset {
    fn from(asset: &Asset) -> Self {
        Self {
            asset_id: Some(asset.id().into()),
            value: Some(asset.to_value_word().into()),
        }
    }
}

impl From<Asset> for proto::asset::Asset {
    fn from(asset: Asset) -> Self {
        Self::from(&asset)
    }
}

impl TryFrom<proto::asset::Asset> for Asset {
    type Error = ConversionError;

    fn try_from(message: proto::asset::Asset) -> Result<Self, Self::Error> {
        let decoder = message.decoder();
        let asset_id = required!(decoder, message.asset_id)?;
        let value = required!(decoder, message.value)?;

        Self::new(asset_id, value).map_err(ConversionError::new)
    }
}