zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
use sha2::{Digest, Sha256};
use solana_sdk::pubkey::Pubkey;

use crate::error::{Result, ZeraError};

use super::constants::{
    ATA_PROGRAM_ID, CORE_PROGRAM_ID, METADATA_PROGRAM_ID, TOKEN_BRIDGE_PROGRAM_ID, TOKEN_PROGRAM_ID,
};

pub fn generate_discriminator(name: &str) -> [u8; 8] {
    let digest = Sha256::digest(name.as_bytes());
    let mut discriminator = [0_u8; 8];
    discriminator.copy_from_slice(&digest[..8]);
    discriminator
}

pub fn hex_to_bytes(value: &str) -> Result<Vec<u8>> {
    let normalized = value
        .strip_prefix("0x")
        .or_else(|| value.strip_prefix("0X"))
        .unwrap_or(value);

    hex::decode(normalized)
        .map_err(|error| ZeraError::InvalidInput(format!("invalid hex value: {error}")))
}

pub fn bytes_to_hex(bytes: &[u8]) -> String {
    hex::encode(bytes)
}

pub fn encode_u64_le(value: u64) -> [u8; 8] {
    value.to_le_bytes()
}

pub fn encode_u64_be(value: u64) -> [u8; 8] {
    value.to_be_bytes()
}

pub fn encode_u32_le(value: u32) -> [u8; 4] {
    value.to_le_bytes()
}

pub fn encode_u16_le(value: u16) -> [u8; 2] {
    value.to_le_bytes()
}

pub fn encode_u16_be(value: u16) -> [u8; 2] {
    value.to_be_bytes()
}

pub fn encode_borsh_string(value: &str) -> Vec<u8> {
    let value_bytes = value.as_bytes();
    let length = encode_u32_le(value_bytes.len() as u32);
    concat_bytes(&[length.as_ref(), value_bytes])
}

pub fn encode_borsh_option<T, F>(value: Option<T>, encoder: F) -> Vec<u8>
where
    F: FnOnce(T) -> Vec<u8>,
{
    match value {
        Some(value) => {
            let encoded = encoder(value);
            concat_bytes(&[&[1], encoded.as_slice()])
        }
        None => vec![0],
    }
}

pub fn concat_bytes(parts: &[&[u8]]) -> Vec<u8> {
    let total_len = parts.iter().map(|part| part.len()).sum();
    let mut bytes = Vec::with_capacity(total_len);
    for part in parts {
        bytes.extend_from_slice(part);
    }
    bytes
}

pub fn hash_contract_id(contract_id: &str) -> [u8; 32] {
    let digest = Sha256::digest(contract_id.as_bytes());
    let mut hash = [0_u8; 32];
    hash.copy_from_slice(&digest);
    hash
}

pub fn derive_router_signer_pda() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"router_signer"], &TOKEN_BRIDGE_PROGRAM_ID)
}

pub fn derive_router_config_pda() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"router_cfg"], &CORE_PROGRAM_ID)
}

pub fn derive_verified_transfer_pda(expected_hash: &[u8]) -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"verified_transfer", expected_hash], &CORE_PROGRAM_ID)
}

pub fn derive_released_transfer_pda(expected_hash: &[u8]) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"released_transfer", expected_hash],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_vault_pda() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"vault"], &TOKEN_BRIDGE_PROGRAM_ID)
}

pub fn derive_rate_limit_state_pda() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"rate_limit_state"], &TOKEN_BRIDGE_PROGRAM_ID)
}

pub fn derive_token_registration_pda(mint: &Pubkey) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"token_registration", mint.as_ref()],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_wrapped_mint_pda(contract_id: &str) -> (Pubkey, u8) {
    let contract_hash = hash_contract_id(contract_id);
    Pubkey::find_program_address(&[b"mint", &contract_hash], &TOKEN_BRIDGE_PROGRAM_ID)
}

pub fn derive_wrapped_mint_authority_pda(wrapped_mint: &Pubkey) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"mint_authority", wrapped_mint.as_ref()],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_locked_transfer_pda(mint: &Pubkey, sender: &Pubkey, nonce: u64) -> (Pubkey, u8) {
    let nonce_bytes = encode_u64_le(nonce);
    Pubkey::find_program_address(
        &[
            b"locked_transfer",
            mint.as_ref(),
            sender.as_ref(),
            nonce_bytes.as_ref(),
        ],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_metadata_pda(mint: &Pubkey) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"metadata", METADATA_PROGRAM_ID.as_ref(), mint.as_ref()],
        &METADATA_PROGRAM_ID,
    )
}

pub fn derive_bridge_info_pda(wrapped_mint: &Pubkey) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"bridge_info", wrapped_mint.as_ref()],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_pending_registration_pda(mint: &Pubkey) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[b"pending_registration", mint.as_ref()],
        &TOKEN_BRIDGE_PROGRAM_ID,
    )
}

pub fn derive_token_price_registry_pda() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"token_price_registry"], &TOKEN_BRIDGE_PROGRAM_ID)
}

pub fn get_ata(owner: &Pubkey, mint: &Pubkey) -> Pubkey {
    Pubkey::find_program_address(
        &[owner.as_ref(), TOKEN_PROGRAM_ID.as_ref(), mint.as_ref()],
        &ATA_PROGRAM_ID,
    )
    .0
}