light_sdk/
address.rs

1use anchor_lang::{solana_program::pubkey::Pubkey, AnchorDeserialize, AnchorSerialize};
2use light_utils::{hash_to_bn254_field_size_be, hashv_to_bn254_field_size_be};
3
4use crate::merkle_context::{AddressMerkleContext, RemainingAccounts};
5
6#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
7pub struct NewAddressParams {
8    pub seed: [u8; 32],
9    pub address_queue_pubkey: Pubkey,
10    pub address_merkle_tree_pubkey: Pubkey,
11    pub address_merkle_tree_root_index: u16,
12}
13
14#[derive(Debug, PartialEq, Default, Clone, Copy, AnchorDeserialize, AnchorSerialize)]
15pub struct NewAddressParamsPacked {
16    pub seed: [u8; 32],
17    pub address_queue_account_index: u8,
18    pub address_merkle_tree_account_index: u8,
19    pub address_merkle_tree_root_index: u16,
20}
21
22pub struct AddressWithMerkleContext {
23    pub address: [u8; 32],
24    pub address_merkle_context: AddressMerkleContext,
25}
26
27pub fn pack_new_addresses_params(
28    addresses_params: &[NewAddressParams],
29    remaining_accounts: &mut RemainingAccounts,
30) -> Vec<NewAddressParamsPacked> {
31    addresses_params
32        .iter()
33        .map(|x| {
34            let address_queue_account_index =
35                remaining_accounts.insert_or_get(x.address_queue_pubkey);
36            let address_merkle_tree_account_index =
37                remaining_accounts.insert_or_get(x.address_merkle_tree_pubkey);
38            NewAddressParamsPacked {
39                seed: x.seed,
40                address_queue_account_index,
41                address_merkle_tree_account_index,
42                address_merkle_tree_root_index: x.address_merkle_tree_root_index,
43            }
44        })
45        .collect::<Vec<_>>()
46}
47
48pub fn pack_new_address_params(
49    address_params: NewAddressParams,
50    remaining_accounts: &mut RemainingAccounts,
51) -> NewAddressParamsPacked {
52    pack_new_addresses_params(&[address_params], remaining_accounts)[0]
53}
54
55/// Derives a single address seed for a compressed account, based on the
56/// provided multiple `seeds`, `program_id` and `merkle_tree_pubkey`.
57///
58/// # Examples
59///
60/// ```ignore
61/// use light_sdk::{address::derive_address, pubkey};
62///
63/// let address = derive_address(
64///     &[b"my_compressed_account"],
65///     &crate::ID,
66/// );
67/// ```
68pub fn derive_address_seed(seeds: &[&[u8]], program_id: &Pubkey) -> [u8; 32] {
69    let mut inputs = Vec::with_capacity(seeds.len() + 1);
70
71    let program_id = program_id.to_bytes();
72    inputs.push(program_id.as_slice());
73
74    inputs.extend(seeds);
75
76    let address = hashv_to_bn254_field_size_be(inputs.as_slice());
77    address
78}
79
80/// Derives an address for a compressed account, based on the provided singular
81/// `seed` and `address_merkle_context`:
82pub fn derive_address(
83    address_seed: &[u8; 32],
84    address_merkle_context: &AddressMerkleContext,
85) -> [u8; 32] {
86    let merkle_tree_pubkey = address_merkle_context.address_merkle_tree_pubkey.to_bytes();
87    let input = [merkle_tree_pubkey, *address_seed].concat();
88
89    // PANICS: Not being able to find the bump for truncating the hash is
90    // practically impossible. Quite frankly, we should just remove that error
91    // inside.
92    hash_to_bn254_field_size_be(input.as_slice()).unwrap().0
93}
94
95#[cfg(test)]
96mod test {
97    use light_macros::pubkey;
98
99    use super::*;
100
101    #[test]
102    fn test_derive_address_seed() {
103        let program_id = pubkey!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
104
105        let address_seed = derive_address_seed(&[b"foo", b"bar"], &program_id);
106        assert_eq!(
107            address_seed,
108            [
109                0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155,
110                251, 72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110
111            ]
112        );
113
114        let address_seed = derive_address_seed(&[b"ayy", b"lmao"], &program_id);
115        assert_eq!(
116            address_seed,
117            [
118                0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120,
119                214, 152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233
120            ]
121        );
122    }
123
124    #[test]
125    fn test_derive_address() {
126        let address_merkle_context = AddressMerkleContext {
127            address_merkle_tree_pubkey: pubkey!("11111111111111111111111111111111"),
128            address_queue_pubkey: pubkey!("22222222222222222222222222222222222222222222"),
129        };
130        let program_id = pubkey!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
131
132        let address_seed = derive_address_seed(&[b"foo", b"bar"], &program_id);
133        let address = derive_address(&address_seed, &address_merkle_context);
134        let expected_address = pubkey!("139uhyyBtEh4e1CBDJ68ooK5nCeWoncZf9HPyAfRrukA");
135        assert_eq!(address, expected_address.to_bytes());
136
137        let address_seed = derive_address_seed(&[b"ayy", b"lmao"], &program_id);
138        let address = derive_address(&address_seed, &address_merkle_context);
139        let expected_address = pubkey!("12bhHm6PQjbNmEn3Yu1Gq9k7XwVn2rZpzYokmLwbFazN");
140        assert_eq!(address, expected_address.to_bytes());
141    }
142}