Platform Operations
Ongoing management operations for Push Oracle networks including governance actions, node management, funding, and reward collection.
Prerequisites
- Deployed oracle (Deployment Guide)
oracle-owner-actions.ymlconfigured correctly- Access to oracle owner wallet (or multisig parties)
- Reference script created (recommended for lower fees)
Configuration File
All operations use oracle-owner-actions.yml. Ensure it contains:
# Network and connectivity
network: "testnet"
chain_query:
blockfrost:
api_url: "https://cardano-preprod.blockfrost.io/api"
project_id: "your_project_id"
# Oracle details
oracle_owner:
oracle_addr: "addr_test1wp..."
minting_nft_hash: "1f2d9f..."
c3_token_hash: "436941..."
c3_token_name: "C3"
script_start_slot: 28585322
reference_script_input: "tx_hash#index" # Optional
oracle_platform:
multisig_pkhs: ["vkh1", "vkh2"]
multisig_threshold: 1Governance Operations
All governance operations require platform authorization. For multisig (threshold > 1), transactions must be signed by required parties.
Update Oracle Settings
Modify oracle parameters after deployment. Settings are stored in AggState UTXO datum.
Command:
python scripts/oracle_owner_actions.py mk-edit-settingsInteractive workflow:
Current settings: OracleSettings(...)
0: os_minimum_deposit
1: os_updated_nodes
2: os_updated_node_time
3: os_aggregate_time
4: os_aggregate_change
5: os_node_fee_price.node_fee
6: os_node_fee_price.aggregate_fee
7: os_node_fee_price.platform_fee
8: os_iqr_multiplier
9: os_divergence
10: os_aggregate_valid_range
Please enter the setting number or 'q' to finish: 1
Enter a new value for os_updated_nodes: 7000
Please enter the setting number or 'q' to finish: q
Enter a platform pkh or 'q' to quit: 1a550d57572584e1add125b5712f709ac3b9828ad86581a4759022ba
Enter a platform pkh or 'q' to quit: q
Created edit settings tx id: <tx_id>
Tx written to file edit_settings.cborWhat you can update:
| Setting | Description | Constraints |
|---|---|---|
os_minimum_deposit | Minimum C3 in oracle | > 0 |
os_updated_nodes | Required nodes % | ≤ 10000 (100%) |
os_updated_node_time | Node update validity | > 0 ms |
os_aggregate_time | Min aggregation interval | > 0 ms |
os_aggregate_change | Min price change % | ≤ 10000 (100%) |
os_node_fee_price.* | Fee structure | ≥ 0 |
os_iqr_multiplier | Outlier detection | > 0 |
os_divergence | Max divergence % | ≤ 10000 (100%) |
os_aggregate_valid_range | Aggregation validity | > 0 ms |
Important: Cannot update os_node_list using this command. Use mk-add-nodes or mk-remove-nodes instead.
Single signature workflow:
# Build, sign, and submit automatically
python scripts/oracle_owner_actions.py mk-edit-settingsMultisig workflow:
# Party 1: Build transaction
python scripts/oracle_owner_actions.py mk-edit-settings
# Output: edit_settings.cbor
# Party 2-N: Sign
python scripts/oracle_owner_actions.py sign-tx
# Enter filename: edit_settings.cbor
# Do you want to sign? y
# Final party: Submit
python scripts/oracle_owner_actions.py sign-and-submit-tx
# Enter filename: edit_settings.cbor
# Do you want to sign and submit? yAdd Nodes
Add new node operators to the oracle network.
Command:
python scripts/oracle_owner_actions.py mk-add-nodesInteractive workflow:
Enter a node to add or 'q' to quit: 018ab1dd5f33ca2e0ae6ccb694ea379d841bf5f4d2d5756452a2117d
Enter a node to add or 'q' to quit: f3a8e5c9b2d4a7e6c1f8d3b5a9e2c7f4d1a8b3e5c2f7d4a1b8e3c5f2
Enter a node to add or 'q' to quit: q
Enter a platform pkh or 'q' to quit: 1a550d57572584e1add125b5712f709ac3b9828ad86581a4759022ba
Enter a platform pkh or 'q' to quit: q
Created add nodes tx id: <tx_id>
Tx written to file add_nodes.cborThe SDK:
- Validates node VKHs (hex format, not already in oracle)
- Creates new NodeFeed UTXOs for each node
- Mints NodeFeed NFTs (C3NF tokens)
- Updates AggState datum with new node list
- Updates RewardState datum (adds reward entries with 0 balance)
- Builds transaction
Node VKH format:
- Must be hex-encoded verification key hash
- NOT bech32 format (no
addr_vkh1prefix) - Get from:
cardano-cli address key-hash --payment-verification-key-file node.vkey
Multisig workflow:
# Party 1: Build
python scripts/oracle_owner_actions.py mk-add-nodes
# Output: add_nodes.cbor
# Parties 2-N: Sign and submit
python scripts/oracle_owner_actions.py sign-tx
python scripts/oracle_owner_actions.py sign-and-submit-txVerify nodes added:
# Count NodeFeed NFTs
curl "<oracle_address_utxos>" | jq '[.[] | select(.amount[] | .unit | contains("NodeFeed"))] | length'Remove Nodes
Remove node operators from the oracle network. Accumulated rewards are paid out before removal.
Command:
python scripts/oracle_owner_actions.py mk-remove-nodesInteractive workflow:
Enter a node to remove or 'q' to quit: 018ab1dd5f33ca2e0ae6ccb694ea379d841bf5f4d2d5756452a2117d
Enter a node to remove or 'q' to quit: q
Enter a platform pkh or 'q' to quit: 1a550d57572584e1add125b5712f709ac3b9828ad86581a4759022ba
Enter a platform pkh or 'q' to quit: q
Created remove nodes tx id: <tx_id>
Tx written to file remove_nodes.cborThe SDK:
- Validates nodes exist in oracle
- Calculates accumulated rewards for each node
- Creates payout outputs (if rewards > 0)
- Burns NodeFeed NFTs
- Updates AggState datum (removes from node list)
- Updates RewardState datum (removes reward entries)
- Builds transaction
Important: If a node has accumulated rewards, they will be automatically paid to the node’s address before removal. The node does NOT need to manually collect first.
Multisig workflow:
# Party 1: Build
python scripts/oracle_owner_actions.py mk-remove-nodes
# Output: remove_nodes.cbor
# Parties 2-N: Sign and submitVerify nodes removed:
# Count NodeFeed NFTs (should decrease)
curl "<oracle_address_utxos>" | jq '[.[] | select(.amount[] | .unit | contains("NodeFeed"))] | length'Funding Operations
Add Funds
Add C3 tokens to the oracle to pay for future node updates and aggregations.
Command:
python scripts/oracle_owner_actions.py add-funds <amount>Example:
# Add 1000 C3 tokens (assuming 6 decimals)
python scripts/oracle_owner_actions.py add-funds 1000000000The transaction:
- Consumes AggState UTXO
- Adds C3 tokens from your wallet
- Outputs updated AggState UTXO with increased C3 balance
- Requires only platform wallet signature (no multisig needed)
Verify funding:
# Query AggState UTXO
curl "<oracle_address_utxos>" | \
jq '.[] | select(.amount[] | .unit | contains("AggState"))'
# Check C3 token amountWhen to add funds:
- Before deploying new nodes (to ensure fees can be paid)
- When C3 balance is running low
- To support higher aggregation frequency
Estimate required funding:
Funds needed = (expected_aggregations × total_fees_per_aggregation)
Where:
total_fees = (node_fee × avg_participating_nodes) + aggregate_fee + platform_feeReward Operations
Platform Collect
Collect accumulated platform fees from the oracle’s RewardState.
Command:
python scripts/oracle_owner_actions.py mk-platform-collectInteractive workflow:
Enter a platform pkh or 'q' to quit: 1a550d57572584e1add125b5712f709ac3b9828ad86581a4759022ba
Enter a platform pkh or 'q' to quit: q
Enter withdrawal address: addr_test1vr...
Created platform collect tx id: <tx_id>
Tx written to file platform_collect.cborThe SDK:
- Queries RewardState UTXO
- Checks
platform_rewardbalance - Creates output sending platform fees to withdrawal address
- Updates RewardState (sets
platform_reward = 0) - Requires AggState UTXO as reference input
- Builds transaction
Multisig workflow:
# Party 1: Build
python scripts/oracle_owner_actions.py mk-platform-collect
# Output: platform_collect.cbor
# Parties 2-N: Sign and submitWhen to collect:
- Periodically as platform fees accumulate
- Before oracle removal (to claim final fees)
- When needing C3 tokens for other purposes
Node Collect (Node Operator Operation)
Node operators collect their accumulated rewards. This is NOT a platform operation but is included for completeness.
How it works:
- Node operator queries their accumulated rewards in RewardState
- Calls
node.collect(reward_address) - Receives C3 tokens to specified address
- RewardState updated (operator’s reward set to 0)
Example (from node operator perspective):
from charli3_offchain_core.node import Node
# Initialize node instance
node = Node(
network=network,
chain_query=chain_query,
signing_key=node_signing_key,
verification_key=node_verification_key,
# ... other parameters
)
# Collect rewards to node operator's address
await node.collect(node.address)Platform operators don’t typically run this command, but may assist node operators if needed.
Oracle Lifecycle Management
Pause Oracle (Not implemented in Push Oracle)
Push Oracle doesn’t have a native pause mechanism. To halt operations:
- Coordinate with node operators to stop submitting updates
- Stop collecting platform fees (don’t run aggregations)
- Wait for pending aggregations to complete
- Don’t add new funding
This is an operational pause, not an on-chain mechanism.
Close Oracle
Permanently remove the oracle and recover locked ADA and remaining C3 tokens.
Command:
python scripts/oracle_owner_actions.py mk-oracle-closeInteractive workflow:
Enter a platform pkh or 'q' to quit: 1a550d57572584e1add125b5712f709ac3b9828ad86581a4759022ba
Enter a platform pkh or 'q' to quit: q
Enter the withdrawal address for the C3 tokens: addr_test1vr...
Select an option:
TO_NODES: Pay unclaimed C3 tokens to node operators and collect the network remainder
TO_ONE_ADDRESS: Collect all C3 tokens in the network, including unclaimed C3 tokens
Option [TO_NODES]: TO_NODES
Created oracle close tx id: <tx_id>
Tx written to file oracle_close.cborDisbursement options:
TO_NODES:
- Pays each node operator their accumulated rewards
- Collects remaining C3 from AggState, OracleFeed
- Sends platform fees to withdrawal address
- Fair distribution to all parties
TO_ONE_ADDRESS:
- Collects ALL C3 tokens from oracle (including unclaimed node rewards)
- Sends to single withdrawal address
- Use with caution - nodes lose their rewards
The transaction:
- Consumes all oracle UTXOs (AggState, OracleFeed, RewardState, all NodeFeeds)
- Burns all oracle NFTs
- Distributes C3 tokens according to option
- Returns locked ADA to platform wallet
- Oracle becomes permanently non-functional
Important: This operation is irreversible. Ensure all parties have been notified and rewards collected if using TO_NODES option.
Multisig workflow:
# Party 1: Build
python scripts/oracle_owner_actions.py mk-oracle-close
# Output: oracle_close.cbor
# Parties 2-N: Sign and submitVerify oracle removed:
# Query oracle address (should have no UTXOs with oracle NFTs)
curl "<oracle_address_utxos>" | \
jq '[.[] | select(.amount[] | .unit | contains("C3"))]'
# Should return empty or only non-NFT UTXOsReference Script Management
Create Reference Script
Creates a reference script UTXO to reduce transaction costs.
Command:
python scripts/oracle_owner_actions.py create-reference-script \
--script-path ./tmp/OracleV3.plutusThe transaction:
- Creates UTXO at oracle address with oracle script attached
- Locks ~66 ADA (recoverable on oracle close)
- Allows validators to be referenced (not included) in txs
- Significantly reduces transaction fees
When to create:
- After oracle deployment (if not created during deployment)
- If reference script UTXO was accidentally spent
- When upgrading to new oracle script version
Verify reference script:
cardano-cli query utxo --address <oracle_address> --testnet-magic 1
# Look for UTXO with large size and script hashTransaction Management (Multisig)
Sign Transaction
Add your signature to a pending multisig transaction.
Command:
python scripts/oracle_owner_actions.py sign-txInteractive workflow:
Enter filename containing tx cbor: platform_collect.cbor
Were you the one who created and balanced this tx with your own inputs? y/n: n
Transaction{
body: TransactionBody{...}
witness_set: TransactionWitnessSet{
vkey_witnesses: [VKeyWitness(...), ...]
}
}
Please review contents of the above tx once again manually before signing
Do you want to sign this tx? y/n: y
Tx signed
Tx written to file platform_collect.cborImportant:
- Answer ‘n’ to “Were you the one who created…” unless you built the transaction yourself
- Carefully review transaction contents before signing
- SDK validates transaction structure and prevents invalid operations
- Transaction file is updated with your signature
Submit Transaction
Submit a fully-signed multisig transaction to the blockchain.
Command:
python scripts/oracle_owner_actions.py sign-and-submit-txInteractive workflow:
Enter filename containing tx cbor: platform_collect.cbor
Were you the one who created and balanced this tx with your own inputs? y/n: n
Transaction{...}
Please review contents of the above tx once again manually before signing
Do you want to sign and submit this tx? y/n: y
Tx signed
Submitting transaction: <tx_id>
Transaction submitted with tx_id: <tx_id>
Tx signed and submittedValidation:
- SDK checks threshold is met before submitting
- Validates all signatures are correct
- Ensures transaction hasn’t expired
- Monitors confirmation status
Transaction fails if:
- Insufficient signatures (< threshold)
- Any signature is invalid
- Transaction validity window expired
- On-chain validation fails
Monitoring & Validation
Query Oracle State
Using Blockfrost:
curl "https://cardano-preprod.blockfrost.io/api/v0/addresses/<oracle_address>/utxos" \
-H "project_id: <project_id>"Using Kupo:
curl "http://localhost:1442/matches/<oracle_address>/*"What to check:
AggState UTXO:
- C3 token balance (funding level)
- Node list (current operators)
- Oracle settings
RewardState UTXO:
- Platform reward balance
- Each node’s accumulated rewards
- Total rewards outstanding
OracleFeed UTXO:
- Latest aggregated price
- Timestamp of last aggregation
- Expiry time
NodeFeed UTXOs:
- Count (should equal number of nodes)
- Each node’s latest feed value
- Last update timestamp
Validate Transaction Before Signing
View transaction details:
cardano-cli transaction view --tx-file <operation>.cborCheck for:
- Correct inputs (oracle UTXOs being consumed)
- Expected outputs (updated UTXOs, payouts)
- Reasonable fees (typically 0.2-0.5 ADA)
- Correct datums (settings, rewards, prices)
- No unexpected outputs or withdrawals
Parse transaction in Python:
from pycardano import Transaction
tx = Transaction.from_cbor(open('platform_collect.cbor', 'rb').read())
print("Inputs:", tx.transaction_body.inputs)
print("Outputs:", tx.transaction_body.outputs)
print("Fee:", tx.transaction_body.fee)
print("Signatures:", len(tx.transaction_witness_set.vkey_witnesses or []))Monitor Oracle Health
Key metrics to track:
-
C3 Balance: Ensure sufficient funding
# Query AggState C3 balance curl "<oracle_utxos>" | jq '.[] | select(...) | .amount[] | select(.unit | contains("<c3_policy>"))' -
Node Activity: Check recent updates
# Query NodeFeed timestamps # Parse datums to see last update times -
Reward Balances: Track accumulated fees
# Query RewardState datum # Check platform_reward and node rewards -
Aggregation Frequency: Monitor oracle feeds
# Query OracleFeed timestamps # Calculate time between aggregations -
Transaction Success Rate: Track failed txs
# Monitor blockchain for failed transactions # Check validation errors
Best Practices
Multisig Coordination
- Establish communication channel - Secure method for sharing transaction files
- Define roles - Who builds, who signs, who submits
- Version control configs - All parties use identical configuration
- Test on testnet - Validate operations before mainnet
- Backup transaction files - Store signed transactions securely
- Document signing order - Clear process for each operation type
Security
- Protect signing keys - Use hardware wallets for mainnet
- Verify transactions - Always review before signing
- Separate wallets - Different wallets for platform and personal funds
- Regular audits - Review access controls periodically
- Incident response - Prepare for key compromise scenarios
- Monitor operations - Regular health checks and anomaly detection
Operations
- Regular funding - Maintain adequate C3 balance
- Periodic collection - Collect platform fees regularly
- Node coordination - Keep operators informed of changes
- Monitor health - Track key metrics continuously
- Document changes - Log all governance actions
- Plan maintenance - Schedule updates during low activity
- Test upgrades - Validate changes on testnet first
Cost Management
- Use reference scripts - Significantly reduces tx fees
- Batch operations - Combine multiple node additions when possible
- Optimize timing - Perform operations during low network congestion
- Monitor C3 usage - Track fee burn rate
- Budget appropriately - Plan for ongoing funding needs
Troubleshooting
Transaction Building Issues
“AggState UTxO not found”
- Cause: Oracle address incorrect or oracle not deployed
- Solution:
- Verify
oracle_addrin config matches deployed oracle - Query oracle address for UTXOs
- Check AggState NFT (C3AS) exists
- Verify
“Platform auth NFT not found”
- Cause: Minting NFT hash incorrect
- Solution:
- Verify
minting_nft_hashmatches oracle NFT policy - Check native script hash calculation
- Query platform address for NFT
- Verify
“Insufficient C3 tokens”
- Cause: Oracle C3 balance too low
- Solution:
- Check AggState C3 balance
- Add funds:
python scripts/oracle_owner_actions.py add-funds <amount> - Ensure wallet has C3 tokens
“Node VKH not found in oracle”
- Cause: Trying to remove node that doesn’t exist
- Solution:
- Query oracle state for current node list
- Verify VKH is correct hex format
- Check node was added successfully
“Cannot modify osNodeList”
- Cause: Trying to update
os_node_listviamk-edit-settings - Solution:
- Use
mk-add-nodesto add nodes - Use
mk-remove-nodesto remove nodes - Settings update only for other parameters
- Use
Multisig Issues
“Insufficient signatures”
- Cause: Transaction threshold not met
- Solution:
- Count current signatures
- Ensure all required parties have signed
- Check for duplicate signatures
- Verify each party uses their own key
“Signature verification failed”
- Cause: Invalid signature or wrong signing key
- Solution:
- Verify party is using correct signing key file
- Check key matches VKH in
pmultisig_pkhs - Ensure key file is not corrupted
- Regenerate signature if needed
“Transaction already signed by this party”
- Cause: Duplicate signature attempt
- Solution:
- Check transaction file for existing signatures
- Coordinate with other parties to avoid duplicates
- If error, skip this party and continue
On-Chain Validation Failures
“Script execution failed”
- Cause: On-chain validator rejected transaction
- Solution:
- Review transaction with
cardano-cli transaction view - Check datum structure matches expected format
- Verify redeemer is correct for operation
- Ensure all prerequisites met (e.g., nodes exist)
- Check logs for specific validation error
- Review transaction with
“Datum mismatch”
- Cause: On-chain datum doesn’t match expected structure
- Solution:
- Re-query oracle UTXOs to get current datums
- Rebuild transaction with fresh data
- Verify datum types match (AggDatum, RewardDatum, etc.)
“Invalid redeemer”
- Cause: Wrong redeemer type for operation
- Solution:
- Verify operation type (AddNodes, RemoveNodes, etc.)
- Check redeemer construction in code
- Ensure redeemer matches validator expectations
“Timing constraints violated”
- Cause: Transaction validity window expired
- Solution:
- Rebuild transaction (creates new validity range)
- Sign and submit more quickly
- Coordinate multisig parties better
- Check system clock accuracy
Reward Collection Issues
“No platform reward available”
- Cause: Platform fees haven’t accumulated yet
- Solution:
- Wait for aggregations to occur (fees allocated after each aggregation)
- Query RewardState to see
platform_rewardvalue - Collect only when balance > 0
“Node collection failed”
- Cause: Node signature doesn’t match registered VKH
- Solution:
- Verify node operator is using correct signing key
- Check VKH matches entry in
os_node_list - Ensure node was properly added to oracle
Oracle Removal Issues
“Cannot remove oracle - pending rewards”
- Cause: Nodes have unclaimed rewards
- Solution:
- Option 1: Use
TO_NODESdisbursement (automatically pays out) - Option 2: Manually collect all node rewards first, then use
TO_ONE_ADDRESS
- Option 1: Use
“Oracle close transaction too large”
- Cause: Many nodes with many outputs
- Solution:
- Remove some nodes first to reduce transaction size
- Collect node rewards before closing (reduces outputs)
- Use
TO_ONE_ADDRESSif acceptable (fewer outputs)