use solana_address::Address;
use solana_instruction::{AccountMeta, Instruction};
use winterwallet_common::{
ID, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_PASSTHROUGH_ACCOUNTS, SIGNATURE_LEN, WINTERNITZ_SCALARS,
discriminator,
};
use crate::Error;
const _: () = assert!(
SIGNATURE_LEN == (WINTERNITZ_SCALARS + 2) * 32,
"SIGNATURE_LEN must equal (WINTERNITZ_SCALARS + 2) * 32"
);
pub fn initialize(
payer: &Address,
wallet_pda: &Address,
signature_bytes: &[u8; SIGNATURE_LEN],
next_root: &[u8; 32],
) -> Instruction {
let mut data = Vec::with_capacity(1 + SIGNATURE_LEN + 32);
data.push(discriminator::INITIALIZE);
data.extend_from_slice(signature_bytes);
data.extend_from_slice(next_root);
Instruction {
program_id: ID,
accounts: vec![
AccountMeta::new(*payer, true),
AccountMeta::new(*wallet_pda, false),
AccountMeta::new_readonly(solana_system_interface::program::id(), false),
],
data,
}
}
pub fn advance(
wallet_pda: &Address,
passthrough_accounts: &[AccountMeta],
signature_bytes: &[u8; SIGNATURE_LEN],
new_root: &[u8; 32],
payload: &[u8],
) -> Instruction {
let mut data = Vec::with_capacity(1 + SIGNATURE_LEN + 32 + payload.len());
data.push(discriminator::ADVANCE);
data.extend_from_slice(signature_bytes);
data.extend_from_slice(new_root);
data.extend_from_slice(payload);
let mut accounts = Vec::with_capacity(1 + passthrough_accounts.len());
accounts.push(AccountMeta::new(*wallet_pda, false));
accounts.extend_from_slice(passthrough_accounts);
Instruction {
program_id: ID,
accounts,
data,
}
}
pub fn withdraw(wallet_pda: &Address, receiver: &Address, lamports: u64) -> Instruction {
let mut data = Vec::with_capacity(1 + 8);
data.push(discriminator::WITHDRAW);
data.extend_from_slice(&lamports.to_le_bytes());
Instruction {
program_id: ID,
accounts: vec![
AccountMeta::new(*wallet_pda, false),
AccountMeta::new(*receiver, false),
],
data,
}
}
pub fn close(wallet_pda: &Address, receiver: &Address) -> Instruction {
Instruction {
program_id: ID,
accounts: vec![
AccountMeta::new(*wallet_pda, false),
AccountMeta::new(*receiver, false),
],
data: vec![discriminator::CLOSE],
}
}
pub struct AdvancePayload {
pub data: Vec<u8>,
pub accounts: Vec<AccountMeta>,
}
pub fn encode_advance(inner_instructions: &[Instruction]) -> Result<AdvancePayload, Error> {
if inner_instructions.len() > 255 {
return Err(Error::PayloadTooLarge("more than 255 inner instructions"));
}
let total_accounts: usize = inner_instructions
.iter()
.map(|ix| 1 + ix.accounts.len())
.sum();
if total_accounts > MAX_PASSTHROUGH_ACCOUNTS {
return Err(Error::PayloadTooLarge(
"total passthrough accounts exceeds MAX_PASSTHROUGH_ACCOUNTS (128)",
));
}
let payload_len: usize = 1 + inner_instructions
.iter()
.map(|ix| 1 + 2 + ix.data.len())
.sum::<usize>();
let mut data = Vec::with_capacity(payload_len);
let mut accounts = Vec::with_capacity(total_accounts);
data.push(inner_instructions.len() as u8);
for ix in inner_instructions {
if ix.accounts.len() > MAX_CPI_INSTRUCTION_ACCOUNTS {
return Err(Error::PayloadTooLarge(
"inner instruction exceeds MAX_CPI_INSTRUCTION_ACCOUNTS (16)",
));
}
if ix.data.len() > u16::MAX as usize {
return Err(Error::PayloadTooLarge(
"inner instruction data exceeds u16::MAX",
));
}
data.push(ix.accounts.len() as u8);
data.extend_from_slice(&(ix.data.len() as u16).to_le_bytes());
data.extend_from_slice(&ix.data);
accounts.push(AccountMeta::new_readonly(ix.program_id, false));
for meta in &ix.accounts {
if meta.is_writable {
accounts.push(AccountMeta::new(meta.pubkey, false));
} else {
accounts.push(AccountMeta::new_readonly(meta.pubkey, false));
}
}
}
Ok(AdvancePayload { data, accounts })
}