cow-settlement-interface 0.3.0

Account layouts, PDA derivations, and instruction builders for the CoW Protocol Solana settlement program.
Documentation
//! Order PDA seed and address derivation.
//!
//! The body stored at this address is [`crate::data::order::EncodedOrderAccount`];
//! the UID feeding the seeds is [`crate::data::intent::EncodedOrderIntent::hash`].
//!
//! Any account derived from [`crate::pda::order::find_order_pda`] that has
//! already been created on-chain can be considered safe to use.
//!
//! For every valid [`crate::data::intent::OrderIntent`], there exists only
//! a single valid PDA representing that intent.

use solana_hash::Hash;
use solana_pubkey::Pubkey;

use crate::pda::SETTLEMENT_SEED;

/// Trailing seed identifying the order PDAs.
pub const ORDER_SEED: &[u8] = b"order";

/// Canonical seed components for the order PDA at `uid`.
pub fn order_pda_seeds(uid: &Hash) -> [&[u8]; 3] {
    [SETTLEMENT_SEED, uid.as_ref(), ORDER_SEED]
}

/// Canonical seeds for signing as the order PDA at `uid` with `bump`. The
/// on-chain `CreateOrder` handler uses this to construct the CPI signer.
/// By design, order PDAs can be created only if they use the canonical bump.
/// Calling this function with another bump could lead to a theoretically
/// valid PDA that however cannot and should not be instantiated.
pub fn order_pda_signer_seeds<'a>(uid: &'a Hash, bump: &'a [u8; 1]) -> [&'a [u8]; 4] {
    let [s0, s1, s2] = order_pda_seeds(uid);
    [s0, s1, s2, bump]
}

/// Derive the canonical order PDA address (and bump) for `uid`.
///
/// `uid` is the unique identifier of an intent. See
/// [`crate::data::intent::OrderIntent::uid`].
pub fn find_order_pda(program_id: &Pubkey, uid: &Hash) -> (Pubkey, u8) {
    Pubkey::find_program_address(&order_pda_seeds(uid), program_id)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pda::tests::assert_distinct_versions_yield_distinct_pdas;

    #[test]
    fn find_order_pda_uses_canonical_seeds() {
        let uid = Hash::new_from_array(*Pubkey::new_unique().as_array());

        crate::pda::tests::assert_canonical_bump(
            |program_id| find_order_pda(program_id, &uid),
            order_pda_seeds(&uid),
        );
    }

    #[test]
    fn distinct_versions_yield_distinct_order_pdas() {
        let uid = Hash::new_from_array(*Pubkey::new_unique().as_array());
        let (pda, _) = find_order_pda(&crate::ID, &uid);

        assert_distinct_versions_yield_distinct_pdas(&pda, &[uid.as_ref(), ORDER_SEED]);
    }

    mod proptest {
        use ::proptest::prelude::*;

        use super::*;

        proptest! {
            #[test]
            fn distinct_uids_yield_distinct_pdas(
                program_id in any::<[u8; 32]>(),
                uid1 in any::<[u8; 32]>(),
                uid2 in any::<[u8; 32]>(),
            ) {
                prop_assume!(uid1 != uid2);
                let program_id = Pubkey::new_from_array(program_id);
                let (pda1, _) = find_order_pda(&program_id, &Hash::new_from_array(uid1));
                let (pda2, _) = find_order_pda(&program_id, &Hash::new_from_array(uid2));
                prop_assert_ne!(pda1, pda2);
            }
        }
    }
}