poseidon_client/transactions/
pda.rs

1use crate::{
2    AccountMeta, Instruction, PdaPublicKey, PoseidonError, PoseidonResult, PublicKey,
3    SystemInstruction, MAX_SEED_LEN, PDA_MARKER,
4};
5use core::fmt;
6use serde::{Deserialize, Serialize};
7
8/// ### Create a Program Derived Address and it's `Instruction`
9/// This struct builds a Solana Program Derived account from the given
10/// parameters yielding an instruction.
11///
12/// ```no_run
13/// #[derive(Serialize, Deserialize)]
14/// pub struct PdaBuilder {
15///     from_public_key: PublicKey,
16///     to_public_key: PublicKey,
17///     base: PublicKey,
18///     space: u64,
19///     owner: PublicKey,
20///     seed: String,
21///     lamports: u64,
22/// }
23#[derive(Serialize, Deserialize)]
24pub struct PdaBuilder {
25    from_public_key: PublicKey,
26    to_public_key: PublicKey,
27    base: PublicKey,
28    space: u64,
29    owner: PublicKey,
30    seed: String,
31    lamports: u64,
32}
33
34impl PdaBuilder {
35    pub fn new() -> Self {
36        PdaBuilder {
37            from_public_key: PublicKey::default(),
38            to_public_key: PublicKey::default(),
39            base: PublicKey::default(),
40            space: u64::default(),
41            owner: PublicKey::default(),
42            seed: String::default(),
43            lamports: u64::default(),
44        }
45    }
46
47    pub fn add_from(&mut self, public_key: PublicKey) -> &mut Self {
48        self.from_public_key = public_key;
49
50        self
51    }
52
53    pub fn add_base(&mut self, public_key: PublicKey) -> &mut Self {
54        self.base = public_key;
55
56        self
57    }
58
59    pub fn add_space(&mut self, space: u64) -> &mut Self {
60        self.space = space;
61
62        self
63    }
64
65    pub fn add_owner(&mut self, public_key: PublicKey) -> &mut Self {
66        self.owner = public_key;
67
68        self
69    }
70
71    pub fn add_seed(&mut self, seed: &str) -> &mut Self {
72        self.seed = seed.to_owned();
73
74        self
75    }
76
77    pub fn add_lamports(&mut self, lamports: u64) -> &mut Self {
78        self.lamports = lamports;
79
80        self
81    }
82
83    pub fn derive_public_key(&mut self) -> PoseidonResult<PdaPublicKey> {
84        use sha2::{Digest, Sha256};
85
86        if self.seed.len() > MAX_SEED_LEN {
87            return Err(PoseidonError::MaxSeedLengthExceeded);
88        }
89
90        if self.owner.len() >= PDA_MARKER.len() {
91            let slice = &self.owner[self.owner.len() - PDA_MARKER.len()..];
92            if slice == PDA_MARKER {
93                return Err(PoseidonError::IllegalOwner);
94            }
95        }
96
97        let mut hasher = Sha256::new();
98        hasher.update(&self.base);
99        hasher.update(&self.seed);
100        hasher.update(&self.owner);
101
102        let sha256_pda: [u8; 32] = hasher.finalize().into();
103
104        self.to_public_key = sha256_pda;
105
106        Ok(sha256_pda)
107    }
108
109    pub fn pda_pk_base58(&self) -> String {
110        bs58::encode(&self.to_public_key).into_string()
111    }
112
113    pub fn build(&self) -> PoseidonResult<Instruction> {
114        let system_instruction = SystemInstruction::CreateAccountWithSeed {
115            base: self.base,
116            seed: self.seed.to_owned(),
117            lamports: self.lamports,
118            space: self.space,
119            owner: self.owner,
120        };
121
122        let data = bincode::serialize(&system_instruction)?;
123
124        Ok(Instruction {
125            program_id: crate::SYSTEM_PROGRAM_ID,
126            accounts: vec![
127                AccountMeta::new(self.from_public_key, true),
128                AccountMeta::new(self.to_public_key, false),
129                AccountMeta::new_readonly(self.from_public_key, true),
130            ],
131            data,
132        })
133    }
134}
135
136impl fmt::Debug for PdaBuilder {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("PdaBuilder")
139            .field(
140                "from_public_key",
141                &bs58::encode(&self.from_public_key).into_string(),
142            )
143            .field(
144                "to_public_key",
145                &bs58::encode(&self.to_public_key).into_string(),
146            )
147            .field("base", &bs58::encode(&self.base).into_string())
148            .field("space", &self.space)
149            .field("owner", &bs58::encode(&self.owner).into_string())
150            .field("seed", &self.seed)
151            .field("lamports", &self.lamports)
152            .finish()
153    }
154}