11use anchor_lang:: prelude:: * ;
22
3- use crate :: error :: LazorKitError ;
3+ use crate :: security :: validation ;
44use crate :: state:: { Chunk , WalletState } ;
55use crate :: utils:: { execute_cpi, PdaSigner } ;
66use crate :: { constants:: SMART_WALLET_SEED , ID } ;
77use anchor_lang:: solana_program:: hash:: { HASH_BYTES , Hasher , hash} ;
88
99/// Execute a previously created chunk
10+ /// Always returns Ok(()) - errors are logged but don't fail the transaction
1011pub fn execute_chunk (
1112 ctx : Context < ExecuteChunk > ,
1213 instruction_data_list : Vec < Vec < u8 > > ,
@@ -21,39 +22,68 @@ pub fn execute_chunk(
2122 let authorized_timestamp = chunk. authorized_timestamp ;
2223 let expected_cpi_hash = chunk. cpi_hash ;
2324
24- let now = Clock :: get ( ) ?. unix_timestamp ;
25- let session_end = authorized_timestamp + crate :: security:: MAX_SESSION_TTL_SECONDS ;
26- require ! (
27- now >= authorized_timestamp && now <= session_end,
28- LazorKitError :: TransactionTooOld
29- ) ;
30- require ! (
31- chunk. owner_wallet_address == smart_wallet_key,
32- LazorKitError :: InvalidAccountOwner
33- ) ;
34-
35- require ! (
36- !instruction_data_list. is_empty( ) ,
37- LazorKitError :: InsufficientCpiAccounts
38- ) ;
39- require ! (
40- instruction_data_list. len( ) == split_index. len( ) + 1 ,
41- LazorKitError :: InvalidInstructionData
42- ) ;
43-
44- let instruction_count: u32 = instruction_data_list. len ( ) . try_into ( )
45- . map_err ( |_| LazorKitError :: InvalidInstructionData ) ?;
25+ // Validate owner first (fail fast)
26+ if chunk. owner_wallet_address != smart_wallet_key {
27+ msg ! ( "InvalidAccountOwner: Invalid account owner: expected={}, got={}" , smart_wallet_key, chunk. owner_wallet_address) ;
28+ return Ok ( ( ) ) ;
29+ }
30+
31+ // Get current timestamp
32+ let now = match Clock :: get ( ) {
33+ Ok ( clock) => clock. unix_timestamp ,
34+ Err ( e) => {
35+ msg ! ( "InvalidInstruction: Error getting clock: {:?}" , e) ;
36+ return Ok ( ( ) ) ;
37+ }
38+ } ;
39+
40+ // Validate timestamp - check if chunk is expired
41+ let session_end = authorized_timestamp + crate :: security:: MAX_SESSION_TTL_SECONDS * 2 ;
42+ if now > session_end {
43+ msg ! ( "TransactionTooOld: Chunk expired: now={}, session_end={}" , now, session_end) ;
44+ // Chunk will be closed automatically due to close = session_refund constraint
45+ return Ok ( ( ) ) ;
46+ }
47+
48+ // Validate instruction data
49+ if instruction_data_list. is_empty ( ) {
50+ msg ! ( "InvalidInstructionData: Instruction data list is empty" ) ;
51+ return Ok ( ( ) ) ;
52+ }
53+
54+ if instruction_data_list. len ( ) != split_index. len ( ) + 1 {
55+ msg ! ( "InvalidInstructionData: Invalid instruction data: instruction_data_list.len()={}, split_index.len()={}" ,
56+ instruction_data_list. len( ) , split_index. len( ) ) ;
57+ return Ok ( ( ) ) ;
58+ }
59+
60+ // Serialize CPI data
61+ let instruction_count: u32 = match instruction_data_list. len ( ) . try_into ( ) {
62+ Ok ( count) => count,
63+ Err ( e) => {
64+ msg ! ( "InvalidInstructionData: Failed to convert instruction count: {:?}" , e) ;
65+ return Ok ( ( ) ) ;
66+ }
67+ } ;
68+
4669 let mut serialized_cpi_data = Vec :: new ( ) ;
4770 serialized_cpi_data. extend_from_slice ( & instruction_count. to_le_bytes ( ) ) ;
71+
4872 for instruction_data in & instruction_data_list {
49- let data_len: u32 = instruction_data. len ( ) . try_into ( )
50- . map_err ( |_| LazorKitError :: InvalidInstructionData ) ?;
73+ let data_len: u32 = match instruction_data. len ( ) . try_into ( ) {
74+ Ok ( len) => len,
75+ Err ( e) => {
76+ msg ! ( "InvalidInstructionData: Failed to convert instruction data length: {:?}" , e) ;
77+ return Ok ( ( ) ) ;
78+ }
79+ } ;
5180 serialized_cpi_data. extend_from_slice ( & data_len. to_le_bytes ( ) ) ;
5281 serialized_cpi_data. extend_from_slice ( instruction_data) ;
5382 }
5483
5584 let cpi_data_hash = hash ( & serialized_cpi_data) . to_bytes ( ) ;
5685
86+ // Hash CPI accounts
5787 let mut rh = Hasher :: default ( ) ;
5888 for account in cpi_accounts. iter ( ) {
5989 rh. hash ( account. key ( ) . as_ref ( ) ) ;
@@ -67,25 +97,48 @@ pub fn execute_chunk(
6797 cpi_combined[ HASH_BYTES ..] . copy_from_slice ( & cpi_accounts_hash) ;
6898 let cpi_hash = hash ( & cpi_combined) . to_bytes ( ) ;
6999
70- require ! ( cpi_hash == expected_cpi_hash, LazorKitError :: HashMismatch ) ;
100+ // Validate hash
101+ if cpi_hash != expected_cpi_hash {
102+ msg ! ( "HashMismatch: Hash mismatch: expected={:?}, got={:?}" , expected_cpi_hash, cpi_hash) ;
103+ return Ok ( ( ) ) ;
104+ }
105+
106+ // Calculate account ranges
107+ let account_ranges = match crate :: utils:: calculate_account_ranges ( cpi_accounts, & split_index) {
108+ Ok ( ranges) => ranges,
109+ Err ( e) => {
110+ msg ! ( "InvalidInstructionData: Failed to calculate account ranges: {:?}" , e) ;
111+ return Ok ( ( ) ) ;
112+ }
113+ } ;
71114
72- let account_ranges = crate :: utils:: calculate_account_ranges ( cpi_accounts, & split_index) ?;
73- crate :: utils:: validate_programs_in_ranges ( cpi_accounts, & account_ranges) ?;
115+ // Validate programs in ranges
116+ if let Err ( e) = crate :: utils:: validate_programs_in_ranges ( cpi_accounts, & account_ranges) {
117+ msg ! ( "InvalidInstruction: Failed to validate programs in ranges: {:?}" , e) ;
118+ return Ok ( ( ) ) ;
119+ }
74120
121+ // Execute CPI instructions
75122 let wallet_signer = PdaSigner {
76123 seeds : vec ! [ SMART_WALLET_SEED . to_vec( ) , wallet_id. to_le_bytes( ) . to_vec( ) ] ,
77124 bump : wallet_bump,
78125 } ;
79126
80- for ( cpi_data, & ( range_start, range_end) ) in
81- instruction_data_list. iter ( ) . zip ( account_ranges. iter ( ) )
127+ for ( idx , ( cpi_data, & ( range_start, range_end) ) ) in
128+ instruction_data_list. iter ( ) . zip ( account_ranges. iter ( ) ) . enumerate ( )
82129 {
83130 let instruction_accounts = & cpi_accounts[ range_start..range_end] ;
84131 let program_account = & instruction_accounts[ 0 ] ;
85132 let instruction_accounts = & instruction_accounts[ 1 ..] ;
86133
87- execute_cpi ( instruction_accounts, cpi_data, program_account, & wallet_signer) ?;
134+ if let Err ( e) = execute_cpi ( instruction_accounts, cpi_data, program_account, & wallet_signer) {
135+ msg ! ( "InvalidInstruction: Failed to execute CPI instruction {}: {:?}" , idx, e) ;
136+ return Ok ( ( ) ) ;
137+ }
88138 }
139+
140+ let last_nonce = ctx. accounts . wallet_state . last_nonce ;
141+ ctx. accounts . wallet_state . last_nonce = validation:: safe_increment_nonce ( last_nonce) ;
89142
90143 Ok ( ( ) )
91144}
@@ -103,6 +156,7 @@ pub struct ExecuteChunk<'info> {
103156 pub smart_wallet : SystemAccount < ' info > ,
104157
105158 #[ account(
159+ mut ,
106160 seeds = [ WalletState :: PREFIX_SEED , smart_wallet. key( ) . as_ref( ) ] ,
107161 bump,
108162 owner = ID ,
0 commit comments