The Binary Size and Execution Cost Tracker is a tool for monitoring and comparing the compiled binary sizes and execution costs of OpShin smart contracts across different optimization levels.
This tool helps identify performance regressions by tracking:
- Binary Sizes: The size of compiled CBOR files
- Execution Costs: CPU and memory costs when running contracts with test inputs
- Track binary sizes across multiple optimization levels (O0, O1, O2, O3)
- Track execution costs (CPU/MEM) for specific test cases
- Compare against baseline measurements from previous releases
- Report significant changes with warnings
- Backward compatible with old baseline format
- Automatic execution in CI/CD on pull requests
Contracts to track are defined in scripts/binary_size_config.yaml:
contracts:
- name: "contract_name"
path: "path/to/contract.py"
purpose: "spending"
description: "Description of the contract"
extra_flags: [] # Optional compiler flags
test_inputs: # Optional: for execution cost tracking
- test_case_name: "valid_input"
inputs_hex:
- "14" # datum (hex-encoded CBOR)
- "16" # redeemer (hex-encoded CBOR)
- "d8799f..." # script context (hex-encoded CBOR)To track execution costs, add test_inputs to a contract configuration:
- test_case_name: A descriptive name for the test case
- inputs_hex: A list of hex-encoded CBOR values representing the contract parameters
The inputs should match the contract's expected parameters:
- For spending validators:
[datum, redeemer, script_context] - For minting policies:
[redeemer, script_context] - For other purposes: consult the contract signature
You can get hex-encoded CBOR inputs using uplc or pycardano libraries:
# Example: encode an integer
import uplc.ast as uplc
datum = uplc.PlutusInteger(20)
hex_datum = uplc.plutus_cbor_dumps(datum).hex()
# Example: encode a PlutusData structure
from pycardano import PlutusData
from dataclasses import dataclass
@dataclass
class PubKeyCredential(PlutusData):
CONSTR_ID = 0
credential_hash: bytes
datum = PubKeyCredential(credential_hash=b"deadbeef")
hex_datum = datum.to_cbor().hex()Generate a baseline file with current measurements:
python scripts/binary_size_tracker.py generate --baseline-file baseline.jsonCompare current measurements with a baseline:
python scripts/binary_size_tracker.py compare --baseline-file baseline.jsonThe script will:
- Compile all configured contracts at each optimization level
- Measure binary sizes
- Evaluate contracts with test inputs (if provided) to measure execution costs
- Compare against baseline and report changes
The tracker runs automatically on pull requests via .github/workflows/binary-size-tracker.yml:
- Downloads baseline from latest release (or generates one from the dev branch)
- Measures current binary sizes and execution costs
- Compares and reports changes
- Comments on the PR with results
- Fails if significant changes are detected (configurable thresholds)
{
"contracts": {
"contract_name": {
"sizes": {
"O1": {
"size": 1234,
"execution_costs": [
{
"test_case": "valid_input",
"cpu": 50000000,
"memory": 25000000
}
]
}
},
"path": "path/to/contract.py",
"purpose": "spending",
"description": "Contract description",
"extra_flags": []
}
},
"metadata": {
"optimization_levels": ["O1", "O2", "O3"],
"total_contracts": 1
}
}Example output:
======================================================================
BINARY SIZE AND EXECUTION COST COMPARISON REPORT
======================================================================
Contract: assert_sum
Description: Simple spending validator with assertion
--------------------------------------------------
O2: 1,200 → 1,180 bytes (-20 bytes, -1.7%) ↘️ (size reduced)
Test 'valid_sum': CPU 50,000,000 → 48,000,000 (-2,000,000, -4.0%) | MEM 25,000,000 → 24,000,000 (-1,000,000, -4.0%) ↘️ (costs reduced)
======================================================================
✅ No significant changes detected
======================================================================
Configurable in binary_size_config.yaml:
thresholds:
warning: 5.0 # Warn if changes exceed 5%
significant: 10.0 # Mark as significant if changes exceed 10%
ignore_warnings: ["O0", "O1"] # Don't warn for these optimization levelsThe tool supports both old and new baseline formats:
- Old format: Sizes are stored as integers
- New format: Sizes are stored as objects with optional execution_costs
This ensures existing baselines continue to work without modification.
To add a new contract for tracking:
- Add an entry to
scripts/binary_size_config.yaml:
- name: "my_contract"
path: "examples/smart_contracts/my_contract.py"
purpose: "spending"
description: "My contract description"- Optionally add test inputs for execution cost tracking:
test_inputs:
- test_case_name: "typical_usage"
inputs_hex:
- "..." # datum
- "..." # redeemer
- "..." # script context- Regenerate the baseline:
python scripts/binary_size_tracker.py generate- Ensure test inputs are valid hex-encoded CBOR
- Check that inputs match the contract's parameter types
- Verify the contract compiles successfully
- The eval_uplc output format may have changed
- Check that
opshin eval_uplcis working correctly - Verify the output contains "CPU:" and "MEM:" lines
- Run
generateto create a baseline first - For CI/CD, ensure the latest release includes a baseline artifact
scripts/binary_size_tracker.py- Main tracking scriptscripts/binary_size_config.yaml- Configuration file.github/workflows/binary-size-tracker.yml- CI/CD workflowscripts/check_binary_sizes.py- Legacy size checking script