zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
use zera_proto::zera_api::{TokenFeeInfo, TokenFeeInfoResponse};

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

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct GetTokenFeeInfoParams {
    pub contract_ids: Vec<String>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TokenInfo {
    pub contract_id: String,
    pub denomination: String,
    pub rate: String,
    pub authorized: bool,
    pub allowed_fees: String,
    pub used_fees: String,
    pub contract_fees: Option<zera_proto::zera_txn::ContractFees>,
}

pub async fn get_token_fee_info(
    params: GetTokenFeeInfoParams,
    config: RpcConfig,
) -> Result<TokenFeeInfoResponse> {
    let client = ValidatorApiClient::new(config)?;
    get_token_fee_info_with_client(params, &client).await
}

pub async fn get_token_fee_info_with_client<T>(
    params: GetTokenFeeInfoParams,
    client: &ValidatorApiClient<T>,
) -> Result<TokenFeeInfoResponse>
where
    T: UnaryTransport,
{
    let mut response = client
        .get_token_fee_info(&params.contract_ids)
        .await
        .map_err(|error| {
            ZeraError::Rpc(format!(
                "Failed to get token fee info from validator: {error}"
            ))
        })?;

    for token in &mut response.tokens {
        normalize_token(token);
    }

    Ok(response)
}

pub async fn get_token_info_for_single(contract_id: &str, config: RpcConfig) -> Result<TokenInfo> {
    let client = ValidatorApiClient::new(config)?;
    get_token_info_for_single_with_client(contract_id, &client).await
}

pub async fn get_token_info_for_single_with_client<T>(
    contract_id: &str,
    client: &ValidatorApiClient<T>,
) -> Result<TokenInfo>
where
    T: UnaryTransport,
{
    if contract_id.trim().is_empty() {
        return Err(ZeraError::InvalidInput(
            "Contract ID is required".to_string(),
        ));
    }

    let normalized_contract_id = normalize_contract_id(contract_id);

    let response = get_token_fee_info_with_client(
        GetTokenFeeInfoParams {
            contract_ids: vec![normalized_contract_id.clone()],
        },
        client,
    )
    .await?;

    let token = response
        .tokens
        .into_iter()
        .find(|token| {
            token.contract_id == normalized_contract_id
                || normalize_contract_id(&token.contract_id) == normalized_contract_id
        })
        .ok_or_else(|| {
            ZeraError::Rpc(format!(
                "Token information not found for contract ID: {normalized_contract_id}"
            ))
        })?;

    Ok(TokenInfo {
        contract_id: token.contract_id,
        denomination: token.denomination,
        rate: token.rate,
        authorized: token.authorized,
        allowed_fees: token.allowed_fees,
        used_fees: token.used_fees,
        contract_fees: token.contract_fees,
    })
}

fn normalize_token(token: &mut TokenFeeInfo) {
    if token.rate.is_empty() {
        token.rate = "0".to_string();
    }
    if token.denomination.is_empty() {
        token.denomination = "1".to_string();
    }
    if token.allowed_fees.is_empty() {
        token.allowed_fees = "0".to_string();
    }
    if token.used_fees.is_empty() {
        token.used_fees = "0".to_string();
    }

    if let Some(contract_fees) = token.contract_fees.as_mut() {
        if contract_fees.fee.is_empty() {
            contract_fees.fee = "0".to_string();
        }
        if contract_fees.burn.is_empty() {
            contract_fees.burn = "0".to_string();
        }
        if contract_fees.validator.is_empty() {
            contract_fees.validator = "0".to_string();
        }
    }
}