zera-sdk 0.1.0

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

use crate::contract::shared::validate_key_pair;
use crate::error::{Result, ZeraError};
use crate::grpc::{UnaryTransport, ValidatorApiClient};
use crate::sign::sign_with_key;
use crate::smart_contracts::execute::{
    build_smart_contract_execute_txn, build_smart_contract_execute_txn_with_client,
    BuildSmartContractExecuteOptions, ExecuteParameter, ParameterType,
};

use super::types::BridgeZeraOptions;

pub const BRIDGE_CONTRACT_NAME: &str = "zera_bridge_proxy";
pub const BRIDGE_INSTANCE: u32 = 1;

pub fn format_guardian_signatures(payload: &ZeraPayload) -> String {
    let sig_pairs = payload
        .signatures
        .iter()
        .enumerate()
        .map(|(index, signature)| {
            let public_key = payload.public_keys.get(index).map_or("", String::as_str);
            format!("{signature},{public_key}")
        })
        .collect::<Vec<_>>()
        .join("|");

    format!("{}|{}", payload.signed_hash, sig_pairs)
}

pub async fn create_bridge_transaction(
    function_name: &str,
    parameter_value: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    fee_id: &str,
    options: BridgeZeraOptions,
) -> 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 transaction = build_smart_contract_execute_txn(
        BRIDGE_CONTRACT_NAME,
        BRIDGE_INSTANCE,
        "execute",
        &bridge_parameters(function_name, parameter_value),
        public_key_base58_identifier,
        BuildSmartContractExecuteOptions {
            memo: options.memo,
            grpc_config: options.grpc_config,
            gas_fee_in_usd: options.gas_fee_in_usd,
            overestimate_percent: options.overestimate_percent,
            nonce: options.nonce,
            fee_id: Some(fee_id.to_string()),
            fee_amount_parts: options.fee_amount_usd,
        },
    )
    .await?;

    sign_with_key(
        &mut transaction,
        private_key_base58,
        public_key_base58_identifier,
    )?;

    Ok(transaction)
}

pub async fn create_bridge_transaction_with_client<T>(
    function_name: &str,
    parameter_value: &str,
    public_key_base58_identifier: &str,
    private_key_base58: &str,
    fee_id: &str,
    options: BridgeZeraOptions,
    client: &ValidatorApiClient<T>,
) -> Result<SmartContractExecuteTxn>
where
    T: UnaryTransport,
{
    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 transaction = build_smart_contract_execute_txn_with_client(
        BRIDGE_CONTRACT_NAME,
        BRIDGE_INSTANCE,
        "execute",
        &bridge_parameters(function_name, parameter_value),
        public_key_base58_identifier,
        BuildSmartContractExecuteOptions {
            memo: options.memo,
            grpc_config: None,
            gas_fee_in_usd: options.gas_fee_in_usd,
            overestimate_percent: options.overestimate_percent,
            nonce: options.nonce,
            fee_id: Some(fee_id.to_string()),
            fee_amount_parts: options.fee_amount_usd,
        },
        client,
    )
    .await?;

    sign_with_key(
        &mut transaction,
        private_key_base58,
        public_key_base58_identifier,
    )?;

    Ok(transaction)
}

fn bridge_parameters(function_name: &str, parameter_value: &str) -> [ExecuteParameter; 2] {
    [
        ExecuteParameter {
            parameter_type: ParameterType::String.as_str().to_string(),
            value: function_name.as_bytes().to_vec(),
        },
        ExecuteParameter {
            parameter_type: ParameterType::String.as_str().to_string(),
            value: parameter_value.as_bytes().to_vec(),
        },
    ]
}