zera-sdk 0.1.0

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

use crate::api::get_token_info_for_single_with_client;
use crate::error::{Result, ZeraError};
use crate::grpc::{UnaryTransport, ValidatorApiClient};
use crate::smart_contracts::send_smart_contract_execute_txn;
use crate::smart_contracts::use_cases::bridge::zera::types::{BridgeZeraOptions, BurnSolOptions};
use crate::smart_contracts::use_cases::bridge::zera::utils::create_bridge_transaction_with_client;
use crate::utils::amount::to_smallest_units;

pub async fn lock_zera(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BridgeZeraOptions,
) -> Result<SmartContractExecuteTxn> {
    let client = ValidatorApiClient::new(options.grpc_config.clone().unwrap_or_default())?;
    lock_zera_with_client(
        contract_id,
        amount,
        to_solana_address,
        public_key_base58_identifier,
        private_key_base58,
        options,
        &client,
    )
    .await
}

pub async fn lock_zera_with_client<T>(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BridgeZeraOptions,
    client: &ValidatorApiClient<T>,
) -> Result<SmartContractExecuteTxn>
where
    T: UnaryTransport,
{
    if contract_id.is_empty() {
        return Err(ZeraError::Validation("contractId is required".to_string()));
    }
    if amount.is_empty() {
        return Err(ZeraError::Validation("amount is required".to_string()));
    }
    if to_solana_address.is_empty() {
        return Err(ZeraError::Validation(
            "toSolanaAddress is required".to_string(),
        ));
    }
    if public_key_base58_identifier.is_empty() {
        return Err(ZeraError::Validation(
            "publicKeyBase58Identifier is required".to_string(),
        ));
    }
    if private_key_base58.is_empty() {
        return Err(ZeraError::Validation(
            "privateKeyBase58 is required".to_string(),
        ));
    }

    let token_info = get_token_info_for_single_with_client(contract_id, client).await?;
    let amount_in_parts =
        to_smallest_units(amount, contract_id, Some(&token_info.denomination), None)?;

    let parameter_value = format!("{contract_id},{amount_in_parts},{to_solana_address}");
    let fee_id = options
        .fee_id
        .clone()
        .unwrap_or_else(|| contract_id.to_string());

    create_bridge_transaction_with_client(
        "lock_zera",
        &parameter_value,
        public_key_base58_identifier,
        private_key_base58,
        &fee_id,
        options,
        client,
    )
    .await
}

pub async fn lock_zera_and_send(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BridgeZeraOptions,
) -> Result<String> {
    let grpc_config = options.grpc_config.clone();
    let transaction = lock_zera(
        contract_id,
        amount,
        to_solana_address,
        public_key_base58_identifier,
        private_key_base58,
        options,
    )
    .await?;
    send_smart_contract_execute_txn(&transaction, grpc_config).await
}

pub use lock_zera as bridge_zera_to_sol;
pub use lock_zera_and_send as bridge_zera_to_sol_and_send;

pub async fn burn_sol(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BurnSolOptions,
) -> Result<SmartContractExecuteTxn> {
    let bridge_options: BridgeZeraOptions = options.clone().into();
    let client = ValidatorApiClient::new(bridge_options.grpc_config.clone().unwrap_or_default())?;
    burn_sol_with_client(
        contract_id,
        amount,
        to_solana_address,
        public_key_base58_identifier,
        private_key_base58,
        options,
        &client,
    )
    .await
}

pub async fn burn_sol_with_client<T>(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BurnSolOptions,
    client: &ValidatorApiClient<T>,
) -> Result<SmartContractExecuteTxn>
where
    T: UnaryTransport,
{
    if contract_id.is_empty() {
        return Err(ZeraError::Validation("contractId is required".to_string()));
    }
    if amount.is_empty() {
        return Err(ZeraError::Validation("amount is required".to_string()));
    }
    if to_solana_address.is_empty() {
        return Err(ZeraError::Validation(
            "toSolanaAddress is required".to_string(),
        ));
    }
    if public_key_base58_identifier.is_empty() {
        return Err(ZeraError::Validation(
            "publicKeyBase58Identifier is required".to_string(),
        ));
    }
    if private_key_base58.is_empty() {
        return Err(ZeraError::Validation(
            "privateKeyBase58 is required".to_string(),
        ));
    }

    let amount_in_parts = if let Some(denomination) = options.denomination.as_deref() {
        to_smallest_units(amount, contract_id, Some(denomination), None)?
    } else {
        let token_info = get_token_info_for_single_with_client(contract_id, client).await?;
        to_smallest_units(amount, contract_id, Some(&token_info.denomination), None)?
    };

    let parameter_value = format!("{contract_id},{amount_in_parts},{to_solana_address}");
    let fee_id = options
        .fee_id
        .clone()
        .unwrap_or_else(|| contract_id.to_string());

    create_bridge_transaction_with_client(
        "burn_sol",
        &parameter_value,
        public_key_base58_identifier,
        private_key_base58,
        &fee_id,
        options.into(),
        client,
    )
    .await
}

pub async fn burn_sol_and_send(
    contract_id: &str,
    amount: &str,
    to_solana_address: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    options: BurnSolOptions,
) -> Result<String> {
    let grpc_config = options.grpc_config.clone();
    let transaction = burn_sol(
        contract_id,
        amount,
        to_solana_address,
        public_key_base58_identifier,
        private_key_base58,
        options,
    )
    .await?;

    send_smart_contract_execute_txn(&transaction, grpc_config).await
}