zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
use zera_proto::zera_txn::{Parameters, SmartContractExecuteTxn};

use crate::contract::shared::validate_key_pair;
use crate::crypto::address::generate_address_from_public_key;
use crate::error::{Result, ZeraError};
use crate::fees::{FeeConfigHelper, UniversalFeeCalculator};
use crate::grpc::{submit_transaction, UnaryTransport, ValidatorApiClient};
use crate::sign::sign_with_key;
use crate::tx::{
    build_standard_base_txn, get_address_and_nonce_with_client, BuildStandardBaseTxnParams,
};
use crate::types::RpcConfig;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParameterType {
    Bytes,
    Uint32,
    Uint64,
    String,
}

impl ParameterType {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Bytes => "bytes",
            Self::Uint32 => "uint32",
            Self::Uint64 => "uint64",
            Self::String => "string",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecuteParameter {
    pub parameter_type: String,
    pub value: Vec<u8>,
}

#[derive(Debug, Clone, Default, PartialEq)]
pub struct BuildSmartContractExecuteOptions {
    pub memo: Option<String>,
    pub grpc_config: Option<RpcConfig>,
    pub gas_fee_in_usd: Option<f64>,
    pub overestimate_percent: Option<f64>,
    pub nonce: Option<u64>,
    pub fee_id: Option<String>,
    pub fee_amount_parts: Option<String>,
}

pub type CreateSmartContractExecuteOptions = BuildSmartContractExecuteOptions;

pub async fn build_smart_contract_execute_txn(
    smart_contract_name: &str,
    instance: u32,
    function_name: &str,
    parameters: &[ExecuteParameter],
    public_key_base58_identifier: &str,
    options: BuildSmartContractExecuteOptions,
) -> Result<SmartContractExecuteTxn> {
    let client = ValidatorApiClient::new(options.grpc_config.clone().unwrap_or_default())?;
    build_smart_contract_execute_txn_with_client(
        smart_contract_name,
        instance,
        function_name,
        parameters,
        public_key_base58_identifier,
        options,
        &client,
    )
    .await
}

pub async fn build_smart_contract_execute_txn_with_client<T>(
    smart_contract_name: &str,
    instance: u32,
    function_name: &str,
    parameters: &[ExecuteParameter],
    public_key_base58_identifier: &str,
    options: BuildSmartContractExecuteOptions,
    client: &ValidatorApiClient<T>,
) -> Result<SmartContractExecuteTxn>
where
    T: UnaryTransport,
{
    if smart_contract_name.is_empty() {
        return Err(ZeraError::Validation(
            "smartContractName is required".to_string(),
        ));
    }
    if function_name.is_empty() {
        return Err(ZeraError::Validation(
            "functionName is required".to_string(),
        ));
    }
    if public_key_base58_identifier.is_empty() {
        return Err(ZeraError::Validation(
            "publicKeyBase58Identifier is required".to_string(),
        ));
    }
    generate_address_from_public_key(public_key_base58_identifier)?;

    let nonce = if let Some(nonce) = options.nonce {
        nonce
    } else {
        get_address_and_nonce_with_client(public_key_base58_identifier, client)
            .await?
            .1
    };

    let base = build_standard_base_txn(BuildStandardBaseTxnParams {
        public_key_id: public_key_base58_identifier.to_string(),
        fee_id: options.fee_id.clone(),
        fee_amount_parts: options.fee_amount_parts.clone(),
        nonce,
        memo: options.memo.clone(),
    })?;

    let mut execute_txn = SmartContractExecuteTxn {
        base: Some(base),
        smart_contract_name: smart_contract_name.to_string(),
        instance,
        function: function_name.to_string(),
        parameters: parameters
            .iter()
            .map(|parameter| Parameters {
                value: parameter.value.clone(),
                r#type: parameter.parameter_type.clone(),
            })
            .collect(),
    };

    execute_txn = UniversalFeeCalculator::calculate_fee_with_client(
        FeeConfigHelper {
            contract_id: None,
            proto_object: execute_txn,
            token_info_map: std::collections::HashMap::new(),
            base_fee_id: options.fee_id.clone(),
            base_fee: options.fee_amount_parts.clone(),
            contract_fee_id: None,
            contract_fee: None,
            interface_fee_id: None,
            interface_fee: None,
            interface_address: None,
            overestimate_percent: options.overestimate_percent,
            gas_fee_in_usd: options.gas_fee_in_usd,
            grpc_config: None,
            needs_initialization: None,
        },
        client,
    )
    .await?;

    Ok(execute_txn)
}

pub async fn create_smart_contract_execute_txn(
    smart_contract_name: &str,
    instance: u32,
    function_name: &str,
    parameters: &[ExecuteParameter],
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: CreateSmartContractExecuteOptions,
) -> Result<SmartContractExecuteTxn> {
    if private_key_base58.is_empty() {
        return Err(ZeraError::Validation(
            "privateKeyBase58 is required".to_string(),
        ));
    }
    validate_key_pair(public_key_base58_identifier, private_key_base58)?;

    let mut execute_txn = build_smart_contract_execute_txn(
        smart_contract_name,
        instance,
        function_name,
        parameters,
        public_key_base58_identifier,
        options,
    )
    .await?;

    sign_with_key(
        &mut execute_txn,
        private_key_base58,
        public_key_base58_identifier,
    )?;
    Ok(execute_txn)
}

pub async fn send_smart_contract_execute_txn(
    txn: &SmartContractExecuteTxn,
    grpc_config: Option<RpcConfig>,
) -> Result<String> {
    submit_transaction(txn, grpc_config.unwrap_or_default()).await
}