Binocular enables Cardano smart contracts to access and verify Bitcoin blockchain state. Anyone can submit Bitcoin block headers to a single on-chain Oracle UTxO without registration or bonding. All blocks are validated against Bitcoin consensus rules (proof-of-work, difficulty adjustment, timestamp constraints) enforced by a Plutus smart contract written in Scalus.
Using Nix with flakes and/or direnv is recommended.
nix develop
or
direnv allow
Then build with sbt:
sbt compile
sbt test
Build a self-contained fat jar that bundles all dependencies, so you can run the oracle run
daemon, the Bifrost relay watchtower, and every other CLI command with plain java — no sbt
required:
sbt assembly
This produces target/out/jvm/scala-3.3.7/binocular/binocular.jar.
The convenience launcher ./bin/binocular finds the jar, picks up $JAVA_HOME, and forwards all
arguments:
./bin/binocular --config application-preprod.conf run # oracle syncer daemon
./bin/binocular --config application-preprod.conf relay # Bifrost watchtower
./bin/binocular --version
Or invoke java directly:
java --sun-misc-unsafe-memory-access=allow \
-jar target/out/jvm/scala-3.3.7/binocular/binocular.jar \
--config application-preprod.conf run
The --sun-misc-unsafe-memory-access=allow flag is required on JDK 23+ because Scalus' on-chain
interpreter uses sun.misc.Unsafe, which newer JDKs deny by default. The bin/binocular launcher
adds it automatically when the JDK is new enough. Configuration is loaded exactly as for sbt run
(env vars > --config file > application.conf > reference.conf).
Launcher env overrides: BINOCULAR_JAR (jar path), JAVA (java launcher), JAVA_OPTS (extra JVM
options, e.g. JAVA_OPTS=-Xmx4g).
Binocular uses HOCON configuration with PureConfig. Configuration is loaded with the following priority (highest wins):
environment variables > --config file > application.conf > reference.conf
Pass a config file with --config:
binocular --config application-preprod.conf <command>
| Section | Description | Key env vars |
|---|---|---|
bitcoin-node |
Bitcoin RPC connection | BITCOIN_NODE_URL, BITCOIN_NODE_USER, BITCOIN_NODE_PASSWORD, BITCOIN_NETWORK |
cardano |
Cardano network and backend | CARDANO_NETWORK, CARDANO_BACKEND, BLOCKFROST_PROJECT_ID |
wallet |
Wallet mnemonic for signing | WALLET_MNEMONIC |
oracle |
Oracle parameters (UTxO ref, owner) | ORACLE_TX_OUT_REF, ORACLE_OWNER_PKH, ORACLE_START_HEIGHT |
relay |
TMTx relay settings | RELAY_TMTX_POLICY_ID, RELAY_TMTX_ASSET_NAME |
Set bitcoin-node.network to one of: mainnet, testnet, testnet4, regtest.
Set cardano.backend to one of:
blockfrost-- requiresblockfrost-project-idyaci-- local Yaci DevKit, usesyaci-store-urlandyaci-admin-url
All commands accept --config <path> to specify a configuration file.
| Command | Description |
|---|---|
init |
Initialize a new oracle. Options: --start-block, --dry-run |
run |
Continuous daemon: poll Bitcoin and submit oracle updates. Option: --dry-run |
update-oracle |
Submit a single oracle update. Options: --from, --to |
deploy-script |
Deploy the oracle validator as a reference script on-chain |
close |
Close the oracle and burn the NFT |
info |
Display oracle configuration and validator parameters |
list-oracles |
List oracle UTxOs at the script address. Option: --limit |
verify-oracle |
Verify the on-chain oracle state |
blueprint |
Print the CIP-57 Blueprint JSON |
prove-transaction |
Prove a Bitcoin transaction's inclusion in a confirmed block |
These commands support the Bifrost bridge protocol by relaying signed Treasury Movement transactions from Cardano to Bitcoin.
| Command | Description |
|---|---|
relay |
Poll Cardano for TMTx UTxOs and broadcast signed Bitcoin transactions. Option: --dry-run |
create-tmtx |
Create a test TMTx UTxO on Cardano. Argument: BTC_TX_HEX |
spend-tmtx |
Spend (destroy) all TMTx UTxOs at the script address |
| Command | Description |
|---|---|
-v, --version |
Print version and exit |
binocular --config application-preprod.conf runbinocular --config application-preprod.conf init --start-block 129400binocular --config application-preprod.conf prove-transaction <BTC_TX_ID>Set binocular.traffic.enabled = true (or TRAFFIC_ENABLED=true) to add demo traffic to
watchtower. Every five minutes it reads both ledgers and does at most one thing: refund a
deposit the treasury never took, complete a swept peg-in, request a confirmed deposit, or start
a random deposit or peg-out. Bitcoin and Cardano mainnet are refused.
binocular --config application-preprod.conf traffic-address
# Fund the printed Bitcoin address. The sponsor also needs ADA and deployed bridge script refs.
binocular --config application-preprod.conf watchtower --dry-run
binocular --config application-preprod.conf watchtowerKeys: virtual-epoch-slots (match Heimdall), max-amount-sat (amounts are uniform between
the live min_peg_out_fbtc and this, default 50,000), mean-interval (mean time between
random deposits, and between random peg-outs, default 4h). Peg-outs only spend fBTC that
earlier peg-ins minted, so the two sums converge. Fee estimates come from bitcoind, capped at
10,000 sat per transaction; Treasury Movement fees remain Heimdall's responsibility.
The one-day default epoch requires a shortened live bridge schedule; the deployment defaults have a 36-hour stability window and require the normal 432,000-slot epoch. Startup rejects a cycle with no eligible deposit window.
There is no state file: a restart re-reads the ledgers and carries on, and deposits are found
by walking the funding address's history. Every action, successful or failed, is logged and
posted to the configured notifier (Discord); after a failure the next tick tries again.
--dry-run observes both ledgers and logs what the first tick would do, without sending.
# Start the relay daemon
binocular --config application-preprod.conf relay
# Dry-run: check for TMTx UTxOs without broadcasting
binocular --config application-preprod.conf relay --dry-run# Create a test TMTx UTxO with some hex bytes as the Bitcoin transaction
binocular --config application-preprod.conf create-tmtx 0200000001abcdef...
# Clean up: destroy all TMTx UTxOs
binocular --config application-preprod.conf spend-tmtxTo test with real Bitcoin data, you need Bitcoin Core running locally or access to a remote node.
Quick setup:
export BITCOIN_NODE_URL="http://localhost:8332"
export BITCOIN_NODE_USER="bitcoin"
export BITCOIN_NODE_PASSWORD="your_password"For detailed setup instructions, see docs/BITCOIN_SETUP.md.
The relay command broadcasts Bitcoin transactions it finds parked on Cardano
as TMTx UTxOs. To exercise that flow without waiting on real mempool activity,
use regtest-create-tmtx.sh: it mines regtest funds, crafts a BTC transaction,
and submits the raw hex as a TMTx on Cardano.
Prerequisites:
bitcoindrunning in regtest mode with an RPC-enabledbitcoin.conf- The Cardano wallet in
application-regtest.confhas preprod tADA (override the mnemonic viaWALLET_MNEMONICif you'd rather not commit it)
Run it:
BITCOIN_CONF=/path/to/bitcoin.conf ./regtest-create-tmtx.shOptional env overrides: BINOCULAR_CONFIG (default application-regtest.conf),
BTC_WALLET (default binocular-regtest), BTC_SEND_AMOUNT (default 0.001).
Verify the relay picks it up:
sbt "run --config application-regtest.conf relay --dry-run"Drop --dry-run to actually broadcast to regtest bitcoind.
- Whitepaper -- complete technical specification
- Litepaper -- concise overview
./generate-pdfs.shRequirements:
- All dependencies are included in the Nix development environment
- On macOS: install Chrome or Chromium separately (
brew install --cask google-chrome) - On Linux: Chromium is provided through Nix
Output: pdfs/Litepaper.pdf, pdfs/Whitepaper.pdf