solmail_program/instructions/escrow/
create.rs1use pinocchio::{
2 account_info::AccountInfo,
3 instruction::Signer,
4 program_error::ProgramError,
5 pubkey::{find_program_address, Pubkey},
6 seeds,
7 sysvars::{clock::Clock, rent::Rent, Sysvar},
8 ProgramResult,
9};
10use pinocchio_system::instructions::CreateAccount;
11
12use crate::{
13 constants::{DEFAULT_TIMEOUT_SECONDS, ESCROW_SEED, ESCROW_VAULT_SEED, MIN_ESCROW_LAMPORTS},
14 error::SolMailError,
15 state::escrow::{EscrowOrder, ESCROW_ORDER_SIZE},
16};
17
18pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
32 if data.len() < 25 {
34 return Err(ProgramError::InvalidInstructionData);
35 }
36
37 let amount = u64::from_le_bytes(data[0..8].try_into().unwrap());
38 let mail_id_low = u64::from_le_bytes(data[8..16].try_into().unwrap());
39 let mail_id_high = u64::from_le_bytes(data[16..24].try_into().unwrap());
40 let timeout_days = data[24];
41
42 if amount < MIN_ESCROW_LAMPORTS {
44 return Err(SolMailError::AmountBelowMinimum.into());
45 }
46
47 let [payer, escrow_state, escrow_vault, _system_program] = accounts else {
49 return Err(ProgramError::NotEnoughAccountKeys);
50 };
51
52 if !payer.is_signer() {
54 return Err(SolMailError::MissingRequiredSignature.into());
55 }
56
57 let mut mail_id_bytes = [0u8; 16];
59 mail_id_bytes[..8].copy_from_slice(&mail_id_low.to_le_bytes());
60 mail_id_bytes[8..].copy_from_slice(&mail_id_high.to_le_bytes());
61
62 let (escrow_pda, escrow_bump) =
64 find_program_address(&[ESCROW_SEED, &mail_id_bytes], program_id);
65
66 if escrow_state.key() != &escrow_pda {
67 return Err(SolMailError::InvalidPda.into());
68 }
69
70 let (vault_pda, vault_bump) =
72 find_program_address(&[ESCROW_VAULT_SEED, &mail_id_bytes], program_id);
73
74 if escrow_vault.key() != &vault_pda {
75 return Err(SolMailError::InvalidPda.into());
76 }
77
78 let clock = Clock::get()?;
80 let created_at = clock.unix_timestamp;
81
82 let timeout_seconds = if timeout_days == 0 {
84 DEFAULT_TIMEOUT_SECONDS
85 } else {
86 (timeout_days as i64) * 24 * 60 * 60
87 };
88 let timeout_at = created_at + timeout_seconds;
89
90 let rent = Rent::get()?;
92 let rent_lamports = rent.minimum_balance(ESCROW_ORDER_SIZE);
93
94 let escrow_bump_ref = &[escrow_bump];
96 let escrow_seeds = seeds!(ESCROW_SEED, &mail_id_bytes, escrow_bump_ref);
97 let escrow_signer = Signer::from(&escrow_seeds);
98
99 CreateAccount {
101 from: payer,
102 to: escrow_state,
103 lamports: rent_lamports,
104 space: ESCROW_ORDER_SIZE as u64,
105 owner: program_id,
106 }
107 .invoke_signed(&[escrow_signer])?;
108
109 let vault_bump_ref = &[vault_bump];
111 let vault_seeds = seeds!(ESCROW_VAULT_SEED, &mail_id_bytes, vault_bump_ref);
112 let vault_signer = Signer::from(&vault_seeds);
113
114 CreateAccount {
116 from: payer,
117 to: escrow_vault,
118 lamports: amount,
119 space: 0,
120 owner: program_id,
121 }
122 .invoke_signed(&[vault_signer])?;
123
124 let escrow_data = unsafe { escrow_state.borrow_mut_data_unchecked() };
126 let escrow = EscrowOrder::from_bytes_mut(escrow_data)?;
127
128 escrow.payer.copy_from_slice(payer.key().as_ref());
129 escrow.amount = amount;
130 escrow.mail_id = [mail_id_low, mail_id_high];
131 escrow.created_at = created_at;
132 escrow.timeout_at = timeout_at;
133 escrow.is_usdc = false;
134 escrow.delivered = false;
135 escrow.bump = escrow_bump;
136
137 Ok(())
138}