Skip to main content

lightcone_sdk/program/
pda.rs

1//! PDA (Program Derived Address) derivation functions.
2//!
3//! This module provides all PDA derivation functions matching the on-chain program.
4
5use solana_sdk::pubkey::Pubkey;
6
7use crate::program::constants::{
8    CONDITIONAL_MINT_SEED, EXCHANGE_SEED, MARKET_SEED, MINT_AUTHORITY_SEED, ORDER_STATUS_SEED,
9    POSITION_SEED, USER_NONCE_SEED, VAULT_SEED,
10};
11
12/// Get the Exchange PDA.
13///
14/// Seeds: ["central_state"]
15pub fn get_exchange_pda(program_id: &Pubkey) -> (Pubkey, u8) {
16    Pubkey::find_program_address(&[EXCHANGE_SEED], program_id)
17}
18
19/// Get a Market PDA.
20///
21/// Seeds: ["market", market_id (8 bytes LE)]
22pub fn get_market_pda(market_id: u64, program_id: &Pubkey) -> (Pubkey, u8) {
23    Pubkey::find_program_address(&[MARKET_SEED, &market_id.to_le_bytes()], program_id)
24}
25
26/// Get the Vault PDA for a market's deposit mint.
27///
28/// Seeds: ["market_deposit_token_account", deposit_mint, market]
29pub fn get_vault_pda(deposit_mint: &Pubkey, market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
30    Pubkey::find_program_address(
31        &[VAULT_SEED, deposit_mint.as_ref(), market.as_ref()],
32        program_id,
33    )
34}
35
36/// Get the Mint Authority PDA for a market.
37///
38/// Seeds: ["market_mint_authority", market]
39pub fn get_mint_authority_pda(market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
40    Pubkey::find_program_address(&[MINT_AUTHORITY_SEED, market.as_ref()], program_id)
41}
42
43/// Get a Conditional Mint PDA.
44///
45/// Seeds: ["conditional_mint", market, deposit_mint, outcome_index (1 byte)]
46pub fn get_conditional_mint_pda(
47    market: &Pubkey,
48    deposit_mint: &Pubkey,
49    outcome_index: u8,
50    program_id: &Pubkey,
51) -> (Pubkey, u8) {
52    Pubkey::find_program_address(
53        &[
54            CONDITIONAL_MINT_SEED,
55            market.as_ref(),
56            deposit_mint.as_ref(),
57            &[outcome_index],
58        ],
59        program_id,
60    )
61}
62
63/// Get all Conditional Mint PDAs for a market.
64pub fn get_all_conditional_mint_pdas(
65    market: &Pubkey,
66    deposit_mint: &Pubkey,
67    num_outcomes: u8,
68    program_id: &Pubkey,
69) -> Vec<(Pubkey, u8)> {
70    (0..num_outcomes)
71        .map(|i| get_conditional_mint_pda(market, deposit_mint, i, program_id))
72        .collect()
73}
74
75/// Get an Order Status PDA.
76///
77/// Seeds: ["order_status", order_hash (32 bytes)]
78pub fn get_order_status_pda(order_hash: &[u8; 32], program_id: &Pubkey) -> (Pubkey, u8) {
79    Pubkey::find_program_address(&[ORDER_STATUS_SEED, order_hash], program_id)
80}
81
82/// Get a User Nonce PDA.
83///
84/// Seeds: ["user_nonce", user]
85pub fn get_user_nonce_pda(user: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
86    Pubkey::find_program_address(&[USER_NONCE_SEED, user.as_ref()], program_id)
87}
88
89/// Get a Position PDA.
90///
91/// Seeds: ["position", owner, market]
92pub fn get_position_pda(owner: &Pubkey, market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
93    Pubkey::find_program_address(&[POSITION_SEED, owner.as_ref(), market.as_ref()], program_id)
94}
95
96/// Collection of all PDA derivation functions for convenient access.
97pub struct Pda;
98
99impl Pda {
100    /// Get the Exchange PDA.
101    pub fn exchange(program_id: &Pubkey) -> (Pubkey, u8) {
102        get_exchange_pda(program_id)
103    }
104
105    /// Get a Market PDA.
106    pub fn market(market_id: u64, program_id: &Pubkey) -> (Pubkey, u8) {
107        get_market_pda(market_id, program_id)
108    }
109
110    /// Get the Vault PDA.
111    pub fn vault(deposit_mint: &Pubkey, market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
112        get_vault_pda(deposit_mint, market, program_id)
113    }
114
115    /// Get the Mint Authority PDA.
116    pub fn mint_authority(market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
117        get_mint_authority_pda(market, program_id)
118    }
119
120    /// Get a Conditional Mint PDA.
121    pub fn conditional_mint(
122        market: &Pubkey,
123        deposit_mint: &Pubkey,
124        outcome_index: u8,
125        program_id: &Pubkey,
126    ) -> (Pubkey, u8) {
127        get_conditional_mint_pda(market, deposit_mint, outcome_index, program_id)
128    }
129
130    /// Get all Conditional Mint PDAs for a market.
131    pub fn all_conditional_mints(
132        market: &Pubkey,
133        deposit_mint: &Pubkey,
134        num_outcomes: u8,
135        program_id: &Pubkey,
136    ) -> Vec<(Pubkey, u8)> {
137        get_all_conditional_mint_pdas(market, deposit_mint, num_outcomes, program_id)
138    }
139
140    /// Get an Order Status PDA.
141    pub fn order_status(order_hash: &[u8; 32], program_id: &Pubkey) -> (Pubkey, u8) {
142        get_order_status_pda(order_hash, program_id)
143    }
144
145    /// Get a User Nonce PDA.
146    pub fn user_nonce(user: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
147        get_user_nonce_pda(user, program_id)
148    }
149
150    /// Get a Position PDA.
151    pub fn position(owner: &Pubkey, market: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
152        get_position_pda(owner, market, program_id)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use std::str::FromStr;
160
161    fn test_program_id() -> Pubkey {
162        Pubkey::from_str("EfRvELrn4b5aJRwddD1VUrqzsfm1pewBLPebq3iMPDp2").unwrap()
163    }
164
165    #[test]
166    fn test_exchange_pda_is_deterministic() {
167        let program_id = test_program_id();
168        let (pda1, bump1) = get_exchange_pda(&program_id);
169        let (pda2, bump2) = get_exchange_pda(&program_id);
170
171        assert_eq!(pda1, pda2);
172        assert_eq!(bump1, bump2);
173    }
174
175    #[test]
176    fn test_market_pda_is_deterministic() {
177        let program_id = test_program_id();
178        let (pda1, bump1) = get_market_pda(0, &program_id);
179        let (pda2, bump2) = get_market_pda(0, &program_id);
180
181        assert_eq!(pda1, pda2);
182        assert_eq!(bump1, bump2);
183    }
184
185    #[test]
186    fn test_different_market_ids_produce_different_pdas() {
187        let program_id = test_program_id();
188        let (pda1, _) = get_market_pda(0, &program_id);
189        let (pda2, _) = get_market_pda(1, &program_id);
190
191        assert_ne!(pda1, pda2);
192    }
193
194    #[test]
195    fn test_position_pda_is_deterministic() {
196        let program_id = test_program_id();
197        let owner = Pubkey::new_unique();
198        let market = Pubkey::new_unique();
199
200        let (pda1, bump1) = get_position_pda(&owner, &market, &program_id);
201        let (pda2, bump2) = get_position_pda(&owner, &market, &program_id);
202
203        assert_eq!(pda1, pda2);
204        assert_eq!(bump1, bump2);
205    }
206
207    #[test]
208    fn test_conditional_mint_pdas() {
209        let program_id = test_program_id();
210        let market = Pubkey::new_unique();
211        let deposit_mint = Pubkey::new_unique();
212
213        let pdas = get_all_conditional_mint_pdas(&market, &deposit_mint, 3, &program_id);
214        assert_eq!(pdas.len(), 3);
215
216        // All PDAs should be different
217        assert_ne!(pdas[0].0, pdas[1].0);
218        assert_ne!(pdas[1].0, pdas[2].0);
219        assert_ne!(pdas[0].0, pdas[2].0);
220    }
221
222    #[test]
223    fn test_order_status_pda() {
224        let program_id = test_program_id();
225        let order_hash = [42u8; 32];
226
227        let (pda1, bump1) = get_order_status_pda(&order_hash, &program_id);
228        let (pda2, bump2) = get_order_status_pda(&order_hash, &program_id);
229
230        assert_eq!(pda1, pda2);
231        assert_eq!(bump1, bump2);
232    }
233
234    #[test]
235    fn test_pda_struct_methods() {
236        let program_id = test_program_id();
237        let owner = Pubkey::new_unique();
238        let market = Pubkey::new_unique();
239
240        // Verify Pda struct methods match the standalone functions
241        assert_eq!(
242            Pda::exchange(&program_id),
243            get_exchange_pda(&program_id)
244        );
245        assert_eq!(
246            Pda::market(5, &program_id),
247            get_market_pda(5, &program_id)
248        );
249        assert_eq!(
250            Pda::position(&owner, &market, &program_id),
251            get_position_pda(&owner, &market, &program_id)
252        );
253    }
254}