winterwallet_client/
instruction.rs1use solana_address::Address;
2use solana_instruction::{AccountMeta, Instruction};
3use winterwallet_common::{
4 ID, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_PASSTHROUGH_ACCOUNTS, SIGNATURE_LEN, WINTERNITZ_SCALARS,
5 discriminator,
6};
7
8use crate::Error;
9
10const _: () = assert!(
12 SIGNATURE_LEN == (WINTERNITZ_SCALARS + 2) * 32,
13 "SIGNATURE_LEN must equal (WINTERNITZ_SCALARS + 2) * 32"
14);
15
16pub fn initialize(
22 payer: &Address,
23 wallet_pda: &Address,
24 signature_bytes: &[u8; SIGNATURE_LEN],
25 next_root: &[u8; 32],
26) -> Instruction {
27 let mut data = Vec::with_capacity(1 + SIGNATURE_LEN + 32);
28 data.push(discriminator::INITIALIZE);
29 data.extend_from_slice(signature_bytes);
30 data.extend_from_slice(next_root);
31
32 Instruction {
33 program_id: ID,
34 accounts: vec![
35 AccountMeta::new(*payer, true),
36 AccountMeta::new(*wallet_pda, false),
37 AccountMeta::new_readonly(solana_system_interface::program::id(), false),
38 ],
39 data,
40 }
41}
42
43pub fn advance(
49 wallet_pda: &Address,
50 passthrough_accounts: &[AccountMeta],
51 signature_bytes: &[u8; SIGNATURE_LEN],
52 new_root: &[u8; 32],
53 payload: &[u8],
54) -> Instruction {
55 let mut data = Vec::with_capacity(1 + SIGNATURE_LEN + 32 + payload.len());
56 data.push(discriminator::ADVANCE);
57 data.extend_from_slice(signature_bytes);
58 data.extend_from_slice(new_root);
59 data.extend_from_slice(payload);
60
61 let mut accounts = Vec::with_capacity(1 + passthrough_accounts.len());
62 accounts.push(AccountMeta::new(*wallet_pda, false));
63 accounts.extend_from_slice(passthrough_accounts);
64
65 Instruction {
66 program_id: ID,
67 accounts,
68 data,
69 }
70}
71
72pub fn withdraw(wallet_pda: &Address, receiver: &Address, lamports: u64) -> Instruction {
80 let mut data = Vec::with_capacity(1 + 8);
81 data.push(discriminator::WITHDRAW);
82 data.extend_from_slice(&lamports.to_le_bytes());
83
84 Instruction {
85 program_id: ID,
86 accounts: vec![
87 AccountMeta::new(*wallet_pda, false),
88 AccountMeta::new(*receiver, false),
89 ],
90 data,
91 }
92}
93
94pub fn close(wallet_pda: &Address, receiver: &Address) -> Instruction {
101 Instruction {
102 program_id: ID,
103 accounts: vec![
104 AccountMeta::new(*wallet_pda, false),
105 AccountMeta::new(*receiver, false),
106 ],
107 data: vec![discriminator::CLOSE],
108 }
109}
110
111pub struct AdvancePayload {
120 pub data: Vec<u8>,
122 pub accounts: Vec<AccountMeta>,
125}
126
127pub fn encode_advance(inner_instructions: &[Instruction]) -> Result<AdvancePayload, Error> {
144 if inner_instructions.len() > 255 {
145 return Err(Error::PayloadTooLarge("more than 255 inner instructions"));
146 }
147
148 let total_accounts: usize = inner_instructions
149 .iter()
150 .map(|ix| 1 + ix.accounts.len())
151 .sum();
152 if total_accounts > MAX_PASSTHROUGH_ACCOUNTS {
153 return Err(Error::PayloadTooLarge(
154 "total passthrough accounts exceeds MAX_PASSTHROUGH_ACCOUNTS (128)",
155 ));
156 }
157
158 let payload_len: usize = 1 + inner_instructions
159 .iter()
160 .map(|ix| 1 + 2 + ix.data.len())
161 .sum::<usize>();
162
163 let mut data = Vec::with_capacity(payload_len);
164 let mut accounts = Vec::with_capacity(total_accounts);
165
166 data.push(inner_instructions.len() as u8);
167
168 for ix in inner_instructions {
169 if ix.accounts.len() > MAX_CPI_INSTRUCTION_ACCOUNTS {
170 return Err(Error::PayloadTooLarge(
171 "inner instruction exceeds MAX_CPI_INSTRUCTION_ACCOUNTS (16)",
172 ));
173 }
174 if ix.data.len() > u16::MAX as usize {
175 return Err(Error::PayloadTooLarge(
176 "inner instruction data exceeds u16::MAX",
177 ));
178 }
179
180 data.push(ix.accounts.len() as u8);
181 data.extend_from_slice(&(ix.data.len() as u16).to_le_bytes());
182 data.extend_from_slice(&ix.data);
183
184 accounts.push(AccountMeta::new_readonly(ix.program_id, false));
185 for meta in &ix.accounts {
189 if meta.is_writable {
190 accounts.push(AccountMeta::new(meta.pubkey, false));
191 } else {
192 accounts.push(AccountMeta::new_readonly(meta.pubkey, false));
193 }
194 }
195 }
196
197 Ok(AdvancePayload { data, accounts })
198}