sablier_utils/
lib.rs

1pub mod account;
2pub mod explorer;
3pub mod pubkey;
4pub mod pyth;
5pub mod space;
6pub mod thread;
7
8use std::fmt::{Debug, Display, Formatter};
9
10use anchor_lang::{prelude::Pubkey, prelude::*, AnchorDeserialize};
11use base64::{engine::general_purpose::STANDARD, Engine};
12
13pub use sablier_macros::MinSpace;
14pub use space::Space;
15
16/// Crate build information
17#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
18pub struct CrateInfo {
19    /// The link to the crate spec
20    pub spec: String,
21    /// Arbitrary blob that can be set by developers
22    pub blob: String,
23}
24
25impl Display for CrateInfo {
26    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27        write!(f, "spec:{} blob:{}", self.spec, self.blob)
28    }
29}
30
31/// Parse struct from sol_set_return_data in program's logs
32pub trait ProgramLogsDeserializable {
33    fn try_from_program_logs(
34        program_logs: Vec<String>,
35        program_id: &Pubkey,
36    ) -> std::result::Result<Self, ErrorCode>
37    where
38        Self: Sized;
39}
40
41impl<T> ProgramLogsDeserializable for T
42where
43    T: AnchorDeserialize,
44{
45    fn try_from_program_logs(
46        program_logs: Vec<String>,
47        program_id: &Pubkey,
48    ) -> std::result::Result<T, ErrorCode> {
49        // A Program's return data appears in the log in this format:
50        // "Program return: <program-id> <program-generated-data-in-base64>"
51        // https://github.com/solana-labs/solana/blob/b8837c04ec3976c9c16d028fbee86f87823fb97f/program-runtime/src/stable_log.rs#L68
52        let preimage = format!("Program return: {} ", program_id);
53
54        // Extract the return data after Program return: <program-id>
55        let get_return_data_base64 = program_logs
56            .iter()
57            .find(|&s| s.starts_with(&preimage))
58            .ok_or(ErrorCode::AccountDidNotDeserialize)?
59            .strip_prefix(&preimage)
60            .ok_or(ErrorCode::AccountDidNotDeserialize)?;
61
62        let decoded = STANDARD
63            .decode(get_return_data_base64)
64            .map_err(|_err| ErrorCode::AccountDidNotDeserialize)?;
65
66        T::try_from_slice(&decoded).map_err(|_err| ErrorCode::AccountDidNotDeserialize)
67    }
68}