@pina-rs/skill 0.0.0 → 0.10.0
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/README.md +25 -1
- package/SKILL.md +48 -0
- package/agents/openai.yaml +7 -0
- package/bin/pina-skill.cjs +113 -0
- package/package.json +39 -1
- package/references/cli-and-codegen.md +191 -0
- package/references/program-authoring.md +63 -0
- package/references/project-setup.md +69 -0
- package/references/testing.md +69 -0
- package/CHANGELOG.md +0 -3
- package/LICENSE +0 -1
package/README.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
1
|
# @pina-rs/skill
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/pina-rs/pina/main/.github/assets/logo.png" alt="The Pina logo: a low-poly origami pineapple" width="140">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
Agent guidance for creating, auditing, and maintaining Pina Solana programs.
|
|
8
|
+
|
|
9
|
+
The skill covers project setup, discriminator-first data layouts, account validation, PDA design, IDL and client generation, SBF profiling, and proportionate verification. Its instructions preserve `no_std` compatibility and treat the checked-in project configuration as authoritative.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install --global @pina-rs/skill
|
|
15
|
+
pina-skill --install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The default destination is `$CODEX_HOME/skills/pina` when `CODEX_HOME` is set, otherwise `~/.codex/skills/pina`. Installation refuses to replace an existing skill directory.
|
|
19
|
+
|
|
20
|
+
Inspect the source path or print manual installation instructions with:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pina-skill --print-path
|
|
24
|
+
pina-skill --print-install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
At runtime, agents begin with [SKILL.md](./SKILL.md) and load a focused file under [references](./references) only when the task needs it.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pina
|
|
3
|
+
description: Create, audit, and maintain no_std Solana programs built with Pina and pinocchio. Use for Pina project setup, program identity, account and instruction authoring, PDA validation, diagnostics, IDL or client generation, SBF profiling, tests, and project upgrades.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pina
|
|
7
|
+
|
|
8
|
+
Build Pina programs that are small, explicit, and safe at the account boundary. Preserve the project's chosen structure and commands unless the user asks for a redesign.
|
|
9
|
+
|
|
10
|
+
## Establish the local contract
|
|
11
|
+
|
|
12
|
+
Before changing code:
|
|
13
|
+
|
|
14
|
+
1. Read the nearest `AGENTS.md`, `Cargo.toml`, `.cargo/config.toml`, and project documentation.
|
|
15
|
+
2. Inspect the installed interface with `pina --help` and `pina <command> --help`; do not rely on remembered flags.
|
|
16
|
+
3. Identify the program crate, its `pina` version and features, its entrypoint feature, and its existing test harness.
|
|
17
|
+
4. Treat generated IDLs and clients as derived files. Find the repository's generation command before editing them.
|
|
18
|
+
|
|
19
|
+
When no project exists, read [references/project-setup.md](references/project-setup.md). For an existing program, select only the reference that matches the task.
|
|
20
|
+
|
|
21
|
+
## Non-negotiable program invariants
|
|
22
|
+
|
|
23
|
+
- Preserve `no_std` compatibility for on-chain code. Keep host-only tooling and test dependencies outside the program runtime path.
|
|
24
|
+
- Do not introduce `unsafe` code or unstable features.
|
|
25
|
+
- Validate account identity, signer status, writability, ownership, and data shape before casts, mutation, lamport transfers, resize operations, or CPI.
|
|
26
|
+
- Use explicit discriminator values and type-specific PDA seed namespaces. Prefer canonical bump validation.
|
|
27
|
+
- Keep instruction dispatch deterministic: parse once, match explicitly, then construct and validate the accounts type for that instruction.
|
|
28
|
+
- Maintain discriminator-first, fixed-layout storage types expected by Pina and zeropod. Do not place ordinary Rust `bool`, `String`, or variable-length collections in zero-copy account layouts.
|
|
29
|
+
- Preserve error values and wire formats unless the user explicitly accepts a compatibility change.
|
|
30
|
+
|
|
31
|
+
Read [references/program-authoring.md](references/program-authoring.md) before changing macros, account layouts, validation chains, PDAs, CPIs, or close/reallocation logic.
|
|
32
|
+
|
|
33
|
+
## Workflow
|
|
34
|
+
|
|
35
|
+
1. Inspect the smallest relevant surface and state the compatibility boundary: wire format, account layout, program ID, generated IDL, or CLI output.
|
|
36
|
+
2. Make the narrowest idiomatic change. Reuse Pina validation and loader APIs instead of duplicating parsing or ownership checks.
|
|
37
|
+
3. Add or update a regression test at the layer where the behavior is observable.
|
|
38
|
+
4. Run focused tests first, then the repository's documented format, lint, build, and test commands. For a standalone project without task aliases, use Cargo commands from [references/testing.md](references/testing.md).
|
|
39
|
+
5. Regenerate the IDL and clients when the public program surface changes. Review the diff before accepting generated output.
|
|
40
|
+
|
|
41
|
+
## Task routing
|
|
42
|
+
|
|
43
|
+
- Project creation, dependency features, entrypoint wiring, or workspace layout: read [references/project-setup.md](references/project-setup.md).
|
|
44
|
+
- Accounts, instructions, discriminators, PDAs, validation, CPI, resize, or close behavior: read [references/program-authoring.md](references/program-authoring.md).
|
|
45
|
+
- CLI discovery, project diagnostics, program keys, IDL extraction, Codama client generation, terminal docs, completions, or profiling: read [references/cli-and-codegen.md](references/cli-and-codegen.md).
|
|
46
|
+
- Unit, Mollusk, SBF, generated-artifact, or release checks: read [references/testing.md](references/testing.md).
|
|
47
|
+
|
|
48
|
+
Do not load every reference for routine edits.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { cpSync, existsSync, mkdirSync } = require("node:fs");
|
|
5
|
+
const { homedir } = require("node:os");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
9
|
+
const skillEntries = Object.freeze(["SKILL.md", "agents", "references"]);
|
|
10
|
+
|
|
11
|
+
function defaultDestination(environment = process.env) {
|
|
12
|
+
const codexHome = environment.CODEX_HOME || path.join(homedir(), ".codex");
|
|
13
|
+
return path.join(codexHome, "skills", "pina");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function installSkill(destination) {
|
|
17
|
+
const resolvedDestination = path.resolve(destination);
|
|
18
|
+
if (existsSync(resolvedDestination)) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Refusing to replace existing skill directory: ${resolvedDestination}. ` +
|
|
21
|
+
"Remove or move it after reviewing your local changes, then run the installer again.",
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
mkdirSync(resolvedDestination, { recursive: true });
|
|
26
|
+
for (const entry of skillEntries) {
|
|
27
|
+
cpSync(
|
|
28
|
+
path.join(packageRoot, entry),
|
|
29
|
+
path.join(resolvedDestination, entry),
|
|
30
|
+
{
|
|
31
|
+
recursive: true,
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return resolvedDestination;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function helpText() {
|
|
40
|
+
return `Install or locate the Pina agent skill.
|
|
41
|
+
|
|
42
|
+
Usage:
|
|
43
|
+
pina-skill --install [DIR]
|
|
44
|
+
pina-skill --print-path
|
|
45
|
+
pina-skill --print-install
|
|
46
|
+
pina-skill --help
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--install [DIR] Copy the skill to DIR. Defaults to the Codex skill directory.
|
|
50
|
+
--print-path Print the packaged skill source directory.
|
|
51
|
+
--print-install Print manual installation commands.
|
|
52
|
+
-h, --help Show this help.
|
|
53
|
+
|
|
54
|
+
The installer never overwrites an existing skill directory.`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function main(arguments_ = process.argv.slice(2)) {
|
|
58
|
+
const command = arguments_[0];
|
|
59
|
+
if (command === "--print-path") {
|
|
60
|
+
console.log(packageRoot);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (command === "--print-install") {
|
|
65
|
+
const destination = defaultDestination();
|
|
66
|
+
console.log(`Packaged skill: ${packageRoot}`);
|
|
67
|
+
console.log(`Default destination: ${destination}`);
|
|
68
|
+
console.log("Install with: pina-skill --install");
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (command === "--install") {
|
|
73
|
+
const requestedDestination = arguments_[1];
|
|
74
|
+
if (
|
|
75
|
+
arguments_.length > 2 ||
|
|
76
|
+
(requestedDestination !== undefined &&
|
|
77
|
+
requestedDestination.startsWith("--"))
|
|
78
|
+
) {
|
|
79
|
+
console.error("`--install` accepts at most one destination directory.");
|
|
80
|
+
return 2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const destination = requestedDestination ?? defaultDestination();
|
|
84
|
+
console.log(`Installed Pina skill at ${installSkill(destination)}`);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (command === undefined || command === "--help" || command === "-h") {
|
|
89
|
+
console.log(helpText());
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.error(`Unknown argument: ${command}`);
|
|
94
|
+
console.error("Run `pina-skill --help` for usage.");
|
|
95
|
+
return 2;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
defaultDestination,
|
|
100
|
+
helpText,
|
|
101
|
+
installSkill,
|
|
102
|
+
main,
|
|
103
|
+
packageRoot,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (require.main === module) {
|
|
107
|
+
try {
|
|
108
|
+
process.exitCode = main();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +1,39 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "@pina-rs/skill",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Agent skill for creating and maintaining secure Pina Solana programs",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"pina",
|
|
8
|
+
"pinocchio",
|
|
9
|
+
"skill",
|
|
10
|
+
"solana"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://pina.rs",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/pina-rs/pina/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/pina-rs/pina.git",
|
|
20
|
+
"directory": "packages/pina__skill"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"pina-skill": "bin/pina-skill.cjs"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"SKILL.md",
|
|
27
|
+
"README.md",
|
|
28
|
+
"agents",
|
|
29
|
+
"bin",
|
|
30
|
+
"references"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"provenance": true
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# CLI and Code Generation
|
|
2
|
+
|
|
3
|
+
## Discover commands from help
|
|
4
|
+
|
|
5
|
+
The installed CLI is authoritative:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pina --help
|
|
9
|
+
pina build --help
|
|
10
|
+
pina generate --help
|
|
11
|
+
pina idl --help
|
|
12
|
+
pina idl generate --help
|
|
13
|
+
pina idl fetch --help
|
|
14
|
+
pina idl diff --help
|
|
15
|
+
pina idl publish --help
|
|
16
|
+
pina docs --help
|
|
17
|
+
pina init --help
|
|
18
|
+
pina test --help
|
|
19
|
+
pina dev --help
|
|
20
|
+
pina keys --help
|
|
21
|
+
pina doctor --help
|
|
22
|
+
pina completions --help
|
|
23
|
+
pina profile --help
|
|
24
|
+
pina deploy --help
|
|
25
|
+
pina codama generate --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `pina docs` to list bundled terminal topics. Custom topics can be supplied through `PINA_TEMPLATES_DIR` when a project maintains its own operational guidance.
|
|
29
|
+
|
|
30
|
+
## Daily project workflow
|
|
31
|
+
|
|
32
|
+
Run project-aware commands from the program directory or any descendant. Pina uses the nearest ancestor `Pina.toml`; an existing unambiguous Cargo package also works without configuration.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pina build
|
|
36
|
+
pina test --unit
|
|
37
|
+
pina test
|
|
38
|
+
pina generate
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`pina build` compiles SBF with the required `bpf-entrypoint` feature and refreshes the IDL. Pass program features explicitly when required:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
pina build --features logs,cpi --no-default-features
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The library target name determines the canonical outputs:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
<cargo-target>/deploy/<library-name>.so
|
|
51
|
+
<cargo-target>/idl/<library-name>.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`pina generate` refreshes that IDL and renders the client languages selected in `Pina.toml`. Override the selection for one run with repeatable `--client rust`, `--client typescript`, or `--client dart` flags. Generated ecosystem roots may be replaced, so keep hand-written code outside them.
|
|
55
|
+
|
|
56
|
+
## Project diagnostics and identity
|
|
57
|
+
|
|
58
|
+
Use the versioned diagnostic report before changing a project:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pina doctor --json
|
|
62
|
+
pina keys show --json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`doctor --json` keeps stdout valid JSON and returns a failing exit status when required project or SBF prerequisites are unavailable. Its tool requirements follow the clients selected in `Pina.toml`.
|
|
66
|
+
|
|
67
|
+
Treat program identity changes as security-sensitive. `pina keys sync` validates an existing Ed25519 keypair and updates exactly one parsed `declare_id!`. `pina keys new` creates a local identity; only `pina keys new --force` may rotate an existing one. Never copy or print keypair bytes. On platforms where Pina cannot guarantee private permissions, generate the keypair with trusted platform tooling and then run `pina keys sync --keypair <path>`.
|
|
68
|
+
|
|
69
|
+
## Deterministic build artifacts
|
|
70
|
+
|
|
71
|
+
Use the verified-build backend when you need a deterministic SBF artifact:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
pina build --verify
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This requires an exact `solana-verify 0.5.1` installation, a working Docker-compatible daemon, a root `Cargo.lock`, the generated Solana CLI workspace metadata, and a completely clean Git worktree. Pina does not install or update these prerequisites.
|
|
78
|
+
|
|
79
|
+
The successful command prints the canonical `target/deploy` artifact, the generated IDL, a content-addressed SBF artifact, and its Pina-local build-record path. Keep the printed record and adjacent SBF together; consumers recompute the executable hash before trusting the record.
|
|
80
|
+
|
|
81
|
+
`pina build --verify` creates deterministic build inputs and outputs. It does not compare the artifact with an on-chain program or record an on-chain verification result.
|
|
82
|
+
|
|
83
|
+
## Testing and development
|
|
84
|
+
|
|
85
|
+
Use `pina test --unit` for native/Mollusk tests and `pina test` for the generated SBF/Surfpool integration package under `tests/surfpool`. Cargo remains attached to the terminal in both modes. `pina dev` delegates persistent artifact watching and redeployment to Surfpool, which owns terminal input, output, errors, prompts, and Ctrl-C until it exits. Its default is offline, so select `--network` or `--rpc-url` only when remote state is required. Prefer a named network. Explicit RPC URLs must be credential-free HTTP(S) URLs with a host and no user information, query, fragment, or control character. They are visible in Surfpool's child-process arguments, so never put a secret anywhere in the host, path, or other URL text. On the first run, use `pina dev --yes`, then inspect and commit the `txtx.yml` runbook Surfpool creates.
|
|
86
|
+
|
|
87
|
+
## IDL extraction
|
|
88
|
+
|
|
89
|
+
Generate a Codama root-node document from a program crate. Bare invocation remains compatible; `generate` makes the operation explicit:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
pina idl --path ./programs/counter_program --output ./idls/counter_program.json
|
|
93
|
+
pina idl generate --path ./programs/counter_program --output ./idls/counter_program.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Without `--output`, JSON is the only stdout content; progress and extraction counts go to stderr. This makes the command safe in pipelines:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
pina idl --path ./programs/counter_program --compact | jq -e '.program'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Treat the IDL as a public contract. Review instruction, account, PDA, error, and type changes rather than accepting generated churn wholesale.
|
|
103
|
+
|
|
104
|
+
## Canonical on-chain IDLs
|
|
105
|
+
|
|
106
|
+
Network IDL operations always require an explicit cluster. Fetch the canonical direct, zlib-compressed UTF-8 Codama IDL under the fixed `idl` seed:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
pina idl fetch --cluster devnet --program-id <PROGRAM_ADDRESS> --output ./idl.json
|
|
110
|
+
pina idl diff --cluster devnet --program-id <PROGRAM_ADDRESS> --file ./idl.json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`diff` compares parsed JSON: object order and whitespace are ignored, but array order is preserved. Exit status `0` means equal, `2` means different, and `1` means the command failed.
|
|
114
|
+
|
|
115
|
+
Direct publication requires the canonical upgrade-authority keypair and explicit confirmation (`--yes` in automation):
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
pina idl publish --cluster devnet --file ./idl.json \
|
|
119
|
+
--authority ~/.config/solana/upgrade-authority.json --yes
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
For review, multisig, or DAO signing, export every transaction the official planner requires without submitting any:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
pina idl publish --cluster mainnet-beta --file ./idl.json \
|
|
126
|
+
--export <MULTISIG_ADDRESS> --output ./idl-plan.txt
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Do not describe an export as one transaction. Preserve the complete upstream `[Transaction #N]` framing and order. An exported authority is a noop signer; do not combine `--export <ADDRESS>` with local authority or payer keypairs.
|
|
130
|
+
|
|
131
|
+
## Repository-wide client generation
|
|
132
|
+
|
|
133
|
+
Use the legacy Codama surface when a repository intentionally generates clients for several example programs in one command:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
pina codama generate --examples-dir ./programs --idls-dir ./idls \
|
|
137
|
+
--rust-out ./clients/rust --js-out ./clients/js --dart-out ./clients/dart
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use repeatable `--example` filters for a focused run. Generated roots may be replaced; never store hand-written code inside them.
|
|
141
|
+
|
|
142
|
+
Pina's generated clients preserve discriminator-first layouts and zeropod boundary checks. If a repository uses a custom renderer command, keep that command as the source of truth.
|
|
143
|
+
|
|
144
|
+
## Static SBF profiling
|
|
145
|
+
|
|
146
|
+
Profile a compiled shared object:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
pina profile
|
|
150
|
+
pina profile ./target/deploy/counter_program.so
|
|
151
|
+
pina profile ./target/deploy/counter_program.so --json --output ./profile.json
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
When the path is omitted, Pina discovers `<cargo-target>/deploy/<library-name>.so`. The report is a static estimate, not a validator execution trace. Use it for deterministic comparisons and investigate material changes in context. Output files are written atomically and cannot alias the input binary through hardlinks or linked paths.
|
|
155
|
+
|
|
156
|
+
# Verified deployments
|
|
157
|
+
|
|
158
|
+
Use the content-addressed record produced by the deterministic build. Never invent or override its repository, revision, paths, library, or Cargo feature set.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pina build --verify
|
|
162
|
+
pina verify check --program-id <ADDRESS> --cluster devnet
|
|
163
|
+
pina verify record \
|
|
164
|
+
--program-id <ADDRESS> \
|
|
165
|
+
--cluster devnet \
|
|
166
|
+
--build-record ./target/pina/verifiable/my_program-<HASH>.json \
|
|
167
|
+
--authority ./upgrade-authority.json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`pina verify check` is read-only: it compares the local artifact with the deployed executable and returns exit code `2` for a completed hash mismatch. Do not retry that result as an infrastructure failure.
|
|
171
|
+
|
|
172
|
+
`pina verify record` rebuilds the exact repository revision from the validated build record and writes verification metadata on-chain. Review the program, cluster, record, and authority before adding `--yes`; mainnet and unknown remote RPC origins additionally require `--acknowledge-mainnet`.
|
|
173
|
+
|
|
174
|
+
Use `pina verify record --export [AUTHORITY] --output verification.tx` when another signer or multisig must submit the transaction. Export performs Pina's deployed-hash preflight but never submits or rebuilds the repository, does not require `--yes` or `--acknowledge-mainnet`, and writes only the validated base58 or base64 transaction payload. Remote verification begins only after the exported transaction is submitted.
|
|
175
|
+
|
|
176
|
+
`pina verify submit --program-id <ADDRESS> --uploader <ADDRESS>` submits an existing record to the official mainnet remote verifier. `pina verify status --program-id <ADDRESS>` is the corresponding read-only mainnet status query. Never place credentials in an RPC URL; the URL is necessarily visible in child-process arguments.
|
|
177
|
+
|
|
178
|
+
## Safe deployment
|
|
179
|
+
|
|
180
|
+
Plan deployments before permitting a write:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
pina deploy --cluster devnet \
|
|
184
|
+
--upgrade-authority ./keys/devnet-authority.json \
|
|
185
|
+
--payer ./keys/devnet-payer.json \
|
|
186
|
+
--dry-run --json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The cluster is always explicit. Pina never inherits a Solana CLI target or wallet, and deploy never creates a program identity. Conventional artifacts are `<cargo-target>/deploy/<library-name>.so` and `<cargo-target>/deploy/<library-name>-keypair.json`; override either path only when the plan requires it. Review the program ID and complete argument vector in the plan. Keep keypairs below 4 KiB and owner-private; on Unix use mode `0600`, while Windows ACLs must be restricted with operating-system tooling.
|
|
190
|
+
|
|
191
|
+
Remote execution requires an interactive `deploy` confirmation or `--yes`. Named mainnet and custom remote endpoints also require `--allow-mainnet`. Custom URL user information, queries, and fragments are rejected, but accepted hosts and paths remain visible in plan output and process listings. Never put a secret anywhere in the URL; prefer a named cluster. Use `--build` when the canonical artifact must be refreshed before the final plan. Deployment requires the external Agave `solana` executable on `PATH`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Program Authoring
|
|
2
|
+
|
|
3
|
+
## Data and instruction types
|
|
4
|
+
|
|
5
|
+
Use Pina's macros for their specific wire contracts:
|
|
6
|
+
|
|
7
|
+
- `#[discriminator]` defines explicit discriminator bytes.
|
|
8
|
+
- `#[account]` creates discriminator-first, validated zero-copy account storage.
|
|
9
|
+
- `#[instruction]` creates typed instruction data with a discriminator-first wire layout.
|
|
10
|
+
- `#[event]` creates typed event data.
|
|
11
|
+
- `#[error]` maps program errors without an ad hoc conversion layer.
|
|
12
|
+
- `#[pda]` defines PDA constructors and seed helpers.
|
|
13
|
+
- `#[derive(Accounts)]` converts the ordered account slice into a typed instruction account set.
|
|
14
|
+
|
|
15
|
+
Use explicit discriminator values. Reordering enum variants must not change existing wire values.
|
|
16
|
+
|
|
17
|
+
Fixed-layout storage fields must satisfy zeropod's representation and validation rules. Use Pina's POD wrappers for numeric and boolean storage. Keep text and collections in bounded fixed-capacity representations with explicit length validation.
|
|
18
|
+
|
|
19
|
+
## Account validation
|
|
20
|
+
|
|
21
|
+
Validate before reading or mutating account data. A typical chain is:
|
|
22
|
+
|
|
23
|
+
```rust
|
|
24
|
+
account
|
|
25
|
+
.assert_signer()?
|
|
26
|
+
.assert_writable()?
|
|
27
|
+
.assert_owner(program_id)?;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add address, program, sysvar, seed, emptiness, or type assertions when the instruction relies on them. Do not assume a typed cast proves ownership or that a signer proves authority over stored state.
|
|
31
|
+
|
|
32
|
+
Important cases:
|
|
33
|
+
|
|
34
|
+
- Check the invoked program's address before CPI.
|
|
35
|
+
- Check an account is empty before initialization.
|
|
36
|
+
- Check program ownership before changing its lamports.
|
|
37
|
+
- Require writability before resize or mutation.
|
|
38
|
+
- Validate the exact sysvar address before reading sysvar data.
|
|
39
|
+
- Reject duplicate mutable aliases unless the instruction explicitly supports them.
|
|
40
|
+
- Bind an authority signer to the authority stored in program state.
|
|
41
|
+
|
|
42
|
+
## PDAs
|
|
43
|
+
|
|
44
|
+
Use a stable, type-specific byte-string namespace as the first seed. Prefer canonical bump derivation and validation. Do not reuse one seed namespace for unrelated account types.
|
|
45
|
+
|
|
46
|
+
Seed changes alter addresses. Treat them as migrations, not refactors.
|
|
47
|
+
|
|
48
|
+
## Initialization, resize, and close
|
|
49
|
+
|
|
50
|
+
Initialization must prove that the target is empty and that its derived address is correct before allocating or writing state. Resize operations must validate authority, owner, address, writability, and the requested bounds before changing data length.
|
|
51
|
+
|
|
52
|
+
When closing an account, use the Pina close helper that matches the data-erasure requirement. Zero account data before transferring lamports when stale bytes must not remain observable.
|
|
53
|
+
|
|
54
|
+
## Compatibility review
|
|
55
|
+
|
|
56
|
+
Regenerate and inspect the IDL after changing:
|
|
57
|
+
|
|
58
|
+
- a public account, instruction, event, error, or PDA declaration;
|
|
59
|
+
- discriminator values;
|
|
60
|
+
- field order, field type, or fixed capacity;
|
|
61
|
+
- account ordering, signer/writable constraints, or known addresses.
|
|
62
|
+
|
|
63
|
+
If a change moves bytes or addresses, state that explicitly and require the user's approval when it was not already part of the request.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Project Setup
|
|
2
|
+
|
|
3
|
+
## Prefer the scaffold
|
|
4
|
+
|
|
5
|
+
Start a program with the installed CLI so the generated Pina version, feature names, target configuration, and starter code stay aligned:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pina init counter_program
|
|
9
|
+
cd counter_program
|
|
10
|
+
pina doctor
|
|
11
|
+
pina keys show
|
|
12
|
+
pina build
|
|
13
|
+
pina test --unit
|
|
14
|
+
pina test
|
|
15
|
+
pina generate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The scaffold pins the nightly toolchain and `rust-src` needed by `pina build`; install its compatible linker with `cargo install sbpf-linker --version 0.1.8 --locked` before the first SBF build. TypeScript client generation also requires Node.js with npm and `npx`. Keep the generated `Pina.toml` as the project-local discovery and client-selection contract.
|
|
19
|
+
|
|
20
|
+
Before deployment, establish the program identity explicitly. Use `pina keys new` for a fresh local identity or validate a keypair produced by trusted platform tooling with `pina keys sync --keypair <path>`. Never use `--force` unless the intended operation is an identity rotation.
|
|
21
|
+
|
|
22
|
+
Use `pina init --help` before selecting a destination or replacing existing scaffold files. The command preserves existing files unless the user explicitly supplies `--force`; inspect the destination before using that flag.
|
|
23
|
+
|
|
24
|
+
## Expected boundaries
|
|
25
|
+
|
|
26
|
+
A Pina program should keep these concerns separate:
|
|
27
|
+
|
|
28
|
+
- on-chain instruction processing and account types in a `no_std`-compatible library;
|
|
29
|
+
- the SBF entrypoint behind the project's `bpf-entrypoint` feature;
|
|
30
|
+
- host tests and VM fixtures in test-only modules or integration tests;
|
|
31
|
+
- embedded Surfpool RPC tests in the isolated `tests/surfpool` Cargo package, separate from the fast native loop and SBF dependency graph;
|
|
32
|
+
- IDL and generated clients outside hand-written program source.
|
|
33
|
+
|
|
34
|
+
Do not add a general application framework, async runtime, serializer, or allocator to the on-chain path unless the program's requirements justify it.
|
|
35
|
+
|
|
36
|
+
## Existing projects
|
|
37
|
+
|
|
38
|
+
Before adding Pina to an existing crate, inspect its Rust edition, toolchain, Solana dependencies, target configuration, and entrypoint. Prefer the versions and features emitted by the same installed `pina` CLI that will maintain the project. Avoid copying dependency versions from an unrelated repository.
|
|
39
|
+
|
|
40
|
+
The usual structure is:
|
|
41
|
+
|
|
42
|
+
```rust
|
|
43
|
+
#![cfg_attr(not(test), no_std)]
|
|
44
|
+
|
|
45
|
+
use pina::prelude::*;
|
|
46
|
+
|
|
47
|
+
nostd_entrypoint!(process_instruction);
|
|
48
|
+
|
|
49
|
+
fn process_instruction(
|
|
50
|
+
program_id: &Address,
|
|
51
|
+
accounts: &mut [AccountView],
|
|
52
|
+
data: &[u8],
|
|
53
|
+
) -> ProgramResult {
|
|
54
|
+
let instruction: ProgramInstruction = parse_instruction(program_id, &ID, data)?;
|
|
55
|
+
|
|
56
|
+
match instruction {
|
|
57
|
+
ProgramInstruction::Initialize => InitializeAccounts::try_from(accounts)?.process(data),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Adapt names and dispatch arms to the program; do not introduce a generic router when a direct match remains clear.
|
|
63
|
+
|
|
64
|
+
## Feature discipline
|
|
65
|
+
|
|
66
|
+
- Keep `pina` default features off when the project uses an explicit minimal feature set.
|
|
67
|
+
- Enable token or Token-2022 support only for programs that call those APIs.
|
|
68
|
+
- Compile tests without the on-chain entrypoint when the project follows the common library-testing pattern.
|
|
69
|
+
- Build the deployable program with the repository's pinned SBF toolchain and linker configuration.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Testing and Verification
|
|
2
|
+
|
|
3
|
+
## Choose the smallest useful layer
|
|
4
|
+
|
|
5
|
+
- Pure parsing, arithmetic, and state-transition logic: ordinary Rust unit tests.
|
|
6
|
+
- Account metadata, instruction execution, and expected `ProgramError` values: Mollusk or the project's established VM harness.
|
|
7
|
+
- Entrypoint, deployment, linker, or serialized-program behavior: an SBF build plus an integration test.
|
|
8
|
+
- Public macro or IDL changes: fixture tests and generated-artifact drift checks.
|
|
9
|
+
- Layout or borrowing invariants: focused boundary tests; use Miri only when the repository already supports it or the task specifically warrants it.
|
|
10
|
+
|
|
11
|
+
Test the rejected path as well as the successful path for authorization and validation changes. Verify that failed instructions leave relevant account data and lamports unchanged.
|
|
12
|
+
|
|
13
|
+
## Baseline Cargo checks
|
|
14
|
+
|
|
15
|
+
Use project task aliases when they exist. For a standalone project without them:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
cargo test
|
|
19
|
+
cargo build --all-features
|
|
20
|
+
cargo clippy --all-features --all-targets
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Format with the project's configured formatter. In the Pina repository, use `fix:format` or `dprint fmt`; do not invoke `rustfmt` directly.
|
|
24
|
+
|
|
25
|
+
Run `pina doctor --json` when a failure may come from project discovery or a missing tool. Consume the exit status and typed check IDs; do not scrape the human report. Missing configured client tools, `sbpf-linker`, or nightly `-Z` support should be fixed before treating SBF or client-generation failures as code defects.
|
|
26
|
+
|
|
27
|
+
For a project generated by the Pina CLI, preserve both supported loops:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pina test --unit # native Rust and Mollusk
|
|
31
|
+
pina test # real SBF artifact on an isolated Surfpool instance
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use `--filter <name>` for a focused run. Never describe `pina test --unit` as an SBF or RPC test.
|
|
35
|
+
|
|
36
|
+
## SBF builds
|
|
37
|
+
|
|
38
|
+
Prefer the project-aware build, which uses the discovered program, pinned toolchain, and Cargo target directory:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pina build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use the equivalent low-level Cargo command only when diagnosing the compiler or linker invocation:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
cargo build --release --target bpfel-unknown-none \
|
|
48
|
+
-p counter_program -Z build-std=core,alloc -F bpf-entrypoint
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Do not silently skip an SBF-dependent test because the artifact is missing. Build it or report the missing prerequisite.
|
|
52
|
+
|
|
53
|
+
Generated Surfpool tests use the embedded SDK with dynamic ports and explicit teardown. Cargo remains attached to the terminal so test output and interrupts behave normally. Use `pina dev` for a persistent foreground Surfnet; Surfpool owns terminal input, output, errors, prompts, and Ctrl-C for that lifecycle. It is offline unless the user explicitly selects a network or RPC URL. Prefer a named network. An explicit RPC URL must be credential-free HTTP(S) with a host and no user information, query, fragment, or control character. Because Surfpool receives it in child-process arguments, never place a secret anywhere in the URL, including its host or path. The first run requires `pina dev --yes`; inspect and commit the generated `txtx.yml` before ordinary development. Do not replace either workflow with a fixed-port daemon and a readiness sleep.
|
|
54
|
+
|
|
55
|
+
## Generated contracts
|
|
56
|
+
|
|
57
|
+
After a public program-surface change:
|
|
58
|
+
|
|
59
|
+
1. Generate the IDL.
|
|
60
|
+
2. Validate the JSON.
|
|
61
|
+
3. Regenerate each committed client target.
|
|
62
|
+
4. Type-check or compile generated clients.
|
|
63
|
+
5. Fail on unreviewed drift.
|
|
64
|
+
|
|
65
|
+
Counts are useful guardrails: declared instructions, accounts, and errors should match their generated IDL counterparts.
|
|
66
|
+
|
|
67
|
+
## Release checks
|
|
68
|
+
|
|
69
|
+
Follow the repository's release policy. When it uses changesets, add release intent for publishable code or package changes and validate the release metadata before handoff. Do not publish, tag, or open a release request unless the user explicitly authorizes that external action.
|
package/CHANGELOG.md
DELETED
package/LICENSE
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Placeholder package published by monochange. See the source repository for license terms.
|