This file provides context for AI assistants working on this codebase.
Platform CLI is a minimal command-line interface for Avalanche P-Chain operations. It provides functionality for:
- Key management (generate, import, export, encrypt)
- P-Chain transfers and cross-chain (P↔C) transfers
- Primary network staking (validators and delegators)
- Subnet creation and L1 conversion
- L1 validator management
- Chain creation on subnets
cmd/ - Cobra CLI commands (user-facing interface)
├── root.go - Root command, global flags (--network, --private-key, --key-name)
├── keys.go - Key management: generate, import, export, delete, default
├── wallet.go - Wallet info: address, balance
├── transfer.go - Transfers: send, p-to-c, c-to-p, export, import
├── validator.go - Staking: add-permissionless, add-permissionless-delegator
├── subnet.go - Subnets: create, transfer-ownership, convert-to-l1, add-validator
├── l1.go - L1 validators: register-validator, set-validator-weight, increase-validator-balance, disable-validator
├── chain.go - Chains: create chain on subnet
└── node.go - Node utilities: info
pkg/ - Core business logic (importable as library)
├── pchain/ - P-Chain transaction building (DO NOT modify crypto)
├── wallet/ - Wallet abstraction over avalanchego SDK
├── keystore/ - Encrypted key storage (SECURITY CRITICAL)
├── crosschain/ - P↔C chain transfer logic with retry
├── network/ - Network configuration (local, fuji, mainnet)
└── node/ - Node info utilities
e2e/ - End-to-end tests (run against real networks)
- NEVER modify cryptographic code in
pkg/keystore/without explicit security review - NEVER commit private keys, test credentials, or secrets
- NEVER log or print private keys - even in debug/error messages
- NEVER hardcode network URLs, timeouts, or magic numbers - use constants/config
- ALWAYS validate user input before passing to transaction builders
- ALWAYS run
go vet ./...andstaticcheck ./...before committing - ALWAYS add tests for new functionality - unit tests preferred, e2e for integration
- ALWAYS wrap errors with context:
fmt.Errorf("failed to X: %w", err) - NEVER use
panic()in library code - return errors instead - FOLLOW existing patterns - check similar code before writing new code
- Transaction amounts are in nAVAX (1 AVAX = 1,000,000,000 nAVAX)
- Address formats differ: P-Chain uses
P-avax1..., C-Chain uses0x... - Subnet IDs and Chain IDs are different concepts (subnet contains chains)
- L1 validators require BLS keys (not just secp256k1)
- The
ewoqkey is a well-known test key - NEVER use on mainnet
# Build
go build -o platform-cli .
# Lint & vet (run before committing)
go vet ./...
staticcheck ./...
# Unit tests (when they exist)
go test ./pkg/...
# E2E tests - no funds required
go test -v ./e2e/... -run "Help|Params|MissingArgs"
# E2E tests - local network (uses ewoq key)
go test -v ./e2e/... -network=local
# E2E tests - Fuji testnet (requires funded wallet)
PRIVATE_KEY="PrivateKey-..." go test -v ./e2e/... -network=fuji- Address derivation differs between chains - P-Chain and C-Chain derive different addresses from the same private key
- Cross-chain transfers are two-step - export then import, with network confirmation between
- Subnet creation requires P-Chain balance - not C-Chain
- L1 conversion is irreversible - converts permissioned subnet to L1
- Stake duration has minimums - 14 days for validators, 14 days for delegators on mainnet
- Delegation fee is a percentage - 0.02 = 2%, not 2 AVAX
| File | Purpose | Modification Risk |
|---|---|---|
pkg/keystore/keystore.go |
Key encryption/storage | HIGH - security critical |
pkg/pchain/pchain.go |
Transaction building | MEDIUM - affects all ops |
pkg/wallet/wallet.go |
Wallet interface | MEDIUM - core abstraction |
cmd/root.go |
Global flags | LOW - UI only |
pkg/network/network.go |
Network config | LOW - constants only |
Core dependencies (from go.mod):
github.com/ava-labs/avalanchego- Avalanche node SDKgithub.com/ava-labs/libevm- EVM/C-Chain operationsgithub.com/spf13/cobra- CLI frameworkgolang.org/x/crypto- Cryptographic primitives
| Network | Chain ID | Min Stake (Validator) | Min Stake (Delegator) |
|---|---|---|---|
| Local | 1337 | 1 AVAX | 1 AVAX |
| Fuji | 5 | 1 AVAX | 1 AVAX |
| Mainnet | 1 | 2000 AVAX | 25 AVAX |
// Good - wrap with context
if err != nil {
return fmt.Errorf("failed to create subnet: %w", err)
}
// Bad - loses context
if err != nil {
return err
}
// Bad - panics in library code
if err != nil {
panic(err)
}User runs: platform-cli subnet create --network fuji
1. cmd/subnet.go: Parse flags, load key
2. pkg/wallet/wallet.go: Create wallet from private key
3. pkg/pchain/pchain.go: Build CreateSubnetTx
4. avalanchego SDK: Sign and submit transaction
5. cmd/subnet.go: Print result (subnet ID)