@5ive-tech/cli 1.0.19 → 1.0.21

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5ive-tech/cli",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "High-performance CLI for Five VM development with WebAssembly integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,176 +1,1097 @@
1
- # AGENTS.md
1
+ # AGENTS.md - Complete 5IVE DSL Agent Bootstrap
2
2
 
3
- This document defines policies and expectations for automated agents (including LLM-based assistants), bots, and contributors who make automated or human-assisted edits to the repository. The goal is to keep the codebase correct, secure, and maintainable while enabling productive automation.
3
+ This file is written for agents with zero prior 5IVE knowledge.
4
+ It is intentionally self-contained and should be treated as the baseline technical specification for authoring, testing, deploying, and integrating 5IVE programs.
4
5
 
5
- This is normative: agents and humans who operate as "agents" in this repository must follow these rules.
6
+ ## 1) What 5IVE Is
6
7
 
7
- ---
8
+ 5IVE is a DSL and toolchain for compiling compact bytecode programs for Solana execution paths.
8
9
 
9
- ## Purpose & Scope
10
+ Core surfaces:
11
+ 1. Source language: `.v`
12
+ 2. Canonical artifact: `.five` (bytecode + ABI)
13
+ 3. CLI: `@5ive-tech/cli` (`5ive` canonical command; `five` alias supported)
14
+ 4. SDK: `@5ive-tech/sdk` (`FiveSDK`, `FiveProgram`)
10
15
 
11
- - Purpose: Provide clear, enforceable rules for automated agents and maintainers so changes preserve program semantics, security invariants, and developer intent.
12
- - Scope: Applies to any automated edit, patch, or PR created by scripts, CI, LLMs, or other programmatic processes across this repository (all subprojects under `five-org/`).
16
+ ## 2) Source of Truth Policy
13
17
 
14
- ---
18
+ When docs conflict, resolve in this order:
19
+ 1. Compiler/CLI/SDK source code
20
+ 2. Package manifests + command definitions
21
+ 3. READMEs/examples/docs
15
22
 
16
- ## High-level Policies
23
+ Never rely on stale docs when behavior is high-stakes (deploy/execute/CPI encoding).
17
24
 
18
- 1. Preserve semantics first
19
- - Never change code in a way that makes tests pass by hiding bugs or changing tests to match incorrect behavior.
20
- - When tests fail, investigate and fix the root cause rather than masking failures.
25
+ ## 3) Non-Negotiable Workflow
21
26
 
22
- 2. Do not remove or weaken explicitly used `unsafe` blocks
23
- - `unsafe` blocks are permitted only when necessary and must be accompanied by a clear explanatory comment describing:
24
- - Why `unsafe` is required,
25
- - What invariants the code relies on,
26
- - What reasoning justifies the memory/aliasing/safety assumptions.
27
- - Automated agents MUST NOT remove `unsafe` blocks without a careful manual review and a pass of all tests (including anything that validates memory safety).
27
+ 1. Inspect `five.toml` before code changes.
28
+ 2. Compile to `.five`.
29
+ 3. Run local/runtime tests.
30
+ 4. Deploy with explicit target + program ID resolution path.
31
+ 5. Execute and verify confirmed tx metadata (`meta.err == null`).
32
+ 6. Record signatures + compute units.
28
33
 
29
- 3. Do not silence or ignore unused-variable diagnostics without intent
30
- - If a variable is currently unused:
31
- - Either remove it if truly unnecessary, or
32
- - Use it meaningfully, or
33
- - Replace the identifier with an explicit `_name` placeholder only when it is intentionally unused by design (e.g., implementing a trait method that does not use a parameter).
34
- - Automated agents MUST NOT simply silence unused-variable warnings by prefixing with `_` unless the variable is intentionally unused and this is documented in code comments.
34
+ ### 3.1 Strict Authoring Rules
35
35
 
36
- 4. Tests and golden checks are authoritative for behavior
37
- - Tests (unit, integration, and golden bytecode tests) reflect the intended behavior and must pass.
38
- - Agents must add or update tests when behavior changes, not remove or weaken assertions to make a failing build pass.
36
+ These rules are non-negotiable and prevent the most common compilation failures:
39
37
 
40
- 5. Security invariants are non-negotiable
41
- - Any code paths that affect security or VM enforcement (constraint checks, `require`, PDA generation, account init/ownership checks, CPI patterns) must be preserved.
42
- - Agents must not remove, reorder, or weaken constraint-emitting code or validations that protect the runtime behavior.
38
+ 1. **All account fields must end with `;`** — missing semicolons cause parser failure.
39
+ 2. **Use `account @signer` for authorization** not `pubkey @signer`. This preserves the `.key` accessor.
40
+ 3. **Use `.key` on `account`-typed parameters** to extract public keys for comparisons and assignments.
41
+ 4. **Use `-> ReturnType` for functions with return values** — e.g. `-> u64`, `-> pubkey`, `-> bool`.
42
+ 5. **`pubkey(0)` and `0` are valid** for zero-initializing pubkey fields (disabling authorities).
43
+ 6. **`string<N>` is production-safe** — use freely in accounts and function parameters.
44
+ 7. **All comparison operators work in `require()`** — `==`, `!=`, `<`, `<=`, `>`, `>=`, `!`.
45
+ 8. **Local variables are immutable by default** — use `let x = value;` for immutable bindings. **Use `let mut x = value;` if the variable will be reassigned** (e.g., in conditional branches). Attempting to reassign an immutable local causes a compiler error.
43
46
 
44
- ---
47
+ ## 4) DSL Feature Inventory (Deep)
45
48
 
46
- ## Coding & PR Rules for Agents
49
+ This section enumerates language features discovered from parser/compiler code and repo examples.
47
50
 
48
- When an agent produces a change (patch, branch, PR), it must:
51
+ ### 4.1 Top-level declarations
52
+ Observed and/or parsed:
53
+ 1. `account Name { ... }` — **all fields must be terminated with `;`**
54
+ 2. Global fields/variables (including `mut`)
55
+ 3. `init { ... }` block
56
+ 4. `constraints { ... }` block
57
+ 5. Function/instruction definitions (`pub`, `fn`, optional `instruction` keyword)
58
+ 6. `event Name { ... }` definitions
59
+ 7. `interface Name ... { ... }` definitions
60
+ 8. `use` / `import` statements
61
+ 9. Legacy `script Name { ... }` wrapper (parser-supported)
49
62
 
50
- 1. Include a rationale
51
- - Each PR must contain a clear "Why" section describing the reasoning and the root cause for changes.
52
- - If the patch changes semantics, the PR must include tests that assert the new behavior.
63
+ ```v
64
+ // CORRECT semicolons required
65
+ account Mint {
66
+ authority: pubkey;
67
+ supply: u64;
68
+ decimals: u8;
69
+ }
53
70
 
54
- 2. Include tests
55
- - Add or update unit tests, integration tests, and/or golden tests as appropriate.
56
- - Golden tests are preferred for protecting bytecode-level semantics.
71
+ // WRONG — parser failure
72
+ account Mint {
73
+ authority: pubkey
74
+ supply: u64
75
+ }
76
+ ```
57
77
 
58
- 3. Prefer small, focused patches
59
- - Keep changes minimal and atomic; avoid broad unrelated edits.
60
- - Large refactors should be staged across multiple PRs.
78
+ ### 4.2 Function definition forms
79
+ Parser accepts flexible forms:
80
+ 1. `pub add(...) -> ... { ... }`
81
+ 2. `fn add(...) { ... }`
82
+ 3. `instruction add(...) { ... }`
83
+ 4. `pub fn add(...) { ... }`
61
84
 
62
- 4. Annotate `unsafe` and sensitive changes
63
- - If the change introduces or modifies `unsafe`, include a comment block that documents the safety argument.
64
- - The PR description must call out any `unsafe` changes explicitly.
85
+ ### 4.3 Parameter system
86
+ Each parameter supports:
87
+ 1. Name + type: `x: u64`
88
+ 2. Optional marker: `x?: u64`
89
+ 3. Default value: `x: u64 = 10`
90
+ 4. Attributes before or after type
65
91
 
66
- 5. Respect reviewers and code owners
67
- - Assign appropriate reviewers and wait for at least one human reviewer who understands the affected modules to approve before merging.
92
+ Common attributes:
93
+ 1. `@signer`
94
+ 2. `@mut`
95
+ 3. `@init`
96
+ 4. Generic form: `@attribute(args...)`
97
+ 5. Template-observed relation constraints: `@has(field)`
68
98
 
69
- 6. CI and linting
70
- - Ensure CI runs and passes:
71
- - All unit/integration tests
72
- - All golden bytecode tests
73
- - (Optional) clippy/linting if enabled.
74
- - Do not bypass CI failures with force merges.
99
+ ### 4.4 `@init` config support
100
+ `@init` can include config arguments:
101
+ 1. `payer=...`
102
+ 2. `space=...`
103
+ 3. `seeds=[...]`
104
+ 4. `bump=...`
75
105
 
76
- ---
106
+ Examples also show legacy bracket seed forms after `@init`.
77
107
 
78
- ## Specific Rust Guidance
108
+ **Attribute stacking order for account parameters (empirically verified):**
79
109
 
80
- - Unsafe blocks
81
- - Always include a `// SAFETY:` comment just above the `unsafe` block describing the invariants relied upon.
82
- - Example:
83
- ```rust
84
- // SAFETY: The pointer `p` is guaranteed to be valid and aligned for `T` because
85
- // it comes from a pinned heap allocation that is not mutated concurrently.
86
- unsafe { &*p }
87
- ```
88
- - Agents must not remove such comments or replace `unsafe` usage with unchecked "safe" code without a thorough manual review and performance of safety tests.
110
+ ```
111
+ Type @mut @init(payer=name, space=bytes) @signer
112
+ ```
89
113
 
90
- - Unused variables
91
- - If code defines `let foo = ...;` and `foo` is unused:
92
- - Remove the binding if unused and unnecessary.
93
- - If the binding is intentionally unused, rename to `_foo` and add a comment explaining why (e.g., placeholder for future work or matching a trait signature).
94
- - Do NOT simply silence compiler diagnostics by changing names or attributes unless intent is documented.
114
+ Example:
115
+ ```v
116
+ pub init_mint(
117
+ mint_account: Mint @mut @init(payer=authority, space=256) @signer,
118
+ authority: account @mut @signer,
119
+ ...
120
+ )
121
+ ```
95
122
 
96
- - Logging / Debug Statements
97
- - Avoid leaving `println!` or ad-hoc `eprintln!` in production code.
98
- - If temporary debugging is required for a patch, include a clear TODO comment and remove it before final merge.
123
+ Order: (1) Type declaration → (2) `@mut` → (3) `@init(...)` → (4) `@signer`.
99
124
 
100
- ---
125
+ ### 4.5 Types
126
+ Supported/parsed type families:
127
+ 1. Primitive numeric/bool/pubkey/string types (`u8..u128`, `i8..i64`, `bool`, `pubkey`, `string`)
128
+ 2. `Account` type and account-typed params
129
+ 3. **Sized strings: `string<N>`** — production-safe, use in accounts and function parameters
130
+ 4. Arrays:
131
+ - Rust style: `[T; N]`
132
+ - TypeScript-style sized: `T[N]`
133
+ - TypeScript-style dynamic: `T[]`
134
+ 5. Tuples: `(T1, T2, ...)`
135
+ 6. Inline struct types: `{ field: Type, ... }`
136
+ 7. Generic types:
137
+ - `Option<T>`
138
+ - `Result<T, E>`
139
+ - nested generics (`Option<Option<u64>>` etc.)
140
+ 8. Namespaced/custom types: `module::Type`
141
+ 9. Optional account fields in structs/accounts: `field?: Type`
142
+ 10. **`pubkey(0)` and integer `0`** — valid for zero-initialization of pubkey fields (interchangeable)
101
143
 
102
- ## Process for Exceptions
144
+ ### 4.6 Statements
145
+ Observed and parser-supported:
146
+ 1. `let` declarations (with `mut` and optional type annotation)
147
+ - Type inference works: `let is_owner = source.owner == authority.key;` infers `bool`
148
+ - Use `let` without explicit annotation for boolean and scalar expressions
149
+ - **Immutability by default**: `let x = value;` creates an immutable binding; reassignment will fail
150
+ - **For reassignable variables, use `let mut`**: `let mut x: u64 = 0; ... x = new_value;`
151
+ - Example: `let mut shares: u64 = 0; if (condition) { shares = computed_value; }`
152
+ 2. Assignment:
153
+ - direct: `x = y`
154
+ - compound: `+=`, `-=`, `*=`, `/=`, `<<=`, `>>=`, `&=`, `|=`, `^=`
155
+ 3. Field assignment: `obj.field = value`
156
+ 4. Return statements (`return`, `return value`) — see §4.13 for return type syntax
157
+ 5. Guard/assertion: `require(condition)` — **all operators verified:**
158
+ - Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=`
159
+ - Boolean negation: `!expr`
160
+ - Logical: `&&`, `||`
161
+ - Example: `require(source.balance >= amount);`
162
+ - Example: `require(!account.is_frozen);`
163
+ 6. Conditionals:
164
+ - `if (...) {}`
165
+ - `else if (...) {}`
166
+ - `else {}`
167
+ - Conditionals support nested `require()` statements and multiple assignments in both branches
168
+ 7. Pattern matching: `match expr { ... }`, with optional arm guards (`if ...`)
169
+ 8. Loops:
170
+ - `while (...) { ... }`
171
+ - `for (init; cond; update) { ... }`
172
+ - `for (item in iterable) { ... }`
173
+ - `do { ... } while (...);`
174
+ 9. Tuple destructuring:
175
+ - declaration style: `let (a, b) = expr`
176
+ - assignment style for tuple targets
177
+ 10. Event emission: `emit EventName { field: value, ... }`
178
+ 11. Expression statements (function/method calls, constructors like `Some(...)`)
103
179
 
104
- - If a change needs to intentionally diverge from these policies (e.g., to quickly mitigate a production issue), the agent must:
105
- 1. Open a short-lived PR with the mitigation and mark it as `emergency/`.
106
- 2. Immediately notify repository owners / on-call maintainers.
107
- 3. Follow up with a proper fix and revert the emergency change as soon as possible.
180
+ ### 4.7 Expressions and operators
181
+ Parser handles:
182
+ 1. Arithmetic: `+`, `-`, `*`, `/`, `%`
183
+ 2. Checked-arithmetic tokens in grammar: `+?`, `-?`, `*?`
184
+ - Some repo tests indicate these were replaced/legacy in current examples.
185
+ 3. Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=`
186
+ 4. Boolean: `&&`, `||`, `!`
187
+ 5. Bitwise: `&`, `|`, `^`, `~`
188
+ 6. Shifts/bit ops: `<<`, `>>`, `>>>`, `<<<`
189
+ 7. Range operator: `..`
190
+ 8. Unary `+`/`-`
191
+ 9. Cast syntax: `expr as Type`
192
+ 10. Error propagation postfix: `expr?`
193
+ 11. Field access: `obj.field`
194
+ 12. Tuple access: `obj.0`
195
+ 13. Array indexing: `arr[idx]`
196
+ 14. Function calls
197
+ 15. Method calls: `obj.method(args...)`
198
+ 16. Namespaced calls: `module::function(...)`
199
+ 17. Struct literals: `{ field: expr, ... }`
200
+ 18. Array literals: `[a, b, c]`
201
+ 19. Tuple literals: `(a, b)`
202
+ 20. Option/Result constructors and patterns:
203
+ - `Some(...)`, `None`
204
+ - `Ok(...)`, `Err(...)`
108
205
 
109
- - Permanent exceptions (rare) must be documented in the PR and approved by at least two senior maintainers.
206
+ ### 4.8 Imports and modules
207
+ `use`/`import` system supports:
208
+ 1. External module specifier via quoted literal
209
+ 2. Local module specifier via identifier path
210
+ 3. Nested local module paths using `::`
211
+ 4. Scoped namespace forms with symbols: `!`, `@`, `#`, `$`, `%`
212
+ 5. Member imports:
213
+ - single: `::name`
214
+ - list: `::{a, b}`
215
+ - typed list entries: `method foo`, `interface Bar`
110
216
 
111
- ---
217
+ ### 4.9 Interfaces and CPI (Cross-Program Invocation)
112
218
 
113
- ## Enforcement & Automation
219
+ Interfaces define external program calls. **Empirically verified rules:**
114
220
 
115
- - CI will run the full test suite and golden bytecode tests. Any change failing CI should not be merged.
116
- - Maintain a set of golden tests to ensure bytecode-level behavior does not regress.
117
- - We will periodically run an automated audit that checks for:
118
- - Missing `SAFETY:` comments around `unsafe` blocks,
119
- - Any new occurrences of unused variables without documentation,
120
- - New or removed golden tests affecting critical semantics.
221
+ 1. **Program binding:** always use `@program("...")` (the `@` prefix is required)
222
+ 2. **Serializer options:**
223
+ - **Default (bincode):** omit `@serializer(...)` bincode is the default, works for SPL programs and most Solana programs
224
+ - **Anchor programs (borsh):** use `@anchor` marker — automatically sets borsh serializer **and** auto-generates discriminators from method names
225
+ - **Explicit borsh:** use `@serializer("borsh")` if needed without `@anchor`
226
+ 3. **Discriminators:**
227
+ - **Manual:** use single `u8` value inline on method: `method @discriminator(N) (...)`
228
+ - **Anchor auto-generation:** `@anchor` interface automatically computes discriminators from method names — **do not** manually specify `@discriminator` with `@anchor`
229
+ - **Format:** single u8 value, **not** array format `@discriminator([3, 0, 0, 0])`
230
+ 4. **Account parameters in interfaces:** use `Account` type, **not** `pubkey`
231
+ - `pubkey` is for data values only; `Account` represents an on-chain account passed to the CPI
232
+ 5. **Calling interface methods:** use dot notation `InterfaceName.method(...)`, **not** `InterfaceName::method(...)`
233
+ 6. **Passing accounts to CPI:** pass `account`-typed parameters directly, **not** `param.key`
234
+ 7. **Function parameters for CPI accounts:** must be typed `account @mut` (not `pubkey`)
121
235
 
122
- ---
236
+ ```v
237
+ // ✅ CORRECT: SPL Token (bincode, manual discriminators)
238
+ interface SPLToken @program("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") {
239
+ transfer @discriminator(3) (
240
+ source: Account,
241
+ destination: Account,
242
+ authority: Account,
243
+ amount: u64
244
+ );
245
+ }
123
246
 
124
- ## Review Checklist (for humans and agents producing PRs)
247
+ // CORRECT: Anchor program (borsh, auto discriminators)
248
+ interface MyAnchorProgram @anchor @program("...") {
249
+ initialize( // discriminator auto-generated from "initialize"
250
+ state: Account,
251
+ authority: Account,
252
+ value: u64
253
+ );
254
+ }
125
255
 
126
- Before marking a PR ready, verify:
127
- - [ ] Tests added/updated for behavior changes.
128
- - [ ] No ad-hoc debug prints remain.
129
- - [ ] `unsafe` blocks have `SAFETY:` comments and a clear justification.
130
- - [ ] No unused variables left without intent (documented or removed).
131
- - [ ] Golden tests included for bytecode-affecting changes.
132
- - [ ] CI is green and artifacts (if any) are attached for inspection.
133
- - [ ] Reviewers and code owners assigned.
256
+ // CORRECT: CPI call
257
+ pub call_external(
258
+ external_account: account @mut,
259
+ authority: account @signer,
260
+ value: u64
261
+ ) {
262
+ MyAnchorProgram.initialize(external_account, authority, value);
263
+ }
134
264
 
135
- ---
265
+ // ❌ WRONG — common mistakes
266
+ // interface Program program("...") ← missing @ on program
267
+ // @discriminator([3, 0, 0, 0]) ← array format, not u8
268
+ // transfer(src: pubkey, dst: pubkey, ...) ← pubkey instead of Account
269
+ // Program::method(...) ← :: instead of .
270
+ // Program.method(account.key, ...) ← .key unnecessary for accounts
271
+ // @anchor with @discriminator(3) ← @anchor auto-generates, don't specify manually
272
+ ```
136
273
 
137
- ## Communication & Escalation
274
+ CPI hard rules for agents:
275
+ 1. Always use `@program("...")` with correct program ID
276
+ 2. For Anchor programs: use `@anchor`, omit `@discriminator`
277
+ 3. For non-Anchor programs: set `@discriminator(N)` as single u8 on each method, omit `@serializer`
278
+ 4. Use `Account` for on-chain account params, scalar types for data params
279
+ 5. Call with dot notation and pass account params directly
138
280
 
139
- - For ambiguous cases (e.g., whether an `unsafe` is removable), open a draft PR with the proposed change and request maintainers' input.
140
- - Use the repository issue tracker or the designated team channels for security-sensitive discussions.
281
+ ### 4.10 Events and error/enums
282
+ Parser/AST include:
283
+ 1. Event definitions + `emit` statements
284
+ 2. Enum/error-style definitions (`enum` path in parser)
141
285
 
142
- ---
286
+ Production note:
287
+ Treat event/error enum features as available in syntax, but verify runtime/compiler behavior in your exact toolchain version before relying on them for critical flows.
143
288
 
144
- ## Suggested CLAUDE.md Update (add this snippet)
289
+ ### 4.11 Testing-oriented language features
290
+ From tokenizer/parser support:
291
+ 1. `#[...]` test attributes
292
+ 2. `test` function parse path
293
+ 3. Test attribute names/tokens include:
294
+ - `ignore`
295
+ - `should_fail`
296
+ - `timeout`
297
+ 4. Assertion tokens:
298
+ - `assert_eq`
299
+ - `assert_true`
300
+ - `assert_false`
301
+ - `assert_fails`
302
+ - `assert_approx_eq`
145
303
 
146
- Please add the following note to `docs/CLAUDE.md` (or the relevant CLAUDE guide) so agent behavior is consistent with repository policy:
304
+ Repository tests also use comment-based param conventions (`// @test-params ...`) in many scripts.
147
305
 
148
- > Agent Policy Note
149
- >
150
- > - Never remove or weaken `unsafe` blocks. `unsafe` exists for explicit, documented reasons. If you believe an `unsafe` is unnecessary, open a PR with a clear safety argument, tests, and wait for human review from core maintainers.
151
- > - Do not silence "unused variable" diagnostics by hiding the variable. Either remove the variable, use it, or rename it to `_name` only when the variable is intentionally unused and this is documented in the code comments.
152
- > - Golden tests (bytecode-level assertions) are authoritative for runtime semantics. Do not change golden tests to match incorrect generated output; fix the generator or parser so the golden tests remain meaningful.
306
+ ### 4.12 Blockchain-oriented built-ins
307
+ Core built-ins available in all contracts:
308
+ 1. `derive_pda(...)` (including bump-return and bump-specified variants)
309
+ 2. `get_clock()`
310
+ 3. `get_key(...)`
311
+ 4. **Account key access: `param.key`** — **core pattern for all `account`-typed parameters.** Use `.key` to extract pubkeys for comparisons and assignments.
153
312
 
154
- ---
313
+ ```v
314
+ pub action(
315
+ state: MyAccount @mut,
316
+ caller: account @signer,
317
+ ...
318
+ ) {
319
+ require(state.authority == caller.key); // ownership check
320
+ state.last_actor = caller.key; // record who acted
321
+ }
322
+ ```
155
323
 
156
- ## FAQ
324
+ 5. **Authority revocation pattern:** assign `0` to any pubkey field to permanently disable it.
325
+ ```v
326
+ state.authority = 0; // revokes authority — irreversible
327
+ ```
157
328
 
158
- Q: What if an automated agent's edit breaks tests?
159
- - A: Do not merge. Investigate and fix root cause. If immediate mitigation is required, follow the emergency process above.
329
+ ### 4.13 Return types and values
330
+ Functions can declare return types with `->` syntax:
160
331
 
161
- Q: Can agents add `unsafe` blocks?
162
- - A: Yes, but only when necessary and with a `SAFETY:` justification in code and an explicit explanation in the PR.
332
+ ```v
333
+ pub get_value(state: MyAccount) -> u64 {
334
+ return state.amount;
335
+ }
163
336
 
164
- Q: Who enforces these rules?
165
- - A: CI rules, code reviews by maintainers, and periodic audits. Violations should be reported in the issue tracker or to the maintainers.
337
+ pub initialize(
338
+ state: MyAccount @mut @init(payer=creator, space=256) @signer,
339
+ creator: account @mut @signer,
340
+ ...
341
+ ) -> pubkey {
342
+ state.authority = creator.key;
343
+ return state.key;
344
+ }
345
+ ```
166
346
 
167
- ---
347
+ Confirmed return types: `u8`, `u16`, `u32`, `u64`, `u128`, `i8`..`i64`, `bool`, `pubkey`.
168
348
 
169
- ## Ownership & Maintenance
349
+ ## 5) Feature Maturity Matrix (Agent Safety)
170
350
 
171
- - Maintainers: repo owners and core compiler team.
172
- - This file should be reviewed along with the repository governance and updated if policies change.
351
+ ### 5.1 Generally production-oriented (widely used in templates)
352
+ 1. Accounts, `@mut`, `@signer`, `@init` (with attribute stacking)
353
+ 2. `require` with all comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`, `!`)
354
+ 3. Basic control flow (`if`, `else`, `while`) with nested logic
355
+ 4. Arithmetic/comparison/boolean expressions
356
+ 5. `.five` compile/deploy/execute path
357
+ 6. `interface` + explicit discriminator + explicit serializer CPI patterns
358
+ 7. Return type declarations (`-> Type`) with `return value;`
359
+ 8. `string<N>` fixed-size strings in accounts and parameters
360
+ 9. `account.key` extraction from `account`-typed parameters
361
+ 10. Authority disabling via `0` assignment to pubkey fields
362
+ 11. `let` with type inference for scalar/boolean expressions
363
+ 12. `pubkey(0)` zero-initialization
173
364
 
174
- ---
365
+ ### 5.2 Available but validate per-version before critical use
366
+ 1. Match expressions with `Option`/`Result`
367
+ 2. Tuple destructuring and tuple returns
368
+ 3. Advanced loop forms (`for`, `do-while`)
369
+ 4. Event definition/emit workflows
370
+ 5. Namespaced imports and scoped namespace symbols
371
+ 6. Bitwise/shift operator-heavy code
175
372
 
176
- Thank you follow these rules to keep the codebase honest, secure, and maintainable.
373
+ ### 5.3 Parser tokens exist; treat as reserved/experimental unless proven in your path
374
+ 1. `query`, `when`, `realloc`, `pda` keywords
375
+ 2. Some assertion/test keyword paths in non-test production code
376
+ 3. Legacy checked-arithmetic operators (`+?`, `-?`, `*?`) where examples indicate migration
377
+
378
+ ## 6) CLI Canonical Usage
379
+
380
+ ### 6.1 Install and identity
381
+ ```bash
382
+ npm install -g @5ive-tech/cli
383
+ 5ive --version
384
+ ```
385
+
386
+ ### 6.2 Initialize
387
+ ```bash
388
+ 5ive init my-program
389
+ cd my-program
390
+ ```
391
+
392
+ ### 6.3 Compile
393
+ ```bash
394
+ 5ive compile src/main.v -o build/main.five
395
+ # or project-aware
396
+ 5ive build
397
+ ```
398
+
399
+ ### 6.4 Local execute
400
+ ```bash
401
+ 5ive execute build/main.five --local -f 0
402
+ ```
403
+
404
+ ### 6.5 Configure devnet
405
+ ```bash
406
+ 5ive config init
407
+ 5ive config set --target devnet
408
+ 5ive config set --keypair ~/.config/solana/id.json
409
+ 5ive config set --program-id <FIVE_VM_PROGRAM_ID> --target devnet
410
+ ```
411
+
412
+ ### 6.6 Deploy + execute on-chain
413
+ ```bash
414
+ 5ive deploy build/main.five --target devnet
415
+ 5ive execute build/main.five --target devnet -f 0
416
+ ```
417
+
418
+ ### 6.7 Advanced deploy modes
419
+ ```bash
420
+ 5ive deploy build/main.five --target devnet --optimized --progress
421
+ 5ive deploy build/main.five --target devnet --force-chunked --chunk-size 900
422
+ 5ive deploy build/main.five --target devnet --dry-run --format json
423
+ ```
424
+
425
+ ### 6.8 Test modes
426
+ ```bash
427
+ 5ive test --sdk-runner
428
+ 5ive test tests/ --on-chain --target devnet
429
+ 5ive test --sdk-runner --format json
430
+ ```
431
+
432
+ ## 7) Program ID and Target Resolution
433
+
434
+ On-chain command precedence (`deploy`, `execute`, `namespace`):
435
+ 1. `--program-id`
436
+ 2. `five.toml [deploy].program_id`
437
+ 3. `5ive config` value for current target
438
+ 4. `FIVE_PROGRAM_ID`
439
+
440
+ Never run on-chain commands with ambiguous target/program-id context.
441
+
442
+ ## 8) SDK Canonical Usage
443
+
444
+ ### 8.1 Load artifact
445
+ ```ts
446
+ import fs from "fs";
447
+ import { FiveSDK } from "@5ive-tech/sdk";
448
+
449
+ const fiveFileText = fs.readFileSync("build/main.five", "utf8");
450
+ const { abi } = await FiveSDK.loadFiveFile(fiveFileText);
451
+ ```
452
+
453
+ ### 8.2 Program client
454
+ ```ts
455
+ import { FiveProgram } from "@5ive-tech/sdk";
456
+
457
+ const program = FiveProgram.fromABI("<SCRIPT_ACCOUNT>", abi, {
458
+ fiveVMProgramId: "<FIVE_VM_PROGRAM_ID>",
459
+ vmStateAccount: "<VM_STATE_ACCOUNT>",
460
+ feeReceiverAccount: "<FEE_RECEIVER_ACCOUNT>",
461
+ });
462
+ ```
463
+
464
+ ### 8.3 Execution verification pattern
465
+ 1. Build instruction via `program.function(...).accounts(...).args(...).instruction()`
466
+ 2. Submit with preflight enabled
467
+ 3. Fetch confirmed tx
468
+ 4. Assert `meta.err == null`
469
+ 5. Record `meta.computeUnitsConsumed`
470
+
471
+ ### 8.4 SDK program ID resolution precedence
472
+ 1. Explicit `fiveVMProgramId`
473
+ 2. `FiveSDK.setDefaultProgramId(...)`
474
+ 3. `FIVE_PROGRAM_ID`
475
+ 4. release-baked default (if present)
476
+
477
+ ## 9) Frontend Integration Baseline
478
+
479
+ 1. Build execute instructions via SDK (`FiveProgram`), not custom serializers.
480
+ 2. Keep network selection explicit (`localnet`, `devnet`, `mainnet`).
481
+ 3. Surface signatures, CU metrics, and rich error states.
482
+ 4. Use LSP-backed editing where available to reduce DSL mistakes.
483
+
484
+ ## 10) Contract Pattern Recipes
485
+
486
+ This section provides composable patterns for the most common contract archetypes. When building a novel contract, identify which patterns apply and combine them.
487
+
488
+ ### 10.1 Authority-Gated State (Vault, Treasury, Config)
489
+
490
+ Core pattern: one or more pubkey fields control who can mutate state. Used in almost every contract.
491
+
492
+ ```v
493
+ account Config {
494
+ authority: pubkey;
495
+ value: u64;
496
+ is_locked: bool;
497
+ }
498
+
499
+ pub update_value(
500
+ config: Config @mut,
501
+ authority: account @signer,
502
+ new_value: u64
503
+ ) {
504
+ require(config.authority == authority.key);
505
+ require(!config.is_locked);
506
+ config.value = new_value;
507
+ }
508
+ ```
509
+
510
+ **Key ingredients:** ownership check via `.key`, boolean guard with `!`, field mutation.
511
+
512
+ ### 10.2 Custody & Withdraw (Vault, Staking)
513
+
514
+ Core pattern: deposit into an account, enforce balance invariants on withdraw.
515
+
516
+ ```v
517
+ pub deposit(
518
+ vault: VaultAccount @mut,
519
+ depositor: account @signer,
520
+ amount: u64
521
+ ) {
522
+ require(amount > 0);
523
+ vault.balance = vault.balance + amount;
524
+ }
525
+
526
+ pub withdraw(
527
+ vault: VaultAccount @mut,
528
+ authority: account @signer,
529
+ amount: u64
530
+ ) {
531
+ require(vault.authority == authority.key);
532
+ require(vault.balance >= amount);
533
+ require(amount > 0);
534
+ vault.balance = vault.balance - amount;
535
+ }
536
+ ```
537
+
538
+ **Key ingredients:** `>= amount` balance guard, arithmetic on fields, `> 0` zero-amount prevention.
539
+
540
+ ### 10.3 Lifecycle State Machine (Escrow, Auction, Proposal)
541
+
542
+ Core pattern: a status field controls which operations are valid. Transitions are guarded.
543
+
544
+ ```v
545
+ account Escrow {
546
+ seller: pubkey;
547
+ buyer: pubkey;
548
+ amount: u64;
549
+ status: u8; // 0=open, 1=funded, 2=released, 3=cancelled
550
+ }
551
+
552
+ pub fund_escrow(
553
+ escrow: Escrow @mut,
554
+ buyer: account @signer,
555
+ amount: u64
556
+ ) {
557
+ require(escrow.buyer == buyer.key);
558
+ require(escrow.status == 0);
559
+ require(amount == escrow.amount);
560
+ escrow.status = 1;
561
+ }
562
+
563
+ pub release_escrow(
564
+ escrow: Escrow @mut,
565
+ buyer: account @signer
566
+ ) {
567
+ require(escrow.buyer == buyer.key);
568
+ require(escrow.status == 1);
569
+ escrow.status = 2;
570
+ }
571
+ ```
572
+
573
+ **Key ingredients:** integer status field with `==` checks for state transitions, dual-party authorization.
574
+
575
+ ### 10.4 Supply Accounting (Token, Mint, Points)
576
+
577
+ Core pattern: a central supply counter stays synchronized with distributed balances.
578
+
579
+ ```v
580
+ pub mint_to(
581
+ supply_state: SupplyAccount @mut,
582
+ destination: BalanceAccount @mut,
583
+ authority: account @signer,
584
+ amount: u64
585
+ ) {
586
+ require(supply_state.authority == authority.key);
587
+ require(amount > 0);
588
+ supply_state.total_supply = supply_state.total_supply + amount;
589
+ destination.balance = destination.balance + amount;
590
+ }
591
+
592
+ pub burn(
593
+ supply_state: SupplyAccount @mut,
594
+ source: BalanceAccount @mut,
595
+ owner: account @signer,
596
+ amount: u64
597
+ ) {
598
+ require(source.owner == owner.key);
599
+ require(source.balance >= amount);
600
+ source.balance = source.balance - amount;
601
+ supply_state.total_supply = supply_state.total_supply - amount;
602
+ }
603
+ ```
604
+
605
+ **Key ingredients:** paired increment/decrement across two accounts, conservation invariant.
606
+
607
+ ### 10.5 Delegation & Approval (Token, DAO, Proxy)
608
+
609
+ Core pattern: an owner grants limited permissions to a delegate.
610
+
611
+ ```v
612
+ pub approve(
613
+ state: DelegableAccount @mut,
614
+ owner: account @signer,
615
+ delegate: pubkey,
616
+ limit: u64
617
+ ) {
618
+ require(state.owner == owner.key);
619
+ state.delegate = delegate;
620
+ state.delegated_limit = limit;
621
+ }
622
+
623
+ pub revoke(
624
+ state: DelegableAccount @mut,
625
+ owner: account @signer
626
+ ) {
627
+ require(state.owner == owner.key);
628
+ state.delegate = 0;
629
+ state.delegated_limit = 0;
630
+ }
631
+ ```
632
+
633
+ **Key ingredients:** delegate pubkey field, limit tracking, zero-assignment to revoke.
634
+
635
+ ### 10.6 Conservation Math (AMM, Orderbook, Settlement)
636
+
637
+ Core pattern: total value across accounts must remain constant.
638
+
639
+ ```v
640
+ pub swap(
641
+ pool_a: PoolAccount @mut,
642
+ pool_b: PoolAccount @mut,
643
+ user_a: UserAccount @mut,
644
+ user_b: UserAccount @mut,
645
+ trader: account @signer,
646
+ amount_in: u64
647
+ ) {
648
+ require(user_a.owner == trader.key);
649
+ require(user_a.balance >= amount_in);
650
+ require(amount_in > 0);
651
+ let amount_out = (pool_b.reserve * amount_in) / (pool_a.reserve + amount_in);
652
+ require(amount_out > 0);
653
+ user_a.balance = user_a.balance - amount_in;
654
+ pool_a.reserve = pool_a.reserve + amount_in;
655
+ pool_b.reserve = pool_b.reserve - amount_out;
656
+ user_b.balance = user_b.balance + amount_out;
657
+ }
658
+ ```
659
+
660
+ **Key ingredients:** `let` with computed expression, multi-account mutation, balance checks on both sides.
661
+
662
+ ### 10.7 Threshold & Risk Checks (Lending, Collateral, Liquidation)
663
+
664
+ Core pattern: actions gated by ratio or threshold comparisons.
665
+
666
+ ```v
667
+ pub borrow(
668
+ position: LoanPosition @mut,
669
+ borrower: account @signer,
670
+ collateral_value: u64,
671
+ borrow_amount: u64
672
+ ) {
673
+ require(position.owner == borrower.key);
674
+ require(borrow_amount > 0);
675
+ // Enforce 150% collateral ratio: collateral * 100 >= total_debt * 150
676
+ let new_debt = position.debt + borrow_amount;
677
+ require(collateral_value * 100 >= new_debt * 150);
678
+ position.debt = new_debt;
679
+ }
680
+ ```
681
+
682
+ **Key ingredients:** `let` for intermediate computation, integer math for ratio checks, compound conditions.
683
+
684
+ ### 10.8 External Program Integration (CPI)
685
+
686
+ Core pattern: call external Solana programs from within your contract via interfaces.
687
+
688
+ ```v
689
+ // Non-Anchor program (bincode, manual discriminators)
690
+ interface ExternalProgram @program("ExternalProgramID111111111111111111111111111") {
691
+ process @discriminator(1) (
692
+ state: Account,
693
+ authority: Account,
694
+ amount: u64
695
+ );
696
+ }
697
+
698
+ // Anchor program (borsh, auto discriminators)
699
+ interface AnchorProgram @anchor @program("AnchorProgramID11111111111111111111111111111") {
700
+ execute( // discriminator auto-generated
701
+ config: Account,
702
+ user: Account,
703
+ value: u64
704
+ );
705
+ }
706
+
707
+ pub perform_action(
708
+ local_state: MyState @mut,
709
+ external_account: account @mut,
710
+ user: account @signer,
711
+ amount: u64
712
+ ) {
713
+ require(local_state.authority == user.key);
714
+ require(amount > 0);
715
+
716
+ // CPI to external program
717
+ ExternalProgram.process(external_account, user, amount);
718
+
719
+ // Update local state after CPI
720
+ local_state.last_amount = amount;
721
+ local_state.call_count = local_state.call_count + 1;
722
+ }
723
+ ```
724
+
725
+ **Key ingredients:** interface with `@program`, `@discriminator` (or `@anchor` for auto), `Account` types for CPI params, dot-notation calls, `account @mut` function params for all CPI accounts, mixing CPI calls with local state updates.
726
+
727
+ ## 11) Mainnet Safety Policy
728
+
729
+ Required preflight gates:
730
+ 1. Freeze artifact hash
731
+ 2. Lock target/program-id/RPC/key source
732
+ 3. Validate key custody
733
+ 4. Run simulation/dry-run path
734
+ 5. Predefine rollback/containment actions
735
+
736
+ Post-deploy:
737
+ 1. smoke execute
738
+ 2. confirmed tx validation
739
+ 3. CU baseline capture
740
+ 4. incident process if anomalies appear
741
+
742
+ ## 12) Common Failure Signatures
743
+
744
+ 1. `No program ID resolved for Five VM`:
745
+ - set explicit program-id source
746
+ 2. `Function '<name>' not found in ABI`:
747
+ - use exact ABI name (including namespace)
748
+ 3. `Missing required account/argument`:
749
+ - satisfy `.accounts(...)` and `.args(...)`
750
+ 4. owner/program mismatch:
751
+ - verify target program ownership assumptions
752
+ 5. CPI mismatch:
753
+ - verify explicit serializer/discriminator/account order
754
+
755
+ ## 13) Definition of Done
756
+
757
+ Complete means:
758
+ 1. `.five` artifact produced
759
+ 2. tests passed with evidence
760
+ 3. deployment confirmed (if in scope)
761
+ 4. execution confirmed with `meta.err == null` (if in scope)
762
+ 5. signatures + CU metrics recorded
763
+ 6. integration snippet delivered (SDK/frontend when requested)
764
+
765
+ ## 14) Agent Behavior Rules
766
+
767
+ 1. Prefer deterministic, minimal command paths.
768
+ 2. Verify tx outcomes; do not assume send success == execution success.
769
+ 3. Avoid hidden defaults for deploy/CPI critical parameters.
770
+ 4. Keep changes auditable and reproducible.
771
+ 5. If uncertain, inspect compiler/CLI source directly.
772
+
773
+ ## 15) Agent One-Shot Contract Generation Procedure
774
+
775
+ Follow this procedure to produce correct 5IVE contracts on first compilation, regardless of contract type.
776
+
777
+ ### Step 1: Define account schemas
778
+ - Identify every distinct on-chain state object your contract needs.
779
+ - Each gets an `account Name { ... }` block with **all fields terminated by `;`**.
780
+ - Choose field types from: `pubkey`, `u8`–`u128`, `i8`–`i64`, `bool`, `string<N>`.
781
+ - Include an `authority: pubkey;` field on any account that needs access control.
782
+ - Include a `status: u8;` field for lifecycle/state-machine accounts.
783
+
784
+ ### Step 2: Define initializer functions
785
+ - For each account that users create at runtime, write an `init_*` function.
786
+ - The account parameter uses the full attribute stack: `Type @mut @init(payer=name, space=bytes) @signer`.
787
+ - The payer is `account @mut @signer`.
788
+ - Set every field to a known value (don't leave uninitialized fields).
789
+ - Return `-> pubkey` with `return account.key;` when callers need the address.
790
+
791
+ ### Step 3: Define interfaces (if calling external programs)
792
+ - Declare `interface Name @program("...") { ... }` at the top of your file.
793
+ - Each method gets `@discriminator(N)` as a single u8 inline: `method @discriminator(N) (...)`.
794
+ - Use `Account` type for on-chain account params, scalar types for data params.
795
+ - **Do not** add `@serializer(...)` — bincode is the default.
796
+
797
+ ### Step 4: Define action functions
798
+ - Every state-mutating function takes the relevant account(s) as `AccountType @mut`.
799
+ - Authorization: take an `account @signer` parameter, then `require(state.authority == signer.key);`.
800
+ - Guards: use `require()` with any comparison operator (`==`, `!=`, `<`, `<=`, `>`, `>=`, `!`).
801
+ - For balance operations: always check `require(source.balance >= amount);` before subtraction.
802
+ - For state machines: check `require(state.status == EXPECTED_STATUS);` before transition.
803
+ - Use `let` for intermediate computations (type inference handles it).
804
+ - For CPI calls: pass `account`-typed params directly (not `.key`), use dot notation `Interface.method(...)`.
805
+
806
+ ### Step 5: Define read/query functions
807
+ - Use `-> ReturnType` syntax for functions that return values.
808
+ - `return state.field;` to return account data.
809
+
810
+ ### Step 6: Compile and verify
811
+ - Run `5ive build` or `5ive compile src/main.v -o build/main.five`.
812
+ - Fix any parser errors (most common: missing `;` in account fields).
813
+
814
+ ### Syntax quick-reference
815
+
816
+ | Pattern | Syntax |
817
+ |---|---|
818
+ | Account field | `name: type;` (semicolon required) |
819
+ | Init parameter | `acc: Type @mut @init(payer=p, space=N) @signer` |
820
+ | Signer parameter | `caller: account @signer` or `caller: account @mut @signer` |
821
+ | Ownership check | `require(state.authority == caller.key);` |
822
+ | Balance guard | `require(state.balance >= amount);` |
823
+ | Boolean guard | `require(!state.is_locked);` |
824
+ | Zero-amount guard | `require(amount > 0);` |
825
+ | Revoke authority | `state.authority = 0;` |
826
+ | Status transition | `state.status = 1;` |
827
+ | Local variable (immutable) | `let x = expr;` |
828
+ | Local variable (mutable) | `let mut x = expr;` (required for reassignment) |
829
+ | Reassign local variable | `let mut x: u64 = 0; ... x = new_value;` |
830
+ | Return value | `pub fn(...) -> u64 { return state.value; }` |
831
+ | Fixed string field | `name: string<32>;` |
832
+ | Zero-init pubkey | `state.delegate = 0;` or `state.delegate = pubkey(0);` |
833
+ | Interface decl | `interface Name @program("...") { ... }` |
834
+ | Interface method | `method @discriminator(N) (param: Account, val: u64);` |
835
+ | CPI call | `InterfaceName.method(acct_param, value);` |
836
+ | CPI account param | `name: account @mut` (not `pubkey`) |
837
+
838
+ ## 16) Reference Implementations
839
+
840
+ Three verified, compilable patterns covering distinct contract archetypes. Use as canonical references.
841
+
842
+ ### 16.1 Token (Supply Accounting + Delegation + Freeze)
843
+
844
+ ```v
845
+ account Mint {
846
+ authority: pubkey;
847
+ freeze_authority: pubkey;
848
+ supply: u64;
849
+ decimals: u8;
850
+ name: string<32>;
851
+ symbol: string<32>;
852
+ }
853
+
854
+ account TokenAccount {
855
+ owner: pubkey;
856
+ mint: pubkey;
857
+ balance: u64;
858
+ is_frozen: bool;
859
+ delegate: pubkey;
860
+ delegated_amount: u64;
861
+ }
862
+
863
+ pub init_mint(
864
+ mint_account: Mint @mut @init(payer=authority, space=256) @signer,
865
+ authority: account @mut @signer,
866
+ freeze_authority: pubkey,
867
+ decimals: u8,
868
+ name: string<32>,
869
+ symbol: string<32>
870
+ ) -> pubkey {
871
+ require(decimals <= 20);
872
+ mint_account.authority = authority.key;
873
+ mint_account.freeze_authority = freeze_authority;
874
+ mint_account.supply = 0;
875
+ mint_account.decimals = decimals;
876
+ mint_account.name = name;
877
+ mint_account.symbol = symbol;
878
+ return mint_account.key;
879
+ }
880
+
881
+ pub transfer(
882
+ source: TokenAccount @mut,
883
+ destination: TokenAccount @mut,
884
+ owner: account @signer,
885
+ amount: u64
886
+ ) {
887
+ require(source.owner == owner.key);
888
+ require(source.balance >= amount);
889
+ require(source.mint == destination.mint);
890
+ require(!source.is_frozen);
891
+ require(!destination.is_frozen);
892
+ require(amount > 0);
893
+ source.balance = source.balance - amount;
894
+ destination.balance = destination.balance + amount;
895
+ }
896
+
897
+ pub approve(
898
+ source: TokenAccount @mut,
899
+ owner: account @signer,
900
+ delegate: pubkey,
901
+ amount: u64
902
+ ) {
903
+ require(source.owner == owner.key);
904
+ source.delegate = delegate;
905
+ source.delegated_amount = amount;
906
+ }
907
+ ```
908
+
909
+ **Patterns exercised:** `@init` stacking, supply accounting, freeze guards, delegation, `.key` extraction, `string<N>`, `-> pubkey` return.
910
+
911
+ ### 16.2 Vault (Custody + Authority Gating)
912
+
913
+ ```v
914
+ account Vault {
915
+ authority: pubkey;
916
+ balance: u64;
917
+ is_locked: bool;
918
+ }
919
+
920
+ pub init_vault(
921
+ vault: Vault @mut @init(payer=creator, space=128) @signer,
922
+ creator: account @mut @signer
923
+ ) -> pubkey {
924
+ vault.authority = creator.key;
925
+ vault.balance = 0;
926
+ vault.is_locked = false;
927
+ return vault.key;
928
+ }
929
+
930
+ pub deposit(
931
+ vault: Vault @mut,
932
+ depositor: account @signer,
933
+ amount: u64
934
+ ) {
935
+ require(!vault.is_locked);
936
+ require(amount > 0);
937
+ vault.balance = vault.balance + amount;
938
+ }
939
+
940
+ pub withdraw(
941
+ vault: Vault @mut,
942
+ authority: account @signer,
943
+ amount: u64
944
+ ) {
945
+ require(vault.authority == authority.key);
946
+ require(!vault.is_locked);
947
+ require(vault.balance >= amount);
948
+ require(amount > 0);
949
+ vault.balance = vault.balance - amount;
950
+ }
951
+
952
+ pub lock_vault(
953
+ vault: Vault @mut,
954
+ authority: account @signer
955
+ ) {
956
+ require(vault.authority == authority.key);
957
+ vault.is_locked = true;
958
+ }
959
+
960
+ pub transfer_authority(
961
+ vault: Vault @mut,
962
+ current_authority: account @signer,
963
+ new_authority: pubkey
964
+ ) {
965
+ require(vault.authority == current_authority.key);
966
+ vault.authority = new_authority;
967
+ }
968
+ ```
969
+
970
+ **Patterns exercised:** authority gating, boolean lock, balance guards, authority transfer, `@init` stacking.
971
+
972
+ ### 16.3 Escrow (Lifecycle State Machine + Dual-Party Auth)
973
+
974
+ ```v
975
+ account Escrow {
976
+ seller: pubkey;
977
+ buyer: pubkey;
978
+ amount: u64;
979
+ status: u8;
980
+ }
981
+
982
+ pub create_escrow(
983
+ escrow: Escrow @mut @init(payer=seller, space=128) @signer,
984
+ seller: account @mut @signer,
985
+ buyer: pubkey,
986
+ amount: u64
987
+ ) -> pubkey {
988
+ require(amount > 0);
989
+ escrow.seller = seller.key;
990
+ escrow.buyer = buyer;
991
+ escrow.amount = amount;
992
+ escrow.status = 0;
993
+ return escrow.key;
994
+ }
995
+
996
+ pub fund_escrow(
997
+ escrow: Escrow @mut,
998
+ buyer: account @signer,
999
+ amount: u64
1000
+ ) {
1001
+ require(escrow.buyer == buyer.key);
1002
+ require(escrow.status == 0);
1003
+ require(amount == escrow.amount);
1004
+ escrow.status = 1;
1005
+ }
1006
+
1007
+ pub release(
1008
+ escrow: Escrow @mut,
1009
+ buyer: account @signer
1010
+ ) {
1011
+ require(escrow.buyer == buyer.key);
1012
+ require(escrow.status == 1);
1013
+ escrow.status = 2;
1014
+ }
1015
+
1016
+ pub cancel(
1017
+ escrow: Escrow @mut,
1018
+ seller: account @signer
1019
+ ) {
1020
+ require(escrow.seller == seller.key);
1021
+ require(escrow.status == 0);
1022
+ escrow.status = 3;
1023
+ }
1024
+ ```
1025
+
1026
+ **Patterns exercised:** integer status for state machine, dual-party authorization, lifecycle transitions, exact-amount matching.
1027
+
1028
+ ### 16.4 CPI to External Program (Interface + Cross-Program Calls)
1029
+
1030
+ ```v
1031
+ // Interface for external program (non-Anchor, bincode)
1032
+ interface ExternalProgram @program("ExternalProgramID111111111111111111111111111") {
1033
+ update_value @discriminator(5) (
1034
+ state: Account,
1035
+ authority: Account,
1036
+ new_value: u64
1037
+ );
1038
+ }
1039
+
1040
+ // Interface for Anchor program (borsh, auto discriminators)
1041
+ interface AnchorProgram @anchor @program("AnchorProgramID11111111111111111111111111111") {
1042
+ process( // discriminator auto-generated from method name
1043
+ config: Account,
1044
+ user: Account,
1045
+ amount: u64
1046
+ );
1047
+ }
1048
+
1049
+ account Controller {
1050
+ authority: pubkey;
1051
+ counter: u64;
1052
+ last_value: u64;
1053
+ }
1054
+
1055
+ pub init_controller(
1056
+ controller: Controller @mut @init(payer=creator, space=128) @signer,
1057
+ creator: account @mut @signer
1058
+ ) -> pubkey {
1059
+ controller.authority = creator.key;
1060
+ controller.counter = 0;
1061
+ controller.last_value = 0;
1062
+ return controller.key;
1063
+ }
1064
+
1065
+ pub call_external(
1066
+ controller: Controller @mut,
1067
+ external_state: account @mut,
1068
+ authority: account @signer,
1069
+ value: u64
1070
+ ) {
1071
+ require(controller.authority == authority.key);
1072
+ require(value > 0);
1073
+
1074
+ // CPI to external program
1075
+ ExternalProgram.update_value(external_state, authority, value);
1076
+
1077
+ // Update local state
1078
+ controller.counter = controller.counter + 1;
1079
+ controller.last_value = value;
1080
+ }
1081
+
1082
+ pub call_anchor(
1083
+ controller: Controller @mut,
1084
+ anchor_config: account @mut,
1085
+ user: account @signer,
1086
+ amount: u64
1087
+ ) {
1088
+ require(controller.authority == user.key);
1089
+
1090
+ // CPI to Anchor program
1091
+ AnchorProgram.process(anchor_config, user, amount);
1092
+
1093
+ controller.counter = controller.counter + 1;
1094
+ }
1095
+ ```
1096
+
1097
+ **Patterns exercised:** dual interface types (bincode with manual discriminators, Anchor with auto discriminators), `@program` + `@discriminator` vs `@anchor`, `Account` types in interfaces, dot-notation CPI calls, `account @mut` params for CPI, local state updates after CPI.