API Reference
Panda extends the standard Ethereum JSON-RPC and Solana JSON-RPC APIs with custom methods for Python smart contract management. This document covers all Panda-specific API extensions.
Ethereum JSON-RPC Extensions
These methods are available on panda-geth RPC endpoints (default port 8545). They supplement the standard eth_*, net_*, and web3_* methods.
panda_deployContract
Deploy a Python smart contract to the Panda-enabled Ethereum chain.
Parameters:
| Name | Type | Description |
|---|---|---|
code | string | Python source code of the contract |
constructorArgs | object | Arguments to pass to the initialize() method (optional) |
from | string | Address of the deployer |
gas | string | Gas limit (hex-encoded) |
gasPrice | string | Gas price in wei (hex-encoded, optional for EIP-1559) |
Returns: Transaction hash (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_deployContract",
"params": [{
"code": "from panda import contract, call, query\n\n@contract\nclass Counter:\n class State:\n count: int = 0\n\n @call\n def increment(self, ctx, amount: int = 1):\n self.state.count += amount\n\n @query\n def get_count(self) -> int:\n return self.state.count",
"constructorArgs": {},
"from": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"gas": "0x2DC6C0"
}],
"id": 1
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0xabc123def456789012345678901234567890123456789012345678901234abcd"
}
panda_callContract
Call a state-mutating (@call) method on a deployed contract. Produces a transaction.
Parameters:
| Name | Type | Description |
|---|---|---|
address | string | Contract address |
method | string | Method name to call |
args | object | Method arguments as key-value pairs |
from | string | Caller address |
gas | string | Gas limit (hex-encoded) |
Returns: Transaction hash (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_callContract",
"params": [{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"method": "increment",
"args": {"amount": 5},
"from": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"gas": "0x100000"
}],
"id": 2
}
// Response
{
"jsonrpc": "2.0",
"id": 2,
"result": "0xdef456789012345678901234567890123456789012345678901234567890abcd"
}
panda_queryContract
Call a read-only (@query) method on a deployed contract. Does not produce a transaction. No gas cost.
Parameters:
| Name | Type | Description |
|---|---|---|
address | string | Contract address |
method | string | Method name to query |
args | object | Method arguments (optional) |
blockNumber | string | Block number to query at (default: "latest") |
Returns: The method's return value (JSON-encoded)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_queryContract",
"params": [{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"method": "get_count",
"args": {}
}],
"id": 3
}
// Response
{
"jsonrpc": "2.0",
"id": 3,
"result": 5
}
panda_getContractState
Retrieve the full state of a deployed contract.
Parameters:
| Name | Type | Description |
|---|---|---|
address | string | Contract address |
blockNumber | string | Block number (default: "latest") |
Returns: Object containing all state fields and their current values.
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_getContractState",
"params": ["0x1234567890abcdef1234567890abcdef12345678", "latest"],
"id": 4
}
// Response
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"count": 5,
"owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
}
}
panda_getContractCode
Retrieve the Python source code of a deployed contract.
Parameters:
| Name | Type | Description |
|---|---|---|
address | string | Contract address |
Returns: Python source code (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_getContractCode",
"params": ["0x1234567890abcdef1234567890abcdef12345678"],
"id": 5
}
// Response
{
"jsonrpc": "2.0",
"id": 5,
"result": "from panda import contract, call, query\n\n@contract\nclass Counter:\n class State:\n count: int = 0\n..."
}
panda_lintContract
Lint a Python smart contract without deploying it. Returns detailed analysis results.
Parameters:
| Name | Type | Description |
|---|---|---|
code | string | Python source code to lint |
Returns: Lint result object
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_lintContract",
"params": ["from panda import contract, call\nimport os\n\n@contract\nclass Bad:\n class State:\n x: int = 0\n @call\n def hack(self, ctx):\n os.system('whoami')"],
"id": 6
}
// Response
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"valid": false,
"errors": [
{
"line": 2,
"column": 0,
"severity": "error",
"code": "E001",
"message": "Forbidden import: 'os' is not allowed in Panda contracts"
},
{
"line": 10,
"column": 8,
"severity": "error",
"code": "E002",
"message": "Forbidden function call: 'os.system' is not allowed"
}
],
"warnings": [],
"contract_info": {
"name": "Bad",
"state_fields": [{"name": "x", "type": "int", "default": "0"}],
"call_methods": ["hack"],
"query_methods": []
}
}
}
panda_estimateGas
Estimate the gas cost of calling a contract method.
Parameters:
| Name | Type | Description |
|---|---|---|
address | string | Contract address |
method | string | Method name |
args | object | Method arguments |
from | string | Caller address (optional) |
Returns: Estimated gas object
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_estimateGas",
"params": [{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"method": "train",
"args": {
"features": [[1.0, 2.0], [3.0, 4.0]],
"labels": [1.0, 2.0]
}
}],
"id": 7
}
// Response
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"pandaComputeUnits": 142857,
"evmGasEquivalent": "0x15F391",
"breakdown": {
"bytecodeInstructions": 98432,
"numpyOperations": 34210,
"stateReads": 5120,
"stateWrites": 5095
}
}
}
panda_getProof
Retrieve the ZK proof for a transaction that required one.
Parameters:
| Name | Type | Description |
|---|---|---|
txHash | string | Transaction hash |
Returns: ZK proof object
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_getProof",
"params": ["0xabc123..."],
"id": 8
}
// Response
{
"jsonrpc": "2.0",
"id": 8,
"result": {
"proofType": "validity",
"backend": "risc_zero",
"proof": "0x...",
"publicInputs": {
"contractHash": "0x...",
"inputStateRoot": "0x...",
"outputStateRoot": "0x...",
"callDataHash": "0x..."
},
"verified": true,
"generationTimeMs": 45230,
"proofSizeBytes": 1024
}
}
panda_verifyProof
Verify a ZK proof on-chain.
Parameters:
| Name | Type | Description |
|---|---|---|
proof | object | Proof object (as returned by panda_getProof) |
Returns: boolean -- true if the proof is valid
Example:
// Request
{
"jsonrpc": "2.0",
"method": "panda_verifyProof",
"params": [{"proofType": "validity", "proof": "0x...", "publicInputs": {}}],
"id": 9
}
// Response
{
"jsonrpc": "2.0",
"id": 9,
"result": true
}
Solana RPC Extensions
These methods are available on panda-solana RPC endpoints (default port 8899). They supplement the standard Solana RPC methods.
pandaDeployProgram
Deploy a Python smart contract as a Solana program.
Parameters:
| Name | Type | Description |
|---|---|---|
code | string | Python source code |
config | object | Deployment configuration (optional) |
payer | string | Base58-encoded payer public key |
Returns: Transaction signature (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "pandaDeployProgram",
"params": [{
"code": "from panda import contract, call, query\n\n@contract\nclass Counter:\n class State:\n count: int = 0\n\n @call\n def increment(self, ctx, amount: int = 1):\n self.state.count += amount\n\n @query\n def get_count(self) -> int:\n return self.state.count",
"payer": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}],
"id": 1
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "5VERv8NMhRYqCq2U7HDj2cyPNUNbqNpKEjYaA8QR3JcNXEQMpfGzNxsK3rbPY7u8XbUxfJ4S8WT2aNzNKwSzqAmp"
}
pandaCallProgram
Call a state-mutating method on a deployed Panda program.
Parameters:
| Name | Type | Description |
|---|---|---|
programId | string | Base58-encoded program public key |
method | string | Method name |
args | object | Method arguments |
payer | string | Base58-encoded payer public key |
Returns: Transaction signature (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "pandaCallProgram",
"params": [{
"programId": "PNDAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"method": "increment",
"args": {"amount": 10},
"payer": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}],
"id": 2
}
// Response
{
"jsonrpc": "2.0",
"id": 2,
"result": "2xNweLHLqrbx4zo1waDvgWNdFC25MHHB9BfAENMPYYBA1vKn5wLJ2dYBHXZbcm4c6U5xNXV3t4B7gJhVmhx2W8Q"
}
pandaQueryProgram
Query a read-only method on a deployed Panda program. Uses transaction simulation -- no transaction is submitted.
Parameters:
| Name | Type | Description |
|---|---|---|
programId | string | Base58-encoded program public key |
method | string | Method name |
args | object | Method arguments (optional) |
Returns: The method's return value (JSON-encoded)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "pandaQueryProgram",
"params": [{
"programId": "PNDAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"method": "get_count"
}],
"id": 3
}
// Response
{
"jsonrpc": "2.0",
"id": 3,
"result": 10
}
pandaGetProgramState
Retrieve the full state of a deployed Panda program by reading its state PDA.
Parameters:
| Name | Type | Description |
|---|---|---|
programId | string | Base58-encoded program public key |
Returns: Object with all state fields
Example:
// Request
{
"jsonrpc": "2.0",
"method": "pandaGetProgramState",
"params": ["PNDAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
"id": 4
}
// Response
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"count": 10,
"owner": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}
}
pandaGetProgramCode
Retrieve the Python source code of a deployed Panda program.
Parameters:
| Name | Type | Description |
|---|---|---|
programId | string | Base58-encoded program public key |
Returns: Python source code (string)
Example:
// Request
{
"jsonrpc": "2.0",
"method": "pandaGetProgramCode",
"params": ["PNDAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
"id": 5
}
// Response
{
"jsonrpc": "2.0",
"id": 5,
"result": "from panda import contract, call, query\n\n@contract\nclass Counter:\n..."
}
pandaLintContract
Lint a contract on the Solana side. Identical behavior to the Ethereum variant.
Parameters:
| Name | Type | Description |
|---|---|---|
code | string | Python source code |
Returns: Lint result object (same format as panda_lintContract)
Error Codes
Both Ethereum and Solana RPC extensions use consistent error codes:
| Code | Name | Description |
|---|---|---|
| -32000 | EXECUTION_ERROR | Contract execution failed (Python exception) |
| -32001 | LINT_ERROR | Contract failed linting/validation |
| -32002 | OUT_OF_GAS | Execution exceeded gas limit |
| -32003 | TIMEOUT | Execution exceeded wall-clock timeout |
| -32004 | SANDBOX_VIOLATION | Contract attempted forbidden operation |
| -32005 | STATE_ERROR | State read/write failure |
| -32006 | NOT_A_PANDA_CONTRACT | Address is not a Panda contract |
| -32007 | METHOD_NOT_FOUND | Specified method does not exist on contract |
| -32008 | PROOF_ERROR | ZK proof generation or verification failed |
| -32009 | DEPLOYMENT_ERROR | Contract deployment failed |
| -32010 | DETERMINISM_ERROR | Non-deterministic execution detected |
Example error response:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Contract execution failed",
"data": {
"type": "AssertionError",
"message": "Insufficient balance",
"traceback": " File \"contract.py\", line 15, in transfer\n assert balance >= amount, \"Insufficient balance\"",
"gasUsed": 12345
}
}
}
WebSocket Subscriptions
Ethereum WebSocket (port 8546)
Subscribe to Panda-specific events via WebSocket:
// Subscribe to all Panda contract events
{
"jsonrpc": "2.0",
"method": "eth_subscribe",
"params": ["logs", {
"topics": ["0xPANDA_EVENT_TOPIC"]
}],
"id": 1
}
// Subscribe to specific contract events
{
"jsonrpc": "2.0",
"method": "eth_subscribe",
"params": ["logs", {
"address": "0x1234567890abcdef1234567890abcdef12345678",
"topics": ["0xPANDA_EVENT_TOPIC"]
}],
"id": 2
}
Solana WebSocket (port 8900)
Use standard Solana WebSocket subscriptions to watch Panda program activity:
// Subscribe to program account changes
{
"jsonrpc": "2.0",
"method": "programSubscribe",
"params": [
"PNDAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
{"encoding": "jsonParsed"}
],
"id": 1
}
Rate Limits
Default rate limits (configurable per deployment):
| Endpoint Type | Limit |
|---|---|
Query methods (panda_queryContract, pandaQueryProgram) | 100 req/sec |
Transaction methods (panda_callContract, pandaCallProgram) | 20 req/sec |
Lint (panda_lintContract) | 10 req/sec |
| Gas estimation | 50 req/sec |
| State queries | 100 req/sec |
| WebSocket subscriptions | 50 concurrent per connection |
SDK Integration
Python
import requests
import json
class PandaClient:
def __init__(self, rpc_url="http://localhost:8545"):
self.rpc_url = rpc_url
self._id = 0
def _call(self, method, params):
self._id += 1
response = requests.post(self.rpc_url, json={
"jsonrpc": "2.0",
"method": method,
"params": [params] if isinstance(params, dict) else params,
"id": self._id,
})
result = response.json()
if "error" in result:
raise Exception(f"RPC Error: {result['error']['message']}")
return result["result"]
def deploy(self, code, from_addr, gas="0x2DC6C0"):
return self._call("panda_deployContract", {
"code": code,
"from": from_addr,
"gas": gas,
})
def call(self, address, method, args=None, from_addr=None, gas="0x100000"):
return self._call("panda_callContract", {
"address": address,
"method": method,
"args": args or {},
"from": from_addr,
"gas": gas,
})
def query(self, address, method, args=None):
return self._call("panda_queryContract", {
"address": address,
"method": method,
"args": args or {},
})
def get_state(self, address):
return self._call("panda_getContractState", [address, "latest"])
def lint(self, code):
return self._call("panda_lintContract", [code])
# Usage
client = PandaClient("http://localhost:30545")
tx = client.deploy(open("counter.py").read(), "0x742d35Cc...")
state = client.get_state("0x1234...")
result = client.query("0x1234...", "get_count")
JavaScript / TypeScript
import { ethers } from "ethers";
class PandaProvider {
private provider: ethers.JsonRpcProvider;
constructor(rpcUrl: string = "http://localhost:8545") {
this.provider = new ethers.JsonRpcProvider(rpcUrl);
}
async deploy(code: string, from: string): Promise<string> {
return this.provider.send("panda_deployContract", [{
code,
from,
gas: "0x2DC6C0",
}]);
}
async call(address: string, method: string, args: object = {}): Promise<string> {
return this.provider.send("panda_callContract", [{
address,
method,
args,
from: await this.provider.getSigner().then(s => s.getAddress()),
gas: "0x100000",
}]);
}
async query(address: string, method: string, args: object = {}): Promise<any> {
return this.provider.send("panda_queryContract", [{
address,
method,
args,
}]);
}
async getState(address: string): Promise<object> {
return this.provider.send("panda_getContractState", [address, "latest"]);
}
async getCode(address: string): Promise<string> {
return this.provider.send("panda_getContractCode", [address]);
}
async lint(code: string): Promise<object> {
return this.provider.send("panda_lintContract", [code]);
}
}