agent-bober 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +495 -0
  4. package/agents/bober-evaluator.md +323 -0
  5. package/agents/bober-generator.md +245 -0
  6. package/agents/bober-planner.md +248 -0
  7. package/dist/cli/commands/eval.d.ts +6 -0
  8. package/dist/cli/commands/eval.d.ts.map +1 -0
  9. package/dist/cli/commands/eval.js +129 -0
  10. package/dist/cli/commands/eval.js.map +1 -0
  11. package/dist/cli/commands/init.d.ts +5 -0
  12. package/dist/cli/commands/init.d.ts.map +1 -0
  13. package/dist/cli/commands/init.js +547 -0
  14. package/dist/cli/commands/init.js.map +1 -0
  15. package/dist/cli/commands/plan.d.ts +5 -0
  16. package/dist/cli/commands/plan.d.ts.map +1 -0
  17. package/dist/cli/commands/plan.js +87 -0
  18. package/dist/cli/commands/plan.js.map +1 -0
  19. package/dist/cli/commands/run.d.ts +5 -0
  20. package/dist/cli/commands/run.d.ts.map +1 -0
  21. package/dist/cli/commands/run.js +120 -0
  22. package/dist/cli/commands/run.js.map +1 -0
  23. package/dist/cli/commands/sprint.d.ts +6 -0
  24. package/dist/cli/commands/sprint.d.ts.map +1 -0
  25. package/dist/cli/commands/sprint.js +206 -0
  26. package/dist/cli/commands/sprint.js.map +1 -0
  27. package/dist/cli/index.d.ts +3 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +124 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/config/defaults.d.ts +15 -0
  32. package/dist/config/defaults.d.ts.map +1 -0
  33. package/dist/config/defaults.js +226 -0
  34. package/dist/config/defaults.js.map +1 -0
  35. package/dist/config/index.d.ts +4 -0
  36. package/dist/config/index.d.ts.map +1 -0
  37. package/dist/config/index.js +8 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/config/loader.d.ts +18 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +189 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/config/schema.d.ts +904 -0
  44. package/dist/config/schema.d.ts.map +1 -0
  45. package/dist/config/schema.js +181 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/contracts/eval-result.d.ts +205 -0
  48. package/dist/contracts/eval-result.d.ts.map +1 -0
  49. package/dist/contracts/eval-result.js +87 -0
  50. package/dist/contracts/eval-result.js.map +1 -0
  51. package/dist/contracts/index.d.ts +4 -0
  52. package/dist/contracts/index.d.ts.map +1 -0
  53. package/dist/contracts/index.js +16 -0
  54. package/dist/contracts/index.js.map +1 -0
  55. package/dist/contracts/spec.d.ts +101 -0
  56. package/dist/contracts/spec.d.ts.map +1 -0
  57. package/dist/contracts/spec.js +51 -0
  58. package/dist/contracts/spec.js.map +1 -0
  59. package/dist/contracts/sprint-contract.d.ts +141 -0
  60. package/dist/contracts/sprint-contract.d.ts.map +1 -0
  61. package/dist/contracts/sprint-contract.js +80 -0
  62. package/dist/contracts/sprint-contract.js.map +1 -0
  63. package/dist/evaluators/builtin/api-check.d.ts +13 -0
  64. package/dist/evaluators/builtin/api-check.d.ts.map +1 -0
  65. package/dist/evaluators/builtin/api-check.js +152 -0
  66. package/dist/evaluators/builtin/api-check.js.map +1 -0
  67. package/dist/evaluators/builtin/build-check.d.ts +17 -0
  68. package/dist/evaluators/builtin/build-check.d.ts.map +1 -0
  69. package/dist/evaluators/builtin/build-check.js +155 -0
  70. package/dist/evaluators/builtin/build-check.js.map +1 -0
  71. package/dist/evaluators/builtin/command-runner.d.ts +26 -0
  72. package/dist/evaluators/builtin/command-runner.d.ts.map +1 -0
  73. package/dist/evaluators/builtin/command-runner.js +114 -0
  74. package/dist/evaluators/builtin/command-runner.js.map +1 -0
  75. package/dist/evaluators/builtin/lint.d.ts +17 -0
  76. package/dist/evaluators/builtin/lint.d.ts.map +1 -0
  77. package/dist/evaluators/builtin/lint.js +264 -0
  78. package/dist/evaluators/builtin/lint.js.map +1 -0
  79. package/dist/evaluators/builtin/playwright.d.ts +16 -0
  80. package/dist/evaluators/builtin/playwright.d.ts.map +1 -0
  81. package/dist/evaluators/builtin/playwright.js +238 -0
  82. package/dist/evaluators/builtin/playwright.js.map +1 -0
  83. package/dist/evaluators/builtin/typescript-check.d.ts +12 -0
  84. package/dist/evaluators/builtin/typescript-check.d.ts.map +1 -0
  85. package/dist/evaluators/builtin/typescript-check.js +155 -0
  86. package/dist/evaluators/builtin/typescript-check.js.map +1 -0
  87. package/dist/evaluators/builtin/unit-test.d.ts +18 -0
  88. package/dist/evaluators/builtin/unit-test.d.ts.map +1 -0
  89. package/dist/evaluators/builtin/unit-test.js +279 -0
  90. package/dist/evaluators/builtin/unit-test.js.map +1 -0
  91. package/dist/evaluators/index.d.ts +11 -0
  92. package/dist/evaluators/index.d.ts.map +1 -0
  93. package/dist/evaluators/index.js +13 -0
  94. package/dist/evaluators/index.js.map +1 -0
  95. package/dist/evaluators/plugin-interface.d.ts +50 -0
  96. package/dist/evaluators/plugin-interface.d.ts.map +1 -0
  97. package/dist/evaluators/plugin-interface.js +2 -0
  98. package/dist/evaluators/plugin-interface.js.map +1 -0
  99. package/dist/evaluators/plugin-loader.d.ts +18 -0
  100. package/dist/evaluators/plugin-loader.d.ts.map +1 -0
  101. package/dist/evaluators/plugin-loader.js +107 -0
  102. package/dist/evaluators/plugin-loader.js.map +1 -0
  103. package/dist/evaluators/registry.d.ts +78 -0
  104. package/dist/evaluators/registry.d.ts.map +1 -0
  105. package/dist/evaluators/registry.js +238 -0
  106. package/dist/evaluators/registry.js.map +1 -0
  107. package/dist/index.d.ts +17 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +22 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/orchestrator/context-handoff.d.ts +543 -0
  112. package/dist/orchestrator/context-handoff.d.ts.map +1 -0
  113. package/dist/orchestrator/context-handoff.js +133 -0
  114. package/dist/orchestrator/context-handoff.js.map +1 -0
  115. package/dist/orchestrator/evaluator-agent.d.ts +15 -0
  116. package/dist/orchestrator/evaluator-agent.d.ts.map +1 -0
  117. package/dist/orchestrator/evaluator-agent.js +233 -0
  118. package/dist/orchestrator/evaluator-agent.js.map +1 -0
  119. package/dist/orchestrator/generator-agent.d.ts +16 -0
  120. package/dist/orchestrator/generator-agent.d.ts.map +1 -0
  121. package/dist/orchestrator/generator-agent.js +147 -0
  122. package/dist/orchestrator/generator-agent.js.map +1 -0
  123. package/dist/orchestrator/pipeline.d.ts +24 -0
  124. package/dist/orchestrator/pipeline.d.ts.map +1 -0
  125. package/dist/orchestrator/pipeline.js +290 -0
  126. package/dist/orchestrator/pipeline.js.map +1 -0
  127. package/dist/orchestrator/planner-agent.d.ts +10 -0
  128. package/dist/orchestrator/planner-agent.d.ts.map +1 -0
  129. package/dist/orchestrator/planner-agent.js +187 -0
  130. package/dist/orchestrator/planner-agent.js.map +1 -0
  131. package/dist/state/helpers.d.ts +5 -0
  132. package/dist/state/helpers.d.ts.map +1 -0
  133. package/dist/state/helpers.js +8 -0
  134. package/dist/state/helpers.js.map +1 -0
  135. package/dist/state/history.d.ts +39 -0
  136. package/dist/state/history.d.ts.map +1 -0
  137. package/dist/state/history.js +162 -0
  138. package/dist/state/history.js.map +1 -0
  139. package/dist/state/index.d.ts +8 -0
  140. package/dist/state/index.d.ts.map +1 -0
  141. package/dist/state/index.js +22 -0
  142. package/dist/state/index.js.map +1 -0
  143. package/dist/state/plan-state.d.ts +21 -0
  144. package/dist/state/plan-state.d.ts.map +1 -0
  145. package/dist/state/plan-state.js +108 -0
  146. package/dist/state/plan-state.js.map +1 -0
  147. package/dist/state/sprint-state.d.ts +20 -0
  148. package/dist/state/sprint-state.d.ts.map +1 -0
  149. package/dist/state/sprint-state.js +98 -0
  150. package/dist/state/sprint-state.js.map +1 -0
  151. package/dist/utils/fs.d.ts +31 -0
  152. package/dist/utils/fs.d.ts.map +1 -0
  153. package/dist/utils/fs.js +67 -0
  154. package/dist/utils/fs.js.map +1 -0
  155. package/dist/utils/git.d.ts +35 -0
  156. package/dist/utils/git.d.ts.map +1 -0
  157. package/dist/utils/git.js +84 -0
  158. package/dist/utils/git.js.map +1 -0
  159. package/dist/utils/index.d.ts +4 -0
  160. package/dist/utils/index.d.ts.map +1 -0
  161. package/dist/utils/index.js +4 -0
  162. package/dist/utils/index.js.map +1 -0
  163. package/dist/utils/logger.d.ts +45 -0
  164. package/dist/utils/logger.d.ts.map +1 -0
  165. package/dist/utils/logger.js +73 -0
  166. package/dist/utils/logger.js.map +1 -0
  167. package/hooks/hooks.json +10 -0
  168. package/package.json +67 -0
  169. package/scripts/detect-stack.sh +287 -0
  170. package/scripts/init-project.sh +206 -0
  171. package/scripts/run-eval.sh +175 -0
  172. package/skills/bober.anchor/SKILL.md +365 -0
  173. package/skills/bober.anchor/references/anchor-guide.md +567 -0
  174. package/skills/bober.brownfield/SKILL.md +422 -0
  175. package/skills/bober.brownfield/references/codebase-analysis.md +304 -0
  176. package/skills/bober.eval/SKILL.md +235 -0
  177. package/skills/bober.eval/references/eval-strategies.md +407 -0
  178. package/skills/bober.eval/references/feedback-format.md +182 -0
  179. package/skills/bober.plan/SKILL.md +244 -0
  180. package/skills/bober.plan/references/clarification-guide.md +124 -0
  181. package/skills/bober.plan/references/spec-schema.md +253 -0
  182. package/skills/bober.react/SKILL.md +330 -0
  183. package/skills/bober.react/references/react-scaffold.md +344 -0
  184. package/skills/bober.run/SKILL.md +303 -0
  185. package/skills/bober.solidity/SKILL.md +416 -0
  186. package/skills/bober.solidity/references/solidity-guide.md +487 -0
  187. package/skills/bober.sprint/SKILL.md +280 -0
  188. package/skills/bober.sprint/references/contract-schema.md +251 -0
  189. package/templates/base/CLAUDE.md +20 -0
  190. package/templates/base/bober.config.json +35 -0
  191. package/templates/brownfield/CLAUDE.md +34 -0
  192. package/templates/brownfield/bober.config.json +37 -0
  193. package/templates/presets/anchor/CLAUDE.md +163 -0
  194. package/templates/presets/anchor/bober.config.json +9 -0
  195. package/templates/presets/api-node/CLAUDE.md +153 -0
  196. package/templates/presets/api-node/bober.config.json +10 -0
  197. package/templates/presets/nextjs/CLAUDE.md +82 -0
  198. package/templates/presets/nextjs/bober.config.json +14 -0
  199. package/templates/presets/python-api/CLAUDE.md +202 -0
  200. package/templates/presets/python-api/bober.config.json +9 -0
  201. package/templates/presets/react-vite/CLAUDE.md +71 -0
  202. package/templates/presets/react-vite/bober.config.json +53 -0
  203. package/templates/presets/react-vite/scaffold/package.json +45 -0
  204. package/templates/presets/react-vite/scaffold/server/index.ts +38 -0
  205. package/templates/presets/react-vite/scaffold/server/tsconfig.json +24 -0
  206. package/templates/presets/react-vite/scaffold/src/App.tsx +37 -0
  207. package/templates/presets/react-vite/scaffold/src/index.html +12 -0
  208. package/templates/presets/react-vite/scaffold/src/main.tsx +12 -0
  209. package/templates/presets/react-vite/scaffold/tsconfig.json +27 -0
  210. package/templates/presets/react-vite/scaffold/vite.config.ts +34 -0
  211. package/templates/presets/solidity/CLAUDE.md +106 -0
  212. package/templates/presets/solidity/bober.config.json +9 -0
@@ -0,0 +1,567 @@
1
+ # Anchor / Solana Development Reference Guide
2
+
3
+ ## Anchor Project Structure
4
+
5
+ ```
6
+ project-root/
7
+ programs/ # Solana programs (one or more)
8
+ my-program/
9
+ src/
10
+ lib.rs # Program entry point, declares modules
11
+ instructions/ # Instruction handler modules
12
+ mod.rs # Re-exports all instructions
13
+ initialize.rs
14
+ transfer.rs
15
+ state/ # Account struct definitions
16
+ mod.rs
17
+ user_account.rs
18
+ vault.rs
19
+ errors.rs # Custom error definitions
20
+ constants.rs # Seeds, sizes, and other constants
21
+ Cargo.toml # Rust dependencies for this program
22
+ tests/ # Integration tests (TypeScript)
23
+ my-program.ts
24
+ helpers/
25
+ setup.ts
26
+ utils.ts
27
+ app/ # Optional client application
28
+ sdk/ # Optional TypeScript SDK
29
+ migrations/ # Anchor migration scripts
30
+ deploy.ts
31
+ target/ # Build output (generated)
32
+ idl/ # Generated IDL files
33
+ types/ # Generated TypeScript types
34
+ deploy/ # Program keypairs
35
+ Anchor.toml # Anchor workspace configuration
36
+ Cargo.toml # Workspace Cargo.toml
37
+ package.json
38
+ tsconfig.json
39
+ ```
40
+
41
+ **Key config (`Anchor.toml`):**
42
+ ```toml
43
+ [features]
44
+ seeds = false
45
+ skip-lint = false
46
+
47
+ [programs.localnet]
48
+ my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
49
+
50
+ [programs.devnet]
51
+ my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
52
+
53
+ [registry]
54
+ url = "https://api.apr.dev"
55
+
56
+ [provider]
57
+ cluster = "Localnet"
58
+ wallet = "~/.config/solana/id.json"
59
+
60
+ [scripts]
61
+ test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
62
+ ```
63
+
64
+ ## Account Types and Constraints
65
+
66
+ ### Common Account Types
67
+
68
+ ```rust
69
+ use anchor_lang::prelude::*;
70
+
71
+ #[derive(Accounts)]
72
+ pub struct Initialize<'info> {
73
+ // Mutable signer -- the user paying for account creation
74
+ #[account(mut)]
75
+ pub payer: Signer<'info>,
76
+
77
+ // New account to be created (init)
78
+ #[account(
79
+ init,
80
+ payer = payer,
81
+ space = 8 + UserAccount::INIT_SPACE, // 8 bytes for discriminator
82
+ seeds = [b"user", payer.key().as_ref()],
83
+ bump,
84
+ )]
85
+ pub user_account: Account<'info, UserAccount>,
86
+
87
+ // Existing account (validated by ownership and type)
88
+ pub config: Account<'info, GlobalConfig>,
89
+
90
+ // System program (required for account creation)
91
+ pub system_program: Program<'info, System>,
92
+ }
93
+ ```
94
+
95
+ ### Account Data Struct
96
+
97
+ ```rust
98
+ #[account]
99
+ #[derive(InitSpace)]
100
+ pub struct UserAccount {
101
+ pub authority: Pubkey, // 32 bytes
102
+ pub balance: u64, // 8 bytes
103
+ pub is_active: bool, // 1 byte
104
+ pub bump: u8, // 1 byte
105
+ #[max_len(32)]
106
+ pub name: String, // 4 + 32 bytes (prefix + max chars)
107
+ pub created_at: i64, // 8 bytes
108
+ }
109
+ ```
110
+
111
+ ### Constraint Reference
112
+
113
+ ```rust
114
+ #[derive(Accounts)]
115
+ pub struct UpdateUser<'info> {
116
+ // Must be a signer
117
+ pub authority: Signer<'info>,
118
+
119
+ // Must be mutable, must match authority field, PDA with seeds+bump
120
+ #[account(
121
+ mut,
122
+ has_one = authority,
123
+ seeds = [b"user", authority.key().as_ref()],
124
+ bump = user_account.bump,
125
+ )]
126
+ pub user_account: Account<'info, UserAccount>,
127
+
128
+ // Custom constraint with error
129
+ #[account(
130
+ constraint = vault.balance > 0 @ CustomError::VaultEmpty
131
+ )]
132
+ pub vault: Account<'info, Vault>,
133
+
134
+ // Close an account and send rent to a destination
135
+ #[account(
136
+ mut,
137
+ close = authority,
138
+ has_one = authority,
139
+ )]
140
+ pub account_to_close: Account<'info, TemporaryAccount>,
141
+
142
+ // Realloc (resize) an account
143
+ #[account(
144
+ mut,
145
+ realloc = 8 + UserAccount::INIT_SPACE + new_data_len,
146
+ realloc::payer = authority,
147
+ realloc::zero = false,
148
+ )]
149
+ pub resizable_account: Account<'info, UserAccount>,
150
+ }
151
+ ```
152
+
153
+ ### Token Account Constraints
154
+
155
+ ```rust
156
+ use anchor_spl::token::{Mint, Token, TokenAccount};
157
+ use anchor_spl::associated_token::AssociatedToken;
158
+
159
+ #[derive(Accounts)]
160
+ pub struct TransferTokens<'info> {
161
+ #[account(mut)]
162
+ pub authority: Signer<'info>,
163
+
164
+ // Mint account
165
+ pub mint: Account<'info, Mint>,
166
+
167
+ // Source token account (must be owned by authority)
168
+ #[account(
169
+ mut,
170
+ associated_token::mint = mint,
171
+ associated_token::authority = authority,
172
+ )]
173
+ pub source_ata: Account<'info, TokenAccount>,
174
+
175
+ // Destination token account (init if needed)
176
+ #[account(
177
+ init_if_needed,
178
+ payer = authority,
179
+ associated_token::mint = mint,
180
+ associated_token::authority = recipient,
181
+ )]
182
+ pub destination_ata: Account<'info, TokenAccount>,
183
+
184
+ /// CHECK: recipient can be any account
185
+ pub recipient: UncheckedAccount<'info>,
186
+
187
+ pub token_program: Program<'info, Token>,
188
+ pub associated_token_program: Program<'info, AssociatedToken>,
189
+ pub system_program: Program<'info, System>,
190
+ }
191
+ ```
192
+
193
+ ## PDA Patterns
194
+
195
+ ### Basic PDA Derivation
196
+
197
+ ```rust
198
+ // In constants.rs
199
+ pub const USER_SEED: &[u8] = b"user";
200
+ pub const VAULT_SEED: &[u8] = b"vault";
201
+ pub const CONFIG_SEED: &[u8] = b"config";
202
+
203
+ // In instruction context
204
+ #[account(
205
+ init,
206
+ payer = payer,
207
+ space = 8 + UserAccount::INIT_SPACE,
208
+ seeds = [USER_SEED, authority.key().as_ref()],
209
+ bump,
210
+ )]
211
+ pub user_account: Account<'info, UserAccount>,
212
+
213
+ // In instruction handler -- store the bump for future use
214
+ pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
215
+ let user = &mut ctx.accounts.user_account;
216
+ user.authority = ctx.accounts.authority.key();
217
+ user.bump = ctx.bumps.user_account;
218
+ Ok(())
219
+ }
220
+ ```
221
+
222
+ ### PDA as Signer (for CPIs)
223
+
224
+ ```rust
225
+ // When a PDA needs to sign a CPI
226
+ pub fn transfer_from_vault(ctx: Context<TransferFromVault>, amount: u64) -> Result<()> {
227
+ let vault = &ctx.accounts.vault;
228
+ let seeds = &[
229
+ VAULT_SEED,
230
+ vault.authority.as_ref(),
231
+ &[vault.bump],
232
+ ];
233
+ let signer_seeds = &[&seeds[..]];
234
+
235
+ // CPI with PDA signer
236
+ let cpi_accounts = Transfer {
237
+ from: ctx.accounts.vault_token_account.to_account_info(),
238
+ to: ctx.accounts.user_token_account.to_account_info(),
239
+ authority: ctx.accounts.vault.to_account_info(),
240
+ };
241
+ let cpi_program = ctx.accounts.token_program.to_account_info();
242
+ let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);
243
+
244
+ token::transfer(cpi_ctx, amount)?;
245
+ Ok(())
246
+ }
247
+ ```
248
+
249
+ ### Multi-Seed PDAs
250
+
251
+ ```rust
252
+ // PDA derived from multiple seeds
253
+ #[account(
254
+ init,
255
+ payer = payer,
256
+ space = 8 + StakeRecord::INIT_SPACE,
257
+ seeds = [
258
+ b"stake",
259
+ pool.key().as_ref(),
260
+ user.key().as_ref(),
261
+ &pool.stake_count.to_le_bytes(),
262
+ ],
263
+ bump,
264
+ )]
265
+ pub stake_record: Account<'info, StakeRecord>,
266
+ ```
267
+
268
+ ### Client-Side PDA Derivation (TypeScript)
269
+
270
+ ```typescript
271
+ import { PublicKey } from "@solana/web3.js";
272
+
273
+ // Derive a PDA
274
+ const [userAccountPda, bump] = PublicKey.findProgramAddressSync(
275
+ [Buffer.from("user"), authority.toBuffer()],
276
+ programId
277
+ );
278
+
279
+ // Derive a PDA with multiple seeds
280
+ const [stakeRecordPda] = PublicKey.findProgramAddressSync(
281
+ [
282
+ Buffer.from("stake"),
283
+ poolPubkey.toBuffer(),
284
+ userPubkey.toBuffer(),
285
+ new anchor.BN(stakeCount).toArrayLike(Buffer, "le", 8),
286
+ ],
287
+ programId
288
+ );
289
+ ```
290
+
291
+ ## CPI Patterns
292
+
293
+ ### Token Transfer CPI
294
+
295
+ ```rust
296
+ use anchor_spl::token::{self, Transfer, Token};
297
+
298
+ pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
299
+ let cpi_accounts = Transfer {
300
+ from: ctx.accounts.source.to_account_info(),
301
+ to: ctx.accounts.destination.to_account_info(),
302
+ authority: ctx.accounts.authority.to_account_info(),
303
+ };
304
+ let cpi_program = ctx.accounts.token_program.to_account_info();
305
+ token::transfer(CpiContext::new(cpi_program, cpi_accounts), amount)?;
306
+ Ok(())
307
+ }
308
+ ```
309
+
310
+ ### Mint Tokens CPI
311
+
312
+ ```rust
313
+ use anchor_spl::token::{self, MintTo, Token};
314
+
315
+ pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
316
+ let seeds = &[
317
+ b"mint_authority",
318
+ &[ctx.accounts.config.mint_authority_bump],
319
+ ];
320
+ let signer_seeds = &[&seeds[..]];
321
+
322
+ let cpi_accounts = MintTo {
323
+ mint: ctx.accounts.mint.to_account_info(),
324
+ to: ctx.accounts.destination.to_account_info(),
325
+ authority: ctx.accounts.mint_authority.to_account_info(),
326
+ };
327
+ let cpi_program = ctx.accounts.token_program.to_account_info();
328
+ token::mint_to(
329
+ CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds),
330
+ amount,
331
+ )?;
332
+ Ok(())
333
+ }
334
+ ```
335
+
336
+ ### System Program CPI (SOL Transfer)
337
+
338
+ ```rust
339
+ use anchor_lang::system_program::{self, Transfer};
340
+
341
+ pub fn transfer_sol(ctx: Context<TransferSol>, amount: u64) -> Result<()> {
342
+ let cpi_accounts = Transfer {
343
+ from: ctx.accounts.from.to_account_info(),
344
+ to: ctx.accounts.to.to_account_info(),
345
+ };
346
+ let cpi_program = ctx.accounts.system_program.to_account_info();
347
+ system_program::transfer(CpiContext::new(cpi_program, cpi_accounts), amount)?;
348
+ Ok(())
349
+ }
350
+ ```
351
+
352
+ ## Testing with Bankrun
353
+
354
+ Bankrun provides a fast, in-process Solana test environment.
355
+
356
+ ### Setup
357
+
358
+ ```bash
359
+ npm install --save-dev solana-bankrun @solana/web3.js @coral-xyz/anchor
360
+ ```
361
+
362
+ ### Basic Bankrun Test
363
+
364
+ ```typescript
365
+ import { startAnchor } from "solana-bankrun";
366
+ import { BankrunProvider } from "anchor-bankrun";
367
+ import { Program } from "@coral-xyz/anchor";
368
+ import { PublicKey, Keypair } from "@solana/web3.js";
369
+ import { MyProgram } from "../target/types/my_program";
370
+ import IDL from "../target/idl/my_program.json";
371
+
372
+ describe("my-program", () => {
373
+ let provider: BankrunProvider;
374
+ let program: Program<MyProgram>;
375
+ let payer: Keypair;
376
+
377
+ beforeAll(async () => {
378
+ const context = await startAnchor(".", [], []);
379
+ provider = new BankrunProvider(context);
380
+ program = new Program(IDL as MyProgram, provider);
381
+ payer = context.payer;
382
+ });
383
+
384
+ it("initializes correctly", async () => {
385
+ const [userPda] = PublicKey.findProgramAddressSync(
386
+ [Buffer.from("user"), payer.publicKey.toBuffer()],
387
+ program.programId
388
+ );
389
+
390
+ await program.methods
391
+ .initialize()
392
+ .accounts({
393
+ payer: payer.publicKey,
394
+ userAccount: userPda,
395
+ systemProgram: SystemProgram.programId,
396
+ })
397
+ .signers([payer])
398
+ .rpc();
399
+
400
+ const account = await program.account.userAccount.fetch(userPda);
401
+ expect(account.authority.toString()).toEqual(payer.publicKey.toString());
402
+ });
403
+ });
404
+ ```
405
+
406
+ ### Testing with Local Validator
407
+
408
+ ```typescript
409
+ import * as anchor from "@coral-xyz/anchor";
410
+ import { Program } from "@coral-xyz/anchor";
411
+ import { MyProgram } from "../target/types/my_program";
412
+
413
+ describe("my-program", () => {
414
+ const provider = anchor.AnchorProvider.env();
415
+ anchor.setProvider(provider);
416
+
417
+ const program = anchor.workspace.MyProgram as Program<MyProgram>;
418
+
419
+ it("initializes correctly", async () => {
420
+ const [userPda] = anchor.web3.PublicKey.findProgramAddressSync(
421
+ [Buffer.from("user"), provider.wallet.publicKey.toBuffer()],
422
+ program.programId
423
+ );
424
+
425
+ const tx = await program.methods
426
+ .initialize()
427
+ .accounts({
428
+ payer: provider.wallet.publicKey,
429
+ userAccount: userPda,
430
+ })
431
+ .rpc();
432
+
433
+ console.log("Transaction signature:", tx);
434
+
435
+ const account = await program.account.userAccount.fetch(userPda);
436
+ assert.ok(account.authority.equals(provider.wallet.publicKey));
437
+ });
438
+
439
+ it("rejects unauthorized access", async () => {
440
+ const unauthorized = anchor.web3.Keypair.generate();
441
+
442
+ try {
443
+ await program.methods
444
+ .adminOnlyInstruction()
445
+ .accounts({
446
+ authority: unauthorized.publicKey,
447
+ })
448
+ .signers([unauthorized])
449
+ .rpc();
450
+ assert.fail("Should have thrown");
451
+ } catch (err) {
452
+ assert.ok(err.message.includes("ConstraintHasOne") ||
453
+ err.message.includes("Unauthorized"));
454
+ }
455
+ });
456
+ });
457
+ ```
458
+
459
+ ## Common Solana-Specific Issues
460
+
461
+ ### Rent
462
+
463
+ All accounts on Solana must maintain a minimum balance (rent-exempt threshold). This is proportional to the account's data size.
464
+
465
+ ```rust
466
+ // Calculate space for an account (include 8-byte discriminator for Anchor accounts)
467
+ space = 8 + UserAccount::INIT_SPACE
468
+
469
+ // In TypeScript, calculate minimum rent:
470
+ const rentExemptBalance = await connection.getMinimumBalanceForRentExemption(space);
471
+ ```
472
+
473
+ **Common pitfall:** Forgetting the 8-byte Anchor discriminator when calculating space. Every Anchor account needs `8 + actual_data_size` bytes.
474
+
475
+ ### Compute Budget
476
+
477
+ Each transaction has a default compute budget of 200,000 compute units per instruction. Complex operations may exceed this.
478
+
479
+ ```typescript
480
+ // Request additional compute units
481
+ import { ComputeBudgetProgram } from "@solana/web3.js";
482
+
483
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
484
+ units: 400_000,
485
+ });
486
+
487
+ const tx = new Transaction()
488
+ .add(modifyComputeUnits)
489
+ .add(yourInstruction);
490
+ ```
491
+
492
+ **Common pitfall:** Excessive `msg!()` logging consumes compute units. Minimize logging in production.
493
+
494
+ ### Transaction Size
495
+
496
+ Solana transactions are limited to 1232 bytes. This includes:
497
+ - Signatures (64 bytes each)
498
+ - Message header (3 bytes)
499
+ - Account keys (32 bytes each)
500
+ - Recent blockhash (32 bytes)
501
+ - Instructions (variable)
502
+
503
+ **Mitigations:**
504
+ - Use Address Lookup Tables (ALTs) to reduce account key sizes
505
+ - Split large operations into multiple transactions
506
+ - Use versioned transactions (`MessageV0`)
507
+
508
+ ```typescript
509
+ // Using Address Lookup Tables
510
+ import { AddressLookupTableProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
511
+
512
+ const messageV0 = new TransactionMessage({
513
+ payerKey: payer.publicKey,
514
+ recentBlockhash,
515
+ instructions,
516
+ }).compileToV0Message([lookupTableAccount]);
517
+
518
+ const tx = new VersionedTransaction(messageV0);
519
+ ```
520
+
521
+ ### Account Size Limits
522
+
523
+ - Maximum account size: 10 MB (10,240 bytes for realloc per instruction)
524
+ - For large data, consider using multiple accounts or off-chain storage with on-chain hashes
525
+
526
+ ### Clock and Timestamps
527
+
528
+ ```rust
529
+ // Get current timestamp
530
+ let clock = Clock::get()?;
531
+ let current_timestamp = clock.unix_timestamp; // i64
532
+ let current_slot = clock.slot; // u64
533
+
534
+ // Use in constraints
535
+ #[account(
536
+ constraint = clock.unix_timestamp > stake.unlock_time @ CustomError::StillLocked
537
+ )]
538
+ pub stake: Account<'info, StakeRecord>,
539
+ ```
540
+
541
+ **Common pitfall:** Solana's clock is based on validator consensus and can drift. Do not rely on exact second precision. Use slot numbers for ordering when possible.
542
+
543
+ ### Serialization and Borsh
544
+
545
+ Anchor uses Borsh serialization. Be aware of:
546
+ - Strings are serialized as `length (4 bytes) + utf8 bytes`
547
+ - Vectors are serialized as `length (4 bytes) + elements`
548
+ - Options are serialized as `1 byte (tag) + value (if Some)`
549
+ - Enums are serialized as `1 byte (variant index) + variant data`
550
+
551
+ ```rust
552
+ #[account]
553
+ #[derive(InitSpace)]
554
+ pub struct DataAccount {
555
+ pub authority: Pubkey, // 32 bytes
556
+ pub count: u64, // 8 bytes
557
+ pub is_active: bool, // 1 byte
558
+ pub bump: u8, // 1 byte
559
+ #[max_len(50)]
560
+ pub name: String, // 4 + 50 bytes
561
+ #[max_len(10)]
562
+ pub items: Vec<u64>, // 4 + (10 * 8) bytes
563
+ pub optional_field: Option<Pubkey>, // 1 + 32 bytes
564
+ }
565
+ // Total INIT_SPACE = 32 + 8 + 1 + 1 + 54 + 84 + 33 = 213 bytes
566
+ // Account space = 8 (discriminator) + 213 = 221 bytes
567
+ ```