@5ive-tech/cli 1.0.18 → 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.
- package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/package.json +3 -3
- package/templates/AGENTS.md +123 -1038
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@5ive-tech/cli",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
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
|
+
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -1,1091 +1,176 @@
|
|
|
1
|
-
# AGENTS.md
|
|
1
|
+
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
This
|
|
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
|
-
|
|
5
|
+
This is normative: agents and humans who operate as "agents" in this repository must follow these rules.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
---
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
1. Compiler/CLI/SDK source code
|
|
20
|
-
2. Package manifests + command definitions
|
|
21
|
-
3. READMEs/examples/docs
|
|
14
|
+
---
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
## High-level Policies
|
|
24
17
|
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
+
---
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
## Coding & PR Rules for Agents
|
|
49
47
|
|
|
50
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
76
|
+
---
|
|
106
77
|
|
|
107
|
-
|
|
78
|
+
## Specific Rust Guidance
|
|
108
79
|
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
+
---
|
|
214
112
|
|
|
215
|
-
|
|
113
|
+
## Enforcement & Automation
|
|
216
114
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
-
```v
|
|
322
|
-
state.authority = 0; // revokes authority — irreversible
|
|
323
|
-
```
|
|
156
|
+
## FAQ
|
|
324
157
|
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
-
|
|
334
|
-
|
|
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
|
-
|
|
167
|
+
---
|
|
344
168
|
|
|
345
|
-
##
|
|
169
|
+
## Ownership & Maintenance
|
|
346
170
|
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|