A full-stack decentralized webstore platform built on Solana by ZKXP Innovation Inc. This project combines a robust Anchor-based smart contract with a modern React frontend to enable secure, blockchain-based store and product management with NFT support.
Solana Webstore is a complete solution for creating decentralized storefronts on the Solana blockchain. Store owners can create their own stores, list products (including NFT-based digital goods), and customers can purchase items directly through blockchain transactions with full transparency and immutability.
- Store Management - Create and configure decentralized stores with metadata
- Product Management - List and manage products with pricing, stock tracking, and active/inactive status
- Secure Transactions - Purchase products using Solana token transfers
- NFT Support - List digital goods as NFTs with metadata verification
- License Verification - Verify product licenses and ownership for NFT purchases
- Multi-Wallet Support - Connect with Phantom, Solflare, Ledger, and other Solana wallets
- Type-Safe Frontend - Full TypeScript implementation with React Router and Vite
- Anchor Framework (v0.31.1) - Solana program development framework
- Rust - Type-safe smart contract implementation
- SPL Token Program - Token and NFT handling
- Borsh - Efficient serialization
- React (v19.1.0) - UI framework
- React Router (v7.7.1) - Application routing and SSR support
- TypeScript (v5.8.3) - Type safety and better developer experience
- Tailwind CSS (v4.1.4) - Utility-first styling
- Vite (v6.3.3) - Lightning-fast build tool
- @coral-xyz/anchor (v0.31.1) - Solana program client
- @solana/wallet-adapter - Multi-wallet integration
solana-webstore/
├── app/ # React Frontend Application
│ ├── components/ # Reusable UI components
│ │ ├── Header.tsx # Navigation header
│ │ ├── StoreManagement.tsx # Store CRUD operations
│ │ ├── ProductManagement.tsx # Product listing management
│ │ └── PurchaseFlow.tsx # Purchase workflow
│ ├── routes/ # Page routes
│ │ ├── home.tsx # Landing page
│ │ ├── dashboard.tsx # Main application dashboard
│ │ └── docs.tsx # Usage documentation
│ ├── solana/ # Blockchain integration
│ │ ├── useProgram.ts # React hook for program instructions
│ │ ├── SolanaProvider.tsx # Wallet configuration and setup
│ │ └── client/ # Program client utilities
│ │ ├── index.ts # Client initialization
│ │ ├── pda.ts # Program Derived Accounts
│ │ └── rpc.ts # RPC utilities
│ └── root.tsx # App layout wrapper
│
├── programs/webstore/ # Solana Smart Contract
│ └── src/
│ ├── lib.rs # Program entry point and instructions
│ ├── instructions/ # Individual instruction handlers
│ │ ├── create_store.rs
│ │ ├── update_store_config.rs
│ │ ├── create_product.rs
│ │ ├── update_product.rs
│ │ ├── purchase_product.rs
│ │ └── verify_license.rs
│ ├── state/ # Account data structures
│ │ ├── store.rs
│ │ ├── product.rs
│ │ └── purchase.rs
│ ├── constants.rs # Program constants
│ └── error.rs # Custom error codes
│
├── package.json # Frontend dependencies and scripts
├── Anchor.toml # Anchor framework configuration
├── Cargo.toml # Rust workspace configuration
├── vite.config.ts # Vite build configuration
├── react-router.config.ts # React Router configuration
├── tsconfig.json # TypeScript configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── Dockerfile # Production container setup
└── README.md # This file
The smart contract implements 6 core instructions for store and product management:
Initialize a new store on the blockchain.
Parameters:
name: String- Store name (up to 255 characters)description: String- Store description/metadata
Accounts Used:
- Store PDA (derived from
[b"store", owner_pubkey]) - System Program (for account creation)
Returns: StoreAccount with:
owner: Pubkey- Store owner's wallet addressname: String- Store namedescription: String- Store descriptionis_active: bool- Active/inactive status (default: true)total_products: u64- Product counterbump: u8- PDA bump seed
Modify an existing store's configuration.
Parameters:
name: Option<String>- New store name (optional)description: Option<String>- New description (optional)is_active: Option<bool>- New active status (optional)
Security: Only store owner can call this instruction.
Create a new product listing in a store.
Parameters:
product_id: u64- Unique product identifier within storename: String- Product namedescription: String- Product descriptionprice: u64- Price in lamports (1 SOL = 1,000,000,000 lamports)stock: u64- Initial stock quantityis_nft: bool- Whether this is an NFT productmetadata: Option<String>- NFT metadata URI (if is_nft = true)
Accounts Created:
- ProductAccount (683 bytes) - Product listing data
- SPL Token Mint - Token mint for the product
- NftMetadataAccount (2176 bytes) - NFT metadata (if is_nft = true)
Returns: ProductAccount containing product details and associated mint address.
Update product details (price, stock, name, active status).
Parameters:
name: Option<String>- New product name (optional)description: Option<String>- New description (optional)price: Option<u64>- New price in lamports (optional)stock: Option<u64>- New stock quantity (optional)is_active: Option<bool>- New active status (optional)
Security: Only store owner can update products.
Purchase a product from a store (executes token transfer).
Parameters:
amount: u64- Purchase amount in lamports (must match product price)
Process:
- Transfers tokens from buyer to store owner
- Decrements product stock
- Creates immutable
PurchaseAccountrecord
Accounts Used:
- Buyer's token account (payer)
- Store owner's token account (receiver)
PurchaseAccountPDA - Records transaction for verification
Returns: PurchaseAccount with:
buyer: Pubkey- Buyer's wallet addressproduct: Pubkey- Product purchasedamount: u64- Amount paid in lamportstimestamp: i64- Unix timestamp of purchase
Verify NFT product ownership and license validity (for license verification flow).
Parameters:
nft_mint: Pubkey- NFT mint address to verify ownership
Note: Currently a placeholder implementation. Production systems should integrate with Metaplex Token Metadata standard for full NFT verification.
- Rust 1.70 or higher
- Solana CLI - Latest version
- Anchor CLI - v0.31.1
- Node.js - v18 or higher
- npm or yarn - Package manager
# Clone the repository
git clone https://github.com/zkxp-innovation/solana-webstore.git
cd solana-webstore
# Install dependencies
npm install# Start development server with hot reload
npm run dev
# Type check and generate router types
npm run typecheck
# Format code with Prettier
npm run formatThe frontend will be available at http://localhost:5173 (or the next available port).
# Build the Solana program
anchor build
# Run smart contract tests
anchor test# Build the frontend
npm run build
# Start production server
npm start# Deploy to local validator
anchor deploy
# Deploy to Solana Devnet
anchor deploy --provider.cluster devnet
# Deploy to Solana Testnet
anchor deploy --provider.cluster testnet
# Deploy to Solana Mainnet
anchor deploy --provider.cluster mainnetAfter deployment, update the VITE_SOLANA_PROGRAM_ID environment variable with the deployed program ID.
Create a .env file in the project root:
# Solana Program ID (from deployment)
VITE_SOLANA_PROGRAM_ID=<YOUR_DEPLOYED_PROGRAM_ID>
# Solana Network: localnet | devnet | testnet | mainnet
VITE_SOLANA_NETWORK=devnetBuild and run the application in Docker:
# Build Docker image
docker build -t solana-webstore:latest .
# Run container
docker run -p 3000:3000 solana-webstore:latest- Connect Wallet - Click "Connect" and select your Solana wallet (Phantom, Solflare, etc.)
- Create Store - Fill in store details (name, description)
- Add Products - List products with prices, descriptions, and optional NFT metadata
- Manage Inventory - Update product prices, stock, and active status
- Connect Wallet - Connect your Solana wallet
- Browse Products - Explore available products in stores
- Purchase - Complete purchases with secure on-chain transactions
- Verify License - For NFT products, verify ownership and license
- Account Validation - All instructions validate account ownership and signer authorization
- PDA Derivation - Proper Program Derived Account (PDA) usage for deterministic accounts
- Rent Exemption - All initialized accounts maintain minimum balance for rent exemption
- CPI Transfers - Token transfers executed securely through Cross-Program Invocation (CPI)
- Error Handling - Comprehensive error codes and edge case handling
- Type Safety - Full TypeScript and Rust type checking throughout the codebase
npm run testanchor testThe smart contract uses Program Derived Accounts (PDAs) for deterministic, collision-free account generation:
- Size: 634 bytes
- PDA Derivation:
[b"store", owner_pubkey] - Fields:
owner: Pubkey- Store owner's wallet addressname: String- Store namedescription: String- Store descriptionis_active: bool- Whether store is accepting products/purchasestotal_products: u64- Number of products in this storebump: u8- PDA bump seed for account derivation
- Size: 683 bytes
- PDA Derivation:
[b"product", store_pubkey, product_id] - Fields:
store: Pubkey- Reference to parent storeproduct_id: u64- Unique ID within storename: String- Product namedescription: String- Product descriptionprice: u64- Price in lamportsstock: u64- Available quantityis_active: bool- Whether product can be purchasedis_nft: bool- Whether this is an NFT productmetadata_mint: Option<Pubkey>- Associated NFT mint (if is_nft = true)bump: u8- PDA bump seed
- Size: 89 bytes
- PDA Derivation:
[b"purchase", buyer_pubkey, product_pubkey] - Fields:
buyer: Pubkey- Buyer's wallet addressproduct: Pubkey- Product purchasedamount: u64- Amount paid in lamportstimestamp: i64- Unix timestamp of purchase (immutable record)
- Size: 2176 bytes
- PDA Derivation:
[b"metadata", metadata_mint_pubkey] - Fields:
mint: Pubkey- Associated token mintname: String- NFT namesymbol: String- Token symboluri: String- Metadata URIdecimals: u8- Token decimalsis_fungible: bool- Fungibility flag
The program defines the following error codes for transaction validation:
| Code | Name | Description |
|---|---|---|
| 6000 | Unauthorized |
Signer is not authorized for this operation |
| 6001 | StoreNotFound |
Referenced store account does not exist |
| 6002 | ProductNotFound |
Referenced product account does not exist |
| 6003 | InsufficientStock |
Product has insufficient stock for purchase |
| 6004 | InsufficientFunds |
Buyer has insufficient balance for transaction |
| 6005 | ProductNotActive |
Product is marked as inactive and cannot be purchased |
| 6006 | StoreNotActive |
Store is marked as inactive |
| 6007 | InvalidProductId |
Product ID is invalid or out of range |
| 6008 | LicenseNotValid |
NFT license verification failed |
The React application provides a user interface for interacting with the smart contract:
Navigation bar with:
- Logo and branding
- Links to Vite, React Router, and Solana documentation
- Wallet connection/disconnection button
- Public key display (truncated for privacy)
Store creation and management interface:
- Form to create new stores (name, description)
- Displays connected wallet's stores
- Uses
useProgram().createStoreSendAndConfirm()for transactions
Product listing and management:
- Display products from selected store
- Create new products with pricing and stock
- Update product details and active status
- Integrates with SPL Token minting for products
Customer purchase interface:
- Search and browse products
- Purchase flow with transaction confirmation
- Handles SOL-based token transfers
- Records purchase on-chain
Main hook for smart contract interaction, providing:
Instruction Methods (send transactions):
createStoreSendAndConfirm(name, description)→StoreAccountupdateStoreConfigSendAndConfirm(storeKey, updates)→ UpdatedStoreAccountcreateProductSendAndConfirm(storeKey, product_id, details)→ProductAccountupdateProductSendAndConfirm(productKey, updates)→ UpdatedProductAccountpurchaseProductSendAndConfirm(productKey, amount)→PurchaseAccountverifyLicenseSendAndConfirm(nftMint)→ License verification result
Account Fetchers:
getStoreAccount(storeKey)→StoreAccount | nullgetProductAccount(productKey)→ProductAccount | nullgetPurchaseAccount(purchaseKey)→PurchaseAccount | nullgetNftMetadataAccount(metadataKey)→NftMetadataAccount | null
PDA Derivers:
deriveProgramStoreAccount(owner)→ PDA addressderiveProgramProductAccount(store, product_id)→ PDA addressderiveProgramPurchaseAccount(buyer, product)→ PDA addressderiveProgramNftMetadataAccount(mint)→ PDA addressgetProgramId()→ Current program ID from environment
Wallet configuration and connection setup:
- Multi-wallet support (Phantom, Solflare, Ledger, UnsafeBurner)
- Cluster configuration (localnet/devnet/testnet/mainnet)
- RPC endpoint management
- Wallet selection UI
Create a .env file in the project root with the following variables:
# REQUIRED: Solana program ID (obtained after deploying the smart contract)
VITE_SOLANA_PROGRAM_ID=G1Gu4pWjLjHdDFFKmfYN3H8FMPbYjKs9r14mN3oqw9UU
# Network selection: localnet | devnet | testnet | mainnet (default: localnet)
VITE_SOLANA_NETWORK=devnet
# Optional: Custom RPC endpoint (overrides network default)
VITE_SOLANA_RPC_URL=https://api.devnet.solana.com
# Optional: Comma-separated list of enabled wallets (default: all supported)
VITE_SOLANA_WALLETS=Phantom,Solflare,LedgerExample Configuration for Different Networks:
# Local Development
VITE_SOLANA_PROGRAM_ID=G1Gu4pWjLjHdDFFKmfYN3H8FMPbYjKs9r14mN3oqw9UU
VITE_SOLANA_NETWORK=localnet
VITE_SOLANA_RPC_URL=http://localhost:8899
# Devnet Deployment
VITE_SOLANA_PROGRAM_ID=G1Gu4pWjLjHdDFFKmfYN3H8FMPbYjKs9r14mN3oqw9UU
VITE_SOLANA_NETWORK=devnet
VITE_SOLANA_RPC_URL=https://api.devnet.solana.comTo integrate the Solana Webstore program client in an external application:
- Install Dependencies:
npm install @coral-xyz/anchor @solana/web3.js- Import and Initialize:
import { useProgram } from './app/solana/useProgram';
const YourComponent = () => {
const program = useProgram();
// Create a store
const result = await program.createStoreSendAndConfirm(
"My Store",
"My store description"
);
if (result.error) {
console.error("Transaction failed:", result.error);
} else {
console.log("Store created:", result.signature);
}
};- Fetch Account Data:
const storeAccount = await program.getStoreAccount(storePublicKey);
console.log("Store:", storeAccount);- Derive PDAs:
const storePDA = program.deriveProgramStoreAccount(ownerPublicKey);
const productPDA = program.deriveProgramProductAccount(storePDA, productId);User connects wallet
↓
Frontend displays stores and products
↓
User selects product and clicks "Purchase"
↓
Frontend constructs transaction with:
- Purchase instruction (amount = product.price)
- Buyer's token account
- Store owner's token account
↓
Wallet signs transaction
↓
Transaction submitted to Solana RPC
↓
Program executes:
- Validates buyer has sufficient balance
- Transfers tokens from buyer to owner
- Decrements product stock
- Creates PurchaseAccount record
↓
Transaction confirmed on blockchain
↓
Frontend displays purchase confirmation
- PDA Authorization: All accounts use PDAs with proper seed derivation
- Owner Verification: State-changing instructions verify signer is account owner
- Rent Exemption: All created accounts maintain minimum balance
- Token Transfers via CPI: Uses Cross-Program Invocation for secure SPL token transfers
- Immutable Records: Purchase accounts are immutable once created
- Type Safety: Full Rust type system with compile-time guarantees
-
License Verification Placeholder
verify_licenseinstruction is not fully implemented- For production: Integrate with Metaplex Token Metadata standard
- Currently only validates instruction structure, not NFT ownership
-
NFT Metadata
- Uses custom NFT metadata account instead of Metaplex standard
- May cause compatibility issues with NFT marketplaces
- Recommendation: Migrate to Metaplex if ecosystem integration needed
-
Token Account Management
- Frontend must manage associated token account setup
- Buyers must have SOL token account before purchase
- No automatic ATA (Associated Token Account) creation
-
Stock Management
- No bulk stock updates or inventory management
- Each product update transaction updates one field at a time
- Stock decrements happen per purchase (no batch processing)
-
Price Constraints
- Prices stored as u64 in lamports (max: ~4.6M SOL per product)
- No dynamic pricing or discount mechanisms
- No decimal precision in pricing
-
Wallet Security
- Demo uses wallet seed phrase export (unsafe for production)
- UnsafeBurner wallet included for development only
- Production deployments must use secure wallet signers
-
Implement Metaplex Integration
- Use Metaplex Token Metadata standard for NFTs
- Enables marketplace compatibility
- File:
/programs/webstore/src/instructions/verify_license.rs
-
Add Inventory Management
- Implement bulk update instructions
- Add restock/reserve mechanics
- Track inventory history
-
Enhanced Security Audit
- Professional Rust security audit
- Frontend security review
- Wallet integration best practices review
-
Testing Coverage
- Implement comprehensive unit and integration tests
- Add frontend test suite (currently missing)
- Create CI/CD pipeline with automated testing
-
Performance Optimization
- Consider indexing products (currently O(n) lookups)
- Add pagination for large store inventories
- Optimize metadata account storage
npm run format # Auto-format all files with Prettier
npm run format:check # Check formatting without making changesnpm run typecheck # Run TypeScript type checking and generate router typesContributions are welcome! Please ensure:
- Code is properly formatted with Prettier (
npm run format) - TypeScript types are correct (
npm run typecheck) - Smart contract tests pass (
anchor test) - Frontend builds without errors (
npm run build)
MIT License - See LICENSE file for details
Created by ZKXP Innovation Inc.