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
}