rocket-anchor
v1.0.13
Published
Hardhat-style deployment tool for Solana Anchor programs with seeding support
Maintainers
Readme
🚀 Rocket Anchor (RA)
Hardhat-style deployment toolkit for Solana Anchor programs with advanced seeding capabilities.
Rocket Anchor brings the familiar Hardhat developer experience to Solana, making it easy to deploy, manage, and seed your Anchor programs across multiple networks with a simple, declarative configuration.
📋 Table of Contents
- Features
- Installation
- Quick Start
- Configuration
- CLI Commands
- Seeding & Initialization
- Programmatic Usage
- Advanced Topics
- Examples
- Best Practices
- Troubleshooting
- CI/CD Integration
- API Reference
- Contributing
- License
✨ Features
- 🎯 Hardhat-style Configuration - Familiar
ra.config.tsfor network and deployment settings - 🌐 Multi-Network Support - Easy switching between localnet, devnet, testnet, and mainnet
- 🌱 Declarative Seeding - Initialize and populate programs with simple configuration
- 🔑 Flexible Keypair Management - Support for file paths, base58 strings, and environment variables
- 🔄 PDA Resolution - Automatic Program Derived Address generation in seed scripts
- ✅ Deployment Verification - Built-in on-chain verification
- 📦 Automated Building - Integrated Anchor build support
- 🎨 Beautiful CLI - Colored output with clear progress indicators
- 📝 TypeScript First - Full type safety and IntelliSense support
- 🔌 Programmatic API - Use in your own scripts and tools
- 🚀 Fast & Efficient - Optimized deployment pipeline
📦 Installation
Global Installation (Recommended for CLI)
npm install -g rocket-anchorLocal Installation (For Project Integration)
npm install --save-dev rocket-anchorUsing with npx (No Installation)
npx rocket-anchor init🚀 Quick Start
1. Initialize Configuration
npx ra initThis creates ra.config.ts in your project root.
2. Configure Networks
Edit ra.config.ts:
import type { RAConfig } from "rocket-anchor";
const config: RAConfig = {
networks: {
solana_devnet: {
url: 'https://api.devnet.solana.com',
accounts: ['~/.config/solana/devnet.json'],
commitment: 'confirmed',
type: 'devnet' // localnet | devnet | testnet | mainnet
},
},
};
export default config;3. Deploy Your Program
npx ra deploy --network devnet4. (Optional) Add Seeding
Create seeds/index.ts:
import type { SeedConfig } from 'rocket-anchor';
export const seeds: SeedConfig[] = [
{
program: 'my_program',
initialize: {
function: 'initialize',
accounts: {
authority: 'signer',
state: 'pda:state',
systemProgram: 'systemProgram',
},
args: ['Production', 1000],
},
},
];
export default seeds;Deploy with seeding:
npx ra deploy --network devnet --seed⚙️ Configuration
Network Configuration
interface NetworkConfig {
url: string; // RPC endpoint URL
accounts?: string[]; // Keypair paths or base58 keys
timeout?: number; // Transaction timeout (ms)
commitment?: Commitment; // 'processed' | 'confirmed' | 'finalized'
skipPreflight?: boolean; // Skip preflight checks
websocket?: string; // WebSocket endpoint (optional)
type: NetworkType // Newtork type, one of these: localnet | devnet | testnet | mainnet
}Complete Configuration Example
import type { RAConfig } from "rocket-anchor";
import * as dotenv from 'dotenv';
dotenv.config();
const config: RAConfig = {
networks: {
solana_localnet: {
url: 'http://127.0.0.1:8899',
accounts: ['~/.config/solana/id.json'],
commitment: 'confirmed',
type: 'localnet'
},
solana_devnet: {
url: process.env.DEVNET_RPC_URL || 'https://api.devnet.solana.com',
accounts: [process.env.DEVNET_KEYPAIR_PATH!],
commitment: 'confirmed',
timeout: 60000,
type: 'devnet'
},
solana_testnet: {
url: 'https://api.testnet.solana.com',
accounts: ['./keypairs/testnet.json'],
commitment: 'confirmed',
type: 'testnet'
},
solana_mainnet: {
url: process.env.MAINNET_RPC_URL || 'https://api.mainnet-beta.solana.com',
accounts: [process.env.MAINNET_KEYPAIR_PATH!],
commitment: 'finalized',
skipPreflight: false,
timeout: 90000,
websocket: 'wss://api.mainnet-beta.solana.com',
type: 'mainnet'
},
},
paths: {
programs: './programs',
tests: './tests',
artifacts: './target',
},
solana: {
version: '1.18.0',
},
anchor: {
version: '0.29.0',
},
};
export default config;🖥️ CLI Commands
Deploy
Deploy Anchor programs to a specified network.
# Basic deployment
npx ra deploy --network solana_devnet
# Deploy specific program
npx ra deploy --network solana_devnet --program token_vault
# Skip build step
npx ra deploy --network solana_devnet --skip-build
# Deploy and verify
npx ra deploy --network solana_mainnet --verify
# Deploy as non-upgradeable
npx ra deploy --network solana_mainnet --upgradeable false
# Deploy and run seeds
npx ra deploy --network solana_devnet --seed
# Deploy with custom seed script
npx ra deploy --network solana_devnet --seed --seed-script ./scripts/custom.tsSeed
Run initialization and seed scripts for deployed programs.
# Run seeds
npx ra seed --network solana_devnet
# Seed specific program
npx ra seed --network solana_devnet --program my_program
# Use custom seed script
npx ra seed --network solana_devnet --script ./scripts/advanced-seed.tsBuild
Build Anchor programs.
# Standard build
npx ra build
# Verifiable build
npx ra build --verifiableTest
Run Anchor tests.
# Run tests on localnet
npx ra test
# Run tests on specific network
npx ra test --network solana_devnetInit
Initialize configuration file.
npx ra init🌱 Seeding & Initialization
Rocket Anchor provides powerful seeding capabilities to automatically initialize and populate your programs after deployment.
Basic Seed Configuration
Create seeds/index.ts:
import type { SeedConfig } from 'rocket-anchor';
export const seeds: SeedConfig[] = [
{
program: 'counter',
initialize: {
function: 'initialize',
accounts: {
counter: 'pda:counter',
authority: 'signer',
systemProgram: 'systemProgram',
},
args: [0], // initial_count
},
seeds: [
{
function: 'increment',
accounts: {
counter: 'pda:counter',
authority: 'signer',
},
args: [],
repeat: 5, // Run 5 times
},
],
},
];
export default seeds;Account Resolution Patterns
Rocket Anchor supports multiple account resolution patterns:
1. Signer/Payer
accounts: {
authority: 'signer', // Uses deployer wallet
payer: 'payer', // Also uses deployer wallet
}2. System Programs
accounts: {
systemProgram: 'systemProgram', // SystemProgram.programId
rent: 'rent', // SYSVAR_RENT_PUBKEY
}3. Generate New Keypair
accounts: {
newAccount: 'new:', // Generates and signs with new keypair
}4. Derive PDA
accounts: {
vault: 'pda:vault', // Single seed
userVault: 'pda:vault:signer', // Multiple seeds
}5. Direct Public Key
accounts: {
treasury: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
}Advanced Seed Example
import type { SeedConfig } from 'rocket-anchor';
export const seeds: SeedConfig[] = [
{
program: 'marketplace',
initialize: {
function: 'initialize',
accounts: {
marketplace: 'pda:marketplace',
authority: 'signer',
feeRecipient: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
systemProgram: 'systemProgram',
rent: 'rent',
},
args: [
500, // fee_basis_points (5%)
'Main Marketplace',
],
},
seeds: [
{
function: 'createCollection',
accounts: {
marketplace: 'pda:marketplace',
collection: 'new:',
authority: 'signer',
systemProgram: 'systemProgram',
},
args: ['NFT Collection', 'NFTC'],
repeat: 3, // Create 3 collections
},
{
function: 'updateFee',
accounts: {
marketplace: 'pda:marketplace',
authority: 'signer',
},
args: [250], // Lower fee to 2.5%
},
],
},
];
export default seeds;Custom Seed Scripts
For complex seeding logic, create custom TypeScript scripts:
// scripts/custom-seed.ts
import * as anchor from '@coral-xyz/anchor';
import { Program, AnchorProvider } from '@coral-xyz/anchor';
import { PublicKey, Keypair, SystemProgram } from '@solana/web3.js';
export async function customSeed(
provider: AnchorProvider,
programId: PublicKey
) {
const program = new Program(idl, programId, provider);
// Complex initialization logic
const config = await program.account.config.fetch(configPda);
if (config.initialized) {
console.log('Already initialized, skipping...');
return;
}
// Conditional seeding based on network
const network = provider.connection.rpcEndpoint;
const isMainnet = network.includes('mainnet');
const feeRate = isMainnet ? 100 : 50; // 1% mainnet, 0.5% devnet
await program.methods
.initialize(feeRate)
.accounts({
config: configPda,
authority: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
})
.rpc();
console.log('Custom seed completed!');
}
export default customSeed;Use custom script:
npx ra seed --network solana_devnet --script ./scripts/custom-seed.ts💻 Programmatic Usage
Rocket Anchor can be used programmatically in your TypeScript/JavaScript projects.
Basic Usage
import { RocketAnchor } from 'rocket-anchor';
async function deployPrograms() {
const ra = new RocketAnchor();
// Load configuration
await ra.loadConfig('./ra.config.ts');
// Deploy
const results = await ra.deploy({
network: 'devnet',
program: 'my_program',
skipBuild: false,
verify: true,
seed: true,
});
// Check results
results.forEach(result => {
if (result.success) {
console.log(`✅ ${result.programName}: ${result.programId}`);
console.log(` Tx: ${result.txSignature}`);
} else {
console.error(`❌ ${result.programName}: ${result.error}`);
}
});
}
deployPrograms().catch(console.error);Advanced Usage
import { RocketAnchor, DeployOptions } from 'rocket-anchor';
class DeploymentManager {
private ra: RocketAnchor;
constructor() {
this.ra = new RocketAnchor();
}
async deployToMultipleNetworks(networks: string[]) {
await this.ra.loadConfig();
for (const network of networks) {
console.log(`\nDeploying to ${network}...`);
try {
const results = await this.ra.deploy({
network,
verify: network === 'mainnet',
seed: true,
});
await this.saveDeployment(network, results);
} catch (error) {
console.error(`Failed to deploy to ${network}:`, error);
}
}
}
async saveDeployment(network: string, results: DeployResult[]) {
// Save deployment info to database or file
const deployment = {
network,
timestamp: new Date().toISOString(),
programs: results.map(r => ({
name: r.programName,
programId: r.programId,
txSignature: r.txSignature,
})),
};
// Save logic here
console.log('Deployment saved:', deployment);
}
async seedWithRetry(network: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
await this.ra.seed(network);
console.log('Seeding successful');
return;
} catch (error) {
console.log(`Seed attempt ${i + 1} failed, retrying...`);
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
}
}
// Usage
const manager = new DeploymentManager();
await manager.deployToMultipleNetworks(['solana_devnet', 'solana_testnet']);🔧 Advanced Topics
Environment Variables
Use .env files for sensitive data:
# .env
DEVNET_RPC_URL=https://api.devnet.solana.com
MAINNET_RPC_URL=https://my-private-rpc.com
DEVNET_KEYPAIR_PATH=./keypairs/devnet.json
MAINNET_KEYPAIR_PATH=./keypairs/mainnet.json// ra.config.ts
import * as dotenv from 'dotenv';
dotenv.config();
const config: RAConfig = {
networks: {
devnet: {
url: process.env.DEVNET_RPC_URL!,
accounts: [process.env.DEVNET_KEYPAIR_PATH!],
},
mainnet: {
url: process.env.MAINNET_RPC_URL!,
accounts: [process.env.MAINNET_KEYPAIR_PATH!],
},
},
};Multiple Keypairs
Manage different keypairs for different purposes:
const config: RAConfig = {
networks: {
solana_mainnet: {
url: 'https://api.mainnet-beta.solana.com',
accounts: [
'./keypairs/mainnet-deployer.json', // Primary deployer
'./keypairs/mainnet-authority.json', // Program authority
],
},
},
};Custom Build Commands
// In your deployment script
import { execSync } from 'child_process';
// Custom build with features
execSync('anchor build -- --features mainnet', { stdio: 'inherit' });
// Then deploy
await ra.deploy({
network: 'solana_mainnet',
skipBuild: true, // Already built
});📚 Examples
Example 1: Simple Counter Program
// programs/counter/src/lib.rs
use anchor_lang::prelude::*;
#[program]
pub mod counter {
use super::*;
pub fn initialize(ctx: Context<Initialize>, initial_count: u64) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count = initial_count;
counter.authority = ctx.accounts.authority.key();
Ok(())
}
pub fn increment(ctx: Context<Increment>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += 1;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = authority,
space = 8 + 8 + 32,
seeds = [b"counter"],
bump
)]
pub counter: Account<'info, Counter>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Increment<'info> {
#[account(mut, seeds = [b"counter"], bump)]
pub counter: Account<'info, Counter>,
pub authority: Signer<'info>,
}
#[account]
pub struct Counter {
pub count: u64,
pub authority: Pubkey,
}// seeds/index.ts
import type { SeedConfig } from 'rocket-anchor';
export const seeds: SeedConfig[] = [
{
program: 'counter',
initialize: {
function: 'initialize',
accounts: {
counter: 'pda:counter',
authority: 'signer',
systemProgram: 'systemProgram',
},
args: [0],
},
seeds: [
{
function: 'increment',
accounts: {
counter: 'pda:counter',
authority: 'signer',
},
args: [],
repeat: 10,
},
],
},
];
export default seeds;# Deploy and seed
npx ra deploy --network solana_devnet --seed
# Counter will be at 10 (0 + 10 increments)Example 2: Token Vault
// seeds/index.ts
import type { SeedConfig } from 'rocket-anchor';
export const seeds: SeedConfig[] = [
{
program: 'token_vault',
initialize: {
function: 'initializeVault',
accounts: {
vault: 'pda:vault',
authority: 'signer',
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
systemProgram: 'systemProgram',
tokenProgram: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
rent: 'rent',
},
args: [500], // 5% fee
},
},
];
export default seeds;🎯 Best Practices
1. Security
Never commit private keys:
# .gitignore
*.json
!package.json
!tsconfig.json
.env
keypairs/Use environment variables for sensitive data:
const config: RAConfig = {
networks: {
solana_mainnet: {
url: process.env.MAINNET_RPC_URL!,
accounts: [process.env.MAINNET_KEYPAIR_PATH!],
type: "mainnet"
},
},
};Use hardware wallets for mainnet:
Consider integrating Ledger support for mainnet deployments.
2. Testing
Always test on devnet first:
# Test on devnet
npx ra deploy --network solana_devnet --seed
# Thoroughly test all functionality
# Then deploy to mainnet
npx ra deploy --network solana_mainnet --verifyUse separate keypairs per network:
keypairs/
├── localnet.json
├── devnet.json
├── testnet.json
└── mainnet.json3. Deployment
Verify solana_mainnet deployments:
npx ra deploy --network solana_mainnet --verifyUse verifiable builds for mainnet:
npx ra build --verifiable
npx ra deploy --network solana_mainnet --skip-buildKeep deployment logs:
npx ra deploy --network solana_mainnet 2>&1 | tee deployment-$(date +%Y%m%d-%H%M%S).log4. Configuration Management
Use different configs for different environments:
ra.config.dev.ts
ra.config.staging.ts
ra.config.prod.tsexport RA_CONFIG=ra.config.prod.ts
npx ra deploy --network solana_mainnet🔍 Troubleshooting
Common Issues
Issue: "ra.config.ts not found"
Solution:
npx ra initIssue: "Deployer account has no SOL balance"
Solution:
# For devnet
solana airdrop 2 <your-address> --url devnet
# For mainnet, fund your walletIssue: "Program keypair not found"
Solution:
# Build your program first
anchor buildIssue: "Network not found in config"
Solution:
Check your network name matches the config:
networks: {
devnet: { ... } // Use: --network devnet
}Issue: "IDL not found for program"
Solution:
# Ensure program is built
anchor build
# Check target/idl/ directory exists
ls target/idl/Debug Mode
Enable verbose logging:
DEBUG=ra:* npx ra deploy --network solana_mainnetGetting Help
- GitHub Issues: https://github.com/ra-sun-gold/rocket-anchor/issues
- Discussions: https://github.com/ra-sun-gold/rocket-anchor/discussions
- Discord: Join our Discord
🔄 CI/CD Integration
GitHub Actions
name: Deploy to Devnet
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Install Rocket Anchor
run: npm install -g rocket-anchor
- name: Setup Solana
run: |
sh -c "$(curl -sSfL https://release.solana.com/v1.18.0/install)"
echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
- name: Setup Anchor
run: |
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
avm install 0.29.0
avm use 0.29.0
- name: Create keypair
run: |
echo '${{ secrets.DEPLOYER_KEYPAIR }}' > deployer.json
- name: Deploy
run: npx ra deploy --network devnet --seed
env:
DEPLOYER_KEYPAIR_PATH: ./deployer.json
- name: Cleanup
if: always()
run: rm -f deployer.jsonGitLab CI
deploy:
stage: deploy
image: node:18
before_script:
- npm install -g rocket-anchor
- curl -sSfL https://release.solana.com/v1.18.0/install | sh
- export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
script:
- echo "$DEPLOYER_KEYPAIR" > deployer.json
- npx ra deploy --network solana_devnet --seed
after_script:
- rm -f deployer.json
only:
- main📖 API Reference
RocketAnchor Class
class RocketAnchor {
// Load configuration from file
async loadConfig(configPath?: string): Promise<RAConfig>
// Deploy programs
async deploy(options: DeployOptions): Promise<DeployResult[]>
// Run seed scripts
async seed(network: string, program?: string, seedScript?: string): Promise<void>
// Get loaded configuration
getConfig(): RAConfig | null
}Types
interface DeployOptions {
network: string;
program?: string;
skipBuild?: boolean;
verify?: boolean;
upgradeable?: boolean;
programId?: string;
seed?: boolean;
seedScript?: string;
}
interface DeployResult {
success: boolean;
programName: string;
programId: string;
txSignature?: string;
error?: string;
}
interface SeedConfig {
program: string;
initialize?: {
function: string;
accounts: { [key: string]: string };
args: any[];
};
seeds?: {
function: string;
accounts: { [key: string]: string };
args: any[];
repeat?: number;
}[];
}Functions
// Load configuration
export function loadConfig(configPath?: string): Promise<RAConfig>
// Deploy programs
export function deploy(
config: RAConfig,
networkName: string,
options: DeployOptions
): Promise<DeployResult[]>
// Run seed scripts
export function runSeeds(
config: RAConfig,
networkName: string,
options: { program?: string; seedScript?: string }
): Promise<void>
// Load keypair
export function loadKeypair(keyPath?: string): Promise<Keypair>🤝 Contributing
Contributions are welcome! Please follow these guidelines:
Development Setup
git clone https://github.com/ra-sun-gold/rocket-anchor.git
cd rocket-anchor
npm install
npm run build
npm linkRunning Tests
npm testCode Style
npm run lint
npm run formatSubmitting Changes
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
npm test - Commit:
git commit -am 'Add new feature' - Push:
git push origin feature/my-feature - Create a Pull Request
Commit Guidelines
feat:New featurefix:Bug fixdocs:Documentation changesstyle:Code style changesrefactor:Code refactoringtest:Test updateschore:Build/tooling changes
📄 License
MIT License - see LICENSE file for details.
Copyright (c) 2025 Ra [email protected]
🙏 Acknowledgments
📞 Support
- Documentation: https://github.com/ra-sun-gold/rocket-anchor
- Issues: https://github.com/ra-sun-gold/rocket-anchor/issues
- Discussions: https://github.com/ra-sun-gold/rocket-anchor/discussions
- Email: [email protected]
Made with ❤️ for the Solana community
