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,
]
}