This guide walks you through setting up on-chain provenance anchoring for the Swarm Provenance CLI.
Blockchain anchoring registers Swarm hashes on the DataProvenance smart contract (Base chain), providing:
- Immutable proof that data was registered at a specific time
- Ownership tracking tied to an Ethereum address
- Transformation lineage linking original data to derived versions
- Access logging recording who accessed what data
Once a Swarm hash is anchored on-chain, anyone can independently verify its registration, ownership, and history.
┌──────────────────────────────────────────────────────────────────────────────┐
│ Blockchain Anchoring Flow │
└──────────────────────────────────────────────────────────────────────────────┘
Your CLI Base Chain Block Explorer
│ │ │
│ 1. Anchor hash │ │
│ (sign + send tx) │ │
│─────────────────────────────>│ │
│ │ │
│ 2. TX mined in block │ │
│<─────────────────────────────│ │
│ { tx_hash, block_number } │ │
│ │ │
│ 3. Verify on explorer │ │
│──────────────────────────────────────────────────────────-->│
│ │ │
│ Later: Query provenance │ │
│─────────────────────────────>│ │
│ │ │
│ { owner, timestamp, │ │
│ transformations, │ │
│ accessors, status } │ │
│<─────────────────────────────│ │
Key points:
- Each anchor operation is a blockchain transaction (requires ETH for gas)
- Read operations (get, verify) are free — no gas required
- Data is NOT stored on-chain; only the hash is registered
- The actual data remains on Swarm; the chain provides proof of registration
- Python 3.8+
- An Ethereum wallet (MetaMask, hardware wallet, etc.)
- Base Sepolia ETH (for gas fees on testnet)
Install the CLI with blockchain support:
pip install -e .[blockchain]This installs:
web3— Blockchain interaction (RPC calls, transaction sending)eth-account— Ethereum account management and transaction signing
Export your private key from MetaMask or another wallet. Be careful with private keys!
In MetaMask:
- Click the three dots menu
- Go to Account Details
- Click "Export Private Key"
- Enter your password
- Copy the private key (starts with 0x)
from eth_account import Account
account = Account.create()
print(f"Address: {account.address}")
print(f"Private key: {account.key.hex()}")Important: Store your private key securely. Never commit it to version control.
For development and testing, use Base Sepolia testnet. You need ETH for gas fees.
- Go to https://www.alchemy.com/faucets/base-sepolia
- Enter your wallet address
- Request test ETH
Alternative faucets:
- https://faucet.quicknode.com/base/sepolia
- https://www.coinbase.com/faucets/base-ethereum-goerli-faucet
Typical costs: An anchor transaction uses ~95,000 gas. At current Base gas prices, this costs fractions of a cent.
Add to your .env file:
# Your wallet private key (KEEP SECRET!)
PROVENANCE_WALLET_KEY=0x...your_private_key_here...
# Chain: base-sepolia (testnet) or base (mainnet)
CHAIN_NAME=base-sepolia
# Optional: Enable chain features by default
# CHAIN_ENABLED=true
# Optional: Custom RPC URL (uses preset if not set)
# CHAIN_RPC_URL=https://your-rpc-provider.com
# Optional: Custom contract address (uses preset if not set)
# CHAIN_CONTRACT=0x...
# Optional: Custom block explorer URL (uses preset if not set)
# CHAIN_EXPLORER_URL=https://your-explorer.com# Check wallet balance and chain info
swarm-prov-upload chain balance
# Expected output:
# Chain Wallet:
# ----------------------------------------
# Address: 0x742d...fE00
# Balance: 0.01 ETH
# Chain: base-sepolia
# Contract: 0xD4a7...f80a
#
# Get testnet ETH: https://www.alchemy.com/faucets/base-sepolia# Anchor a Swarm hash on-chain
swarm-prov-upload chain anchor <swarm_hash>
# Output:
# Anchored successfully!
# Hash: a028d937...
# Type: swarm-provenance
# Tx: 0xbb...
# Block: 12345679
# Gas: 95000
# Explorer: https://base-sepolia.blockscout.com/tx/0xbb...# Check if a hash is registered (exit code 0=yes, 1=no)
swarm-prov-upload chain verify <swarm_hash>
# Get the full provenance record
swarm-prov-upload chain get <swarm_hash># 1. Upload data to Swarm
swarm-prov-upload upload --file data.txt
# Output: Swarm Reference Hash: abc123...
# 2. Anchor the Swarm hash on-chain
swarm-prov-upload chain anchor abc123...
# 3. Later: verify the data is anchored
swarm-prov-upload chain verify abc123...
# 4. Get full provenance record
swarm-prov-upload chain get abc123... --jsonWhen you transform data (filter, anonymize, aggregate), link the original and derived hashes:
# Original data must be anchored first
swarm-prov-upload chain anchor <original_hash>
# Record the transformation
swarm-prov-upload chain transform <original_hash> <new_hash> --description "Anonymized PII fields"When you combine multiple data sources into one, record it as a merge transformation:
# All source hashes must be anchored first
swarm-prov-upload chain anchor <hash_a>
swarm-prov-upload chain anchor <hash_b>
# Record the merge (2-50 sources supported)
swarm-prov-upload chain merge <hash_a> <hash_b> <merged_hash> --description "Combined datasets"
# With a custom data type for the merged result
swarm-prov-upload chain merge <hash_a> <hash_b> <merged_hash> --type "combined-dataset" -d "Merged A+B"Trace data lineage back through transformations:
# Get full provenance record
swarm-prov-upload chain get <hash>
# Walk the full transformation chain (follows links to root)
swarm-prov-upload chain get <hash> --follow
# Limit traversal depth
swarm-prov-upload chain get <hash> --follow --depth 2
# JSON output for scripting
swarm-prov-upload chain get <hash> --follow --jsonLog that data was accessed (idempotent — safe to record multiple times):
swarm-prov-upload chain access <swarm_hash>swarm-prov-upload chain anchor <hash> --type "dataset"
swarm-prov-upload chain anchor <hash> --type "model-weights"All chain commands support --json for machine-readable output:
swarm-prov-upload chain get <hash> --json
swarm-prov-upload chain balance --json
swarm-prov-upload chain anchor <hash> --json# Use a specific chain
swarm-prov-upload --chain base chain balance
# Use a custom RPC endpoint
swarm-prov-upload --chain-rpc https://your-rpc.io chain balanceOn-chain records have a status field:
| Status | Value | Meaning |
|---|---|---|
ACTIVE |
0 | Data is live and accessible (default) |
RESTRICTED |
1 | Data access is restricted |
DELETED |
2 | Data has been logically deleted |
Status changes are recorded on-chain and can be audited.
When ready for production:
- Get real ETH on Base mainnet
- Update your
.env:
CHAIN_NAME=baseOr use the CLI flag:
swarm-prov-upload --chain base chain anchor <hash>Warning: Mainnet uses real funds. Start with small amounts and test thoroughly.
Install the blockchain extras:
pip install -e .[blockchain]Set the PROVENANCE_WALLET_KEY environment variable:
export PROVENANCE_WALLET_KEY=0x...Or add it to your .env file.
Check your RPC endpoint. The default Base Sepolia RPC (https://sepolia.base.org) is public and rate-limited. The CLI automatically tries fallback RPCs (base-sepolia-rpc.publicnode.com, base-sepolia.drpc.org) if the primary fails. For production, use a dedicated RPC provider:
export CHAIN_RPC_URL=https://base-sepolia.g.alchemy.com/v2/YOUR_KEY
# Or set multiple fallback URLs (comma-separated)
export CHAIN_RPC_URLS=https://fallback1.io,https://fallback2.ioCommon causes:
- Insufficient gas — Get more testnet ETH from the faucet
- Hash already registered — A hash can only be anchored once
- Not the owner — Only the data owner can modify records (unless using delegates)
- Invalid hash format — Must be 64 hex characters
Your RPC URL doesn't match the selected chain. Ensure CHAIN_NAME and CHAIN_RPC_URL are consistent:
base-sepoliaexpects chain ID 84532baseexpects chain ID 8453
Base mainnet contract is not yet deployed. Use base-sepolia for testing, or provide a custom address:
export CHAIN_CONTRACT=0x...your_contract_address...- Never commit private keys to version control
- Use a dedicated wallet for anchoring, not your main wallet
- Start with testnet before using real funds
- Use environment files (
.env) instead of command line arguments for secrets - Monitor gas costs — anchor transactions are cheap but add up at scale
- Use batch operations (
batch_anchor,batch_accessvia Python API) for efficiency
| Property | Value |
|---|---|
| Chain ID | 84532 |
| RPC URL | https://sepolia.base.org |
| Fallback RPCs | base-sepolia-rpc.publicnode.com, base-sepolia.drpc.org |
| Block Explorer | https://base-sepolia.blockscout.com |
| DataProvenance Contract | 0xD4a724CD7f5C4458cD2d884C2af6f011aC3Af80a |
| Deploy Block | 39,075,766 |
| Property | Value |
|---|---|
| Chain ID | 8453 |
| RPC URL | https://mainnet.base.org |
| Fallback RPCs | base-rpc.publicnode.com, base.drpc.org |
| Block Explorer | https://basescan.org |
| DataProvenance Contract | Not yet deployed |
| Property | Value |
|---|---|
| Chain ID | 31337 |
| RPC URL | http://127.0.0.1:8545 |
| DataProvenance Contract | 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 |
Use --chain localhost for local Hardhat development.