supabase
Handles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
App-Specific ZK Rollup — from STF definition to L1 verification
Full skill instructions
Specialized skill for creating and operating App-Specific ZK Rollups. Guides the entire pipeline: STF (State Transition Function) definition → PythonRuntime wrapping → Rollup lifecycle (setup → submit → batch → prove → L1 verify).
| File | Role |
|---|---|
ethclient/l2/rollup.py | Rollup orchestrator |
ethclient/l2/runtime.py | PythonRuntime — wraps callable as STF |
ethclient/l2/types.py | L2Tx, STFResult, Batch, BatchReceipt, L2State |
ethclient/l2/sequencer.py | Sequencer — mempool, nonce tracking, batch assembly |
ethclient/l2/prover.py | Groth16ProofBackend (pure Python) |
ethclient/l2/native_prover.py | NativeProverBackend (rapidsnark/snarkjs) |
ethclient/l2/interfaces.py | 4 pluggable interface definitions |
ethclient/l2/config.py | L2Config settings |
from ethclient.l2.types import L2Tx, STFResult
from ethclient.l2.rollup import Rollup
# 1. Define STF — write app logic as a pure Python function
def my_stf(state: dict, tx: L2Tx) -> STFResult:
op = tx.data.get("op")
if op == "increment":
state["counter"] = state.get("counter", 0) + 1
return STFResult(success=True, output={"counter": state["counter"]})
return STFResult(success=False, error=f"unknown op: {op}")
# 2. Create Rollup (callable STF is auto-wrapped with PythonRuntime)
rollup = Rollup(stf=my_stf)
# 3. Trusted Setup (ZK circuit + verifier deployment)
rollup.setup()
# 4. Submit transaction
USER = b"\x01" * 20
error = rollup.submit_tx(L2Tx(sender=USER, nonce=0, data={"op": "increment"}))
assert error is None
# 5. Produce batch + prove + submit to L1
batch = rollup.produce_batch()
receipt = rollup.prove_and_submit(batch)
assert receipt.verified
# 6. Check state
print(rollup.state.get("counter")) # 1
@dataclass
class L2Tx:
sender: bytes # Must be 20 bytes (ValueError if != 20)
nonce: int = 0 # >= 0 (ValueError if < 0)
data: dict = {} # Values must be str, int, bytes, or dict only
value: int = 0 # >= 0
tx_type: L2TxType = L2TxType.CALL # CALL=0, DEPOSIT=1, WITHDRAWAL=2
signature: bytes = b""
timestamp: int = 0 # 0 means auto-set to time.time()
Data serialization rules: Dict values use tag-based RLP encoding. \x01=int, \x02=bytes, \x03=str, \x04=nested dict. Keys are sorted alphabetically.
def counter_stf(state: dict, tx: L2Tx) -> STFResult:
state["counter"] = state.get("counter", 0) + 1
return STFResult(success=True)
from ethclient.l2.runtime import PythonRuntime
def my_validator(state: dict, tx: L2Tx) -> str | None:
if "op" not in tx.data:
return "missing 'op' field"
return None # pass
runtime = PythonRuntime(
func=my_stf,
validator=my_validator,
genesis={"counter": 0, "admin": b"\x01" * 20},
)
rollup = Rollup(stf=runtime)
@dataclass
class STFResult:
success: bool
output: dict = {} # App-specific return value on success
error: str | None = None # Error message on failure
None → STFResult(success=True)dict → STFResult(success=True, output=dict)STFResult(success=False, error=str(e))The Sequencer enforces strict nonce ordering:
# Correct pattern
rollup.submit_tx(L2Tx(sender=ALICE, nonce=0, data=...)) # OK
rollup.submit_tx(L2Tx(sender=ALICE, nonce=1, data=...)) # OK
rollup.submit_tx(L2Tx(sender=BOB, nonce=0, data=...)) # OK (different sender)
# Incorrect pattern
rollup.submit_tx(L2Tx(sender=ALICE, nonce=2, data=...)) # Error: nonce too high
rollup.submit_tx(L2Tx(sender=ALICE, nonce=0, data=...)) # Error: nonce too low
rollup = Rollup(stf=counter_stf)
rollup.setup()
ALICE = b"\x01" * 20
# Batch 0
rollup.submit_tx(L2Tx(sender=ALICE, nonce=0, data={"op": "inc"}))
rollup.submit_tx(L2Tx(sender=ALICE, nonce=1, data={"op": "inc"}))
batch0 = rollup.produce_batch()
receipt0 = rollup.prove_and_submit(batch0)
assert receipt0.verified
# Batch 1 — old_state_root == batch0.new_state_root (auto-chaining)
rollup.submit_tx(L2Tx(sender=ALICE, nonce=2, data={"op": "inc"}))
batch1 = rollup.produce_batch()
receipt1 = rollup.prove_and_submit(batch1)
assert receipt1.verified
assert batch1.old_state_root == batch0.new_state_root
from ethclient.l2.config import L2Config
config = L2Config(
name="my-rollup",
chain_id=42170,
max_txs_per_batch=32, # Circuit capacity (default 32)
batch_timeout=5, # Auto-seal interval in seconds (default 5)
mempool_max_size=10000, # Mempool size limit
state_backend="memory", # "memory" or "lmdb"
l1_backend="memory", # "memory" or "eth_rpc"
prover_backend="python", # "python" or "native"
hash_function="keccak256", # "keccak256" or "poseidon" (ZK-friendly)
l1_confirmations=2, # L1 block confirmations before finality (default 2)
rate_limit_rps=100, # RPC rate limit per IP (requests/sec)
max_request_size=1_048_576, # Max RPC request body size in bytes (1MB)
cors_origins=["*"], # CORS allowed origins
enable_metrics=True, # Enable /metrics endpoint
# LMDB settings
data_dir="./data/my-rollup",
# EthL1Backend settings
l1_rpc_url="https://...",
l1_private_key="hex...",
l1_chain_id=11155111,
# NativeProverBackend settings
prover_binary="rapidsnark",
prover_working_dir="./prover",
)
rollup = Rollup(stf=my_stf, config=config)
| Method | Returns | Description |
|---|---|---|
setup() | None | ZK circuit setup + verifier deployment. Must be called before prove |
submit_tx(tx) | str|None | Error string on failure, None on success |
produce_batch() | Batch | Process mempool + seal. RuntimeError if no txs |
prove_and_submit(batch) | BatchReceipt | Generate proof + submit to L1 (one step) |
prove_batch(batch) | Batch | Generate proof only (no L1 submission) |
submit_batch(batch) | BatchReceipt | Submit already-proven batch to L1 |
chain_info() | dict | name, chain_id, state_root, is_setup, pending_txs, etc. |
recover() | None | Crash recovery from LMDB WAL |
Groth16 proves execution-trace binding only — it guarantees that the claimed state transition matches the computation, but does not verify the correctness or safety of the STF logic itself.
The circuit enforces:
old_root × ∏ᵢ private_i ≡ new_root × tx_commitment (mod p)
This proves: "The prover knows private values that algebraically connect these three public inputs."
What the circuit does NOT enforce:
apply_tx(state, tx) was executed correctlynew_state_root is the honest result of applying the STF to old_state_rootAttack scenarios:
new_state_root, and generates a valid proof for (old_root, evil_new_root, tx_commitment). The L1 verifier accepts it.Defense layers:
| Layer | Mechanism | Trust Model |
|---|---|---|
| Groth16 proof | Prevents proof forgery for fixed public inputs | Trustless (mathematical) |
| Data availability | Tx data on L1 (calldata/blob) allows re-execution | DA assumption |
| Off-chain re-execution | Verifiers re-run STF on DA data, compare new_root | 1-of-N honest verifier |
| Social consensus | Community detects mismatch, responds | Governance |
This is effectively an optimistic verification model: the ZK proof guarantees execution-trace binding, but STF correctness relies on off-chain re-execution and data availability.
Every production STF should implement these safeguards:
def secure_stf(state: dict, tx: L2Tx) -> STFResult:
# 1. Transaction authentication — verify sender signature
if not verify_signature(tx):
return STFResult(success=False, error="invalid signature")
# 2. Replay protection — enforce strict nonce ordering
expected = state.get(f"nonce:{tx.sender.hex()}", 0)
if tx.nonce != expected:
return STFResult(success=False, error="invalid nonce")
# 3. Input validation — sanitize all data fields
if not validate_data(tx.data):
return STFResult(success=False, error="invalid data")
# 4. Access control — check permissions
if tx.data.get("op") == "admin_action":
if tx.sender != state.get("admin"):
return STFResult(success=False, error="unauthorized")
# 5. MEV prevention — use commit-reveal or ordering rules
# 6. Transaction expiry — reject stale transactions
if tx.timestamp > 0 and time.time() - tx.timestamp > 3600:
return STFResult(success=False, error="tx expired")
# ... actual app logic ...
state[f"nonce:{tx.sender.hex()}"] = tx.nonce + 1
return STFResult(success=True)
| Property | Guarantee | Limitation |
|---|---|---|
| Validity | Groth16 proof ensures state transition matches execution trace | Does not verify STF logic correctness |
| Data Availability | Batch data posted to L1 calldata/blobs | Blob data expires after ~18 days |
| Censorship Resistance | Force inclusion mechanism on L1 (50-block window) | Relies on L1 liveness; no forced execution |
| Value Safety | Escape hatch allows L1 fund recovery if L2 halts | Only recovers ETH deposits with value > 0 |
setup() required: Must be called before prove_and_submit(). Otherwise RuntimeErrormax_txs_per_batch - 1 txs. The last slot is reserved for balance factorhash_function="poseidon", state roots use Poseidon (~240 R1CS constraints) instead of keccak256 (~150,000). More ZK-friendly but less battle-testedl1_confirmations >= 2 for production. PoS Ethereum requires 2 epoch finality (~13 min); without sufficient confirmations, L1 reorgs may invalidate submitted batchesHandles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
Comprehensive guides and best practices for Neon Serverless Postgres, covering setup, connection methods, authentication, and platform APIs.
Guide for setting up and using Firebase Authentication. Use this skill when the user's app requires user sign-in, user management, or secure data access using auth rules.
A skill to evaluate how secure Firestore security rules are. Use this when Firestore security rules are updated to ensure that the generated rules are extremely secure and robust.
Official skill for integrating Firebase AI Logic (Gemini API) into web applications. Covers setup, multimodal inference, structured output, and security.
Complete Better Auth server and client setup with database adapters, session management, plugins, and security configuration.
Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. "deploy to vercel", "set up vercel", "add environment variables to vercel".
Complete Cloudflare platform integration with decision trees for compute, storage, AI, networking, security, and infrastructure-as-code.
Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.
Send, read, and manage Gmail messages, drafts, labels, and account settings.
Comprehensive website auditing across 230+ rules in 21 categories including SEO, performance, security, and accessibility.
Shared authentication, CLI syntax, and output formatting patterns for gws Google Workspace commands.
Discover your celebrity doppelgänger with StarByFace!
GeoSpy: Pricing, Features, FAQs, and Alternatives for AI Teams
Detect AI-generated voices to protect against audio fraud.
Ensure Your Content's Originality with AI Plagiarism Checker
Upscale images by 400% without quality loss
Protect your social media from copyright disputes
Accurately Detect AI-Generated Content with TheChecker.AI
Chrome extension that detects and flags AI-generated content