clockwork_utils/
lib.rs

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