solscript_codegen/
rust_gen.rs

1//! Rust/Anchor code generator
2//!
3//! Generates Anchor-compatible Rust code from Solana IR.
4
5use crate::error::CodegenError;
6use crate::ir::*;
7use crate::GeneratedProject;
8
9/// Rust code generator for Anchor programs
10#[derive(Default)]
11pub struct RustGenerator {
12    /// Events for looking up field names
13    events: Vec<Event>,
14    /// Current instruction's signer parameter names (for generating ctx.accounts access)
15    signer_params: std::collections::HashSet<String>,
16    /// Internal (non-public) function names
17    internal_functions: std::collections::HashSet<String>,
18    /// Whether we're currently generating a helper function body (not inside #[program])
19    in_helper_function: bool,
20}
21
22impl RustGenerator {
23    pub fn new() -> Self {
24        Self {
25            events: Vec::new(),
26            signer_params: std::collections::HashSet::new(),
27            internal_functions: std::collections::HashSet::new(),
28            in_helper_function: false,
29        }
30    }
31
32    /// Generate a complete Anchor project from Solana IR
33    pub fn generate(
34        &mut self,
35        programs: &[SolanaProgram],
36    ) -> Result<GeneratedProject, CodegenError> {
37        if programs.is_empty() {
38            return Err(CodegenError::MissingElement(
39                "No deployable contracts found (abstract contracts cannot be deployed)".to_string(),
40            ));
41        }
42
43        // Use the last contract (child contracts come after their parents)
44        // In inheritance hierarchies, we generate code for the most derived contract
45        let program = programs.last().unwrap();
46
47        // Store events for lookup during emit generation
48        self.events = program.events.clone();
49
50        // Collect internal function names for proper call generation
51        self.internal_functions.clear();
52        for instruction in &program.instructions {
53            if !instruction.is_public {
54                self.internal_functions
55                    .insert(to_snake_case(&instruction.name));
56            }
57        }
58
59        let lib_rs = self.generate_lib_rs(program)?;
60        let state_rs = self.generate_state_rs(program)?;
61        let instructions_rs = self.generate_instructions_rs(program)?;
62        let error_rs = self.generate_error_rs(program)?;
63        let events_rs = self.generate_events_rs(program)?;
64        let anchor_toml = self.generate_anchor_toml(program);
65        let cargo_toml = self.generate_cargo_toml(program);
66
67        // Generate TypeScript client
68        let mut ts_gen = crate::ts_gen::TypeScriptGenerator::new();
69        let client_ts = ts_gen.generate(program)?;
70
71        // Generate TypeScript tests
72        let mut test_gen = crate::test_gen::TestGenerator::new();
73        let tests_ts = test_gen.generate(program)?;
74
75        // Generate IDL
76        let mut idl_gen = crate::idl_gen::IdlGenerator::new();
77        let idl_json = idl_gen.generate(program)?;
78
79        // Generate package.json
80        let package_json = self.generate_package_json(program);
81
82        // Generate README and .gitignore
83        let readme = self.generate_readme(program);
84        let gitignore = self.generate_gitignore();
85
86        // Generate Rust tests from #[test] functions
87        let rust_tests = self.generate_rust_tests(program)?;
88        let has_tests = !program.tests.is_empty();
89
90        Ok(GeneratedProject {
91            lib_rs,
92            state_rs,
93            instructions_rs,
94            error_rs,
95            events_rs,
96            anchor_toml,
97            cargo_toml,
98            client_ts,
99            tests_ts,
100            idl_json,
101            package_json,
102            readme,
103            gitignore,
104            rust_tests,
105            has_tests,
106        })
107    }
108
109    /// Generate Rust unit tests from #[test] functions
110    fn generate_rust_tests(&self, program: &SolanaProgram) -> Result<String, CodegenError> {
111        if program.tests.is_empty() {
112            return Ok(String::new());
113        }
114
115        let mut output = String::new();
116        output.push_str("//! Generated tests from SolScript #[test] functions\n\n");
117        output.push_str("#[cfg(test)]\n");
118        output.push_str("mod solscript_tests {\n");
119        output.push_str("    use super::*;\n\n");
120
121        for test in &program.tests {
122            let test_name = to_snake_case(&test.name);
123
124            // Determine if this is a should_fail test
125            if let Some(expected_msg) = &test.should_fail {
126                if expected_msg.is_empty() {
127                    output.push_str(&format!(
128                        "    #[test]\n    #[should_panic]\n    fn {}() {{\n",
129                        test_name
130                    ));
131                } else {
132                    output.push_str(&format!(
133                        "    #[test]\n    #[should_panic(expected = \"{}\")]\n    fn {}() {{\n",
134                        expected_msg, test_name
135                    ));
136                }
137            } else {
138                output.push_str(&format!("    #[test]\n    fn {}() {{\n", test_name));
139            }
140
141            // Generate test body
142            for stmt in &test.body {
143                let stmt_code = self.generate_statement(stmt, 2)?;
144                output.push_str(&stmt_code);
145            }
146
147            output.push_str("    }\n\n");
148        }
149
150        output.push_str("}\n");
151        Ok(output)
152    }
153
154    fn generate_lib_rs(&mut self, program: &SolanaProgram) -> Result<String, CodegenError> {
155        let name = to_snake_case(&program.name);
156        let uses_token = program.instructions.iter().any(|i| i.uses_token_program);
157
158        let mut imports = String::from("use anchor_lang::prelude::*;\n");
159        if uses_token {
160            imports.push_str("use anchor_spl::token::CpiContext;\n");
161        }
162
163        // Generate helper functions (internal/private functions)
164        let helper_fns = self.generate_helper_functions(program)?;
165
166        Ok(format!(
167            r#"//! Generated by SolScript compiler
168//! Contract: {}
169
170{}
171mod state;
172mod instructions;
173mod error;
174mod events;
175
176pub use state::*;
177pub use instructions::*;
178pub use error::*;
179// Events are accessed via events:: prefix to avoid name collisions
180
181declare_id!("11111111111111111111111111111111");
182
183{}
184
185#[program]
186pub mod {} {{
187    use super::*;
188
189{}
190}}
191"#,
192            program.name,
193            imports,
194            helper_fns,
195            name,
196            self.generate_instruction_handlers(program)?
197        ))
198    }
199
200    fn generate_helper_functions(
201        &mut self,
202        program: &SolanaProgram,
203    ) -> Result<String, CodegenError> {
204        let mut helpers = String::new();
205
206        for instruction in &program.instructions {
207            if !instruction.is_public {
208                helpers.push_str(&self.generate_helper_function(instruction, program)?);
209                helpers.push('\n');
210            }
211        }
212
213        Ok(helpers)
214    }
215
216    fn generate_helper_function(
217        &mut self,
218        instruction: &Instruction,
219        program: &SolanaProgram,
220    ) -> Result<String, CodegenError> {
221        let name = to_snake_case(&instruction.name);
222
223        // Generate parameters
224        let params: Vec<String> = instruction
225            .params
226            .iter()
227            .map(|p| format!("{}: {}", to_snake_case(&p.name), self.type_to_rust(&p.ty)))
228            .collect();
229
230        // Add state parameter for functions that access state
231        let state_type = format!("&mut crate::state::{}State", to_pascal_case(&program.name));
232        let mut all_params = vec![format!("state: {}", state_type)];
233        all_params.extend(params);
234        let params_str = all_params.join(", ");
235
236        // Return type
237        let return_type = match &instruction.returns {
238            Some(ty) => format!("Result<{}>", self.type_to_rust(ty)),
239            None => "Result<()>".to_string(),
240        };
241
242        // Generate body (with in_helper_function flag set)
243        self.in_helper_function = true;
244        let body = self.generate_helper_body(instruction, program)?;
245        self.in_helper_function = false;
246
247        Ok(format!(
248            r#"/// Internal helper function: {}
249fn {}({}) -> {} {{
250{}
251}}
252"#,
253            instruction.name, name, params_str, return_type, body
254        ))
255    }
256
257    fn generate_helper_body(
258        &mut self,
259        instruction: &Instruction,
260        _program: &SolanaProgram,
261    ) -> Result<String, CodegenError> {
262        let mut body = String::new();
263
264        // Generate statements
265        for stmt in &instruction.body {
266            body.push_str(&self.generate_statement(stmt, 1)?);
267            body.push('\n');
268        }
269
270        // Add return if not present
271        if instruction.returns.is_none() && !body.contains("Ok(())") {
272            body.push_str("    Ok(())\n");
273        }
274
275        Ok(body)
276    }
277
278    fn generate_instruction_handlers(
279        &mut self,
280        program: &SolanaProgram,
281    ) -> Result<String, CodegenError> {
282        let mut handlers = String::new();
283
284        for instruction in &program.instructions {
285            // Only generate public functions as Anchor instructions
286            if instruction.is_public {
287                handlers.push_str(&self.generate_instruction_handler(instruction, program)?);
288                handlers.push('\n');
289            }
290        }
291
292        Ok(handlers)
293    }
294
295    fn generate_instruction_handler(
296        &mut self,
297        instruction: &Instruction,
298        program: &SolanaProgram,
299    ) -> Result<String, CodegenError> {
300        let name = to_snake_case(&instruction.name);
301        let ctx_type = to_pascal_case(&instruction.name);
302
303        // Generate parameters (skip Signer types as they're in ctx.accounts)
304        let params: Vec<String> = instruction
305            .params
306            .iter()
307            .filter(|p| !matches!(p.ty, SolanaType::Signer))
308            .map(|p| format!("{}: {}", to_snake_case(&p.name), self.type_to_rust(&p.ty)))
309            .collect();
310
311        let params_str = if params.is_empty() {
312            String::new()
313        } else {
314            format!(", {}", params.join(", "))
315        };
316
317        // Generate return type
318        let return_type = match &instruction.returns {
319            Some(ty) => format!("Result<{}>", self.type_to_rust(ty)),
320            None => "Result<()>".to_string(),
321        };
322
323        // Generate body
324        let body = self.generate_instruction_body(instruction, program)?;
325
326        Ok(format!(
327            "    pub fn {}(ctx: Context<{}>{}) -> {} {{\n{}\n    }}\n",
328            name, ctx_type, params_str, return_type, body
329        ))
330    }
331
332    fn generate_instruction_body(
333        &mut self,
334        instruction: &Instruction,
335        program: &SolanaProgram,
336    ) -> Result<String, CodegenError> {
337        // Track signer params for this instruction
338        self.signer_params.clear();
339        for param in &instruction.params {
340            if matches!(param.ty, SolanaType::Signer) {
341                self.signer_params.insert(to_snake_case(&param.name));
342            }
343        }
344
345        let mut body = String::new();
346
347        // If no modifiers, just generate the function body directly
348        if instruction.modifiers.is_empty() {
349            for stmt in &instruction.body {
350                body.push_str(&self.generate_statement(stmt, 2)?);
351            }
352        } else {
353            // Inline modifiers: wrap the function body with modifier code
354            // For now, we handle single modifiers. Multiple modifiers would need nesting.
355            for modifier_call in &instruction.modifiers {
356                // Find the modifier definition
357                if let Some(modifier_def) = program
358                    .modifiers
359                    .iter()
360                    .find(|m| m.name == modifier_call.name)
361                {
362                    // Generate modifier body, replacing Placeholder with function body
363                    for stmt in &modifier_def.body {
364                        self.generate_inlined_statement(stmt, &instruction.body, 2, &mut body)?;
365                    }
366                } else {
367                    // Modifier not found, add comment and continue
368                    body.push_str(&format!(
369                        "        // Modifier: {} (definition not found)\n",
370                        modifier_call.name
371                    ));
372                    for stmt in &instruction.body {
373                        body.push_str(&self.generate_statement(stmt, 2)?);
374                    }
375                }
376            }
377        }
378
379        // Add default return if needed
380        if instruction.returns.is_none() && !body.contains("Ok(") {
381            body.push_str("        Ok(())\n");
382        }
383
384        Ok(body)
385    }
386
387    /// Generate a statement, replacing Placeholder with the inner function body
388    fn generate_inlined_statement(
389        &self,
390        stmt: &Statement,
391        inner_body: &[Statement],
392        indent: usize,
393        output: &mut String,
394    ) -> Result<(), CodegenError> {
395        match stmt {
396            Statement::Placeholder => {
397                // Replace placeholder with the inner function body
398                for inner_stmt in inner_body {
399                    output.push_str(&self.generate_statement(inner_stmt, indent)?);
400                }
401            }
402            Statement::If {
403                condition,
404                then_block,
405                else_block,
406            } => {
407                // Need to recursively handle if statements that might contain placeholders
408                let ind = "    ".repeat(indent);
409                output.push_str(&format!(
410                    "{}if {} {{\n",
411                    ind,
412                    self.generate_expression(condition)?
413                ));
414                for s in then_block {
415                    self.generate_inlined_statement(s, inner_body, indent + 1, output)?;
416                }
417                if let Some(else_stmts) = else_block {
418                    output.push_str(&format!("{}}} else {{\n", ind));
419                    for s in else_stmts {
420                        self.generate_inlined_statement(s, inner_body, indent + 1, output)?;
421                    }
422                }
423                output.push_str(&format!("{}}}\n", ind));
424            }
425            _ => {
426                // For other statements, generate normally
427                output.push_str(&self.generate_statement(stmt, indent)?);
428            }
429        }
430        Ok(())
431    }
432
433    fn generate_statement(&self, stmt: &Statement, indent: usize) -> Result<String, CodegenError> {
434        let ind = "    ".repeat(indent);
435
436        match stmt {
437            Statement::VarDecl { name, ty, value } => {
438                let name = to_snake_case(name);
439                let ty_str = self.type_to_rust(ty);
440                match value {
441                    Some(expr) => Ok(format!(
442                        "{}let {}: {} = {};\n",
443                        ind,
444                        name,
445                        ty_str,
446                        self.generate_expression(expr)?
447                    )),
448                    None => Ok(format!(
449                        "{}let {}: {} = Default::default();\n",
450                        ind, name, ty_str
451                    )),
452                }
453            }
454            Statement::Assign { target, value } => Ok(format!(
455                "{}{} = {};\n",
456                ind,
457                self.generate_expression(target)?,
458                self.generate_expression(value)?
459            )),
460            Statement::If {
461                condition,
462                then_block,
463                else_block,
464            } => {
465                let mut result = format!("{}if {} {{\n", ind, self.generate_expression(condition)?);
466                for s in then_block {
467                    result.push_str(&self.generate_statement(s, indent + 1)?);
468                }
469                result.push_str(&format!("{}}}", ind));
470
471                if let Some(else_stmts) = else_block {
472                    result.push_str(" else {\n");
473                    for s in else_stmts {
474                        result.push_str(&self.generate_statement(s, indent + 1)?);
475                    }
476                    result.push_str(&format!("{}}}", ind));
477                }
478                result.push('\n');
479                Ok(result)
480            }
481            Statement::While { condition, body } => {
482                let mut result =
483                    format!("{}while {} {{\n", ind, self.generate_expression(condition)?);
484                for s in body {
485                    result.push_str(&self.generate_statement(s, indent + 1)?);
486                }
487                result.push_str(&format!("{}}}\n", ind));
488                Ok(result)
489            }
490            Statement::For {
491                init,
492                condition,
493                update,
494                body,
495            } => {
496                let mut result = String::new();
497
498                // For loops become while loops in Rust
499                if let Some(init_stmt) = init {
500                    result.push_str(&self.generate_statement(init_stmt, indent)?);
501                }
502
503                let cond = match condition {
504                    Some(c) => self.generate_expression(c)?,
505                    None => "true".to_string(),
506                };
507
508                result.push_str(&format!("{}while {} {{\n", ind, cond));
509
510                for s in body {
511                    result.push_str(&self.generate_statement(s, indent + 1)?);
512                }
513
514                if let Some(upd) = update {
515                    result.push_str(&format!("{}    {};\n", ind, self.generate_expression(upd)?));
516                }
517
518                result.push_str(&format!("{}}}\n", ind));
519                Ok(result)
520            }
521            Statement::Return(expr) => match expr {
522                Some(e) => Ok(format!("{}Ok({})\n", ind, self.generate_expression(e)?)),
523                None => Ok(format!("{}Ok(())\n", ind)),
524            },
525            Statement::Emit { event, args } => {
526                // Look up the event to get field names
527                let event_def = self.events.iter().find(|e| e.name == *event);
528                let args_str: Vec<String> = args
529                    .iter()
530                    .enumerate()
531                    .map(|(i, a)| {
532                        let val = self.generate_expression(a)?;
533                        let field_name = event_def
534                            .and_then(|e| e.fields.get(i))
535                            .map(|f| to_snake_case(&f.name))
536                            .unwrap_or_else(|| format!("field{}", i));
537                        Ok(format!("{}: {}", field_name, val))
538                    })
539                    .collect::<Result<Vec<_>, _>>()?;
540                Ok(format!(
541                    "{}emit!(events::{} {{ {} }});\n",
542                    ind,
543                    to_pascal_case(event),
544                    args_str.join(", ")
545                ))
546            }
547            Statement::Require {
548                condition,
549                message: _,
550            } => Ok(format!(
551                "{}require!({}, CustomError::RequireFailed);\n",
552                ind,
553                self.generate_expression(condition)?
554            )),
555            Statement::RevertWithError {
556                error_name,
557                args: _,
558            } => {
559                // Note: Anchor's #[error_code] doesn't support struct-style errors with data
560                // So we generate simple error variants. The error parameters in Solidity
561                // are useful for type checking but don't translate to Anchor error data.
562                Ok(format!(
563                    "{}return Err(error!(CustomError::{}));\n",
564                    ind,
565                    to_pascal_case(error_name)
566                ))
567            }
568            Statement::Delete(target) => {
569                // Check if this is a mapping access (delete closes the PDA)
570                if let Expression::MappingAccess { account_name, .. } = target {
571                    // PDA closing is handled by the `close = signer` account constraint
572                    // The account will be closed and lamports returned to signer
573                    Ok(format!(
574                        "{}// PDA {} closed via `close = signer` constraint\n",
575                        ind,
576                        to_snake_case(account_name)
577                    ))
578                } else {
579                    // For non-mapping targets (state variables): assign Default::default()
580                    let target_expr = self.generate_expression(target)?;
581                    Ok(format!("{}{} = Default::default();\n", ind, target_expr))
582                }
583            }
584            Statement::Selfdestruct { .. } => {
585                // Selfdestruct is handled by the close constraint on the state account
586                // The actual closing is done by Anchor based on the account constraint
587                // We just add a comment here for clarity
588                Ok(format!(
589                    "{}// State account will be closed, rent sent to recipient\n",
590                    ind
591                ))
592            }
593            Statement::Expr(expr) => Ok(format!("{}{};\n", ind, self.generate_expression(expr)?)),
594            Statement::Placeholder => {
595                // Placeholder should be replaced during modifier inlining
596                // This should not appear in generated code
597                Ok(String::new())
598            }
599        }
600    }
601
602    fn generate_expression(&self, expr: &Expression) -> Result<String, CodegenError> {
603        match expr {
604            Expression::Literal(lit) => self.generate_literal(lit),
605            Expression::Var(name) => {
606                let snake_name = to_snake_case(name);
607                // If this is a signer param, access it from ctx.accounts
608                if self.signer_params.contains(&snake_name) {
609                    Ok(format!("ctx.accounts.{}.key()", snake_name))
610                } else {
611                    Ok(snake_name)
612                }
613            }
614            Expression::StateAccess(field) => {
615                if self.in_helper_function {
616                    Ok(format!("state.{}", to_snake_case(field)))
617                } else {
618                    Ok(format!("ctx.accounts.state.{}", to_snake_case(field)))
619                }
620            }
621            Expression::MappingAccess {
622                mapping_name: _,
623                keys: _,
624                account_name,
625            } => {
626                // Access the PDA account's value field
627                Ok(format!(
628                    "ctx.accounts.{}.value",
629                    to_snake_case(account_name)
630                ))
631            }
632            Expression::MsgSender => Ok("ctx.accounts.signer.key()".to_string()),
633            Expression::MsgValue => Ok("0u64 /* msg.value not supported */".to_string()),
634            Expression::BlockTimestamp => Ok("Clock::get()?.unix_timestamp as u64".to_string()),
635            // Solana Clock sysvar fields
636            Expression::ClockSlot => Ok("Clock::get()?.slot".to_string()),
637            Expression::ClockEpoch => Ok("Clock::get()?.epoch".to_string()),
638            Expression::ClockUnixTimestamp => Ok("Clock::get()?.unix_timestamp".to_string()),
639            // Solana Rent sysvar methods
640            Expression::RentMinimumBalance { data_len } => {
641                let len_str = self.generate_expression(data_len)?;
642                Ok(format!(
643                    "Rent::get()?.minimum_balance({} as usize)",
644                    len_str
645                ))
646            }
647            Expression::RentIsExempt { lamports, data_len } => {
648                let lamports_str = self.generate_expression(lamports)?;
649                let len_str = self.generate_expression(data_len)?;
650                Ok(format!(
651                    "Rent::get()?.is_exempt({}, {} as usize)",
652                    lamports_str, len_str
653                ))
654            }
655            Expression::Binary { op, left, right } => {
656                let l = self.generate_expression(left)?;
657                let r = self.generate_expression(right)?;
658                let op_str = match op {
659                    BinaryOp::Add => "+",
660                    BinaryOp::Sub => "-",
661                    BinaryOp::Mul => "*",
662                    BinaryOp::Div => "/",
663                    BinaryOp::Rem => "%",
664                    BinaryOp::Eq => "==",
665                    BinaryOp::Ne => "!=",
666                    BinaryOp::Lt => "<",
667                    BinaryOp::Le => "<=",
668                    BinaryOp::Gt => ">",
669                    BinaryOp::Ge => ">=",
670                    BinaryOp::And => "&&",
671                    BinaryOp::Or => "||",
672                    BinaryOp::BitAnd => "&",
673                    BinaryOp::BitOr => "|",
674                    BinaryOp::BitXor => "^",
675                    BinaryOp::Shl => "<<",
676                    BinaryOp::Shr => ">>",
677                };
678                Ok(format!("({} {} {})", l, op_str, r))
679            }
680            Expression::Unary { op, expr } => {
681                let e = self.generate_expression(expr)?;
682                let op_str = match op {
683                    UnaryOp::Neg => "-",
684                    UnaryOp::Not => "!",
685                    UnaryOp::BitNot => "!",
686                };
687                Ok(format!("({}{})", op_str, e))
688            }
689            Expression::Call { func, args } => {
690                let func_name = to_snake_case(func);
691                let args_str: Vec<String> = args
692                    .iter()
693                    .map(|a| self.generate_expression(a))
694                    .collect::<Result<Vec<_>, _>>()?;
695
696                // Check if this is a call to an internal function
697                if self.internal_functions.contains(&func_name) {
698                    // Internal functions receive state as first parameter
699                    let state_arg = if self.in_helper_function {
700                        "state".to_string()
701                    } else {
702                        "&mut ctx.accounts.state".to_string()
703                    };
704                    let mut all_args = vec![state_arg];
705                    all_args.extend(args_str);
706                    Ok(format!("{}({})?", func_name, all_args.join(", ")))
707                } else {
708                    Ok(format!("{}({})", func_name, args_str.join(", ")))
709                }
710            }
711            Expression::MethodCall {
712                receiver,
713                method,
714                args,
715            } => {
716                // Handle special assignment marker
717                if method == "__assign__" && args.len() == 1 {
718                    let target = self.generate_expression(receiver)?;
719                    let value = self.generate_expression(&args[0])?;
720                    return Ok(format!("{} = {}", target, value));
721                }
722
723                let recv = self.generate_expression(receiver)?;
724                let args_str: Vec<String> = args
725                    .iter()
726                    .map(|a| self.generate_expression(a))
727                    .collect::<Result<Vec<_>, _>>()?;
728                Ok(format!(
729                    "{}.{}({})",
730                    recv,
731                    to_snake_case(method),
732                    args_str.join(", ")
733                ))
734            }
735            Expression::CpiCall {
736                program,
737                interface_name,
738                method,
739                args,
740            } => {
741                let prog = self.generate_expression(program)?;
742                let args_str: Vec<String> = args
743                    .iter()
744                    .map(|a| self.generate_expression(a))
745                    .collect::<Result<Vec<_>, _>>()?;
746
747                // Generate Anchor-style instruction discriminator
748                // Format: sha256("global:{method_name}")[0..8]
749                let method_snake = to_snake_case(method);
750
751                // Build instruction data serialization
752                let mut data_parts = Vec::new();
753                data_parts.push(format!(
754                    "let discriminator = anchor_lang::solana_program::hash::hash(b\"global:{}\").to_bytes();",
755                    method_snake
756                ));
757                data_parts.push("let mut data = discriminator[..8].to_vec();".to_string());
758
759                // Serialize each argument using Borsh
760                for arg in &args_str {
761                    data_parts.push(format!(
762                        "AnchorSerialize::serialize(&({}), &mut data).unwrap();",
763                        arg
764                    ));
765                }
766
767                // Generate account metas from address-type arguments
768                // This is a heuristic - addresses become account metas
769                let mut account_metas = Vec::new();
770                let mut account_infos = Vec::new();
771                for arg in args_str.iter() {
772                    // Add each argument that looks like a pubkey as an account
773                    account_metas.push(format!("AccountMeta::new({}, false)", arg));
774                    account_infos.push(format!("/* account_info for {} */", arg));
775                }
776
777                let accounts_vec = if account_metas.is_empty() {
778                    "vec![]".to_string()
779                } else {
780                    format!("vec![{}]", account_metas.join(", "))
781                };
782
783                Ok(format!(
784                    r#"{{
785            use anchor_lang::prelude::*;
786            // CPI to {interface_name}.{method}
787            let cpi_program = {prog};
788
789            // Build instruction data with Anchor discriminator
790            {data_code}
791
792            // Build the instruction
793            let ix = anchor_lang::solana_program::instruction::Instruction {{
794                program_id: cpi_program,
795                accounts: {accounts},
796                data,
797            }};
798
799            // Execute CPI
800            // Note: You may need to add the appropriate account_infos based on your context
801            anchor_lang::solana_program::program::invoke(
802                &ix,
803                &[cpi_program.to_account_info()],
804            )?
805        }}"#,
806                    interface_name = interface_name,
807                    method = method,
808                    prog = prog,
809                    data_code = data_parts.join("\n            "),
810                    accounts = accounts_vec,
811                ))
812            }
813            Expression::InterfaceCast {
814                interface_name: _,
815                program_id,
816            } => {
817                // InterfaceCast is typically used in method call chains (IERC20(addr).transfer(...))
818                // and converted to CpiCall. If used standalone, just return the program_id.
819                self.generate_expression(program_id)
820            }
821            Expression::TokenTransfer {
822                from,
823                to,
824                authority,
825                amount,
826            } => {
827                let from_str = self.generate_expression(from)?;
828                let to_str = self.generate_expression(to)?;
829                let auth_str = self.generate_expression(authority)?;
830                let amt_str = self.generate_expression(amount)?;
831                // Note: In a real implementation, these would be account references from ctx.accounts
832                // For now, we generate the CPI pattern - the developer needs to adjust account types
833                Ok(format!(
834                    r#"{{
835            let cpi_accounts = anchor_spl::token::Transfer {{
836                from: ctx.accounts.{}.to_account_info(),
837                to: ctx.accounts.{}.to_account_info(),
838                authority: ctx.accounts.{}.to_account_info(),
839            }};
840            let cpi_program = ctx.accounts.token_program.to_account_info();
841            anchor_spl::token::transfer(CpiContext::new(cpi_program, cpi_accounts), {} as u64)?
842        }}"#,
843                    to_snake_case(&from_str),
844                    to_snake_case(&to_str),
845                    to_snake_case(&auth_str),
846                    amt_str
847                ))
848            }
849            Expression::TokenMint {
850                mint,
851                to,
852                authority,
853                amount,
854            } => {
855                let mint_str = self.generate_expression(mint)?;
856                let to_str = self.generate_expression(to)?;
857                let auth_str = self.generate_expression(authority)?;
858                let amt_str = self.generate_expression(amount)?;
859                Ok(format!(
860                    r#"{{
861            let cpi_accounts = anchor_spl::token::MintTo {{
862                mint: ctx.accounts.{}.to_account_info(),
863                to: ctx.accounts.{}.to_account_info(),
864                authority: ctx.accounts.{}.to_account_info(),
865            }};
866            let cpi_program = ctx.accounts.token_program.to_account_info();
867            anchor_spl::token::mint_to(CpiContext::new(cpi_program, cpi_accounts), {} as u64)?
868        }}"#,
869                    to_snake_case(&mint_str),
870                    to_snake_case(&to_str),
871                    to_snake_case(&auth_str),
872                    amt_str
873                ))
874            }
875            Expression::TokenBurn {
876                from,
877                mint,
878                authority,
879                amount,
880            } => {
881                let from_str = self.generate_expression(from)?;
882                let mint_str = self.generate_expression(mint)?;
883                let auth_str = self.generate_expression(authority)?;
884                let amt_str = self.generate_expression(amount)?;
885                Ok(format!(
886                    r#"{{
887            let cpi_accounts = anchor_spl::token::Burn {{
888                from: ctx.accounts.{}.to_account_info(),
889                mint: ctx.accounts.{}.to_account_info(),
890                authority: ctx.accounts.{}.to_account_info(),
891            }};
892            let cpi_program = ctx.accounts.token_program.to_account_info();
893            anchor_spl::token::burn(CpiContext::new(cpi_program, cpi_accounts), {} as u64)?
894        }}"#,
895                    to_snake_case(&from_str),
896                    to_snake_case(&mint_str),
897                    to_snake_case(&auth_str),
898                    amt_str
899                ))
900            }
901            Expression::SolTransfer { to, amount } => {
902                let to_str = self.generate_expression(to)?;
903                let amt_str = self.generate_expression(amount)?;
904                // Use Anchor's system_program CPI for SOL transfers
905                // Validate that the recipient account matches the intended destination
906                Ok(format!(
907                    r#"{{
908            // Validate recipient matches the intended destination
909            require!(ctx.accounts.recipient.key() == {to_str}, CustomError::InvalidRecipient);
910            let cpi_accounts = anchor_lang::system_program::Transfer {{
911                from: ctx.accounts.signer.to_account_info(),
912                to: ctx.accounts.recipient.to_account_info(),
913            }};
914            let cpi_ctx = anchor_lang::prelude::CpiContext::new(
915                ctx.accounts.system_program.to_account_info(),
916                cpi_accounts
917            );
918            anchor_lang::system_program::transfer(cpi_ctx, {amt_str} as u64)?
919        }}"#
920                ))
921            }
922            Expression::GetATA { owner, mint } => {
923                let owner_str = self.generate_expression(owner)?;
924                let mint_str = self.generate_expression(mint)?;
925                Ok(format!(
926                    "anchor_spl::associated_token::get_associated_token_address(&{}, &{})",
927                    owner_str, mint_str
928                ))
929            }
930            Expression::Index { expr, index } => {
931                let e = self.generate_expression(expr)?;
932                let i = self.generate_expression(index)?;
933                // Cast index to usize for array/vec indexing
934                Ok(format!("{}[{} as usize]", e, i))
935            }
936            Expression::Field { expr, field } => {
937                let e = self.generate_expression(expr)?;
938                // Convert Solidity's .length to Rust's .len() with cast to u128
939                if field == "length" {
940                    Ok(format!("({}.len() as u128)", e))
941                } else {
942                    Ok(format!("{}.{}", e, to_snake_case(field)))
943                }
944            }
945            Expression::Ternary {
946                condition,
947                then_expr,
948                else_expr,
949            } => {
950                let c = self.generate_expression(condition)?;
951                let t = self.generate_expression(then_expr)?;
952                let e = self.generate_expression(else_expr)?;
953                Ok(format!("if {} {{ {} }} else {{ {} }}", c, t, e))
954            }
955            Expression::Assert { condition, message } => {
956                let c = self.generate_expression(condition)?;
957                if let Some(msg) = message {
958                    Ok(format!("assert!({}, \"{}\")", c, msg))
959                } else {
960                    Ok(format!("assert!({})", c))
961                }
962            }
963            Expression::AssertEq {
964                left,
965                right,
966                message,
967            } => {
968                let l = self.generate_expression(left)?;
969                let r = self.generate_expression(right)?;
970                if let Some(msg) = message {
971                    Ok(format!("assert_eq!({}, {}, \"{}\")", l, r, msg))
972                } else {
973                    Ok(format!("assert_eq!({}, {})", l, r))
974                }
975            }
976            Expression::AssertNe {
977                left,
978                right,
979                message,
980            } => {
981                let l = self.generate_expression(left)?;
982                let r = self.generate_expression(right)?;
983                if let Some(msg) = message {
984                    Ok(format!("assert_ne!({}, {}, \"{}\")", l, r, msg))
985                } else {
986                    Ok(format!("assert_ne!({}, {})", l, r))
987                }
988            }
989            Expression::AssertGt {
990                left,
991                right,
992                message,
993            } => {
994                let l = self.generate_expression(left)?;
995                let r = self.generate_expression(right)?;
996                if let Some(msg) = message {
997                    Ok(format!("assert!({} > {}, \"{}\")", l, r, msg))
998                } else {
999                    Ok(format!("assert!({} > {})", l, r))
1000                }
1001            }
1002            Expression::AssertGe {
1003                left,
1004                right,
1005                message,
1006            } => {
1007                let l = self.generate_expression(left)?;
1008                let r = self.generate_expression(right)?;
1009                if let Some(msg) = message {
1010                    Ok(format!("assert!({} >= {}, \"{}\")", l, r, msg))
1011                } else {
1012                    Ok(format!("assert!({} >= {})", l, r))
1013                }
1014            }
1015            Expression::AssertLt {
1016                left,
1017                right,
1018                message,
1019            } => {
1020                let l = self.generate_expression(left)?;
1021                let r = self.generate_expression(right)?;
1022                if let Some(msg) = message {
1023                    Ok(format!("assert!({} < {}, \"{}\")", l, r, msg))
1024                } else {
1025                    Ok(format!("assert!({} < {})", l, r))
1026                }
1027            }
1028            Expression::AssertLe {
1029                left,
1030                right,
1031                message,
1032            } => {
1033                let l = self.generate_expression(left)?;
1034                let r = self.generate_expression(right)?;
1035                if let Some(msg) = message {
1036                    Ok(format!("assert!({} <= {}, \"{}\")", l, r, msg))
1037                } else {
1038                    Ok(format!("assert!({} <= {})", l, r))
1039                }
1040            }
1041        }
1042    }
1043
1044    fn generate_literal(&self, lit: &Literal) -> Result<String, CodegenError> {
1045        match lit {
1046            Literal::Bool(b) => Ok(b.to_string()),
1047            Literal::Int(n) => Ok(format!("{}i128", n)),
1048            Literal::Uint(n) => Ok(format!("{}u128", n)),
1049            Literal::String(s) => Ok(format!("\"{}\"", s.replace('\"', "\\\""))),
1050            Literal::Pubkey(s) => {
1051                // For address literals, we'd need to parse or use a placeholder
1052                Ok(format!("Pubkey::default() /* {} */", s))
1053            }
1054            Literal::ZeroAddress => {
1055                // address(0) - the zero/null address
1056                Ok("Pubkey::default()".to_string())
1057            }
1058            Literal::ZeroBytes(n) => {
1059                // bytes32(0), bytes4(0), etc. - zero-filled fixed bytes
1060                Ok(format!("[0u8; {}]", n))
1061            }
1062        }
1063    }
1064
1065    fn generate_state_rs(&self, program: &SolanaProgram) -> Result<String, CodegenError> {
1066        let mut content = String::from(
1067            r#"//! Program state definitions
1068
1069use anchor_lang::prelude::*;
1070
1071"#,
1072        );
1073
1074        // Generate user-defined enums
1075        for enum_def in &program.enums {
1076            content.push_str("#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, PartialEq, Eq, Default)]\n");
1077            content.push_str(&format!("pub enum {} {{\n", to_pascal_case(&enum_def.name)));
1078            for (i, variant) in enum_def.variants.iter().enumerate() {
1079                if i == 0 {
1080                    content.push_str("    #[default]\n");
1081                }
1082                content.push_str(&format!("    {},\n", to_pascal_case(variant)));
1083            }
1084            content.push_str("}\n\n");
1085        }
1086
1087        // Generate user-defined structs (before state account so they can be used as field types)
1088        for struct_def in &program.structs {
1089            content.push_str("#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]\n");
1090            content.push_str(&format!(
1091                "pub struct {} {{\n",
1092                to_pascal_case(&struct_def.name)
1093            ));
1094            for field in &struct_def.fields {
1095                content.push_str(&format!(
1096                    "    pub {}: {},\n",
1097                    to_snake_case(&field.name),
1098                    self.type_to_rust(&field.ty)
1099                ));
1100            }
1101            content.push_str("}\n\n");
1102        }
1103
1104        // Generate state account struct with InitSpace derive for automatic space calculation
1105        content.push_str("#[account]\n");
1106        content.push_str("#[derive(InitSpace)]\n");
1107        content.push_str(&format!(
1108            "pub struct {}State {{\n",
1109            to_pascal_case(&program.name)
1110        ));
1111
1112        for field in &program.state.fields {
1113            // Add #[max_len] attribute for dynamic types (String, Vec, etc.)
1114            if let Some(max_len_attr) = self.get_max_len_attribute(&field.ty) {
1115                content.push_str(&format!("    {}\n", max_len_attr));
1116            }
1117            content.push_str(&format!(
1118                "    pub {}: {},\n",
1119                to_snake_case(&field.name),
1120                self.type_to_rust(&field.ty)
1121            ));
1122        }
1123
1124        content.push_str("}\n\n");
1125
1126        // Generate mapping entry account structs with InitSpace
1127        for mapping in &program.mappings {
1128            let struct_name = format!("{}Entry", to_pascal_case(&mapping.name));
1129            // For nested mappings, get the innermost value type
1130            let innermost_ty = self.innermost_value_type(&mapping.value_ty);
1131            let value_type = self.type_to_rust(&innermost_ty);
1132            let key_type = self.type_to_rust(&mapping.key_ty);
1133
1134            // Check if key or value need max_len attributes
1135            let key_max_len = self.get_max_len_attribute(&mapping.key_ty);
1136            let value_max_len = self.get_max_len_attribute(&innermost_ty);
1137
1138            content.push_str(&format!(
1139                "/// PDA account for {} mapping entries\n#[account]\n#[derive(InitSpace)]\npub struct {} {{\n",
1140                mapping.name,
1141                struct_name,
1142            ));
1143
1144            // Key field with optional max_len
1145            if let Some(attr) = key_max_len {
1146                content.push_str(&format!("    {}\n", attr));
1147            }
1148            content.push_str(&format!(
1149                "    /// The key for this entry\n    pub key: {},\n",
1150                key_type
1151            ));
1152
1153            // Value field with optional max_len
1154            if let Some(attr) = value_max_len {
1155                content.push_str(&format!("    {}\n", attr));
1156            }
1157            content.push_str(&format!(
1158                "    /// The value stored at this key\n    pub value: {},\n",
1159                value_type
1160            ));
1161
1162            content.push_str("}\n\n");
1163        }
1164
1165        Ok(content)
1166    }
1167
1168    /// Generate #[max_len(...)] attribute for dynamic types
1169    /// Returns None for fixed-size types that don't need the attribute
1170    fn get_max_len_attribute(&self, ty: &SolanaType) -> Option<String> {
1171        match ty {
1172            SolanaType::String => Some("#[max_len(200)]".to_string()),
1173            SolanaType::Bytes => Some("#[max_len(1000)]".to_string()),
1174            SolanaType::Vec(elem) => {
1175                // For Vec<T>, we need max_len for the outer vec
1176                // and potentially nested max_len for the element if it's dynamic
1177                if self.get_max_len_attribute(elem).is_some() {
1178                    // Nested dynamic type - use (outer_len, inner_len) format
1179                    let inner_len = match elem.as_ref() {
1180                        SolanaType::String => 200,
1181                        SolanaType::Bytes => 1000,
1182                        _ => 100,
1183                    };
1184                    Some(format!("#[max_len(100, {})]", inner_len))
1185                } else {
1186                    Some("#[max_len(100)]".to_string())
1187                }
1188            }
1189            SolanaType::Option(inner) => self.get_max_len_attribute(inner),
1190            _ => None, // Fixed-size types don't need max_len
1191        }
1192    }
1193
1194    fn generate_instructions_rs(&self, program: &SolanaProgram) -> Result<String, CodegenError> {
1195        // Check if any public instruction uses token program
1196        let uses_token = program
1197            .instructions
1198            .iter()
1199            .filter(|i| i.is_public)
1200            .any(|i| i.uses_token_program);
1201
1202        let mut content =
1203            String::from("//! Instruction account contexts\n\nuse anchor_lang::prelude::*;\n");
1204
1205        if uses_token {
1206            content.push_str("use anchor_spl::token::Token;\n");
1207        }
1208
1209        content.push_str("use crate::state::*;\n\n");
1210
1211        // Generate context struct only for public instructions
1212        for instruction in &program.instructions {
1213            if instruction.is_public {
1214                content.push_str(&self.generate_context_struct(instruction, program)?);
1215                content.push('\n');
1216            }
1217        }
1218
1219        Ok(content)
1220    }
1221
1222    fn generate_context_struct(
1223        &self,
1224        instruction: &Instruction,
1225        program: &SolanaProgram,
1226    ) -> Result<String, CodegenError> {
1227        let name = to_pascal_case(&instruction.name);
1228        let state_name = format!("{}State", to_pascal_case(&program.name));
1229
1230        // Collect instruction params used in mapping seeds
1231        let mut seed_params: Vec<(&String, &SolanaType)> = Vec::new();
1232        for access in &instruction.mapping_accesses {
1233            for key_expr in &access.key_exprs {
1234                self.collect_seed_params(key_expr, instruction, &mut seed_params);
1235            }
1236        }
1237
1238        let mut content = String::new();
1239        content.push_str("#[derive(Accounts)]\n");
1240
1241        // Add #[instruction(...)] if there are params used in seeds
1242        if !seed_params.is_empty() {
1243            let params_str: Vec<String> = seed_params
1244                .iter()
1245                .map(|(name, ty)| format!("{}: {}", to_snake_case(name), self.type_to_rust(ty)))
1246                .collect();
1247            content.push_str(&format!("#[instruction({})]\n", params_str.join(", ")));
1248        }
1249
1250        content.push_str(&format!("pub struct {}<'info> {{\n", name));
1251
1252        // State account
1253        if instruction.name == "initialize" {
1254            content.push_str(&format!(
1255                r#"    #[account(
1256        init,
1257        payer = signer,
1258        space = 8 + {}::INIT_SPACE
1259    )]
1260    pub state: Account<'info, {}>,
1261"#,
1262                state_name, state_name
1263            ));
1264        } else if instruction.is_view {
1265            content.push_str(&format!("    pub state: Account<'info, {}>,\n", state_name));
1266        } else if instruction.closes_state {
1267            // Selfdestruct: close the state account and send rent to signer
1268            content.push_str(&format!(
1269                "    #[account(mut, close = signer)]\n    pub state: Account<'info, {}>,\n",
1270                state_name
1271            ));
1272        } else {
1273            content.push_str(&format!(
1274                "    #[account(mut)]\n    pub state: Account<'info, {}>,\n",
1275                state_name
1276            ));
1277        }
1278
1279        // Signer
1280        content.push_str("    #[account(mut)]\n");
1281        content.push_str("    pub signer: Signer<'info>,\n");
1282
1283        // Add additional signers for parameters with Signer type
1284        for param in &instruction.params {
1285            if matches!(param.ty, SolanaType::Signer) {
1286                content.push_str(&format!(
1287                    "    pub {}: Signer<'info>,\n",
1288                    to_snake_case(&param.name)
1289                ));
1290            }
1291        }
1292
1293        // Add PDA accounts for mapping accesses
1294        for access in &instruction.mapping_accesses {
1295            let entry_type = format!("{}Entry", to_pascal_case(&access.mapping_name));
1296            let account_name = to_snake_case(&access.account_name);
1297
1298            // Generate the key expressions for seeds (handles nested mappings)
1299            let key_seeds: Vec<String> = access
1300                .key_exprs
1301                .iter()
1302                .map(|k| self.generate_key_seed_expr(k))
1303                .collect::<Result<Vec<_>, _>>()?;
1304            let seeds_str = key_seeds
1305                .iter()
1306                .map(|s| format!("{}.as_ref()", s))
1307                .collect::<Vec<_>>()
1308                .join(", ");
1309
1310            if access.should_close {
1311                // Close the PDA and return lamports to signer
1312                content.push_str(&format!(
1313                    r#"    #[account(
1314        mut,
1315        close = signer,
1316        seeds = [b"{}", {}],
1317        bump
1318    )]
1319    pub {}: Account<'info, {}>,
1320"#,
1321                    to_snake_case(&access.mapping_name),
1322                    seeds_str,
1323                    account_name,
1324                    entry_type
1325                ));
1326            } else if access.is_write {
1327                // Use init_if_needed for write accesses
1328                content.push_str(&format!(
1329                    r#"    #[account(
1330        init_if_needed,
1331        payer = signer,
1332        space = 8 + {}::INIT_SPACE,
1333        seeds = [b"{}", {}],
1334        bump
1335    )]
1336    pub {}: Account<'info, {}>,
1337"#,
1338                    entry_type,
1339                    to_snake_case(&access.mapping_name),
1340                    seeds_str,
1341                    account_name,
1342                    entry_type
1343                ));
1344            } else {
1345                // Read-only access
1346                content.push_str(&format!(
1347                    r#"    #[account(
1348        seeds = [b"{}", {}],
1349        bump
1350    )]
1351    pub {}: Account<'info, {}>,
1352"#,
1353                    to_snake_case(&access.mapping_name),
1354                    seeds_str,
1355                    account_name,
1356                    entry_type
1357                ));
1358            }
1359        }
1360
1361        // Recipient account (needed for SOL transfers)
1362        // The recipient must be passed as an UncheckedAccount to receive SOL
1363        if instruction.uses_sol_transfer {
1364            content.push_str(
1365                "    /// CHECK: Recipient account for SOL transfer, validated by the caller\n",
1366            );
1367            content.push_str("    #[account(mut)]\n");
1368            content.push_str("    pub recipient: UncheckedAccount<'info>,\n");
1369        }
1370
1371        // System program (needed if any init_if_needed is used, for payable functions, or for SOL transfers)
1372        let needs_system_program = instruction.name == "initialize"
1373            || instruction.mapping_accesses.iter().any(|a| a.is_write)
1374            || instruction.is_payable
1375            || instruction.uses_sol_transfer;
1376        if needs_system_program {
1377            content.push_str("    pub system_program: Program<'info, System>,\n");
1378        }
1379
1380        // Token program (needed if any SPL token operations are used)
1381        if instruction.uses_token_program {
1382            content.push_str("    pub token_program: Program<'info, Token>,\n");
1383        }
1384
1385        content.push_str("}\n");
1386
1387        Ok(content)
1388    }
1389
1390    /// Generate the seed expression for a mapping key (used in #[account] attributes)
1391    fn generate_key_seed_expr(&self, key_expr: &Expression) -> Result<String, CodegenError> {
1392        match key_expr {
1393            // In account attributes, we reference accounts directly without ctx.accounts prefix
1394            Expression::MsgSender => Ok("signer.key()".to_string()),
1395            Expression::Var(name) => Ok(to_snake_case(name)),
1396            Expression::Literal(Literal::Pubkey(s)) => Ok(format!("Pubkey::default() /* {} */", s)),
1397            Expression::Literal(Literal::ZeroAddress) => Ok("Pubkey::default()".to_string()),
1398            Expression::Literal(Literal::ZeroBytes(n)) => Ok(format!("[0u8; {}]", n)),
1399            Expression::StateAccess(field) => {
1400                // For state field access in seeds, reference via state account
1401                Ok(format!("state.{}", to_snake_case(field)))
1402            }
1403            _ => {
1404                // For other expressions, try to simplify for seed context
1405                // This may need refinement for complex cases
1406                let expr_str = self.generate_expression(key_expr)?;
1407                // Remove ctx.accounts. prefix if present
1408                Ok(expr_str.replace("ctx.accounts.", ""))
1409            }
1410        }
1411    }
1412
1413    /// Collect instruction parameters that are used in seed expressions
1414    fn collect_seed_params<'a>(
1415        &self,
1416        key_expr: &'a Expression,
1417        instruction: &'a Instruction,
1418        params: &mut Vec<(&'a String, &'a SolanaType)>,
1419    ) {
1420        match key_expr {
1421            Expression::Var(name) => {
1422                // Check if this var is an instruction parameter
1423                if let Some(param) = instruction.params.iter().find(|p| &p.name == name) {
1424                    // Avoid duplicates
1425                    if !params.iter().any(|(n, _)| *n == name) {
1426                        params.push((&param.name, &param.ty));
1427                    }
1428                }
1429            }
1430            Expression::MethodCall { receiver, args, .. } => {
1431                self.collect_seed_params(receiver, instruction, params);
1432                for arg in args {
1433                    self.collect_seed_params(arg, instruction, params);
1434                }
1435            }
1436            Expression::Binary { left, right, .. } => {
1437                self.collect_seed_params(left, instruction, params);
1438                self.collect_seed_params(right, instruction, params);
1439            }
1440            _ => {}
1441        }
1442    }
1443
1444    fn generate_error_rs(&self, program: &SolanaProgram) -> Result<String, CodegenError> {
1445        let mut content = String::from(
1446            r#"//! Custom error definitions
1447
1448use anchor_lang::prelude::*;
1449
1450#[error_code]
1451pub enum CustomError {
1452    #[msg("Requirement failed")]
1453    RequireFailed,
1454    #[msg("Invalid recipient account")]
1455    InvalidRecipient,
1456"#,
1457        );
1458
1459        // Add custom errors from the program
1460        for error in &program.errors {
1461            content.push_str(&format!(
1462                "    #[msg(\"{}\")]\n    {},\n",
1463                error.name,
1464                to_pascal_case(&error.name)
1465            ));
1466        }
1467
1468        content.push_str("}\n");
1469
1470        Ok(content)
1471    }
1472
1473    fn generate_events_rs(&self, program: &SolanaProgram) -> Result<String, CodegenError> {
1474        let mut content = String::from(
1475            r#"//! Event definitions
1476
1477use anchor_lang::prelude::*;
1478
1479"#,
1480        );
1481
1482        for event in &program.events {
1483            content.push_str("#[event]\n");
1484            content.push_str(&format!("pub struct {} {{\n", to_pascal_case(&event.name)));
1485
1486            for field in &event.fields {
1487                // Note: #[index] is not supported in Anchor events
1488                // Indexed fields in Solidity become regular fields
1489                content.push_str(&format!(
1490                    "    pub {}: {},\n",
1491                    to_snake_case(&field.name),
1492                    self.type_to_rust(&field.ty)
1493                ));
1494            }
1495
1496            content.push_str("}\n\n");
1497        }
1498
1499        Ok(content)
1500    }
1501
1502    fn generate_anchor_toml(&self, program: &SolanaProgram) -> String {
1503        let name = to_snake_case(&program.name);
1504        format!(
1505            r#"[features]
1506seeds = false
1507skip-lint = false
1508
1509[programs.localnet]
1510{} = "11111111111111111111111111111111"
1511
1512[registry]
1513url = "https://api.apr.dev"
1514
1515[provider]
1516cluster = "localnet"
1517wallet = "~/.config/solana/id.json"
1518
1519[scripts]
1520test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
1521"#,
1522            name
1523        )
1524    }
1525
1526    fn generate_cargo_toml(&self, program: &SolanaProgram) -> String {
1527        let name = to_snake_case(&program.name);
1528        let uses_token = program.instructions.iter().any(|i| i.uses_token_program);
1529
1530        let mut deps = String::from(
1531            "anchor-lang = { version = \"0.32.0\", features = [\"init-if-needed\"] }\n",
1532        );
1533        if uses_token {
1534            deps.push_str("anchor-spl = \"0.32.0\"\n");
1535        }
1536
1537        format!(
1538            r#"[package]
1539name = "{}"
1540version = "0.1.0"
1541description = "Generated by SolScript compiler"
1542edition = "2021"
1543
1544[lib]
1545crate-type = ["cdylib", "lib"]
1546name = "{}"
1547
1548[features]
1549no-entrypoint = []
1550no-idl = []
1551no-log-ix-name = []
1552cpi = ["no-entrypoint"]
1553default = []
1554
1555[dependencies]
1556{}
1557"#,
1558            name, name, deps
1559        )
1560    }
1561
1562    fn type_to_rust(&self, ty: &SolanaType) -> String {
1563        match ty {
1564            SolanaType::U8 => "u8".to_string(),
1565            SolanaType::U16 => "u16".to_string(),
1566            SolanaType::U32 => "u32".to_string(),
1567            SolanaType::U64 => "u64".to_string(),
1568            SolanaType::U128 => "u128".to_string(),
1569            SolanaType::I8 => "i8".to_string(),
1570            SolanaType::I16 => "i16".to_string(),
1571            SolanaType::I32 => "i32".to_string(),
1572            SolanaType::I64 => "i64".to_string(),
1573            SolanaType::I128 => "i128".to_string(),
1574            SolanaType::Bool => "bool".to_string(),
1575            SolanaType::Pubkey => "Pubkey".to_string(),
1576            SolanaType::Signer => "Pubkey".to_string(), // Signers are Pubkeys in function params
1577            SolanaType::String => "String".to_string(),
1578            SolanaType::Bytes => "Vec<u8>".to_string(),
1579            SolanaType::FixedBytes(n) => format!("[u8; {}]", n),
1580            SolanaType::Array(elem, size) => format!("[{}; {}]", self.type_to_rust(elem), size),
1581            SolanaType::Vec(elem) => format!("Vec<{}>", self.type_to_rust(elem)),
1582            SolanaType::Option(inner) => format!("Option<{}>", self.type_to_rust(inner)),
1583            SolanaType::Mapping(_, _) => "/* Mapping - use PDAs */".to_string(),
1584            SolanaType::Custom(name) => to_pascal_case(name),
1585        }
1586    }
1587
1588    /// Get the innermost value type for nested mappings
1589    /// For `mapping(A => mapping(B => C))`, returns `C`
1590    fn innermost_value_type(&self, ty: &SolanaType) -> SolanaType {
1591        match ty {
1592            SolanaType::Mapping(_, value_ty) => self.innermost_value_type(value_ty),
1593            other => other.clone(),
1594        }
1595    }
1596
1597    fn generate_package_json(&self, program: &SolanaProgram) -> String {
1598        let name = to_snake_case(&program.name);
1599        format!(
1600            r#"{{
1601  "name": "{}-client",
1602  "version": "0.1.0",
1603  "description": "Generated client for {} Solana program",
1604  "main": "app/client.ts",
1605  "scripts": {{
1606    "test": "anchor test",
1607    "build": "anchor build",
1608    "deploy": "anchor deploy"
1609  }},
1610  "dependencies": {{
1611    "@coral-xyz/anchor": "^0.32.0",
1612    "@solana/web3.js": "^1.95.0"
1613  }},
1614  "devDependencies": {{
1615    "@types/chai": "^4.3.0",
1616    "@types/mocha": "^10.0.0",
1617    "chai": "^4.3.0",
1618    "mocha": "^10.2.0",
1619    "ts-mocha": "^10.0.0",
1620    "typescript": "^5.0.0"
1621  }}
1622}}
1623"#,
1624            name, program.name
1625        )
1626    }
1627
1628    fn generate_readme(&self, program: &SolanaProgram) -> String {
1629        let name = &program.name;
1630        let _snake_name = to_snake_case(name);
1631
1632        // Count public functions
1633        let public_fns: Vec<&str> = program
1634            .instructions
1635            .iter()
1636            .filter(|i| i.is_public)
1637            .map(|i| i.name.as_str())
1638            .collect();
1639
1640        let fn_list = public_fns
1641            .iter()
1642            .map(|f| format!("- `{}`", to_snake_case(f)))
1643            .collect::<Vec<_>>()
1644            .join("\n");
1645
1646        format!(
1647            r#"# {} Solana Program
1648
1649Generated by [SolScript](https://github.com/cryptuon/solscript) compiler.
1650
1651## Overview
1652
1653This is an Anchor-based Solana program with a TypeScript client.
1654
1655## Project Structure
1656
1657```
1658.
1659├── programs/
1660│   └── solscript_program/
1661│       └── src/
1662│           ├── lib.rs          # Main program entry
1663│           ├── state.rs        # Account state definitions
1664│           ├── instructions.rs # Instruction contexts
1665│           ├── error.rs        # Custom errors
1666│           └── events.rs       # Event definitions
1667├── app/
1668│   └── client.ts               # TypeScript client
1669├── tests/
1670│   └── program.test.ts         # Anchor tests
1671├── target/
1672│   └── idl/
1673│       └── program.json        # Anchor IDL
1674├── Anchor.toml
1675├── Cargo.toml
1676└── package.json
1677```
1678
1679## Available Instructions
1680
1681{}
1682
1683## Getting Started
1684
1685### Prerequisites
1686
1687- [Rust](https://www.rust-lang.org/tools/install)
1688- [Solana CLI](https://docs.solana.com/cli/install-solana-cli-tools)
1689- [Anchor](https://www.anchor-lang.com/docs/installation)
1690- [Node.js](https://nodejs.org/)
1691
1692### Build
1693
1694```bash
1695anchor build
1696```
1697
1698### Test
1699
1700```bash
1701anchor test
1702```
1703
1704### Deploy
1705
1706```bash
1707anchor deploy
1708```
1709
1710## Usage
1711
1712See `app/client.ts` for the TypeScript client implementation.
1713
1714```typescript
1715import {{ {}Client }} from './app/client';
1716
1717// Initialize client with provider
1718const client = new {}Client(provider);
1719
1720// Call instructions...
1721```
1722"#,
1723            name,
1724            fn_list,
1725            to_pascal_case(name),
1726            to_pascal_case(name)
1727        )
1728    }
1729
1730    fn generate_gitignore(&self) -> String {
1731        r#"# Anchor
1732target/
1733.anchor/
1734node_modules/
1735
1736# Rust
1737Cargo.lock
1738**/*.rs.bk
1739
1740# IDE
1741.idea/
1742.vscode/
1743*.swp
1744*.swo
1745
1746# OS
1747.DS_Store
1748Thumbs.db
1749
1750# Solana
1751test-ledger/
1752.env
1753
1754# TypeScript
1755dist/
1756*.js
1757*.d.ts
1758*.map
1759!anchor.js
1760"#
1761        .to_string()
1762    }
1763}
1764
1765// Helper functions
1766fn to_snake_case(s: &str) -> String {
1767    let mut result = String::new();
1768    let mut prev_upper = false;
1769
1770    for (i, c) in s.chars().enumerate() {
1771        if c.is_uppercase() {
1772            if i > 0 && !prev_upper {
1773                result.push('_');
1774            }
1775            result.push(c.to_lowercase().next().unwrap());
1776            prev_upper = true;
1777        } else {
1778            result.push(c);
1779            prev_upper = false;
1780        }
1781    }
1782
1783    result
1784}
1785
1786fn to_pascal_case(s: &str) -> String {
1787    let mut result = String::new();
1788    let mut capitalize_next = true;
1789
1790    for c in s.chars() {
1791        if c == '_' {
1792            capitalize_next = true;
1793        } else if capitalize_next {
1794            result.push(c.to_uppercase().next().unwrap());
1795            capitalize_next = false;
1796        } else {
1797            result.push(c);
1798        }
1799    }
1800
1801    result
1802}