@5ive-tech/cli 1.0.15 → 1.0.17

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.15",
3
+ "version": "1.0.17",
4
4
  "description": "High-performance CLI for Five VM development with WebAssembly integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,69 +31,17 @@ Never rely on stale docs when behavior is high-stakes (deploy/execute/CPI encodi
31
31
  5. Execute and verify confirmed tx metadata (`meta.err == null`).
32
32
  6. Record signatures + compute units.
33
33
 
34
- ## 3.1) Strict Authoring Mode (Required For New Contracts)
35
-
36
- Use this profile unless you have direct proof in your current compiler version that a broader syntax feature is stable.
37
-
38
- 1. Prefer top-level accounts + top-level functions. Do not wrap programs in `script Name { ... }`.
39
- 2. Use account/signer/mut attributes in this order style:
40
- - `owner: pubkey @signer`
41
- - `state: Mint @mut`
42
- - `ata: TokenAccount @mut @init`
43
- 3. Prefer plain function declarations like:
44
- - `pub transfer(...) { ... }`
45
- - `pub mint_to(...) { ... }`
46
- 4. Any function intended to be called via `5ive execute` must be declared `pub`.
47
- 5. Keep logic to `require`, `if/else`, arithmetic, assignments, and simple returns.
48
- 6. Validate after every meaningful edit:
49
- - `5ive compile src/main.v -o build/main.five`
50
-
51
- Avoid these patterns by default (high-risk for invalid output in agent-generated code):
52
- 1. `instruction ...` function form
53
- 2. Attribute-first parameters like `@signer owner: pubkey`
54
- 3. Optional parameters/fields in signatures (`x?: T`) and nullish expressions (`??`)
55
- 4. `enum`/`event` heavy first-pass contracts
56
- 5. Event emission (`event` / `emit`) unless you have compile+runtime proof in your current toolchain
57
- 6. Unverified literals/helpers like `pubkey(0)` unless confirmed in working examples
58
- 7. Parser-only/experimental syntax from tokenizer docs without a compile-verified example
59
-
60
- Safe token-style baseline:
61
- ```v
62
- account Mint {
63
- authority: pubkey;
64
- supply: u64;
65
- decimals: u8;
66
- }
67
-
68
- account TokenAccount {
69
- owner_key: pubkey;
70
- mint: pubkey;
71
- bal: u64;
72
- }
34
+ ### 3.1 Strict Authoring Rules
73
35
 
74
- pub init_mint(
75
- state: Mint @mut,
76
- authority: pubkey @signer,
77
- decimals: u8
78
- ) {
79
- state.authority = authority;
80
- state.supply = 0;
81
- state.decimals = decimals;
82
- }
36
+ These rules are non-negotiable and prevent the most common compilation failures:
83
37
 
84
- pub transfer(
85
- source: TokenAccount @mut,
86
- destination: TokenAccount @mut,
87
- owner: pubkey @signer,
88
- amount: u64
89
- ) {
90
- require(source.owner_key == owner, "owner mismatch");
91
- require(source.mint == destination.mint, "mint mismatch");
92
- require(source.bal >= amount, "insufficient funds");
93
- source.bal = source.bal - amount;
94
- destination.bal = destination.bal + amount;
95
- }
96
- ```
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()`** `==`, `!=`, `<`, `<=`, `>`, `>=`, `!`.
97
45
 
98
46
  ## 4) DSL Feature Inventory (Deep)
99
47
 
@@ -101,7 +49,7 @@ This section enumerates language features discovered from parser/compiler code a
101
49
 
102
50
  ### 4.1 Top-level declarations
103
51
  Observed and/or parsed:
104
- 1. `account Name { ... }`
52
+ 1. `account Name { ... }` — **all fields must be terminated with `;`**
105
53
  2. Global fields/variables (including `mut`)
106
54
  3. `init { ... }` block
107
55
  4. `constraints { ... }` block
@@ -111,6 +59,21 @@ Observed and/or parsed:
111
59
  8. `use` / `import` statements
112
60
  9. Legacy `script Name { ... }` wrapper (parser-supported)
113
61
 
62
+ ```v
63
+ // ✅ CORRECT — semicolons required
64
+ account Mint {
65
+ authority: pubkey;
66
+ supply: u64;
67
+ decimals: u8;
68
+ }
69
+
70
+ // ❌ WRONG — parser failure
71
+ account Mint {
72
+ authority: pubkey
73
+ supply: u64
74
+ }
75
+ ```
76
+
114
77
  ### 4.2 Function definition forms
115
78
  Parser accepts flexible forms:
116
79
  1. `pub add(...) -> ... { ... }`
@@ -141,11 +104,28 @@ Common attributes:
141
104
 
142
105
  Examples also show legacy bracket seed forms after `@init`.
143
106
 
107
+ **Attribute stacking order for account parameters (empirically verified):**
108
+
109
+ ```
110
+ Type @mut @init(payer=name, space=bytes) @signer
111
+ ```
112
+
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
+ ```
121
+
122
+ Order: (1) Type declaration → (2) `@mut` → (3) `@init(...)` → (4) `@signer`.
123
+
144
124
  ### 4.5 Types
145
125
  Supported/parsed type families:
146
126
  1. Primitive numeric/bool/pubkey/string types (`u8..u128`, `i8..i64`, `bool`, `pubkey`, `string`)
147
127
  2. `Account` type and account-typed params
148
- 3. Sized strings: `string<32>`
128
+ 3. **Sized strings: `string<N>`** — production-safe, use in accounts and function parameters
149
129
  4. Arrays:
150
130
  - Rust style: `[T; N]`
151
131
  - TypeScript-style sized: `T[N]`
@@ -158,20 +138,29 @@ Supported/parsed type families:
158
138
  - nested generics (`Option<Option<u64>>` etc.)
159
139
  8. Namespaced/custom types: `module::Type`
160
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)
161
142
 
162
143
  ### 4.6 Statements
163
144
  Observed and parser-supported:
164
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
165
148
  2. Assignment:
166
149
  - direct: `x = y`
167
150
  - compound: `+=`, `-=`, `*=`, `/=`, `<<=`, `>>=`, `&=`, `|=`, `^=`
168
151
  3. Field assignment: `obj.field = value`
169
- 4. Return statements (`return`, `return value`)
170
- 5. Guard/assertion: `require(condition)`
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);`
171
159
  6. Conditionals:
172
160
  - `if (...) {}`
173
161
  - `else if (...) {}`
174
162
  - `else {}`
163
+ - Conditionals support nested `require()` statements and multiple assignments in both branches
175
164
  7. Pattern matching: `match expr { ... }`, with optional arm guards (`if ...`)
176
165
  8. Loops:
177
166
  - `while (...) { ... }`
@@ -221,27 +210,69 @@ Parser handles:
221
210
  - list: `::{a, b}`
222
211
  - typed list entries: `method foo`, `interface Bar`
223
212
 
224
- ### 4.9 Interfaces and CPI features
225
- Interface parser supports:
226
- 1. `interface Name ... { methods... }`
227
- 2. Program binding:
228
- - `program("...")`
229
- - `@program("...")`
230
- 3. Serializer hints:
231
- - `serializer(...)`
232
- - `@serializer(...)`
233
- 4. Anchor marker:
234
- - `@anchor interface ...`
235
- 5. Method discriminators:
236
- - `@discriminator(u8)`
237
- - `@discriminator([byte,...])`
238
- - `discriminator_bytes(...)` forms in parser/compiler AST
239
- 6. Optional interface method return types
240
-
241
- CPI hard rule for agents:
242
- 1. Always set `@program(...)`
243
- 2. Always set `@serializer(...)` explicitly
244
- 3. Always set discriminator explicitly
213
+ ### 4.9 Interfaces and CPI (Cross-Program Invocation)
214
+
215
+ Interfaces define external program calls. **Empirically verified rules:**
216
+
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`)
231
+
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
+ }
242
+
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
+ }
251
+
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
+ }
260
+
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
+ ```
269
+
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
245
276
 
246
277
  ### 4.10 Events and error/enums
247
278
  Parser/AST include:
@@ -268,24 +299,64 @@ From tokenizer/parser support:
268
299
 
269
300
  Repository tests also use comment-based param conventions (`// @test-params ...`) in many scripts.
270
301
 
271
- ### 4.12 Blockchain-oriented built-ins seen in examples
272
- Observed in scripts/templates:
302
+ ### 4.12 Blockchain-oriented built-ins
303
+ Core built-ins available in all contracts:
273
304
  1. `derive_pda(...)` (including bump-return and bump-specified variants)
274
305
  2. `get_clock()`
275
306
  3. `get_key(...)`
276
- 4. account key access: `authority.key`
307
+ 4. **Account key access: `param.key`** — **core pattern for all `account`-typed parameters.** Use `.key` to extract pubkeys for comparisons and assignments.
308
+
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
+ ```
277
319
 
278
- Treat built-ins as compiler/runtime coupled features; verify signatures in current examples before use.
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
+ ```
324
+
325
+ ### 4.13 Return types and values
326
+ Functions can declare return types with `->` syntax:
327
+
328
+ ```v
329
+ pub get_value(state: MyAccount) -> u64 {
330
+ return state.amount;
331
+ }
332
+
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
+ ```
342
+
343
+ Confirmed return types: `u8`, `u16`, `u32`, `u64`, `u128`, `i8`..`i64`, `bool`, `pubkey`.
279
344
 
280
345
  ## 5) Feature Maturity Matrix (Agent Safety)
281
346
 
282
347
  ### 5.1 Generally production-oriented (widely used in templates)
283
- 1. Accounts, `@mut`, `@signer`, `@init`
284
- 2. `require`
285
- 3. Basic control flow (`if`, `while`)
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
286
351
  4. Arithmetic/comparison/boolean expressions
287
352
  5. `.five` compile/deploy/execute path
288
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
289
360
 
290
361
  ### 5.2 Available but validate per-version before critical use
291
362
  1. Match expressions with `Option`/`Result`
@@ -406,18 +477,248 @@ const program = FiveProgram.fromABI("<SCRIPT_ACCOUNT>", abi, {
406
477
  3. Surface signatures, CU metrics, and rich error states.
407
478
  4. Use LSP-backed editing where available to reduce DSL mistakes.
408
479
 
409
- ## 10) Pattern Mapping for Complex Contracts
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
+ ```
410
720
 
411
- 1. Vault:
412
- - authority-gated custody, withdraw invariants
413
- 2. Escrow:
414
- - lifecycle transitions, dual-party authorization
415
- 3. Token/mint authority:
416
- - supply accounting, freeze/delegate controls
417
- 4. AMM/orderbook:
418
- - conservation math, deterministic settlement
419
- 5. Lending/perps/stablecoin:
420
- - collateral/liquidation thresholds, risk checks
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.
421
722
 
422
723
  ## 11) Mainnet Safety Policy
423
724
 
@@ -464,3 +765,327 @@ Complete means:
464
765
  3. Avoid hidden defaults for deploy/CPI critical parameters.
465
766
  4. Keep changes auditable and reproducible.
466
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.