diff --git a/examples/beginner/01_wallet_balance.py b/examples/beginner/01_wallet_balance.py new file mode 100644 index 00000000..939c1d08 --- /dev/null +++ b/examples/beginner/01_wallet_balance.py @@ -0,0 +1,201 @@ +""" +PyCardano Beginner Example 1: Wallet Balance Checker + +This script demonstrates how to check the balance of any Cardano wallet +using PyCardano and the Blockfrost API. + +What you'll learn: +- Connecting to Cardano blockchain via Blockfrost +- Working with Cardano addresses +- Understanding UTXOs (Unspent Transaction Outputs) +- Converting between lovelace and ADA + +Requirements: +- pip install pycardano blockfrost-python +- Blockfrost API key from https://blockfrost.io (free) + +Author: stnltd - Cardano Builderthon 2024 +License: MIT +""" + +import os +from pycardano import Address, BlockFrostChainContext +from blockfrost import ApiUrls + +# ============================================================================== +# CONFIGURATION +# ============================================================================== + +# Get your FREE Blockfrost API key at: https://blockfrost.io +# Create a project for "Preprod Testnet" +BLOCKFROST_PROJECT_ID = os.getenv( + "BLOCKFROST_PROJECT_ID", + "preprodxxxxxxxxxxxxxxxxxxxxx" # Replace with your key +) + +# Example testnet address to check (you can replace with any testnet address) +# This address format starts with 'addr_test1' for testnet +WALLET_ADDRESS = "addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae" + +# ============================================================================== +# STEP 1: Setup Blockchain Connection +# ============================================================================== + +print("="*70) +print("PyCardano Wallet Balance Checker - Beginner Example") +print("="*70) +print() + +try: + # Create a connection to Cardano blockchain using Blockfrost + # Blockfrost is a hosted API service that handles blockchain queries + # We're using 'preprod' testnet - safe for testing without real money + context = BlockFrostChainContext( + project_id=BLOCKFROST_PROJECT_ID, + base_url=ApiUrls.preprod.value # Testnet URL + ) + print("✓ Connected to Cardano Preprod Testnet via Blockfrost") + print() + +except Exception as e: + print("✗ Failed to connect to Blockfrost") + print(f" Error: {e}") + print() + print("Common fixes:") + print(" 1. Check your BLOCKFROST_PROJECT_ID is correct") + print(" 2. Ensure you created a PREPROD testnet project (not mainnet)") + print(" 3. Visit https://blockfrost.io to get/verify your API key") + exit(1) + +# ============================================================================== +# STEP 2: Parse the Wallet Address +# ============================================================================== + +try: + # Convert the string address into a PyCardano Address object + # This validates the address format and extracts its components + address = Address.from_primitive(WALLET_ADDRESS) + print(f"✓ Address parsed successfully") + print(f" Address: {WALLET_ADDRESS[:20]}...{WALLET_ADDRESS[-10:]}") + print() + +except Exception as e: + print("✗ Invalid address format") + print(f" Error: {e}") + print() + print("Common fixes:") + print(" 1. Ensure address starts with 'addr_test1' for testnet") + print(" 2. Check for typos in the address") + print(" 3. Verify address is properly formatted (no spaces)") + exit(1) + +# ============================================================================== +# STEP 3: Query UTXOs (Unspent Transaction Outputs) +# ============================================================================== + +try: + # In Cardano, wallet balance is stored as UTXOs + # UTXO = Unspent Transaction Output (like receiving a check) + # Each UTXO represents some amount of ADA you can spend + utxos = context.utxos(address) + + print(f"✓ Found {len(utxos)} UTXO(s) at this address") + print() + +except Exception as e: + print("✗ Failed to query UTXOs") + print(f" Error: {e}") + print() + print("Common fixes:") + print(" 1. Check if address has ever received a transaction") + print(" 2. Verify Blockfrost API key has correct permissions") + print(" 3. Wait a moment and try again (network issue)") + exit(1) + +# ============================================================================== +# STEP 4: Calculate Total Balance +# ============================================================================== + +# Initialize counters +total_lovelace = 0 # 1 ADA = 1,000,000 lovelace (smallest unit) + +# Loop through each UTXO and sum up the amounts +for utxo in utxos: + # Each UTXO has an output that contains the amount + # The amount is in 'lovelace' (Cardano's smallest unit) + total_lovelace += utxo.output.amount.coin + +# Convert lovelace to ADA for human-readable format +# 1 ADA = 1,000,000 lovelace +total_ada = total_lovelace / 1_000_000 + +# ============================================================================== +# STEP 5: Display Results +# ============================================================================== + +print("="*70) +print("WALLET BALANCE") +print("="*70) +print(f"Address: {WALLET_ADDRESS}") +print(f"Total UTXOs: {len(utxos)}") +print(f"Total Balance: {total_ada:,.6f} ADA") +print(f" ({total_lovelace:,} lovelace)") +print("="*70) +print() + +# ============================================================================== +# STEP 6: Show Detailed UTXO Information (Optional) +# ============================================================================== + +if len(utxos) > 0: + print("UTXO DETAILS:") + print("-" * 70) + + for i, utxo in enumerate(utxos, 1): + # Convert this UTXO's lovelace to ADA + utxo_ada = utxo.output.amount.coin / 1_000_000 + + # Get transaction hash and output index + tx_hash = utxo.input.transaction_id + output_index = utxo.input.index + + print(f"UTXO #{i}:") + print(f" Amount: {utxo_ada:,.6f} ADA ({utxo.output.amount.coin:,} lovelace)") + print(f" Tx Hash: {str(tx_hash)[:20]}...{str(tx_hash)[-10:]}") + print(f" Output #: {output_index}") + print() + +else: + print("ℹ This address has no UTXOs (zero balance)") + print() + print("To receive testnet ADA:") + print(" 1. Visit: https://docs.cardano.org/cardano-testnet/tools/faucet/") + print(" 2. Enter your address") + print(" 3. Wait ~20 seconds for transaction to confirm") + print(" 4. Run this script again") + print() + +# ============================================================================== +# UNDERSTANDING THE OUTPUT +# ============================================================================== + +print("="*70) +print("UNDERSTANDING THE RESULTS") +print("="*70) +print() +print("UTXOs (Unspent Transaction Outputs):") +print(" - Think of them like separate checks in your wallet") +print(" - Each UTXO is from a previous transaction") +print(" - When you spend, you use entire UTXOs (not partial amounts)") +print() +print("Lovelace vs ADA:") +print(" - Lovelace is the smallest unit (like cents)") +print(" - 1 ADA = 1,000,000 lovelace") +print(" - Blockchain stores amounts in lovelace") +print() +print("Next Steps:") +print(" - Try checking your own wallet address") +print(" - Run 02_simple_transfer.py to send ADA") +print(" - Learn about transactions with 03_query_transaction.py") +print() +print("=" * 70) diff --git a/examples/beginner/02_simple_transfer.py b/examples/beginner/02_simple_transfer.py new file mode 100644 index 00000000..321dc06c --- /dev/null +++ b/examples/beginner/02_simple_transfer.py @@ -0,0 +1,201 @@ +""" +PyCardano Beginner Example 2: Simple ADA Transfer + +This script demonstrates how to send ADA from one wallet to another +using PyCardano and the Blockfrost API. + +IMPORTANT: This script works on TESTNET only. It uses test ADA with no value. + +What you'll learn: +- Building transactions +- Signing with payment keys +- Calculating fees +- Submitting transactions to blockchain + +Requirements: +- pip install pycardano blockfrost-python +- Blockfrost API key +- Payment signing key (.skey file) for sender wallet +- Testnet ADA in sender wallet + +Author: stnltd - Cardano Builderthon 2024 +License: MIT +""" + +import os +from pycardano import ( + Address, + BlockFrostChainContext, + PaymentSigningKey, + PaymentVerificationKey, + TransactionBuilder, + TransactionOutput, +) +from blockfrost import ApiUrls + +# Configuration +BLOCKFROST_PROJECT_ID = os.getenv("BLOCKFROST_PROJECT_ID", "preprodxxxxxxxxxxxxxxxxxxxxx") +SENDER_SKEY_PATH = "payment.skey" +RECIPIENT_ADDRESS = "addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae" +AMOUNT_TO_SEND_ADA = 10.0 + +def load_payment_keys(skey_path): + """Load payment signing key from file.""" + try: + with open(skey_path, 'r') as f: + skey_data = f.read() + payment_skey = PaymentSigningKey.from_json(skey_data) + payment_vkey = PaymentVerificationKey.from_signing_key(payment_skey) + return payment_skey, payment_vkey + except FileNotFoundError: + print(f"Key file not found: {skey_path}") + print() + print("Generate keys with cardano-cli:") + print(" cardano-cli address key-gen \\") + print(" --verification-key-file payment.vkey \\") + print(" --signing-key-file payment.skey") + exit(1) + except Exception as e: + print(f"Failed to load keys: {e}") + exit(1) + +print("=" * 70) +print("PyCardano Simple ADA Transfer - Beginner Example") +print("=" * 70) +print() + +try: + context = BlockFrostChainContext( + project_id=BLOCKFROST_PROJECT_ID, + base_url=ApiUrls.preprod.value + ) + print("Connected to Cardano Preprod Testnet") +except Exception as e: + print(f"Connection failed: {e}") + exit(1) + +print(f"Loading payment keys from: {SENDER_SKEY_PATH}") +payment_skey, payment_vkey = load_payment_keys(SENDER_SKEY_PATH) + +sender_address = Address(payment_part=payment_vkey.hash()) +print(f"Sender address: {sender_address}") +print() + +try: + recipient_address = Address.from_primitive(RECIPIENT_ADDRESS) + print(f"Recipient address parsed") + print(f" Recipient: {RECIPIENT_ADDRESS[:20]}...{RECIPIENT_ADDRESS[-10:]}") + print() +except Exception as e: + print(f"Invalid recipient address: {e}") + exit(1) + +print("Checking sender balance...") +try: + utxos = context.utxos(sender_address) + + if not utxos: + print("No UTXOs found in sender wallet (zero balance)") + print() + print("Get testnet ADA:") + print(" 1. Visit: https://docs.cardano.org/cardano-testnet/tools/faucet/") + print(f" 2. Enter address: {sender_address}") + print(" 3. Wait for confirmation (~20 seconds)") + exit(1) + + total_lovelace = sum(utxo.output.amount.coin for utxo in utxos) + total_ada = total_lovelace / 1_000_000 + + print(f"Sender balance: {total_ada:,.6f} ADA ({len(utxos)} UTXOs)") + print() + + if total_ada < (AMOUNT_TO_SEND_ADA + 2): + print(f"Insufficient balance") + print(f" Required: ~{AMOUNT_TO_SEND_ADA + 2} ADA (including fees)") + print(f" Available: {total_ada} ADA") + exit(1) + +except Exception as e: + print(f"Failed to check balance: {e}") + exit(1) + +print("Building transaction...") + +amount_lovelace = int(AMOUNT_TO_SEND_ADA * 1_000_000) + +try: + builder = TransactionBuilder(context) + builder.add_input_address(sender_address) + + output = TransactionOutput( + address=recipient_address, + amount=amount_lovelace + ) + builder.add_output(output) + + signed_tx = builder.build_and_sign( + signing_keys=[payment_skey], + change_address=sender_address + ) + + print("Transaction built and signed") + print() + print("Transaction Details:") + print(f" Amount: {AMOUNT_TO_SEND_ADA} ADA ({amount_lovelace:,} lovelace)") + print(f" Fee: {signed_tx.transaction_body.fee / 1_000_000:.6f} ADA") + print(f" Inputs: {len(signed_tx.transaction_body.inputs)} UTXO(s)") + print(f" Outputs: {len(signed_tx.transaction_body.outputs)} output(s)") + print() + +except Exception as e: + print(f"Failed to build transaction: {e}") + exit(1) + +print("Submitting transaction to blockchain...") +print("WARNING: This will actually send testnet ADA!") +print() + +# Uncomment to actually submit +""" +try: + tx_hash = context.submit_tx(signed_tx) + + print("=" * 70) + print("TRANSACTION SUBMITTED SUCCESSFULLY!") + print("=" * 70) + print() + print(f"Transaction Hash: {tx_hash}") + print() + print("View on explorer:") + print(f" https://preprod.cardanoscan.io/transaction/{tx_hash}") + print() + +except Exception as e: + print(f"Failed to submit transaction: {e}") + exit(1) +""" + +print("=" * 70) +print("TRANSACTION READY (Not Submitted in Example)") +print("=" * 70) +print() +print("To actually send this transaction:") +print(" 1. Uncomment the submission code in this script") +print(" 2. Run the script again") +print() +print("Transaction Preview:") +print(f" From: {sender_address}") +print(f" To: {recipient_address}") +print(f" Amount: {AMOUNT_TO_SEND_ADA} ADA") +print(f" Fee: ~{signed_tx.transaction_body.fee / 1_000_000:.6f} ADA") +print() +print("=" * 70) +print() +print("HOW TRANSACTIONS WORK:") +print(" 1. Input Selection: PyCardano selects UTXOs automatically") +print(" 2. Fee Calculation: Based on transaction size") +print(" 3. Change Output: Excess returns to sender") +print(" 4. Signing: Proves you own the UTXOs") +print(" 5. Submission: Sent to blockchain, confirms in ~20 seconds") +print() +print("=" * 70) \ No newline at end of file diff --git a/examples/beginner/03_query_transaction.py b/examples/beginner/03_query_transaction.py new file mode 100644 index 00000000..41f8ae1e --- /dev/null +++ b/examples/beginner/03_query_transaction.py @@ -0,0 +1,90 @@ +"""PyCardano Beginner Example 3: Query Transaction Details + +Look up and analyze transaction details using a transaction hash. + +Author: stnltd - Cardano Builderthon 2024 +""" + +import os +from pycardano import BlockFrostChainContext +from blockfrost import ApiUrls, BlockFrostApi +from datetime import datetime + +BLOCKFROST_PROJECT_ID = os.getenv("BLOCKFROST_PROJECT_ID", "preprodxxxxxxxxxxxxxxxxxxxxx") +TX_HASH = "e1b9d031f487c23e9b01fa2d67e3c454ab5c44c9e3b1bc3c4c8c7b6f5c3c6c6c" + +print("=" * 70) +print("PyCardano Transaction Query - Beginner Example") +print("=" * 70) +print() + +try: + context = BlockFrostChainContext(project_id=BLOCKFROST_PROJECT_ID, base_url=ApiUrls.preprod.value) + api = BlockFrostApi(project_id=BLOCKFROST_PROJECT_ID, base_url=ApiUrls.preprod.value) + print("Connected to Cardano Preprod Testnet") + print() +except Exception as e: + print(f"Connection failed: {e}") + exit(1) + +print(f"Querying transaction: {TX_HASH[:20]}...{TX_HASH[-10:]}") +print() + +try: + tx_info = api.transaction(TX_HASH) + print("Transaction found") + print() +except Exception as e: + print(f"Transaction not found: {e}") + print("Get a valid hash from: https://preprod.cardanoscan.io/") + exit(1) + +print("=" * 70) +print("TRANSACTION INFORMATION") +print("=" * 70) +print() +print(f"Transaction Hash: {tx_info.hash}") +print(f"Block: {tx_info.block}") +print(f"Block Height: {tx_info.block_height:,}") + +tx_time = datetime.fromtimestamp(tx_info.block_time) +print(f"Time: {tx_time.strftime('%Y-%m-%d %H:%M:%S UTC')}") +print() + +fee_ada = int(tx_info.fees) / 1_000_000 +print(f"Transaction Fee: {fee_ada:.6f} ADA") +print(f"Size: {tx_info.size:,} bytes") +print() + +print("=" * 70) +print(f"INPUTS ({len(tx_info.inputs)})") +print("=" * 70) +print() + +total_input = 0 +for i, inp in enumerate(tx_info.inputs, 1): + amount = int(inp.amount[0].quantity) / 1_000_000 + total_input += amount + print(f"Input #{i}: {amount:,.6f} ADA") + +print(f"\nTotal Input: {total_input:,.6f} ADA") +print() + +print("=" * 70) +print(f"OUTPUTS ({len(tx_info.outputs)})") +print("=" * 70) +print() + +total_output = 0 +for i, out in enumerate(tx_info.outputs, 1): + amount = int(out.amount[0].quantity) / 1_000_000 + total_output += amount + print(f"Output #{i}: {amount:,.6f} ADA") + +print(f"\nTotal Output: {total_output:,.6f} ADA") +print() + +print("=" * 70) +print("View on explorer:") +print(f"https://preprod.cardanoscan.io/transaction/{TX_HASH}") +print("=" * 70) diff --git a/examples/beginner/README.md b/examples/beginner/README.md new file mode 100644 index 00000000..1c3be8d0 --- /dev/null +++ b/examples/beginner/README.md @@ -0,0 +1,131 @@ +markdown# PyCardano Beginner Examples + +Complete beginner-friendly examples for developers new to Cardano and PyCardano. + +## Prerequisites + +- Python 3.8 or higher +- pip installed +- Basic understanding of blockchain concepts +- Blockfrost API key (free at https://blockfrost.io) + +## Setup + +1. Install PyCardano: +```bash +pip install pycardano blockfrost-python +``` + +2. Get Blockfrost API Key: + - Visit https://blockfrost.io + - Sign up for free account + - Create project for "Preprod Testnet" + - Copy your project ID + +3. Set your API key: +```bash +export BLOCKFROST_PROJECT_ID="your_project_id_here" +``` + +Or edit it directly in each script. + +## Examples Overview + +### 01_wallet_balance.py +Check the balance of any Cardano wallet address. Perfect first script to understand: +- Blockfrost API connection +- Address handling +- UTXO concept +- Lovelace to ADA conversion + +**Run:** +```bash +python 01_wallet_balance.py +``` + +### 02_simple_transfer.py +Send ADA from one wallet to another. Learn about: +- Transaction building +- Signing transactions +- Fee calculation +- Submitting to blockchain + +**Run:** +```bash +python 02_simple_transfer.py +``` + +### 03_query_transaction.py +Look up transaction details by hash. Understand: +- Transaction structure +- Inputs and outputs +- Metadata +- Confirmation status + +**Run:** +```bash +python 03_query_transaction.py +``` + +## Getting Testnet ADA + +To test transfers, you need testnet ADA (worthless test tokens): + +1. Visit: https://docs.cardano.org/cardano-testnet/tools/faucet/ +2. Enter your testnet address +3. Receive free test ADA + +## Common Issues + +### "Invalid API key" +- Check your Blockfrost project ID is correct +- Ensure you're using preprod testnet key, not mainnet +- Verify key is set in environment variable or script + +### "Address not found" +- Ensure you're using testnet address (starts with `addr_test1`) +- Check address is properly formatted +- Verify address has received at least one transaction + +### "Insufficient funds" +- Get testnet ADA from faucet (see above) +- Wait for faucet transaction to confirm (~20 seconds) +- Check balance before attempting transfer + +## Learn More + +- PyCardano Docs: https://pycardano.readthedocs.io/ +- Cardano Docs: https://docs.cardano.org/ +- Blockfrost Docs: https://docs.blockfrost.io/ + +## Contributing + +Found an issue or want to improve these examples? Please open an issue or PR! + +## License + +MIT License - Same as PyCardano + +--- + +**Created for Cardano Builderthon 2024** +**Author: stnltd (GitHub)** +``` + +--- + +### **Step 3: Paste into VS Code** + +1. **Click** in the editor area (right side where the blank file opened) +2. **Paste:** `Ctrl+V` +3. **Save:** `Ctrl+S` + +--- + +## ✅ CHECK + +**Left sidebar should now show:** +``` +BEGINNER + 📄 README.md + diff --git a/examples/beginner/requirements.txt b/examples/beginner/requirements.txt new file mode 100644 index 00000000..fc940893 --- /dev/null +++ b/examples/beginner/requirements.txt @@ -0,0 +1,2 @@ +pycardano>=0.11.0 +blockfrost-python>=0.6.0 \ No newline at end of file