Skip to main content

cow_settlement_interface/
token_program.rs

1//! The token programs settlement transfers may be issued against.
2
3use crate::Pubkey;
4use solana_program_error::ProgramError;
5pub use solana_sdk_ids::sysvar::instructions::ID as INSTRUCTIONS_SYSVAR_ID;
6
7/// A token program a token-moving instruction accepts.
8#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
9pub enum TokenProgram {
10    /// The legacy SPL Token program.
11    SplToken,
12    /// The SPL Token-2022 program.
13    Token2022,
14}
15
16impl TokenProgram {
17    /// Every supported token program.
18    pub const ALL: [Self; 2] = [Self::SplToken, Self::Token2022];
19
20    /// The address the program is deployed at.
21    pub const fn address(self) -> Pubkey {
22        match self {
23            Self::SplToken => spl_token_2022_interface::inline_spl_token::ID,
24            Self::Token2022 => spl_token_2022_interface::ID,
25        }
26    }
27}
28
29impl TryFrom<&Pubkey> for TokenProgram {
30    type Error = ProgramError;
31
32    /// Resolves a program address to the token program it identifies,
33    /// rejecting any address that isn't a supported token program.
34    fn try_from(address: &Pubkey) -> Result<Self, Self::Error> {
35        Self::ALL
36            .into_iter()
37            .find(|program| program.address() == *address)
38            .ok_or(ProgramError::IncorrectProgramId)
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use crate::fixtures::pubkey_from_seed;
46
47    /// Adding a variant makes this match non-exhaustive, the compile-time reminder
48    /// to add it to [`TokenProgram::ALL`] as well.
49    const _: () = match TokenProgram::SplToken {
50        TokenProgram::SplToken | TokenProgram::Token2022 => (),
51    };
52
53    #[test]
54    fn every_program_resolves_from_its_own_address() {
55        for program in TokenProgram::ALL {
56            let address = program.address();
57            assert_eq!(
58                TokenProgram::try_from(&address),
59                Ok(program),
60                "{program:?} should resolve from {address}",
61            );
62        }
63    }
64
65    #[test]
66    fn unrelated_program_cannot_be_resolved_as_token_program() {
67        assert_eq!(
68            TokenProgram::try_from(&pubkey_from_seed("not a token program")),
69            Err(ProgramError::IncorrectProgramId),
70        );
71    }
72}