Skip to main content

cow_settlement_interface/pda/
buffer.rs

1//! Buffer PDA seed and address derivation.
2//!
3//! Each buffer is a per-token SPL token account that holds funds controlled
4//! by the settlement state PDA. It lives at a PDA keyed by the token mint, so
5//! there is exactly one buffer address per token.
6//!
7//! The token account stored at this address is initialized by the
8//! `CreateBuffer` instruction; its SPL `owner` (token authority) is the
9//! settlement state PDA (see [`crate::pda::state`]), the single authority
10//! controlling every buffer.
11//!
12//! The seeds start with [`SETTLEMENT_SEED`], which carries
13//! the cargo package major and minor version, so a version bump moves every buffer.
14//! Since only the state PDA can spend a buffer and that address moves too,
15//! buffers must be drained under the old version before a bump ships, or their
16//! contents are stranded.
17//!
18//! Native SOL has a buffer of its own (see [`find_native_sol_buffer_pda`]): an
19//! empty account owned by the settlement program, whose lamports on top of rent
20//! pay out orders buying native SOL. It isn't keyed by a mint, so its seeds
21//! follow a pattern of their own.
22//!
23//! Unlike the order PDA, which stores its own bump (see
24//! [`crate::data::order::OrderAccount`]), a buffer's layout belongs entirely to
25//! the token program, leaving no room for one.
26
27use solana_pubkey::Pubkey;
28
29use crate::pda::SETTLEMENT_SEED;
30
31mod known_mints;
32pub use known_mints::KNOWN_MINTS;
33
34/// Trailing seed identifying the buffer PDAs.
35pub const BUFFER_SEED: &[u8] = b"buffer";
36
37/// Canonical seed components for the buffer PDA holding the specified `mint`
38/// token.
39///
40/// `mint` is the raw 32-byte token mint address, so the same helper serves
41/// both the off-chain builder and the on-chain handler (which holds the mint
42/// as an `Address`).
43pub const fn buffer_pda_seeds(mint: &[u8; 32]) -> [&[u8]; 3] {
44    [SETTLEMENT_SEED, mint, BUFFER_SEED]
45}
46
47/// Canonical seeds for re-deriving the buffer PDA for `mint` with `bump`.
48pub fn buffer_pda_signer_seeds<'a>(mint: &'a [u8; 32], bump: &'a [u8; 1]) -> [&'a [u8]; 4] {
49    let [s0, s1, s2] = buffer_pda_seeds(mint);
50    [s0, s1, s2, bump]
51}
52
53/// Derive the canonical buffer PDA address (and bump) for the token `mint`.
54pub fn find_buffer_pda(program_id: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
55    Pubkey::find_program_address(&buffer_pda_seeds(mint.as_array()), program_id)
56}
57
58/// Trailing seed identifying the native SOL buffer PDA.
59pub const NATIVE_SOL_BUFFER_SEED: &[u8] = b"buffer native sol";
60
61/// Canonical seed components for the native SOL buffer PDA.
62pub const NATIVE_SOL_BUFFER_PDA_SEEDS: [&[u8]; 2] = [SETTLEMENT_SEED, NATIVE_SOL_BUFFER_SEED];
63
64/// Derive the canonical native SOL buffer PDA address (and bump).
65pub fn find_native_sol_buffer_pda(program_id: &Pubkey) -> (Pubkey, u8) {
66    Pubkey::find_program_address(&NATIVE_SOL_BUFFER_PDA_SEEDS, program_id)
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use crate::fixtures::pubkey_from_seed;
73    use crate::pda::tests::assert_distinct_versions_yield_distinct_pdas;
74
75    #[test]
76    fn find_buffer_pda_uses_canonical_seeds() {
77        let token = Pubkey::new_unique();
78
79        crate::pda::tests::assert_canonical_bump(
80            |program_id| find_buffer_pda(program_id, &token),
81            buffer_pda_seeds(token.as_array()),
82        );
83    }
84
85    #[test]
86    fn distinct_versions_yield_distinct_buffer_pdas() {
87        let program_id = pubkey_from_seed("program id");
88        let mint = Pubkey::new_unique();
89        let (pda, _) = find_buffer_pda(&program_id, &mint);
90
91        assert_distinct_versions_yield_distinct_pdas(
92            &program_id,
93            &pda,
94            &[mint.as_array(), BUFFER_SEED],
95        );
96    }
97
98    #[test]
99    fn find_native_sol_buffer_pda_uses_canonical_seeds() {
100        crate::pda::tests::assert_canonical_bump(
101            find_native_sol_buffer_pda,
102            NATIVE_SOL_BUFFER_PDA_SEEDS,
103        );
104    }
105
106    #[test]
107    fn distinct_versions_yield_distinct_native_sol_buffer_pdas() {
108        let program_id = pubkey_from_seed("program id");
109        let (pda, _) = find_native_sol_buffer_pda(&program_id);
110        assert_distinct_versions_yield_distinct_pdas(&program_id, &pda, &[NATIVE_SOL_BUFFER_SEED]);
111    }
112
113    mod proptest {
114        use ::proptest::prelude::*;
115
116        use super::*;
117
118        proptest! {
119            #[test]
120            fn distinct_tokens_yield_distinct_pdas(
121                program_id in any::<[u8; 32]>(),
122                token1 in any::<[u8; 32]>(),
123                token2 in any::<[u8; 32]>(),
124            ) {
125                prop_assume!(token1 != token2);
126                let program_id = Pubkey::new_from_array(program_id);
127                let (pda1, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token1));
128                let (pda2, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token2));
129                prop_assert_ne!(pda1, pda2);
130            }
131        }
132    }
133}