zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
use std::str::FromStr;

use bigdecimal::BigDecimal;
use zera_proto::zera_api::BaseFeeResponse;
use zera_proto::zera_txn::{PublicKey, TransactionType};

use crate::error::{Result, ZeraError};
use crate::grpc::{UnaryTransport, ValidatorApiClient};
use crate::types::RpcConfig;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnhancedBaseFeeResponse {
    pub key_fee: String,
    pub byte_fee: String,
    pub new_wallet_fee: String,
    pub key_fee_usd: String,
    pub byte_fee_usd: String,
}

pub async fn get_base_fee(
    txn_type: TransactionType,
    public_key: Option<PublicKey>,
    config: RpcConfig,
) -> Result<EnhancedBaseFeeResponse> {
    let client = ValidatorApiClient::new(config)?;
    get_base_fee_with_client(txn_type, public_key, &client).await
}

pub async fn get_base_fee_with_client<T>(
    txn_type: TransactionType,
    public_key: Option<PublicKey>,
    client: &ValidatorApiClient<T>,
) -> Result<EnhancedBaseFeeResponse>
where
    T: UnaryTransport,
{
    let response = client
        .get_base_fee(public_key, txn_type)
        .await
        .map_err(|error| {
            ZeraError::Rpc(format!("Failed to get base fee from validator: {error}"))
        })?;
    enhance_base_fee_response(response)
}

pub fn enhance_base_fee_response(response: BaseFeeResponse) -> Result<EnhancedBaseFeeResponse> {
    let key_fee = if response.key_fee.is_empty() {
        "0".to_string()
    } else {
        response.key_fee.clone()
    };
    let byte_fee = if response.byte_fee.is_empty() {
        "0".to_string()
    } else {
        response.byte_fee.clone()
    };
    let new_wallet_fee = if response.new_wallet_fee.is_empty() {
        "0".to_string()
    } else {
        response.new_wallet_fee.clone()
    };

    Ok(EnhancedBaseFeeResponse {
        key_fee_usd: decimal_ratio(&key_fee, "1000000000000000000")?,
        byte_fee_usd: decimal_ratio(&byte_fee, "1000000000000000000")?,
        key_fee,
        byte_fee,
        new_wallet_fee,
    })
}

fn decimal_ratio(numerator: &str, denominator: &str) -> Result<String> {
    let numerator = BigDecimal::from_str(numerator).map_err(|error| {
        ZeraError::Serialization(format!("Invalid decimal \"{numerator}\": {error}"))
    })?;
    let denominator = BigDecimal::from_str(denominator).map_err(|error| {
        ZeraError::Serialization(format!("Invalid decimal \"{denominator}\": {error}"))
    })?;

    Ok((numerator / denominator).normalized().to_string())
}