zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use prost::Message;
use serde::{Deserialize, Serialize};
use zera_proto::zera_txn::{
    AllowanceTxn, BurnSbttxn, CoinTxn, ComplianceTxn, ContractUpdateTxn, DelegatedTxn,
    ExpenseRatioTxn, FastQuorumTxn, GovernanceProposal, GovernanceVote, InstrumentContract,
    ItemizedMintTxn, MintTxn, Nfttxn, QuashTxn, RevokeTxn, SmartContractExecuteTxn,
    SmartContractInstantiateTxn, SmartContractTxn,
};

use crate::error::{Result, ZeraError};
use crate::tx::TypedTransaction;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SerializedTransaction {
    #[serde(rename = "type")]
    pub r#type: String,
    pub data: String,
    pub version: u8,
}

#[derive(Debug, Clone, PartialEq)]
pub enum AnyZeraTransaction {
    CoinTxn(CoinTxn),
    MintTxn(MintTxn),
    ItemizedMintTxn(ItemizedMintTxn),
    InstrumentContract(InstrumentContract),
    GovernanceProposal(GovernanceProposal),
    GovernanceVote(GovernanceVote),
    SmartContractTxn(SmartContractTxn),
    SmartContractExecuteTxn(SmartContractExecuteTxn),
    SmartContractInstantiateTxn(SmartContractInstantiateTxn),
    ExpenseRatioTxn(ExpenseRatioTxn),
    Nfttxn(Nfttxn),
    ContractUpdateTxn(ContractUpdateTxn),
    DelegatedTxn(DelegatedTxn),
    QuashTxn(QuashTxn),
    FastQuorumTxn(FastQuorumTxn),
    RevokeTxn(RevokeTxn),
    ComplianceTxn(ComplianceTxn),
    BurnSbttxn(BurnSbttxn),
    AllowanceTxn(AllowanceTxn),
}

impl AnyZeraTransaction {
    pub fn type_name(&self) -> &'static str {
        match self {
            Self::CoinTxn(_) => CoinTxn::TYPE_NAME,
            Self::MintTxn(_) => MintTxn::TYPE_NAME,
            Self::ItemizedMintTxn(_) => ItemizedMintTxn::TYPE_NAME,
            Self::InstrumentContract(_) => InstrumentContract::TYPE_NAME,
            Self::GovernanceProposal(_) => GovernanceProposal::TYPE_NAME,
            Self::GovernanceVote(_) => GovernanceVote::TYPE_NAME,
            Self::SmartContractTxn(_) => SmartContractTxn::TYPE_NAME,
            Self::SmartContractExecuteTxn(_) => SmartContractExecuteTxn::TYPE_NAME,
            Self::SmartContractInstantiateTxn(_) => SmartContractInstantiateTxn::TYPE_NAME,
            Self::ExpenseRatioTxn(_) => ExpenseRatioTxn::TYPE_NAME,
            Self::Nfttxn(_) => Nfttxn::TYPE_NAME,
            Self::ContractUpdateTxn(_) => ContractUpdateTxn::TYPE_NAME,
            Self::DelegatedTxn(_) => DelegatedTxn::TYPE_NAME,
            Self::QuashTxn(_) => QuashTxn::TYPE_NAME,
            Self::FastQuorumTxn(_) => FastQuorumTxn::TYPE_NAME,
            Self::RevokeTxn(_) => RevokeTxn::TYPE_NAME,
            Self::ComplianceTxn(_) => ComplianceTxn::TYPE_NAME,
            Self::BurnSbttxn(_) => BurnSbttxn::TYPE_NAME,
            Self::AllowanceTxn(_) => AllowanceTxn::TYPE_NAME,
        }
    }
}

pub fn serialize_transaction<T>(txn: &T) -> Result<SerializedTransaction>
where
    T: Message + TypedTransaction,
{
    Ok(SerializedTransaction {
        r#type: T::TYPE_NAME.to_string(),
        data: BASE64.encode(txn.encode_to_vec()),
        version: 1,
    })
}

pub fn deserialize_transaction(envelope: &SerializedTransaction) -> Result<AnyZeraTransaction> {
    if envelope.r#type.is_empty() || envelope.data.is_empty() {
        return Err(ZeraError::Serialization(
            "Invalid serialized transaction: missing type or data".to_string(),
        ));
    }
    if envelope.version != 1 {
        return Err(ZeraError::Serialization(format!(
            "Unsupported serialization version: {}",
            envelope.version
        )));
    }

    let bytes = BASE64.decode(&envelope.data)?;
    match envelope.r#type.as_str() {
        CoinTxn::TYPE_NAME => Ok(AnyZeraTransaction::CoinTxn(CoinTxn::decode(
            bytes.as_slice(),
        )?)),
        MintTxn::TYPE_NAME => Ok(AnyZeraTransaction::MintTxn(MintTxn::decode(
            bytes.as_slice(),
        )?)),
        ItemizedMintTxn::TYPE_NAME => Ok(AnyZeraTransaction::ItemizedMintTxn(
            ItemizedMintTxn::decode(bytes.as_slice())?,
        )),
        InstrumentContract::TYPE_NAME => Ok(AnyZeraTransaction::InstrumentContract(
            InstrumentContract::decode(bytes.as_slice())?,
        )),
        GovernanceProposal::TYPE_NAME => Ok(AnyZeraTransaction::GovernanceProposal(
            GovernanceProposal::decode(bytes.as_slice())?,
        )),
        GovernanceVote::TYPE_NAME => Ok(AnyZeraTransaction::GovernanceVote(
            GovernanceVote::decode(bytes.as_slice())?,
        )),
        SmartContractTxn::TYPE_NAME => Ok(AnyZeraTransaction::SmartContractTxn(
            SmartContractTxn::decode(bytes.as_slice())?,
        )),
        SmartContractExecuteTxn::TYPE_NAME => Ok(AnyZeraTransaction::SmartContractExecuteTxn(
            SmartContractExecuteTxn::decode(bytes.as_slice())?,
        )),
        SmartContractInstantiateTxn::TYPE_NAME => {
            Ok(AnyZeraTransaction::SmartContractInstantiateTxn(
                SmartContractInstantiateTxn::decode(bytes.as_slice())?,
            ))
        }
        ExpenseRatioTxn::TYPE_NAME => Ok(AnyZeraTransaction::ExpenseRatioTxn(
            ExpenseRatioTxn::decode(bytes.as_slice())?,
        )),
        Nfttxn::TYPE_NAME => Ok(AnyZeraTransaction::Nfttxn(Nfttxn::decode(
            bytes.as_slice(),
        )?)),
        ContractUpdateTxn::TYPE_NAME => Ok(AnyZeraTransaction::ContractUpdateTxn(
            ContractUpdateTxn::decode(bytes.as_slice())?,
        )),
        DelegatedTxn::TYPE_NAME => Ok(AnyZeraTransaction::DelegatedTxn(DelegatedTxn::decode(
            bytes.as_slice(),
        )?)),
        QuashTxn::TYPE_NAME => Ok(AnyZeraTransaction::QuashTxn(QuashTxn::decode(
            bytes.as_slice(),
        )?)),
        FastQuorumTxn::TYPE_NAME => Ok(AnyZeraTransaction::FastQuorumTxn(FastQuorumTxn::decode(
            bytes.as_slice(),
        )?)),
        RevokeTxn::TYPE_NAME => Ok(AnyZeraTransaction::RevokeTxn(RevokeTxn::decode(
            bytes.as_slice(),
        )?)),
        ComplianceTxn::TYPE_NAME => Ok(AnyZeraTransaction::ComplianceTxn(ComplianceTxn::decode(
            bytes.as_slice(),
        )?)),
        BurnSbttxn::TYPE_NAME => Ok(AnyZeraTransaction::BurnSbttxn(BurnSbttxn::decode(
            bytes.as_slice(),
        )?)),
        AllowanceTxn::TYPE_NAME => Ok(AnyZeraTransaction::AllowanceTxn(AllowanceTxn::decode(
            bytes.as_slice(),
        )?)),
        other => Err(ZeraError::Serialization(format!(
            "Unknown protobuf type \"{other}\""
        ))),
    }
}

pub fn deserialize_transaction_json(json: &str) -> Result<AnyZeraTransaction> {
    let envelope: SerializedTransaction = serde_json::from_str(json)?;
    deserialize_transaction(&envelope)
}

pub fn get_registered_types() -> Vec<&'static str> {
    vec![
        CoinTxn::TYPE_NAME,
        MintTxn::TYPE_NAME,
        ItemizedMintTxn::TYPE_NAME,
        InstrumentContract::TYPE_NAME,
        GovernanceProposal::TYPE_NAME,
        GovernanceVote::TYPE_NAME,
        SmartContractTxn::TYPE_NAME,
        SmartContractExecuteTxn::TYPE_NAME,
        SmartContractInstantiateTxn::TYPE_NAME,
        ExpenseRatioTxn::TYPE_NAME,
        Nfttxn::TYPE_NAME,
        ContractUpdateTxn::TYPE_NAME,
        DelegatedTxn::TYPE_NAME,
        QuashTxn::TYPE_NAME,
        FastQuorumTxn::TYPE_NAME,
        RevokeTxn::TYPE_NAME,
        ComplianceTxn::TYPE_NAME,
        BurnSbttxn::TYPE_NAME,
        AllowanceTxn::TYPE_NAME,
    ]
}