Skip to content

YASH514131/SwapCredit

Repository files navigation

StudyChain 📚

The First Study App Where Your Subscription is Truly Yours

Solana Flutter Anchor License: MIT

Hackathon: Dev3 Pack Hackathon — Solana Mobile Track
Platform: Android (Flutter) — Native, Mobile-First
Network: Solana Devnet → Mainnet
Solana Mobile Stack: Mobile Wallet Adapter v2.0 · Solana dApp Store


🎬 One Line Pitch

"Buy a study plan, own it as an NFT, sell it when you're done — on Solana."


🧠 The Problem

Every student has been here:

You pay ₹999 for a premium study plan before exams.
Exams finish. You never open the app again.
Your subscription expires. That money is gone forever.
Meanwhile, your friend needs the same plan but can't afford it.

The subscription was yours. But you never truly owned it.

Traditional study apps — Unacademy, BYJU'S, Coursera — lock your plan to your account. You cannot transfer it, sell it, or gift it. The moment you stop using it, the value vanishes.

This is the problem StudyChain solves.


💡 The Solution

StudyChain is a mobile-first Solana dApp where every subscription plan is minted as an NFT to your wallet via the Solana Mobile Stack.

When you buy a plan:

  • ✅ You get real study content (notes, quizzes, mock tests)
  • ✅ You get a Plan NFT minted to your Solana wallet
  • ✅ That NFT is the cryptographic key to your content
  • ✅ You can sell, transfer, or trade that NFT anytime
  • ✅ Whoever holds the NFT gets access — instantly, on-chain

No customer support. No refund requests. No middleman. Just pure ownership on Solana.


🔄 Real World Flows

Scenario 1 — Sell After Exams

Rahul buys Premium Plan (0.3 SOL)
       ↓ [mint_plan instruction → NFT minted to wallet via MWA]
Rahul studies for 2 months. Exams done.
       ↓ [list_plan instruction → NFT moves to escrow PDA]
Rahul lists NFT on StudyChain marketplace (0.15 SOL)
       ↓ [buy_plan instruction → SOL transferred, NFT to Priya]
Priya gets instant Premium access.
Rahul gets 0.15 SOL back.
Everyone wins ✅

Scenario 2 — Gift a Plan

Parent buys Pro Plan NFT
       ↓ [transfer_plan instruction → signed via MWA]
NFT transferred directly to child's wallet
       ↓ [plan_checker.dart reads wallet on launch]
Child opens app → Pro content unlocked automatically ✅

Scenario 3 — Upgrade Plan

User has Basic Plan NFT
       ↓ [list_plan → escrow]
Lists Basic NFT on marketplace
       ↓ [buy_plan → SOL received]
Uses recovered SOL to buy Premium NFT
       ↓
Seamless upgrade with real money back ✅

📦 Subscription Plans

Plan Content Features Price
🥉 Basic 50 Study Notes Read-only access, 5 subjects 0.1 SOL
🥈 Premium 200 Notes + Quizzes All subjects, practice quizzes, progress tracking 0.3 SOL
🥇 Pro Everything + Mock Tests Premium + live mock tests, AI explanations, leaderboard 0.5 SOL

Each plan is a unique NFT on Solana. The app checks your wallet on every session via the MWA authorize method. No NFT = No access. Hold the NFT = Full access to that tier.


📱 Solana Mobile Stack Integration

StudyChain is built as a first-class Solana Mobile Stack (SMS) citizen, not a web port. Every blockchain interaction is handled through native Android integrations.

Mobile Wallet Adapter (MWA) v2.0

StudyChain implements the full Mobile Wallet Adapter 2.0 specification, enabling trustless, session-based wallet connectivity over a local WebSocket transport.

How the MWA session works in StudyChain:

StudyChain App                    MWA-Compliant Wallet (Phantom/Solflare)
      │                                          │
      │  1. Broadcast solana-wallet:// intent    │
      │ ─────────────────────────────────────►  │
      │                                          │
      │  2. Wallet launches, opens local WS      │
      │     ws://localhost:<port>/solana-wallet  │
      │                                          │
      │  3. Session established (MWA v1 or       │
      │     v1.base64 subprotocol negotiated)    │
      │ ◄────────────────────────────────────── │
      │                                          │
      │  4. authorize() → auth_token returned    │
      │ ◄────────────────────────────────────── │
      │                                          │
      │  5. sign_and_send_transaction()          │
      │     (mint/transfer/list/buy/cancel/burn) │
      │ ─────────────────────────────────────►  │
      │                                          │
      │  6. Wallet signs + submits to RPC        │
      │     Returns: tx signature                │
      │ ◄────────────────────────────────────── │

Why sign_and_send_transaction over sign_transactions:

StudyChain exclusively uses sign_and_send_transaction — the wallet signs and submits to the Solana network itself. This is the recommended approach because:

  1. The wallet handles nonce/blockhash freshness, preventing replay attacks with durable nonces
  2. The wallet's RPC connection is trusted and optimized
  3. Reduces the attack surface on the dApp side

MWA Session Lifecycle in StudyChain:

Uses solana_mobile_client — the verified Flutter reference implementation of the MWA client spec, published by cryptoplease.com.

// wallet_service.dart
import 'package:solana_mobile_client/solana_mobile_client.dart';

String? _authToken;
Uint8List? _publicKey;

// 1. Connect & authorize — opens the MWA-compatible wallet via Android intent
Future<void> connectWallet() async {
  final session = await LocalAssociationScenario.create();
  await session.startActivityForResult(null); // fires solana-wallet:// intent

  final client = await session.start();
  final result = await client.authorize(
    identityUri: Uri.parse('https://studychain.app'),
    iconUri: Uri.parse('favicon.ico'),
    identityName: 'StudyChain',
    cluster: 'devnet', // switch to 'mainnet-beta' for production
  );

  if (result != null) {
    _authToken = result.authToken;
    _publicKey = result.publicKey;
    // Cache auth_token in Hive for silent reauth on next launch
    await _hiveBox.put('mwa_auth_token', _authToken);
    await _hiveBox.put('mwa_public_key', base58.encode(_publicKey!));
  }

  await session.close(); // always close after each operation
}

// 2. Sign and send a transaction (mint/transfer/list/buy/cancel/burn)
Future<String?> signAndSendTransaction(Uint8List serializedTx) async {
  final session = await LocalAssociationScenario.create();
  await session.startActivityForResult(null);

  final client = await session.start();
  final result = await client.signAndSendTransactions(
    transactions: [serializedTx],
  );

  await session.close();
  return result?.signatures.first;
}

// 3. Disconnect — deauthorize and clear cached token
Future<void> disconnectWallet() async {
  if (_authToken == null) return;

  final session = await LocalAssociationScenario.create();
  await session.startActivityForResult(null);

  final client = await session.start();
  await client.deauthorize(authToken: _authToken!);
  await session.close();

  _authToken = null;
  _publicKey = null;
  await _hiveBox.delete('mwa_auth_token');
  await _hiveBox.delete('mwa_public_key');
}

Auth token persistence (silent reauth on launch):

To avoid prompting the user to re-authorize every session, StudyChain caches the authToken returned by authorize() using Hive. On subsequent launches, the app passes the cached token back to authorize() as authToken — the wallet silently re-grants access without a user prompt, unless the token has been revoked.

// wallet_service.dart — silent reauth on splash screen

Future<bool> tryReauthorize() async {
  final cachedToken = _hiveBox.get('mwa_auth_token') as String?;
  final cachedKey   = _hiveBox.get('mwa_public_key') as String?;
  if (cachedToken == null || cachedKey == null) return false;

  try {
    final session = await LocalAssociationScenario.create();
    await session.startActivityForResult(null);

    final client = await session.start();
    // Pass the cached authToken — wallet reauthorizes silently
    final result = await client.authorize(
      identityUri: Uri.parse('https://studychain.app'),
      iconUri: Uri.parse('favicon.ico'),
      identityName: 'StudyChain',
      cluster: 'devnet',
      authToken: cachedToken, // triggers silent reauth
    );
    await session.close();

    if (result != null) {
      _authToken  = result.authToken; // wallet may rotate the token
      _publicKey  = result.publicKey;
      await _hiveBox.put('mwa_auth_token', _authToken);
      return true;
    }
    return false;
  } catch (_) {
    // Token revoked or wallet uninstalled — fall through to full connect
    await _hiveBox.delete('mwa_auth_token');
    await _hiveBox.delete('mwa_public_key');
    return false;
  }
}

Sign In With Solana (SIWS) — backend authentication:

For the content-gating API, StudyChain uses wallet message signing as a SIWS-style authentication mechanism. The backend issues a nonce; the app asks the wallet to sign a structured message; the backend verifies the ed25519 signature against the wallet's public key — no passwords, no JWTs.

// wallet_service.dart — sign a SIWS message for backend auth

Future<SIWSCredential?> signInWithSolana(String serverNonce) async {
  final walletAddress = base58.encode(_publicKey!);

  // Build the SIWS message (Sign In With Solana standard format)
  final message =
      'studychain.app wants you to sign in with your Solana account:\n'
      '$walletAddress\n\n'
      'Sign in to StudyChain to access your study content.\n\n'
      'Nonce: $serverNonce';

  final messageBytes = Uint8List.fromList(utf8.encode(message));

  final session = await LocalAssociationScenario.create();
  await session.startActivityForResult(null);

  final client = await session.start();
  // signMessages returns the signed payload from the wallet
  final result = await client.signMessages(
    messages: [messageBytes],
    addresses: [_publicKey!],
  );
  await session.close();

  if (result == null) return null;

  return SIWSCredential(
    walletAddress: walletAddress,
    message: message,
    signature: base58.encode(result.signedPayloads.first),
  );
}

Solana dApp Store

StudyChain is submitted to the Solana dApp Store — the decentralized app distribution platform built for the Solana ecosystem. Unlike Google Play, the dApp Store does not take a 30% revenue cut and does not restrict blockchain-based transactions.

Why the dApp Store matters for StudyChain:

  • Users on Saga/Seeker devices can discover StudyChain natively
  • No payment processing restrictions (in-app SOL purchases work freely)
  • Direct relationship with users — no platform intermediary
  • Community governance of store contents

dApp Store submission metadata (app-metadata.json):

{
  "name": "StudyChain",
  "description": "The first study app where subscriptions are tradeable NFTs on Solana. Buy, sell, transfer your plan on-chain.",
  "categories": ["education", "web3"],
  "minAge": 4,
  "website": "https://studychain.app",
  "github": "https://github.com/yourusername/studychain",
  "testnet": true
}

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                    STUDYCHAIN MOBILE APP                        │
│                     (Flutter / Dart)                            │
│                                                                  │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────────────┐  │
│  │  Screens    │   │  Providers  │   │     Services         │  │
│  │  (UI Layer) │◄──│  (Riverpod) │◄──│  wallet_service.dart │  │
│  └─────────────┘   └─────────────┘   │  solana_service.dart │  │
│                                       │  nft_service.dart    │  │
│                                       │  plan_checker.dart   │  │
│                                       │  marketplace_service │  │
│                                       └──────────┬──────────┘  │
└──────────────────────────────────────────────────┼─────────────┘
                                                   │
              ┌────────────────────┬───────────────┘
              ▼                    ▼
┌─────────────────────┐  ┌─────────────────────────────────────┐
│  MWA-Compatible     │  │        SOLANA BLOCKCHAIN            │
│  Wallet App         │  │         (Devnet / Mainnet)          │
│  (Phantom/Solflare) │  │                                     │
│                     │  │  ┌──────────────────────────────┐  │
│  • Authorize        │  │  │  StudyChain Anchor Program   │  │
│  • Sign+Send Tx     │  │  │                              │  │
│  • SIWS             │  │  │  mint_plan()                 │  │
└─────────────────────┘  │  │  transfer_plan()             │  │
                          │  │  list_plan() → escrow PDA    │  │
              ▼           │  │  buy_plan()                  │  │
┌─────────────────────┐  │  │  cancel_listing()            │  │
│   NODE.JS BACKEND   │  │  │  burn_plan()                 │  │
│                     │  │  └──────────────────────────────┘  │
│  • SIWS verify      │  │                                     │
│  • NFT ownership    │  │  ┌──────────────────────────────┐  │
│    check via RPC    │  │  │   Metaplex Core NFTs         │  │
│  • Serve gated      │  │  │   Plan NFT Collection        │  │
│    study content    │  │  └──────────────────────────────┘  │
│  • Firebase store   │  └─────────────────────────────────────┘
└─────────────────────┘

🏗️ Tech Stack

Mobile App (Flutter)

Layer Technology
Framework Flutter (Dart)
Wallet Integration Mobile Wallet Adapter v2.0 (mobile_wallet_adapter_flutter)
Solana SDK solana: ^0.30.4
State Management Riverpod v2
Local Storage Hive (auth token cache, offline content)
UI Flutter Material 3 + Custom Dark Theme

Blockchain

Layer Technology
Network Solana (Devnet → Mainnet)
Smart Contract Anchor Framework (Rust)
NFT Standard Metaplex Core
Token SPL Token
RPC Provider Helius RPC (enhanced APIs for NFT ownership queries)

Backend

Layer Technology
API Server Node.js + Express
Auth Sign In With Solana (SIWS) — wallet signature verification
Database Firebase Firestore (study content, user progress)
NFT Metadata Arweave (permanent, decentralized storage)
NFT Verification Helius RPC getAssetsByOwner API

📁 Project Structure

studychain/
│
├── flutter_app/
│   ├── android/
│   │   └── app/src/main/AndroidManifest.xml  # MWA intent filter + wallet URI scheme
│   └── lib/
│       ├── main.dart
│       ├── app.dart
│       │
│       ├── screens/
│       │   ├── splash_screen.dart             # MWA reauth + NFT check on launch
│       │   ├── connect_wallet_screen.dart     # MWA authorize() flow
│       │   ├── home_screen.dart               # Dashboard + content preview
│       │   ├── plans_screen.dart              # View & buy subscription plans
│       │   ├── content_screen.dart            # Gated study material
│       │   │   ├── notes_screen.dart
│       │   │   ├── quiz_screen.dart
│       │   │   └── mock_test_screen.dart      # Pro only
│       │   ├── my_plan_screen.dart            # Current plan NFT details
│       │   ├── marketplace_screen.dart        # Buy/sell plan NFTs
│       │   ├── sell_plan_screen.dart          # List NFT for sale
│       │   ├── transfer_screen.dart           # Gift/transfer NFT
│       │   └── transaction_history.dart       # On-chain tx history
│       │
│       ├── models/
│       │   ├── plan_nft.dart                  # Plan NFT data model
│       │   ├── study_content.dart             # Notes/quiz/test model
│       │   └── listing.dart                   # Marketplace listing model
│       │
│       ├── providers/
│       │   ├── wallet_provider.dart           # MWA session + auth state
│       │   ├── plan_provider.dart             # Current user plan (NFT)
│       │   ├── content_provider.dart          # Study content state
│       │   └── marketplace_provider.dart      # Listings state
│       │
│       ├── services/
│       │   ├── wallet_service.dart            # MWA connect/reauth/disconnect + SIWS
│       │   ├── solana_service.dart            # Helius RPC + tx submission
│       │   ├── nft_service.dart               # Mint/burn/transfer plan NFTs
│       │   ├── plan_checker.dart              # Verify NFT in wallet → grant access
│       │   ├── content_service.dart           # Fetch gated study material
│       │   └── marketplace_service.dart       # Listing + purchase logic
│       │
│       └── widgets/
│           ├── plan_card.dart
│           ├── locked_content_banner.dart
│           ├── listing_tile.dart
│           └── wallet_connect_button.dart     # MWA authorize CTA
│
├── smart_contracts/
│   ├── programs/studychain/src/
│   │   ├── lib.rs                             # Program entry + instruction routing
│   │   ├── instructions/
│   │   │   ├── mint_plan.rs                   # Mint plan NFT to buyer
│   │   │   ├── transfer_plan.rs               # Transfer plan NFT
│   │   │   ├── list_plan.rs                   # List NFT on marketplace (escrow PDA)
│   │   │   ├── buy_plan.rs                    # Buy listed NFT (pay + receive)
│   │   │   ├── cancel_listing.rs              # Cancel listing, return NFT
│   │   │   └── burn_plan.rs                   # Burn plan NFT, reclaim rent
│   │   ├── state/
│   │   │   ├── plan_nft.rs                    # PlanNft account struct
│   │   │   └── listing.rs                     # Marketplace listing account
│   │   └── errors.rs                          # Custom program errors
│   ├── tests/
│   │   └── studychain.ts                      # Anchor integration tests
│   ├── Anchor.toml
│   └── Cargo.toml
│
├── backend/
│   ├── src/
│   │   ├── index.js
│   │   ├── middleware/
│   │   │   └── verify_wallet.js               # SIWS signature verification
│   │   ├── routes/
│   │   │   ├── content.js                     # Serve gated study content
│   │   │   ├── plans.js                       # Plan metadata
│   │   │   └── marketplace.js                 # Listing cache + search
│   │   └── services/
│   │       ├── nft_verifier.js                # Helius getAssetsByOwner
│   │       ├── content_gate.js                # Grant/deny content by tier
│   │       ├── arweave.js                     # Upload NFT metadata
│   │       └── firebase.js                    # Firestore client
│   └── package.json
│
└── README.md

🔗 Smart Contract Architecture

Account: PlanNft

// state/plan_nft.rs

#[account]
pub struct PlanNft {
    pub owner: Pubkey,          // Current NFT owner (wallet address)
    pub plan_tier: PlanTier,    // Basic | Premium | Pro
    pub mint: Pubkey,           // NFT mint address (Metaplex Core asset)
    pub purchased_at: i64,      // Unix timestamp of original purchase
    pub is_listed: bool,        // Whether currently listed on marketplace
    pub original_price: u64,    // Original price in lamports
    pub bump: u8,               // PDA bump seed
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
pub enum PlanTier {
    Basic,      // 0.1 SOL = 100_000_000 lamports
    Premium,    // 0.3 SOL = 300_000_000 lamports
    Pro,        // 0.5 SOL = 500_000_000 lamports
}

Account: Listing

// state/listing.rs

#[account]
pub struct Listing {
    pub seller: Pubkey,         // Seller wallet
    pub plan_nft: Pubkey,       // PlanNft account being sold
    pub plan_tier: PlanTier,    // Tier (for marketplace filtering)
    pub price_lamports: u64,    // Sale price in lamports
    pub listed_at: i64,         // Listing timestamp
    pub bump: u8,
}

Instructions

mint_plan — Purchase a new plan

Accounts: [buyer (signer), plan_nft (PDA), mint, token_program, system_program]
  1. Buyer transfers SOL to program treasury
  2. Program creates PlanNft account (PDA seeded by buyer pubkey + plan_tier)
  3. Mints NFT asset via Metaplex Core CPI
  4. NFT transferred to buyer wallet
  5. PlanNft.owner = buyer, PlanNft.plan_tier = requested tier

transfer_plan — Transfer/gift a plan

Accounts: [owner (signer), recipient, plan_nft, nft_mint]
  1. Verify owner == plan_nft.owner
  2. Verify plan_nft.is_listed == false (cannot transfer listed NFT)
  3. Transfer NFT to recipient via Metaplex Core CPI
  4. Update PlanNft.owner = recipient
  → Recipient gets content access instantly on next wallet check

list_plan — List on marketplace

Accounts: [seller (signer), plan_nft, listing (PDA), escrow_token_account]
  1. Verify seller == plan_nft.owner
  2. Verify plan_nft.is_listed == false
  3. Transfer NFT to escrow PDA (program-controlled)
  4. Create Listing account on-chain
  5. Set plan_nft.is_listed = true

buy_plan — Buy a listed NFT

Accounts: [buyer (signer), seller, listing, plan_nft, escrow_token_account]
  1. Verify listing is active and price matches
  2. Transfer SOL from buyer to seller
  3. Transfer NFT from escrow PDA to buyer wallet
  4. Close Listing account (rent refunded to seller)
  5. Update PlanNft.owner = buyer
  → Access granted to buyer on next wallet check

cancel_listing — Cancel a listing

Accounts: [seller (signer), listing, plan_nft, escrow_token_account]
  1. Verify seller == listing.seller
  2. Return NFT from escrow to seller wallet
  3. Close Listing account (rent refunded)
  4. Set plan_nft.is_listed = false

burn_plan — Burn a plan NFT

Accounts: [owner (signer), plan_nft, nft_mint]
  1. Verify owner == plan_nft.owner
  2. Burn NFT via Metaplex Core CPI
  3. Close PlanNft account (SOL rent reclaimed to owner)
  → Access revoked

Custom Program Errors

// errors.rs

#[error_code]
pub enum StudyChainError {
    #[msg("You are not the owner of this Plan NFT")]
    NotNFTOwner,
    #[msg("This plan NFT is currently listed — delist before transferring")]
    PlanIsListed,
    #[msg("Insufficient SOL balance to purchase this plan")]
    InsufficientFunds,
    #[msg("This listing has already been fulfilled")]
    ListingAlreadySold,
    #[msg("Price mismatch — listing price has changed")]
    PriceMismatch,
    #[msg("Invalid plan tier")]
    InvalidPlanTier,
}

🔐 Content Gating — The Heart of StudyChain

Every screen checks wallet NFT ownership. There is no username/password — your wallet is your identity, and your NFT is your subscription key.

Client-Side Check (Flutter)

// plan_checker.dart

class PlanChecker {
  final SolanaService _solana;
  final String _collectionAddress = STUDYCHAIN_COLLECTION_ADDRESS;

  // Called on every app launch after MWA authorization
  Future<PlanTier?> getUserPlan(String walletAddress) async {
    // Query Helius getAssetsByOwner for all NFTs in wallet
    final assets = await _solana.helius.getAssetsByOwner(
      ownerAddress: walletAddress,
      page: 1,
    );

    // Filter for StudyChain collection NFTs
    final planNft = assets.firstWhere(
      (asset) => asset.grouping.any(
        (g) => g.groupKey == 'collection' && g.groupValue == _collectionAddress,
      ),
      orElse: () => null,
    );

    if (planNft == null) return null;

    // Read plan_tier attribute from NFT metadata
    final tierAttr = planNft.content.metadata.attributes
        ?.firstWhere((a) => a.traitType == 'plan_tier');

    return PlanTier.fromString(tierAttr?.value);
  }
}

Server-Side Check (Node.js)

The backend independently re-verifies NFT ownership on every API request. The client result is never trusted alone.

// content_gate.js

const { Helius } = require("helius-sdk");
const helius = new Helius(process.env.HELIUS_API_KEY);

async function canAccessContent(walletAddress, requiredTier) {
  // Independent RPC verification — never trust the client
  const assets = await helius.rpc.getAssetsByOwner({
    ownerAddress: walletAddress,
    page: 1,
  });

  const planNft = assets.items.find(asset =>
    asset.grouping?.some(g =>
      g.group_key === "collection" &&
      g.group_value === process.env.COLLECTION_ADDRESS
    )
  );

  if (!planNft) return { access: false, reason: "No StudyChain plan found" };

  const tierAttr = planNft.content?.metadata?.attributes?.find(
    a => a.trait_type === "plan_tier"
  );

  const tierRank = { basic: 1, premium: 2, pro: 3 };
  const userRank = tierRank[tierAttr?.value] ?? 0;
  const requiredRank = tierRank[requiredTier];

  return {
    access: userRank >= requiredRank,
    tier: tierAttr?.value,
    reason: userRank < requiredRank ? `${requiredTier} plan required` : "Granted",
  };
}

SIWS Backend Authentication

// verify_wallet.js — Express middleware

const { PublicKey } = require("@solana/web3.js");
const bs58 = require("bs58");
const nacl = require("tweetnacl");

async function verifySIWS(req, res, next) {
  const { walletAddress, signedMessage, signature } = req.headers;

  // Verify the SIWS signature using ed25519
  const messageBytes = new TextEncoder().encode(signedMessage);
  const signatureBytes = bs58.decode(signature);
  const publicKeyBytes = new PublicKey(walletAddress).toBytes();

  const isValid = nacl.sign.detached.verify(
    messageBytes,
    signatureBytes,
    publicKeyBytes
  );

  if (!isValid) return res.status(401).json({ error: "Invalid wallet signature" });

  // Attach verified wallet to request
  req.walletAddress = walletAddress;
  next();
}

Access Matrix

Content Basic Premium Pro
50 Study Notes
200 Notes + All Subjects
Practice Quizzes
Progress Tracking
Mock Tests
AI Explanations
Leaderboard

📱 Screen-by-Screen Breakdown

1. Splash Screen

  • Checks Hive cache for mwa_auth_token
  • If cached: attempts reauthorize() silently (MWA v2.0)
  • If successful: calls PlanChecker.getUserPlan() via Helius RPC
  • Routes to HomeScreen with correct access level, or ConnectWalletScreen

2. Connect Wallet Screen

  • Single CTA: Connect Wallet
  • Broadcasts solana-wallet:// intent → opens any MWA-compatible wallet
  • Performs authorize() with app identity (name, URI, icon)
  • Caches returned auth_token in Hive for reauth
  • Fetches Plan NFT → navigates to Home

3. Home Screen

  • Shows user's current plan badge: Basic / Premium / Pro / No Plan
  • Preview cards for Notes, Quizzes, Mock Tests
  • Locked content shows blur overlay + lock icon
  • CTA: Get a Plan or Upgrade on Marketplace

4. Plans Screen

  • 3 plan cards: Basic / Premium / Pro
  • Price in SOL, feature list, current plan highlighted
  • Buy Plan → builds mint_plan transaction → sign_and_send_transaction via MWA → NFT minted → access granted

5. Content Screens (Gated)

  • Notes — Basic and above, searchable
  • Quizzes — Premium and above, timed
  • Mock Tests — Pro only, full exam simulation
  • Locked sections: blurred preview + "Upgrade on Marketplace" deeplink

6. My Plan Screen

  • Current Plan NFT displayed as a card with tier, purchase date, wallet address
  • Transfer → enter recipient wallet → transfer_plan tx via MWA → instant
  • Sell on Marketplace → set price → list_plan tx → live immediately
  • View on Solana Explorer → on-chain proof of ownership

7. Marketplace Screen

  • All active listings from all users
  • Filter by Tier (Basic / Premium / Pro), Price (low → high)
  • Each card: tier badge, price, seller wallet (truncated)
  • Buybuy_plan tx via MWA → SOL transferred → NFT to buyer → instant access

⚙️ Setup & Installation

Prerequisites

# Flutter SDK (3.x+)
https://docs.flutter.dev/get-started/install

# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Solana CLI
sh -c "$(curl -sSfL https://release.solana.com/stable/install)"

# Anchor CLI
cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest && avm use latest

# Node.js v18+
https://nodejs.org

# Android device with MWA-compatible wallet (Phantom or Solflare)
# USB debugging enabled

Android Manifest — MWA Intent Filter

Add the solana-wallet:// URI scheme to AndroidManifest.xml so Android can resolve the wallet intent:

<!-- android/app/src/main/AndroidManifest.xml -->
<manifest>
  <queries>
    <!-- Required: let the app discover MWA-compatible wallets -->
    <intent>
      <action android:name="android.intent.action.VIEW" />
      <data android:scheme="solana-wallet" />
    </intent>
  </queries>
  ...
</manifest>

Step 1 — Clone

git clone https://github.com/yourusername/studychain.git
cd studychain

Step 2 — Deploy Smart Contract

cd smart_contracts

# Target devnet
solana config set --url devnet

# Fund deployer wallet
solana airdrop 4

# Build and deploy
anchor build
anchor deploy

# Output: Program Id: <PROGRAM_ID>
# Save this — you need it in Steps 3 and 4

Step 3 — Start Backend

cd backend
cp .env.example .env

# Edit .env:
# FIREBASE_PROJECT_ID=your-project-id
# ARWEAVE_KEY=your-arweave-wallet-json
# SOLANA_RPC=https://devnet.helius-rpc.com/?api-key=YOUR_KEY
# HELIUS_API_KEY=your-helius-api-key
# PROGRAM_ID=your-deployed-program-id
# COLLECTION_ADDRESS=your-nft-collection-address

npm install
npm start
# Server at http://localhost:3000

Step 4 — Run Flutter App

cd flutter_app
flutter pub get

# Update lib/config/constants.dart:
# const PROGRAM_ID = 'your-program-id';
# const RPC_URL = 'https://devnet.helius-rpc.com/?api-key=YOUR_KEY';
# const BACKEND_URL = 'http://your-backend-ip:3000';
# const COLLECTION_ADDRESS = 'your-collection-address';

# Run on Android device (USB debugging ON)
# Must have Phantom or Solflare installed on the same device
flutter run

# Build release APK
flutter build apk --release
# Output: build/app/outputs/flutter-apk/app-release.apk

📦 Flutter Dependencies

dependencies:
  flutter:
    sdk: flutter

  # Solana Mobile Stack
  solana_mobile_client: ^0.1.2            # MWA client — core SMS integration (pub.dev verified)
  solana: ^0.30.4                          # Solana web3 primitives

  # State Management
  flutter_riverpod: ^2.4.0

  # Local Storage (MWA auth token cache + offline content)
  hive_flutter: ^1.1.0

  # Networking
  dio: ^5.3.0

  # UI
  google_fonts: ^6.1.0
  shimmer: ^3.0.0                          # Loading skeletons for NFT fetch
  cached_network_image: ^3.3.0
  lottie: ^2.7.0
  flutter_svg: ^2.0.9

  # Utilities
  intl: ^0.18.1
  url_launcher: ^6.2.1
  bs58: ^1.0.2                             # Base58 encoding for Solana addresses

🧪 Testing

# Smart contract integration tests (Anchor)
cd smart_contracts && anchor test

# Flutter unit tests
cd flutter_app && flutter test

# Install APK for manual testing (requires MWA wallet on device)
flutter build apk
adb install build/app/outputs/flutter-apk/app-release.apk

# Test MWA connection
# 1. Install Phantom or Solflare on the same Android device
# 2. Set wallets to devnet
# 3. Fund with devnet SOL: https://faucet.solana.com
# 4. Open StudyChain → Connect Wallet → should open Phantom/Solflare

🗺️ Roadmap

✅ Phase 1 — Hackathon MVP

  • Anchor program: mint, transfer, list, buy, cancel, burn
  • MWA v2.0 integration: authorize, reauthorize, sign_and_send_transaction
  • Sign In With Solana (SIWS) for backend auth
  • Flutter: wallet connect, plans screen, content gating
  • Flutter: my plan screen, transfer + sell flow
  • Marketplace: list and buy plan NFTs
  • Helius RPC: NFT ownership verification
  • Deploy to Solana devnet
  • Submit to Solana dApp Store

🔜 Phase 2 — Post Hackathon

  • 100+ real study notes, 500+ quiz questions
  • AI-powered explanations (Pro tier) — Gemini API
  • Progress tracking per wallet address
  • Push notifications for marketplace activity
  • Mainnet deployment

🚀 Phase 3 — Scale

  • Multiple subject categories (JEE, NEET, UPSC, CA, etc.)
  • Creator marketplace — educators mint their own plan NFTs
  • Group buy — split Pro plan NFT among friends (multisig)
  • Cross-app standard — other dApps accept StudyChain plan NFTs
  • Solana Pay integration — QR code plan purchases

📋 Hackathon Submission Checklist

  • Project name: StudyChain
  • Description: Study app where subscription plans are tradeable NFTs on Solana
  • Android APKflutter build apk --release
  • Solana Mobile Stack — Mobile Wallet Adapter v2.0 integrated
  • Mobile-first — Native Flutter Android app, not a PWA or web port
  • MWA Android Manifestsolana-wallet:// intent filter configured
  • Meaningful Solana interaction — mint/transfer/list/buy/cancel/burn on-chain
  • SIWS Auth — Sign In With Solana for backend content gating
  • Submitted to dApp Store — publish.solanamobile.com
  • Contract deployment addresses (devnet) — see below
  • Public GitHub repo with README + setup instructions ✅
  • Demo video under 3 minutes

🔑 Contract Addresses

Contract Network Address
StudyChain Program Devnet TBD after deployment
Plan NFT Collection Devnet TBD after deployment
StudyChain Program Mainnet TBD

👥 Team

Name Role
[Yash Mourya] Flutter Developer + Anchor Smart Contract

🙏 Acknowledgements


Built with ❤️ for the Dev3 Pack Hackathon — Solana Mobile Track
Own your education. Trade your knowledge. Study on Solana.

About

Buy a study plan, own it as an NFT, sell it when you're done — on Solana.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors