@5ive-tech/cli 1.0.17 → 1.0.19

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