@5ive-tech/cli 1.0.16 → 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.16",
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",
@@ -210,27 +210,69 @@ Parser handles:
210
210
  - list: `::{a, b}`
211
211
  - typed list entries: `method foo`, `interface Bar`
212
212
 
213
- ### 4.9 Interfaces and CPI features
214
- Interface parser supports:
215
- 1. `interface Name ... { methods... }`
216
- 2. Program binding:
217
- - `program("...")`
218
- - `@program("...")`
219
- 3. Serializer hints:
220
- - `serializer(...)`
221
- - `@serializer(...)`
222
- 4. Anchor marker:
223
- - `@anchor interface ...`
224
- 5. Method discriminators:
225
- - `@discriminator(u8)`
226
- - `@discriminator([byte,...])`
227
- - `discriminator_bytes(...)` forms in parser/compiler AST
228
- 6. Optional interface method return types
229
-
230
- CPI hard rule for agents:
231
- 1. Always set `@program(...)`
232
- 2. Always set `@serializer(...)` explicitly
233
- 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
234
276
 
235
277
  ### 4.10 Events and error/enums
236
278
  Parser/AST include:
@@ -635,6 +677,49 @@ pub borrow(
635
677
 
636
678
  **Key ingredients:** `let` for intermediate computation, integer math for ratio checks, compound conditions.
637
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
+
638
723
  ## 11) Mainnet Safety Policy
639
724
 
640
725
  Required preflight gates:
@@ -699,19 +784,26 @@ Follow this procedure to produce correct 5IVE contracts on first compilation, re
699
784
  - Set every field to a known value (don't leave uninitialized fields).
700
785
  - Return `-> pubkey` with `return account.key;` when callers need the address.
701
786
 
702
- ### Step 3: Define action functions
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
703
794
  - Every state-mutating function takes the relevant account(s) as `AccountType @mut`.
704
795
  - Authorization: take an `account @signer` parameter, then `require(state.authority == signer.key);`.
705
796
  - Guards: use `require()` with any comparison operator (`==`, `!=`, `<`, `<=`, `>`, `>=`, `!`).
706
797
  - For balance operations: always check `require(source.balance >= amount);` before subtraction.
707
798
  - For state machines: check `require(state.status == EXPECTED_STATUS);` before transition.
708
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(...)`.
709
801
 
710
- ### Step 4: Define read/query functions
802
+ ### Step 5: Define read/query functions
711
803
  - Use `-> ReturnType` syntax for functions that return values.
712
804
  - `return state.field;` to return account data.
713
805
 
714
- ### Step 5: Compile and verify
806
+ ### Step 6: Compile and verify
715
807
  - Run `5ive build` or `5ive compile src/main.v -o build/main.five`.
716
808
  - Fix any parser errors (most common: missing `;` in account fields).
717
809
 
@@ -732,6 +824,10 @@ Follow this procedure to produce correct 5IVE contracts on first compilation, re
732
824
  | Return value | `pub fn(...) -> u64 { return state.value; }` |
733
825
  | Fixed string field | `name: string<32>;` |
734
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`) |
735
831
 
736
832
  ## 16) Reference Implementations
737
833
 
@@ -922,3 +1018,74 @@ pub cancel(
922
1018
  ```
923
1019
 
924
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.