Panda Notebooks

Panda Notebooks are an interactive, browser-based Python environment for exploring and interacting with smart contracts on the Panda chain. Think Jupyter, but connected to a live blockchain.

Notebooks run at explorer.pandachain.io/notebook. No installation required -- Python executes directly in your browser via WebAssembly.

Quick Start

  1. Navigate to /notebook
  2. Click New Notebook
  3. Type Python code in a cell and press Shift+Enter to run
import numpy as np

data = np.random.default_rng(42).normal(0, 1, 1000)
print(f"Mean: {data.mean():.4f}")
print(f"Std:  {data.std():.4f}")

Output appears directly below the cell, just like Jupyter.

Cell Types

Code Cells

Write and execute Python. The runtime is Pyodide -- a full CPython 3.12 compiled to WebAssembly. numpy is pre-loaded; other packages can be imported on demand.

Keyboard shortcuts:

  • Shift+Enter -- Run the current cell
  • Standard Monaco editor shortcuts (Ctrl+Z undo, Ctrl+Shift+K delete line, etc.)

Markdown Cells

Write documentation using standard Markdown. Click a markdown cell to edit; click outside to render. Supports headers, lists, code blocks, links, and images.

Magic Cells

Use %panda magic commands for on-chain operations. See Magic Commands below.

Connecting to a Contract

The Connect button in the notebook toolbar lets you attach a deployed contract to your notebook session. Once connected, a contract variable is automatically injected into every code cell.

How to Connect

  1. Click the Connect button (link icon) in the toolbar
  2. Enter the contract address (e.g., 0x499b06f5f834b070c664ecd2100b00a00776cbb3)
  3. Optionally give it a name (e.g., "CompetitionHub")
  4. Click Connect

A badge showing the contract address appears in the toolbar. Click the disconnect icon to remove it.

Using the Contract Object

Once connected, the contract variable is available in all code cells:

# Query a read-only method
balance = await contract.query("balance_of", address="0xABC...")
print(f"Balance: {balance}")

# Get the full contract state
state = await contract.get_state()
print(state)

# Get contract events from a specific block
events = await contract.get_events(from_block=100)
for event in events:
    print(event)

Contract Class API

MethodDescriptionReturns
await contract.query(method, **kwargs)Call a @query method (read-only, free)Method return value
await contract.get_state()Get the full contract state dictionarydict
await contract.get_events(from_block=0)Get events emitted by the contractlist[dict]
await contract.view(method, **kwargs)Render a @view visualization inlineDisplays chart/SVG/HTML

All methods are async -- use await when calling them.

Manual Contract Creation

You can also create Contract objects manually without using the toolbar:

hub = Contract("0x499b06f5f834b070c664ecd2100b00a00776cbb3")
count = await hub.query("competition_count")
print(f"Total competitions: {count}")

# Work with multiple contracts at once
model = Contract("0x1234567890abcdef1234567890abcdef12345678")
state = await model.get_state()

Visualizations in Notebooks

There are two ways to create visualizations in notebooks: rendering on-chain @view methods, and building charts locally from contract data.

Rendering On-Chain Views

If a contract has @view decorated methods, you can render them directly:

# Using the Contract class
dashboard = Contract("0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18")
await dashboard.view("loss_chart")

Or with the magic command:

%panda view 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 loss_chart

The chart renders inline below the cell. Supported formats: Vega-Lite charts, SVG graphics, HTML widgets, and images. See the Visualizations guide for details on content types.

Building Charts from Contract Data

You can query contract state and build visualizations locally in Python. This is useful when you want to combine data from multiple contracts or do custom analysis.

Example: Charting competition data from the CompetitionHub

# Cell 1: Fetch competition data
hub = Contract("0x499b06f5f834b070c664ecd2100b00a00776cbb3")
count = await hub.query("competition_count")
print(f"Found {count} competitions")

competitions = []
for i in range(1, count + 1):
    comp = await hub.query("get_competition", competition_id=i)
    competitions.append(comp)
    print(f"  #{i}: {comp['title']} ({comp['metric_name']})")
# Cell 2: Analyze with numpy
import numpy as np

prize_pools = [c.get("prize_pool_points", 0) for c in competitions]
print(f"Total prize pool: {sum(prize_pools)} points")
print(f"Average prize: {np.mean(prize_pools):.0f} points")

Example: Using matplotlib

# Install matplotlib (first time only, takes a few seconds)
import micropip
await micropip.install("matplotlib")

import matplotlib
matplotlib.use("AGG")  # Required for Pyodide
import matplotlib.pyplot as plt
import io, base64

# Create a chart
fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(range(len(prize_pools)), prize_pools)
ax.set_xlabel("Competition")
ax.set_ylabel("Prize Pool (points)")
ax.set_title("Prize Pools by Competition")

# Display as base64 image
buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=100, bbox_inches="tight")
buf.seek(0)
img_b64 = base64.b64encode(buf.read()).decode()
print(f"<img src='data:image/png;base64,{img_b64}'/>")
plt.close(fig)

Example: Using pandas

import micropip
await micropip.install("pandas")
import pandas as pd

# Create a DataFrame from on-chain data
df = pd.DataFrame(competitions)
print(df[["title", "metric_name", "prize_pool_points", "competition_type"]].to_string())

Troubleshooting Visualizations

"address X is not a Panda contract" -- The contract address you're trying to connect to doesn't have a deployed Panda contract. Double-check the address. You can verify a contract exists with:

%panda state 0xYOUR_ADDRESS_HERE

If this returns an error, the contract isn't deployed at that address on the current network.

Views not rendering -- Make sure the contract method is decorated with both @query and @view. The method must return a valid content string (JSON for Vega-Lite, SVG markup, or HTML).

Magic Commands

Magic commands start with %panda and interact directly with the chain. Create a magic cell or type the command in a code cell.

%panda connect

Open the wallet connection dialog (MetaMask, WalletConnect, etc.):

%panda connect

%panda deploy [cell_index] [args]

Deploy a contract from another cell in the notebook:

%panda deploy 0
%panda deploy 2 {"initial_supply": 1000000}

The cell index refers to the 0-based position of the code cell containing the contract source. Requires a connected wallet.

%panda call [address] [method] [args]

Call a @call method on a deployed contract (state-modifying, requires wallet):

%panda call 0x742d...bD18 transfer {"to": "0xABC...", "amount": 100}

%panda query [address] [method] [args]

Call a @query method on a deployed contract (read-only, free):

%panda query 0x742d...bD18 get_count
%panda query 0x742d...bD18 balance_of {"address": "0xABC..."}

%panda state [address]

Get the full state of a deployed contract:

%panda state 0x742d...bD18

%panda events [address] [from_block]

Get events emitted by a contract:

%panda events 0x742d...bD18
%panda events 0x742d...bD18 1000

%panda view [address] [method] [args]

Render a @view visualization from a contract:

%panda view 0x742d...bD18 render_chart
%panda view 0x742d...bD18 metrics_dashboard {"epoch_range": [0, 100]}

The visualization renders inline. Supports Vega-Lite charts, SVG, HTML, and images.

%panda balance [address]

Check the native token balance of an address:

%panda balance 0x742d...bD18

%panda save / %panda load [txHash]

Save the current notebook or load one from a transaction hash:

%panda save
%panda load 0xabc123...

Wallet Integration

Notebooks inherit the global wallet connection from the explorer header. If you've already connected MetaMask or another wallet via the top-right button, it's automatically available for %panda deploy and %panda call commands.

You can also use %panda connect to trigger the wallet connection dialog from within a notebook.

Available Libraries

The Pyodide runtime includes:

  • numpy -- Pre-loaded, available immediately
  • Standard library -- json, math, collections, itertools, re, hashlib, etc.
  • Pyodide packages -- Additional packages can be loaded via micropip
import micropip
await micropip.install("pandas")
import pandas as pd

await micropip.install("matplotlib")
import matplotlib
matplotlib.use("AGG")
import matplotlib.pyplot as plt

await micropip.install("scipy")
import scipy

Note: The browser runtime is separate from the on-chain PandaVM. Libraries available in notebooks may differ from those available in deployed contracts. See the Contract Guide for the on-chain library list.

Saving and Loading

Notebooks are saved to your browser's localStorage automatically. The Save button in the toolbar triggers an immediate save. The "Unsaved" badge indicates pending changes.

Each notebook has a unique ID visible in the URL (/notebook/[id]). You can have multiple notebooks and switch between them from the notebook list.

Example: ML Model Analysis

Here's a complete workflow analyzing an on-chain ML model:

# Cell 1: Connect to a trained model contract
model = Contract("0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18")

# Cell 2: Check the model state
state = await model.get_state()
print(f"Trained: {state.get('is_trained', False)}")
print(f"Coefficients: {state.get('coefficients', [])}")

# Cell 3: Run predictions locally with numpy
import numpy as np

coefficients = np.array(state["coefficients"])
intercept = state["intercept"]

test_data = np.array([[1500], [2000], [2500], [3000]])
predictions = test_data @ coefficients + intercept
print("Predictions:", predictions.tolist())

# Cell 4: Compare with on-chain predictions
on_chain = await model.query("predict", features=test_data.tolist())
print("On-chain:", on_chain)
print("Match:", np.allclose(predictions.tolist(), on_chain))

Example: Competition Analytics

Query the Decentralized Kaggle CompetitionHub and analyze results:

# Cell 1: Load competition data
hub = Contract("0x499b06f5f834b070c664ecd2100b00a00776cbb3")
count = await hub.query("competition_count")

competitions = []
for i in range(1, count + 1):
    comp = await hub.query("get_competition", competition_id=i)
    competitions.append({"id": i, **comp})

print(f"Loaded {len(competitions)} competitions")
# Cell 2: Check leaderboard for a specific competition
board = await hub.query("leaderboard", competition_id=1, top_k=10)
for i, row in enumerate(board):
    addr = row["participant"][:10] + "..."
    print(f"  #{i+1}: {addr}  score={row['score']:.4f}")
# Cell 3: Render a @view chart (if the contract has one)
await hub.view("some_chart_method")

Next Steps