Target Audience: Developers with basic React knowledge but zero blockchain experience
Duration: 45-60 minutes
Prerequisites: Basic JavaScript/React familiarity
Table of Contents
- Understanding the Blockchain Basics
- Getting Started with PAPI Simulator
- Building Your First Component: Account Balance Checker
- Adding Interactive Features
- Understanding What's Happening
- Next Steps
Understanding the Blockchain Basics
What is Blockchain? (For Complete Beginners)
Think of blockchain as a digital ledger (like a bank statement) that:
- Records all transactions transparently
- Cannot be altered once written
- Is distributed across many computers
- Requires no central authority (like a bank)
What is Polkadot?
Polkadot is a network of blockchains that can communicate with each other. Imagine it as a "blockchain internet" where different specialized chains can work together.
What is Paseo Network?
Paseo is a test network (testnet) for Polkadot. It's like a playground where developers can:
- Test applications safely
- Use fake tokens (no real money)
- Learn without financial risk
Key Concepts You'll Use:
- Account: Like a digital wallet with a unique address
- Balance: Amount of tokens in an account
- Transaction: Transfer of tokens between accounts
- Block: Group of transactions recorded together
Getting Started with PAPI Simulator
What is PAPI Simulator?
PAPI Simulator is an online development environment where you can:
- Write blockchain code without setup
- Test on real testnets instantly
- See results in real-time
- Learn through interactive examples
Step 1: Access the Simulator
- Open your browser and go to: https://papi-simulator.aipop.fun
- You'll see the main interface with:
- Code Editor (left side)
- Network Selector (top)
- Examples Dropdown (top)
- Console Output (bottom)
Step 2: Select Paseo Network
Let me search for more specific information about the PAPI Simulator interface and Paseo network.Based on the documentation provided and my understanding of the PAPI Simulator, let me continue with the tutorial:
- In the top-right corner, you'll see a network dropdown
- Select "Paseo" from the list
- The interface will show you're now connected to the Paseo testnet
Step 3: Explore a Basic Example
- Click the "Examples" dropdown in the top menu
- Select "Account Balance Checker"
- Click "Run Code" to see it in action
You'll see:
- Code that connects to Paseo network
- A query for an account balance
- Results displayed in the console below
Building Your First Component: Account Balance Checker
Now we'll build a React component that checks account balances on the Paseo network.
Step 1: Understanding the Basic Structure
// This is what we're building - a React component that:
// 1. Connects to Paseo network
// 2. Queries account balances
// 3. Displays results in a nice interface
import React, { useState, useEffect } from 'react';
import { createClient } from "polkadot-api";
import { paseo } from "@polkadot-api/descriptors";
import { getWsProvider } from "polkadot-api/ws-provider/web";
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
What each import does:
-
React
- For building the user interface -
createClient
- Creates connection to blockchain -
paseo
- Type definitions for Paseo network -
getWsProvider
- WebSocket connection provider -
withPolkadotSdkCompat
- Compatibility layer
Step 2: Create the Component Structure
Copy this code into the PAPI Simulator:
// Account Balance Checker Component for Paseo Network
import React, { useState, useEffect } from 'react';
import { createClient } from "polkadot-api";
import { paseo } from "@polkadot-api/descriptors";
import { getWsProvider } from "polkadot-api/ws-provider/web";
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
export default function AccountBalanceChecker() {
// State variables - think of these as the component's memory
const [address, setAddress] = useState("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"); // Alice's test address
const [balance, setBalance] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");
const [client, setClient] = useState(null);
const [api, setApi] = useState(null);
// Network information for Paseo
const networkInfo = {
name: "Paseo",
tokenSymbol: "PAS",
tokenDecimals: 10,
endpoint: "wss://rpc.ibp.network/paseo",
explorer: "https://paseo.subscan.io"
};
// Connect to Paseo network when component loads
useEffect(() => {
const initConnection = async () => {
try {
console.log("🔌 Connecting to Paseo network...");
// Create WebSocket connection to Paseo
const provider = getWsProvider(networkInfo.endpoint);
// Create client with compatibility wrapper
const newClient = createClient(
withPolkadotSdkCompat(provider)
);
// Get typed API for Paseo
const unsafeApi = newClient.getUnsafeApi();
setClient(newClient);
setApi(unsafeApi);
console.log("✅ Connected to Paseo network!");
} catch (err) {
console.error("❌ Connection failed:", err);
setError(`Connection failed: ${err.message}`);
}
};
initConnection();
// Cleanup function - runs when component unmounts
return () => {
if (client) {
client.destroy();
}
};
}, []); // Empty dependency array means this runs once when component loads
// Function to check account balance
const checkBalance = async () => {
if (!api || !address) {
setError("Please enter an address and wait for connection");
return;
}
setIsLoading(true);
setError("");
setBalance(null);
try {
console.log(`🔍 Checking balance for: ${address}`);
// Query the account information from the blockchain
const accountInfo = await api.query.System.Account.getValue(address);
if (!accountInfo) {
throw new Error("Account not found");
}
// Extract balance information
const freeBalance = accountInfo.data.free;
const reservedBalance = accountInfo.data.reserved;
const totalBalance = freeBalance + reservedBalance;
// Convert from smallest units to human-readable format
const divisor = 10n ** BigInt(networkInfo.tokenDecimals);
const formattedFree = Number(freeBalance) / Number(divisor);
const formattedReserved = Number(reservedBalance) / Number(divisor);
const formattedTotal = Number(totalBalance) / Number(divisor);
setBalance({
free: freeBalance,
reserved: reservedBalance,
total: totalBalance,
formattedFree,
formattedReserved,
formattedTotal
});
console.log(`💰 Balance found: ${formattedTotal.toFixed(4)} ${networkInfo.tokenSymbol}`);
} catch (err) {
console.error("❌ Error checking balance:", err);
setError(`Error: ${err.message}`);
} finally {
setIsLoading(false);
}
};
// Component's visual interface
return (
<div style={{
fontFamily: 'system-ui, -apple-system, sans-serif',
maxWidth: '600px',
margin: '0 auto',
backgroundColor: '#fff',
color: '#000',
borderRadius: '12px',
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08)',
overflow: 'hidden'
}}>
{/* Header */}
<div style={{
backgroundColor: '#FF7B00', // Paseo orange color
color: 'white',
padding: '20px',
textAlign: 'center'
}}>
<h2 style={{ margin: 0, fontSize: '24px' }}>
🏦 Paseo Balance Checker
</h2>
<p style={{ margin: '8px 0 0', opacity: 0.9 }}>
Check account balances on Paseo testnet
</p>
</div>
{/* Main content */}
<div style={{ padding: '24px' }}>
{/* Connection status */}
<div style={{
padding: '12px',
backgroundColor: api ? '#e8f5e8' : '#fff3e0',
borderRadius: '8px',
border: `1px solid ${api ? '#4caf50' : '#ff9800'}`,
marginBottom: '20px',
textAlign: 'center'
}}>
{api ? '✅ Connected to Paseo' : '⏳ Connecting to Paseo...'}
</div>
{/* Address input */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '8px',
fontWeight: '600',
color: '#333'
}}>
Account Address:
</label>
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
style={{
width: '100%',
padding: '12px',
borderRadius: '8px',
border: '2px solid #e0e0e0',
fontSize: '14px',
boxSizing: 'border-box',
backgroundColor: '#fff'
}}
placeholder="Enter account address"
disabled={isLoading}
/>
</div>
{/* Check balance button */}
<button
onClick={checkBalance}
disabled={isLoading || !api}
style={{
backgroundColor: api && !isLoading ? '#FF7B00' : '#ccc',
color: 'white',
border: 'none',
padding: '14px 20px',
borderRadius: '8px',
cursor: api && !isLoading ? 'pointer' : 'not-allowed',
fontSize: '16px',
fontWeight: '600',
width: '100%'
}}
>
{isLoading ? '⏳ Checking...' : '🔍 Check Balance'}
</button>
{/* Error display */}
{error && (
<div style={{
marginTop: '20px',
padding: '16px',
backgroundColor: '#ffebee',
color: '#c62828',
borderRadius: '8px',
fontSize: '14px'
}}>
❌ {error}
</div>
)}
{/* Balance results */}
{balance && (
<div style={{
marginTop: '24px',
padding: '20px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
border: '1px solid #e0e0e0'
}}>
<h3 style={{ margin: '0 0 16px', color: '#333' }}>
💰 Balance Information
</h3>
<div style={{
display: 'flex',
justifyContent: 'space-between',
padding: '8px 0',
borderBottom: '1px solid #eee'
}}>
<span style={{ color: '#666' }}>Free Balance:</span>
<span style={{ fontWeight: '600' }}>
{balance.formattedFree.toFixed(4)} {networkInfo.tokenSymbol}
</span>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
padding: '8px 0',
borderBottom: '1px solid #eee'
}}>
<span style={{ color: '#666' }}>Reserved Balance:</span>
<span style={{ fontWeight: '600' }}>
{balance.formattedReserved.toFixed(4)} {networkInfo.tokenSymbol}
</span>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
padding: '8px 0',
fontWeight: 'bold'
}}>
<span>Total Balance:</span>
<span>{balance.formattedTotal.toFixed(4)} {networkInfo.tokenSymbol}</span>
</div>
{/* Raw values for developers */}
<div style={{
marginTop: '16px',
padding: '12px',
backgroundColor: '#f0f0f0',
borderRadius: '6px',
fontSize: '12px',
fontFamily: 'monospace'
}}>
<div>Raw Values (Planck units):</div>
<div>Free: {balance.free.toString()}</div>
<div>Reserved: {balance.reserved.toString()}</div>
<div>Total: {balance.total.toString()}</div>
</div>
</div>
)}
{/* Information box */}
<div style={{
marginTop: '24px',
padding: '16px',
backgroundColor: '#f0f8ff',
borderRadius: '8px',
borderLeft: '4px solid #2196f3',
fontSize: '14px'
}}>
<h4 style={{ margin: '0 0 12px', color: '#1976d2' }}>
💡 About Account Balances
</h4>
<ul style={{ margin: '0', paddingLeft: '20px' }}>
<li style={{ marginBottom: '8px' }}>
<strong>Free Balance:</strong> Tokens available for transfers
</li>
<li style={{ marginBottom: '8px' }}>
<strong>Reserved Balance:</strong> Tokens locked for operations (staking, governance, etc.)
</li>
<li>
<strong>Total Balance:</strong> Sum of free + reserved tokens
</li>
</ul>
</div>
</div>
</div>
);
}
Step 3: Run Your Component
- Copy the code above into the PAPI Simulator editor
- Click "Run Code"
- Watch the console for connection messages
-
Try checking different addresses:
- Alice:
5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
- Bob:
5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty
- Alice:
Step 4: Understanding What Happened
Connection Process:
- ✅ Component loaded and initiated connection
- ✅ WebSocket connected to Paseo network
- ✅ API client created with type safety
- ✅ Ready to make blockchain queries
Balance Query Process:
- 🔍 User enters account address
- 🔍 Component queries
System.Account
storage - 🔍 Blockchain returns account data
- 💰 Component formats and displays balance
Adding Interactive Features
Let's enhance our component with more features:
Step 1: Add Multiple Account Support
// Add this after the networkInfo declaration
const wellKnownAccounts = {
alice: {
name: "Alice (Test Account)",
address: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
},
bob: {
name: "Bob (Test Account)",
address: "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
},
charlie: {
name: "Charlie (Test Account)",
address: "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
}
};
Step 2: Add Quick Select Buttons
Add this before the address input:
{/* Quick select buttons */}
<div style={{ marginBottom: '16px' }}>
<label style={{
display: 'block',
marginBottom: '8px',
fontWeight: '600',
color: '#333'
}}>
Quick Select:
</label>
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
{Object.entries(wellKnownAccounts).map(([key, account]) => (
<button
key={key}
onClick={() => setAddress(account.address)}
style={{
padding: '6px 12px',
backgroundColor: '#f5f5f5',
border: '1px solid #ddd',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '12px'
}}
>
{account.name}
</button>
))}
</div>
</div>
Step 3: Add Real-time Updates
// Add this state variable
const [autoRefresh, setAutoRefresh] = useState(false);
// Add this effect for auto-refresh
useEffect(() => {
if (!autoRefresh || !api || !address) return;
const interval = setInterval(() => {
checkBalance();
}, 10000); // Refresh every 10 seconds
return () => clearInterval(interval);
}, [autoRefresh, api, address]);
// Add auto-refresh toggle
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<input
type="checkbox"
checked={autoRefresh}
onChange={(e) => setAutoRefresh(e.target.checked)}
/>
<span>Auto-refresh every 10 seconds</span>
</label>
</div>
Understanding What's Happening
Blockchain Concepts in Action
1. Network Connection:
// This creates a WebSocket connection to a Paseo node
const provider = getWsProvider(networkInfo.endpoint);
// This wraps it with compatibility features
const client = createClient(withPolkadotSdkCompat(provider));
2. Querying Blockchain State:
// This queries the blockchain's state
const accountInfo = await api.query.System.Account.getValue(address);
// It returns data like this:
{
data: {
free: 1000000000000n, // Free balance in smallest units
reserved: 0n, // Reserved/locked balance
miscFrozen: 0n, // Frozen for various reasons
feeFrozen: 0n // Frozen for transaction fees
},
nonce: 0, // Transaction counter
consumers: 0, // Number of consumers
providers: 1 // Number of providers
}
3. Unit Conversion:
// Blockchain stores values in smallest units (like cents)
// We convert to human-readable format (like dollars)
const divisor = 10n ** BigInt(networkInfo.tokenDecimals);
const formatted = Number(balance) / Number(divisor);
Why This Matters
🔐 Decentralized: No single server controls the data
🌍 Global: Anyone can access the same information
🔍 Transparent: All transactions are publicly verifiable
💎 Immutable: Historical data cannot be changed
Next Steps
Beginner Projects:
- Block Explorer: Show recent blocks and transactions
- Transfer Tool: Send tokens between accounts
- Event Monitor: Watch for specific blockchain events
Intermediate Projects:
- Staking Dashboard: Display staking information
- Governance Tracker: Monitor proposals and voting
- Cross-chain Bridge: Move tokens between parachains
Advanced Projects:
- DeFi Interface: Interact with decentralized finance protocols
- NFT Marketplace: Create and trade digital assets
- DAO Manager: Participate in decentralized organizations
Learning Resources:
Official Documentation:
Community:
Key Takeaways:
✅ You've learned:
- How to connect to a blockchain network
- How to query account information
- How to build interactive blockchain UIs
- Basic blockchain concepts through hands-on coding
✅ You can now:
- Create React components that interact with Paseo
- Understand blockchain data structures
- Debug connection and query issues
- Build upon this foundation for more complex projects
Common Next Questions:
Q: How do I get test tokens?
A: Visit the Paseo Faucet to get free test tokens.
Q: Can I use this on mainnet?
A: Yes, but change the network endpoint and be very careful with real tokens.
Q: How do I send transactions?
A: You'll need to integrate wallet signing - check the "Wallet Transfer" example in PAPI Simulator.
Q: What other networks can I use?
A: Try Westend, or even Polkadot mainnet by changing the descriptors and endpoints.
Troubleshooting Guide
Common Issues:
🔗 Connection Problems:
Error: Connection failed
- Check internet connection
- Try different RPC endpoint
- Verify network is running
📍 Address Format Issues:
Error: Invalid address format
- Ensure address starts with '5' for Paseo
- Check address length (47-48 characters)
- Use test accounts provided
⏰ Timeout Errors:
Error: Request timeout
- Network might be slow
- Try refreshing the page
- Use different RPC endpoint
💾 State Issues:
Error: Account not found
- Address might not exist on chain
- Try well-known test accounts
- Check if using correct network
Remember: This is a testnet - everything here is for learning and testing. No real money is involved, so feel free to experiment!
Congratulations! 🎉 You've just built your first blockchain React component. You're now ready to explore the exciting world of decentralized applications on the Polkadot ecosystem!