Claude
Skills
Sign in
Back

quip-protocol-rs

Included with Lifetime
$97 forever

```markdown

Writing & Docs

What this skill does

```markdown
---
name: quip-protocol-rs
description: Rust implementation of the Quip Protocol blockchain node, forked from Substrate/Polkadot SDK solochain template
triggers:
  - build a substrate node in rust
  - quip protocol blockchain
  - substrate solochain template
  - create a custom pallet in substrate
  - run a substrate development chain
  - substrate FRAME runtime development
  - polkadot sdk rust blockchain
  - substrate node template setup
---

# Quip Protocol RS

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

A Rust implementation of the Quip Protocol blockchain node, forked from the [Substrate](https://substrate.io/) / Polkadot SDK solochain template. It provides a ready-to-hack blockchain node with FRAME pallets, AURA block authoring, GRANDPA finality, and a JSON-RPC server.

---

## What This Project Does

- Runs a standalone Substrate-based blockchain node (solochain)
- Uses FRAME to compose a runtime from pallets (modules)
- Exposes an RPC server (default `ws://localhost:9944`)
- Ships with a template pallet for custom business logic
- Supports single-node dev chains and multi-node testnets

---

## Project Structure

```
quip-protocol-rs/
├── node/
│   └── src/
│       ├── chain_spec.rs   # Genesis state configuration
│       ├── service.rs      # Node implementation (consensus, networking)
│       └── main.rs
├── runtime/
│   └── src/
│       └── lib.rs          # FRAME runtime, pallet composition
├── pallets/
│   └── template/
│       └── src/
│           └── lib.rs      # Example custom pallet
└── Cargo.toml
```

---

## Installation & Prerequisites

### System Dependencies

```bash
# Ubuntu/Debian
sudo apt update && sudo apt install -y \
  build-essential clang curl git libssl-dev \
  llvm libudev-dev make protobuf-compiler

# macOS
brew install cmake protobuf
```

### Rust Toolchain

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

rustup default stable
rustup update
rustup target add wasm32-unknown-unknown
rustup component add rust-src
```

### Clone & Build

```bash
git clone https://github.com/QuipNetwork/quip-protocol-rs.git
cd quip-protocol-rs

# Full release build
cargo build --release

# Dev build (faster, for iteration)
cargo build
```

---

## Key CLI Commands

```bash
# Start single-node dev chain (no state persistence)
./target/release/solochain-template-node --dev

# Start dev chain with persistent state
mkdir -p ./my-chain-state
./target/release/solochain-template-node --dev --base-path ./my-chain-state/

# Purge dev chain state
./target/release/solochain-template-node purge-chain --dev

# Detailed logging
RUST_BACKTRACE=1 ./target/release/solochain-template-node -ldebug --dev

# Custom log targets
RUST_LOG=runtime=debug,txpool=trace ./target/release/solochain-template-node --dev

# Show all CLI options
./target/release/solochain-template-node --help

# Generate and open Rust docs
cargo +nightly doc --open
```

---

## Writing a Custom Pallet

Pallets live in `pallets/<name>/src/lib.rs`. The template pallet is the canonical starting point.

### Minimal Pallet Example

```rust
// pallets/my_pallet/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::{
        pallet_prelude::*,
        traits::Currency,
    };
    use frame_system::pallet_prelude::*;

    type BalanceOf<T> = <<T as Config>::Currency as Currency<
        <T as frame_system::Config>::AccountId,
    >>::Balance;

    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// The runtime event type.
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
        /// Currency used for staking.
        type Currency: Currency<Self::AccountId>;
        /// Max length of a stored value.
        #[pallet::constant]
        type MaxValueLength: Get<u32>;
    }

    #[pallet::pallet]
    pub struct Pallet<T>(_);

    // Storage: map AccountId -> BoundedVec<u8>
    #[pallet::storage]
    #[pallet::getter(fn stored_value)]
    pub type StoredValue<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::AccountId,
        BoundedVec<u8, T::MaxValueLength>,
        OptionQuery,
    >;

    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// A value was stored. [who, value]
        ValueStored { who: T::AccountId, value: BoundedVec<u8, T::MaxValueLength> },
        /// A value was cleared. [who]
        ValueCleared { who: T::AccountId },
    }

    #[pallet::error]
    pub enum Error<T> {
        /// Value exceeds maximum allowed length.
        ValueTooLong,
        /// No value found for this account.
        NothingStored,
    }

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// Store a value for the caller.
        #[pallet::call_index(0)]
        #[pallet::weight(T::DbWeight::get().writes(1))]
        pub fn store_value(
            origin: OriginFor<T>,
            value: Vec<u8>,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;

            let bounded: BoundedVec<u8, T::MaxValueLength> =
                value.try_into().map_err(|_| Error::<T>::ValueTooLong)?;

            StoredValue::<T>::insert(&who, &bounded);
            Self::deposit_event(Event::ValueStored { who, value: bounded });
            Ok(())
        }

        /// Clear the caller's stored value.
        #[pallet::call_index(1)]
        #[pallet::weight(T::DbWeight::get().writes(1))]
        pub fn clear_value(origin: OriginFor<T>) -> DispatchResult {
            let who = ensure_signed(origin)?;
            ensure!(StoredValue::<T>::contains_key(&who), Error::<T>::NothingStored);
            StoredValue::<T>::remove(&who);
            Self::deposit_event(Event::ValueCleared { who });
            Ok(())
        }
    }
}
```

### Adding the Pallet to `Cargo.toml`

```toml
# pallets/my_pallet/Cargo.toml
[package]
name = "pallet-my-pallet"
version = "0.1.0"
edition = "2021"

[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", optional = true, default-features = false }
frame-support = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false }
frame-system = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false }

[features]
default = ["std"]
std = [
    "codec/std",
    "frame-support/std",
    "frame-system/std",
    "scale-info/std",
]
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
try-runtime = ["frame-support/try-runtime"]
```

### Wiring the Pallet into the Runtime

```rust
// runtime/src/lib.rs

// 1. Declare the pallet parameter types
parameter_types! {
    pub const MaxValueLength: u32 = 256;
}

// 2. Implement Config for your pallet
impl pallet_my_pallet::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type Currency = Balances;
    type MaxValueLength = MaxValueLength;
}

// 3. Add to the #[runtime] macro construct_runtime! block
#[runtime]
mod runtime {
    // ... existing pallets ...
    #[runtime::pallet_index(42)]
    pub type MyPallet = pallet_my_pallet::Pallet<Runtime>;
}
```

---

## Chain Specification (Genesis Config)

```rust
// node/src/chain_spec.rs

use sc_service::ChainType;
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_keyring::AccountKeyring;

pub fn development_config() -> Result<ChainSpec, String> {
    Ok(ChainSpec::builder(
        WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
        None,
    )
    .with_name("Development")
    .with_id("dev")
    .with_chain_type(ChainType::Development)
    .with_genesis_config_patch(testne

Related in Writing & Docs