testsvm_core/
account_ref.rs1use crate::TestSVM;
17use anchor_lang::Key;
18use anyhow::{Context, Result};
19use solana_sdk::pubkey::Pubkey;
20use std::fmt;
21use std::marker::PhantomData;
22
23#[derive(Copy, Clone, Debug)]
25pub struct AccountRef<T: anchor_lang::AccountDeserialize> {
26 pub key: Pubkey,
27 _phantom: PhantomData<T>,
28}
29
30impl<T: anchor_lang::AccountDeserialize> Key for AccountRef<T> {
31 fn key(&self) -> Pubkey {
32 self.key
33 }
34}
35
36impl<T: anchor_lang::AccountDeserialize> From<AccountRef<T>> for Pubkey {
37 fn from(val: AccountRef<T>) -> Self {
38 val.key
39 }
40}
41
42impl<T: anchor_lang::AccountDeserialize> From<&AccountRef<T>> for Pubkey {
43 fn from(val: &AccountRef<T>) -> Self {
44 val.key
45 }
46}
47
48impl<T: anchor_lang::AccountDeserialize> AccountRef<T> {
49 pub fn new(key: Pubkey) -> Self {
51 Self {
52 key,
53 _phantom: PhantomData,
54 }
55 }
56
57 pub fn load(&self, env: &TestSVM) -> Result<T> {
59 self.maybe_load(env)?
60 .with_context(|| format!("Account not found: {}", self.key))
61 }
62
63 pub fn maybe_load(&self, env: &TestSVM) -> Result<Option<T>> {
65 match env.svm.get_account(&self.key) {
66 Some(account) => {
67 let mut data = &account.data[..];
68 Ok(Some(
69 T::try_deserialize(&mut data).context("Failed to deserialize account")?,
70 ))
71 }
72 None => Ok(None),
73 }
74 }
75}
76
77impl<T: anchor_lang::AccountDeserialize> fmt::Display for AccountRef<T> {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}", self.key)
80 }
81}
82
83impl<T: anchor_lang::AccountDeserialize> AsRef<[u8]> for AccountRef<T> {
84 fn as_ref(&self) -> &[u8] {
85 self.key.as_ref()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use crate::AccountRef;
92 use anchor_lang::prelude::*;
93 use solana_address_book::pda_seeds::find_pda_with_bump_and_strings;
94
95 #[derive(Debug, Clone)]
97 struct DummyAccount;
98
99 impl anchor_lang::AccountDeserialize for DummyAccount {
100 fn try_deserialize_unchecked(_buf: &mut &[u8]) -> Result<Self> {
101 Ok(DummyAccount)
102 }
103 }
104
105 impl anchor_lang::AccountSerialize for DummyAccount {
106 fn try_serialize<W: std::io::Write>(&self, _writer: &mut W) -> Result<()> {
107 Ok(())
108 }
109 }
110
111 impl anchor_lang::Owner for DummyAccount {
112 fn owner() -> Pubkey {
113 Pubkey::default()
114 }
115 }
116
117 #[test]
118 fn test_account_ref_as_pda_seed() {
119 let program_id = Pubkey::new_unique();
120 let account_pubkey = Pubkey::new_unique();
121 let account_ref: AccountRef<DummyAccount> = AccountRef::new(account_pubkey);
122
123 let (pda, bump) =
125 Pubkey::find_program_address(&[b"prefix", account_ref.key.as_ref()], &program_id);
126
127 let (expected_pda, expected_bump) =
129 Pubkey::find_program_address(&[b"prefix", account_pubkey.as_ref()], &program_id);
130
131 assert_eq!(pda, expected_pda);
132 assert_eq!(bump, expected_bump);
133
134 let derived_pda =
136 find_pda_with_bump_and_strings(&[b"prefix", account_ref.as_ref()], &program_id);
137
138 assert_eq!(derived_pda.key, expected_pda);
139 assert_eq!(derived_pda.bump, expected_bump);
140
141 assert_eq!(derived_pda.seed_strings[0], "prefix");
143 assert_eq!(derived_pda.seed_strings[1], account_pubkey.to_string());
144
145 assert_eq!(derived_pda.seeds[0], b"prefix");
147 assert_eq!(derived_pda.seeds[1], account_pubkey.as_ref());
148 }
149
150 #[test]
151 fn test_account_ref_mixed_seeds() {
152 let program_id = Pubkey::new_unique();
153 let vault_account = AccountRef::<DummyAccount>::new(Pubkey::new_unique());
154 let owner_account = AccountRef::<DummyAccount>::new(Pubkey::new_unique());
155 let nonce: u64 = 42;
156 let nonce_bytes = nonce.to_le_bytes();
157
158 let derived_pda = find_pda_with_bump_and_strings(
160 &[
161 b"vault",
162 vault_account.as_ref(),
163 owner_account.as_ref(),
164 nonce_bytes.as_ref(),
165 ],
166 &program_id,
167 );
168
169 let (expected_pda, expected_bump) = Pubkey::find_program_address(
171 &[
172 b"vault",
173 vault_account.key.as_ref(),
174 owner_account.key.as_ref(),
175 &nonce_bytes,
176 ],
177 &program_id,
178 );
179
180 assert_eq!(derived_pda.key, expected_pda);
181 assert_eq!(derived_pda.bump, expected_bump);
182 assert!(derived_pda.verify(&program_id));
183 }
184}