Skip to main content

hyperstack_idl/
discriminator.rs

1//! Discriminator utilities
2
3use sha2::{Digest, Sha256};
4
5pub fn anchor_discriminator(preimage: &str) -> Vec<u8> {
6    let hash = Sha256::digest(preimage.as_bytes());
7    hash[..8].to_vec()
8}
9
10/// Compute an Anchor-compatible discriminator for a given namespace and name.
11///
12/// The discriminator is the first 8 bytes of SHA256("namespace:name").
13/// This is used to uniquely identify instructions and accounts in Anchor programs.
14///
15/// # Arguments
16/// * `namespace` - The namespace (e.g., "global" for instructions, "account" for accounts)
17/// * `name` - The name in snake_case for instructions or PascalCase for accounts
18///
19/// # Returns
20/// An 8-byte array containing the discriminator
21pub fn compute_discriminator(namespace: &str, name: &str) -> [u8; 8] {
22    let preimage = format!("{}:{}", namespace, name);
23    let bytes = anchor_discriminator(&preimage);
24    let mut result = [0u8; 8];
25    result.copy_from_slice(&bytes);
26    result
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32    
33    #[test]
34    fn test_discriminator_global_initialize() {
35        // Known Anchor discriminator for "global:initialize"
36        let disc = compute_discriminator("global", "initialize");
37        // Verify it's 8 bytes and non-zero
38        assert_eq!(disc.len(), 8);
39        assert!(disc.iter().any(|&b| b != 0));
40    }
41    
42    #[test]
43    fn test_discriminator_consistency() {
44        // Same inputs always produce same output
45        let disc1 = compute_discriminator("global", "deposit");
46        let disc2 = compute_discriminator("global", "deposit");
47        assert_eq!(disc1, disc2);
48    }
49    
50    #[test]
51    fn test_discriminator_different_names() {
52        // Different names produce different discriminators
53        let disc1 = compute_discriminator("global", "deposit");
54        let disc2 = compute_discriminator("global", "withdraw");
55        assert_ne!(disc1, disc2);
56    }
57}