How to Use AI to Build Solana Apps
Artificial Intelligence (AI) is changing how we build software. It can spot and fix bugs, spin up new features, and handle some chores we'd rather not do. Tools like Copilot and Cursor make coding faster and less tedious. But they don't always function well with Solana.
Solana's architecture sets it apart from most blockchains. It processes transactions in parallel, relies on accounts rather than contract-owned state, and uses Program-Derived Addresses (PDAs) for extra security. If you've read our blog posts, you’ll know building on Solana is different from Ethereum. Solana offers high throughput and has a unique approach to account management. This distinction often confuses AI models trained on different coding patterns.
This article will explore why AI sometimes struggles with Solana. It will also provide ways to avoid those issues. Our primary focus will be on:
- Prompt engineering: Writing better instructions for AI.
- Contextual inputs: Supplying your project's details so AI outputs are more accurate.
- Iterative workflows: Build code in small steps. Check and refine before moving on.
Whether new or experienced, these tips will help you get more from AI while working on Solana.
Challenges with AI in Solana Development
Solana's unique architecture and requirements may cause issues for AI. If AI doesn't account for them, you'll waste time debugging your code or rewriting your programs, which defeats the purpose of using AI to streamline development.
Solana's Unique Architecture
Solana sets itself apart in three main ways:
1. Account-Based Model
Many blockchains, like Ethereum, tie data directly to a smart contract's internal storage. Solana’s programming model, on the other hand, organizes everything around "accounts." These accounts hold the data while "programs" (Solana's equivalent of smart contracts) modify and interact with that data.
A key distinction is that on Solana, everything is an account, even the programs themselves. Programs are simply accounts that store executable data, which allows them to process instructions. In contrast, Ethereum distinguishes between externally owned accounts (EOAs), controlled by private keys, and smart contracts, which have their code and internal storage.
This fundamental difference can lead to misunderstandings, especially when writing code for Solana. As with Ethereum smart contracts, developers who are unfamiliar with the account model might incorrectly assume that a program can own its data. Such assumptions can result in runtime errors or security vulnerabilities when deploying Solana.
2. Program Derived Addresses (PDAs)
A Program-Derived Address (PDA) is created from a program ID and a seed. PDAs let programs organize accounts without requiring a private key. However, AI models might not see the difference between a standard address and a PDA. If they forget to handle seeds or check for ownership, the PDA will be the correct Rust code, but it won't be the correct Solana code.
3. Parallel Execution
Solana’s runtime, Sealevel, lets it process transactions in parallel, unlike traditional blockchains that handle transactions one at a time. This works because transactions specify which accounts they will read and write to. With this information, Solana can determine which accounts need locks and safely execute many transactions simultaneously.
AI often misunderstands this model. It might assume transactions will run sequentially, like in the Ethereum Virtual Machine (EVM). This could lead to code that doesn’t optimize for parallelism. Since Solana’s account-based locking system differs from the EVM’s approach, AI might write code that performs poorly or doesn’t scale well.
Common Pain Points
There are a few recurring issues developers face when using AI to build Solana apps:
1. Incorrect Code
AI might deliver functions that compile in Rust yet fail when you deploy them to Solana. For instance, AI pair programming tools could overlook signers or misinterpret account references. The result? Bugs or security vulnerabilities that you can't spot until you run tests, actual transactions, or complete a security audit.
2. Suboptimal Code
Even if the AI-generated code works, it might not be optimized for compute units, lead to slow transaction processing, or introduce hidden bottlenecks that can’t be seen. Imagine an AI writing a function to update accounts one at a time. It fails to recognize that Solana can handle them concurrently if implemented correctly.
3. Tough Debugging
AI models don't know your folder structure or how you've defined your custom data types unless you tell them. This means they might propose solutions that clash with your existing setup, and merging those solutions back into your project can become a headache.
How to Use AI for Solana Development
Follow these recommendations to build Solana apps using AI:
1. Start with a Strong System Prompt
A system prompt is like the AI's job description. It tells the AI its role (e.g. "You are an expert in Solana programming using Anchor...”) and outlines the rules you want it to follow.
Here's a typical example, slightly expanded:
# You are an expert in Solana program development.
Specializing in building and deploying smart contracts using Rust and Anchor,
and integrating on-chain data with @solana/web3.js.
---
## General Guidelines
- Write secure, efficient, and maintainable code for Solana programs.
- Thoroughly test and audit all programs before deployment.
## Solana Program Development with Rust and Anchor
- Prioritize Rust's safety and performance.
- Use Anchor macros to simplify account management, error handling, and
data serialization.
- Keep code modular and maintain a clear separation between logic and data.
## Security and Best Practices
- Enforce strict access controls--ensure only permitted signers can modify data.
- Use PDAs responsibly: validate seeds and ownership checks to prevent conflicts.
## Performance and Optimization
- Minimize transaction costs by bundling operations efficiently.
- Exploit parallelism--don't serialize steps unnecessarily.
- Regularly benchmark your code to spot and remove performance
Why it Works
A well-written system prompt works like a blueprint—it aligns the AI with Solana's architecture. This ensures that every response is accurate, secure, and tailored to your needs.
- Sets the right context: Declaring the AI a "Solana expert" ensures it focuses on Solana-specific features like PDAs, parallelism, and Anchor, rather than making generic blockchain assumptions.
- Produces consistent code: Including best practices like strict access controls and parallel execution guides the AI to create secure, reliable, and predictable outputs.
- Avoids common mistakes: Highlighting Rust safety, Anchor macros, and serialization reduces errors. It saves you time debugging and rewriting code.
- Plays to Solana's strengths: The AI must increase bandwidth and reduce latency. This will optimize code for Solana's high throughput and efficiency.
2. Write Clear Prompts
When asking AI to generate code, precision is key. The more precise you are, the better your results will be. Compare these examples:
Bad Prompt:
"Write a function to initialize an account."
This prompt is too vague. The AI doesn’t know the programming language, framework, or that this prompt is meant for Solana code. It might give you generic or useless code.
Good Prompt:
"Write a function in Rust, using Anchor, to initialize a Solana token account. Use a PDA derived from a seed. Validate the account state and handle errors if the account is already initialized."
Here’s why the good prompt works:
Clear prompts help save time and effort. By clarifying AI’s goals, you prevent the program from having to be corrected later for creating sloppy, unclear, or misaimed code. It also minimizes misinterpretations, making work with AI more effective.
- It’s clear and direct: By naming Rust, Anchor, and PDAs directly, the AI knows exactly what tools and features to use. It won’t guess or include irrelevant details.
- It focuses on Solana’s needs: Mentioning a PDA tells the AI to include Solana-specific logic. Without this, the code might miss important steps like deriving addresses securely.
- It adds depth where needed: The validation and error-handling instructions tell the AI to go beyond the basics. This helps ensure code works in real scenarios.
A good prompt is like a map that guides the AI and helps it avoid pitfalls to reach its goals.
3. Provide the Proper Context
AI works better when you give it the proper context. Many tools let you upload code or link files. This helps the AI understand your project and create solutions that fit your codebase.
But don’t overload it. Feeding the AI too much information—like an entire large codebase—can cause it to hallucinate or produce irrelevant responses.
Instead, focus on giving the relevant parts of your code. This keeps the AI focused and improves the quality of its suggestions.
For example, imagine your app already has a file defining a custom data structure:
#[account]
pub struct TokenAccount {
pub balance: u64,
pub is_initialized: bool,
// ... more fields
}
If you upload this file or include it as part of your input, the AI can use your exact field names (balance
, is_initialized
) in its generated code.
It can also align its suggestions with your project's structure and conventions.
Here’s why providing context works
- Improves accuracy: Without context, the AI might guess field names, introduce unnecessary changes, or assume the wrong data types. For instance, it might call your balance field amount or use an f64 instead of a u64. Providing the actual struct removes this guesswork.
- Keeps code consistent: When the AI understands your naming conventions, it produces code that fits naturally into your project. This saves you the trouble of renaming variables or reworking code to match your existing patterns.
- Reduces integration work: If the AI sees your account structures, it can tailor its output to integrate with them directly. This avoids situations where AI-generated code needs major rewrites to fit your existing logic.
Example: How to Add Context to Improve AI Outputs
Without proper context, a prompt like “Write a function to update the token account balance” might result in AI-generated code like this:
pub fn update_balance(ctx: Context<UpdateAccount>, amount: f64) -> Result<()> {
let account = &mut ctx.accounts.token_account;
account.amount += amount;
Ok(())
}
This code has two issues:
First, it uses f64
for amount instead of u64
.
Solana has very limited support for native floating-point arithmetic. The preferred approach is to use fixed-point arithmetic and scale numbers based on the decimals used, which avoids performance and compatibility issues.
Second, it calls the balance field amount, which doesn’t match your struct. This can lead to confusion and bugs in the future.
Now, if you provide your TokenAccount struct as context, the AI might generate this instead:
pub fn update_balance(ctx: Context<UpdateAccount>, amount: u64) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
token_account.balance += amount;
Ok(())
}
This output aligns much better with your project.
However, even with this improvement, it’s worth adding an overflow check on the token_account.balance += amount
operation to ensure your code is robust and safe.
How to Provide Context
Remember these steps to provide your AI tools with the proper context:
- Upload files: If your AI tool allows it, upload your struct definitions, account schemas, and other project files.
- Reference code in prompts: Copy and paste relevant snippets into your prompts so the AI can use them as a reference.
- Describe the structure: If uploading isn’t an option, describe your data clearly in the prompt. For example, “I have a struct called TokenAccount with fields balance (u64) and is_initialized (bool).”
4. Build on Existing Code
You don’t need to start from scratch every time. Often, you already have a piece of code that works but could be better. AI can help you improve it. Here’s an example:
Here’s the original code:
pub fn create_token_account(ctx: Context<CreateAccount>) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = 0;
Ok(())
}
This code initializes a token account and sets the balance to zero. It's simple but doesn't handle scenarios like updating an existing account or checking ownership.
Your prompt:
"Make this function more robust. If the token account exists, add it to the balance. If not, initialize it with a specific amount. Also, ensure the current program owns the account."
Here’s the AI-generated code after your prompt:
pub fn create_or_update_token_account(
ctx: Context<CreateAccount>,
amount: u64
) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
// Check ownership
if token_account.owner != ctx.program_id {
return err!(ProgramError::IncorrectProgramId);
}
// Update or initialize
if token_account.is_initialized {
token_account.balance += amount;
} else {
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
Why it Works
- Addresses common scenarios: The new code handles initialized and uninitialized accounts, making it more useful in real applications.
- Adds ownership validation: This prevents unauthorized changes because the program checks whether it owns that account.
- Makes the function smarter: The function can now update the balance instead of resetting it to zero every time. This reduces redundancy and fits real-world use cases.
- Saves time: You didn’t have to rewrite the whole function. The AI expanded on what you already had, making it faster to get from a basic function to a robust one.
How to Use this Approach
Remember these steps to follow this approach in the future:
- Start with a working base: Identify functions or snippets that work but could be better.
- Be specific in your prompt: Explain what you want to improve, such as adding error checks, handling edge cases, or improving logic.
- Review AI suggestions: Look at the AI-generated output to ensure it fits your project and follows Solana’s best practices.
- Iterate as needed: If the first output isn’t perfect, adjust your prompt or edit the code to get closer to your goal.
5. Iterate Development One Step at a Time
Building code step by step helps you avoid mistakes and refine your work as you go. Instead of trying to create a complex function all at once, break it into smaller tasks. This makes it easier to test and improve each part before moving on.
Example Workflow
Let’s start small. Ask the AI for a simple function to compute a Program Derived Address (PDA) using a seed.
Here’s an example prompt:
"Write a Rust function that computes a PDA from a seed and a program ID using Anchor."
AI’s Output:
pub fn find_pda(seed: &[u8], program_id: &Pubkey) -> Pubkey {
Pubkey::create_program_address(&[seed], program_id).unwrap()
}
This is a good starting point, but it isn't complete. Let’s add error handling.
Review the code and identify issues. For example, using unwrap() can cause the program to crash if the seed is invalid.
Prompt the AI to fix this:
"Update this function to include error handling for invalid seeds. Return a Result instead of unwrapping."
Updated Code:
pub fn find_pda(seed: &[u8], program_id: &Pubkey) -> Result<Pubkey, ProgramError> {
Pubkey::create_program_address(&[seed], program_id)
.map_err(|_| ProgramError::InvalidSeeds)
}
Now, the function handles invalid seeds gracefully by returning an error instead of panicking.
Next, let’s integrate the function.
Ask the AI to use the PDA function in a larger routine. For example:
"Write a function that uses this PDA to create a new token account. Validate that the PDA is not already in use."
Here’s the AI’s output:
pub fn create_token_account(ctx: Context<CreateAccount>, seed: &[u8], amount: u64) -> Result<()> {
let program_id = ctx.program_id;
let pda = find_pda(seed, &program_id)?;
if ctx.accounts.token_account.key != pda {
return Err(ProgramError::InvalidSeeds);
}
if !ctx.accounts.token_account.is_initialized {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
This step integrates the PDA logic into account creation while ensuring the PDA isn’t reused incorrectly.
Now, let’s optimize and finalize the code. Ask the AI to refine the function further using a prompt like:
"Optimize this function to minimize transaction costs and include ownership validation."
Here’s the AI’s output:
pub fn create_token_account(ctx: Context<CreateAccount>, seed: &[u8], amount: u64) -> Result<()> {
let program_id = ctx.program_id;
let pda = find_pda(seed, &program_id)?;
if ctx.accounts.token_account.key != pda || ctx.accounts.token_account.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
if !ctx.accounts.token_account.is_initialized {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
The final function is now efficient, secure, and aligned with Solana’s best practices.
Why This Approach Works
- Keeps things manageable: Focusing on one small task at a time avoids overwhelming the AI. Each step builds on the last, making it easier to test and refine.
- Catches errors early: Smaller steps mean you can spot problems before they impact the rest of the code. For example, adding error handling early prevents crashes later.
- Promotes iteration: If something doesn’t work perfectly, you can adjust the function without reworking the entire codebase.
- Produces better code: Each refinement makes the code more robust, and you end up with a solution that’s more reliable and easier to maintain.
How to Use This Approach
Remember these tips the next time you’re pair programming with AI:
- Break tasks into smaller steps: Instead of requesting a single complex function, break it down into simpler, more manageable tasks, and execute them one after another.
- Test results after each step: Run the code after each change to confirm it works. If it doesn't, fix the problem or adjust the prompt before moving on to the next step.
- Refine along the way: Do not limit yourself to a single result. Ask the AI to optimize or add features like validations, performance improvements, or better error handling.
Conclusion: Start Working with AI on Solana
Using AI to build on Solana can be incredibly powerful—if you take a careful and thoughtful approach. The architecture of Solana is different from that of other blockchains—just like it uses PDAs, parallel transaction processing, and account-based design. This implies that you will need to lead the AI correctly.
Start with clear prompts. Tell the AI precisely what you want, and be specific about tools like Rust, Anchor, and PDAs. A good prompt might say:
“Write a Rust function to initialize a Solana token account with a PDA, validate the account state, and handle errors.”
This clarity gives the AI a better chance of producing code that works for Solana.
Always provide context. Share your project’s code or explain the structures you’re using, like account schemas or data types. For example, if you have a TokenAccount
struct, include its details so the AI can align its output. But don’t overwhelm it—focus on relevant parts to keep suggestions accurate and valuable.
Break tasks into smaller steps. Use an iterative process to refine the code as you go. Start with simple tasks, like computing a PDA, then build on that with error handling or account validation. Test after each step. By doing this, you can catch problems early and move forward steadily.
Finally, go through the AI work carefully. Most of the time, AI-generated code has minor issues, too, like missing overflow checks or skipped validations. Ensure the output follows Solana's rules, stays bug-free, and uses parallel execution capabilities.
The AI is just a starting point; you must finish the job. Using clear instructions, context, step-by-step development, and careful review, you can use AI to create secure and optimized Solana programs. Stay involved in the process, and the AI will become a valuable tool to help you save time and write better code.
Additional Reading
To learn more about building with AI on Solana, explore these resources:
Related Articles
Subscribe to Helius
Stay up-to-date with the latest in Solana development and receive updates when we post