Skip to content

Contract Analysis

DISCLAIMER // NFA // DYOR

This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.

⊙ generated by robots | curated by humans

Analysis Date: 2026-06-02


Metadata

Primary Contract

PROPERTY VALUE
Contract Name TacitBridgeMixer
Contract Address 0x82eb1246...90cC37 (etherscan)
Network Ethereum Mainnet
Contract Type Standalone, immutable (no proxy, no admin)
Deployment Date 2026-06-01 20:11:23 UTC
Deployment Block 25224831 (etherscan)
Contract Creator 0x42e7b9e9...55483c (etherscan)
Creation TX 0x71db7809...62cfd6 (tx)
Compiler Version Solidity v0.8.34+commit.80d5c536 (optimizer on, viaIR, EVM prague)
Total Functions 26 external/public (3 state-changing, 1 receive, 23 view)
External Contract Dependencies 4 (Bitcoin relay, SP1 pool-root verifier, SP1 gateway, Groth16 verifier)
Upgrade Mechanism ☒ None — Not Upgradable (all configuration is immutable)
Verification Status ☑ Verified (Exact Match) on Etherscan
Audit Status ☒ None — upstream Tacit ships as experimental software, no warranty
TYPE ADDRESS NOTES
Bitcoin header relay 0x36358295...8Ca6D0 (etherscan) HEADER_RELAY. Verified BitcoinLightRelay. Permissionless tip advance; one-time deployer-seeded genesis checkpoint
SP1 pool-root verifier 0x42Ab3d9d...2a3CCb (etherscan) The bound poolVerifier for all 8 pools. Verified SP1PoolRootVerifier. No admin
SP1 verifier gateway 0x397A5f7f...48dA9B (etherscan) Succinct's canonical mainnet SP1VerifierGateway, deployed 2024. Verifies the SP1 state proof
Groth16 burn verifier 0x031b22ba...7Ab2ca (etherscan) BURN_VERIFIER. ☒ Unverified bytecode (1,752 bytes) — bn254 pairing verifier
Contract creator 0x42e7b9e9...55483c (etherscan) Deployed the relay, SP1 pool-root verifier, and Groth16 burn verifier above (the gateway is Succinct's). Distinct from z0r0z's canonical zFi deployer 0x1C0Aa8cC...855A20 (etherscan)
Bound Tacit asset 0x3cba71e1...126f34 (tacitscan) ASSET_ID — the Tacit-side asset this mixer bridges. Not TAC (f0bbe868...94762b (tacitscan))

Executive Summary

TacitBridgeMixer is the Ethereum end of a two-way bridge between native ETH and the Tacit confidential-token meta-protocol on Bitcoin. It locks ETH on Ethereum and lets the depositor mint a matching confidential balance on Bitcoin; later, a burn of that balance on Bitcoin releases the locked ETH back out on Ethereum. It is also a mixer in the Tornado sense — deposits go into fixed-denomination pools and withdrawals are unlinked from them by a zk-SNARK — but the unlinking proof here is verified against state that originates on Bitcoin, not Ethereum.

The contract advertises itself as "fully trustless … No trusted roles," and the bytecode supports the first half of that claim literally: it has no privileged roles, no fee mechanism, and no upgrade path. Every parameter — the relay, the verifiers, the eight denominations, the asset id — is fixed at construction and cannot change. What the contract cannot remove is its dependence on four external contracts and two off-chain cryptographic systems. The contract relocates trust rather than removing it: from a privileged key to (a) the soundness of a Succinct SP1 state proof and its off-chain guest program, (b) a Groth16 burn circuit whose on-chain verifier is unverified bytecode, (c) a Bitcoin light client whose genesis checkpoint was seeded once by the deployer, and (d) the Trusted Setup ceremonies behind both SNARKs.

The intended audience is a Tacit user who wants to move value between Bitcoin-side confidential assets and Ethereum ETH without a custodian or a federation. The withdrawal path stacks six independent checks — Bitcoin proof-of-work inclusion, Tacit envelope validity, an SP1-accepted burn claim, a Groth16 knowledge proof, a domain-bound bindHash, and an aggregate balance cap — so defeating it appears to require breaking one of those primitives rather than stealing a key. The main risks are the unaudited, freshly-deployed status (the contract is one day old as of analysis), the unverified Groth16 verifier, the off-chain SP1 guest program that no on-chain reader can inspect, and a thin anonymity set (all current activity is the deployer's own end-to-end test).


Architecture

graph TD
    User([User / Depositor])
    Recip([Withdrawal recipient])

    subgraph ETH["Ethereum"]
        MIX["TacitBridgeMixer<br/>0x82eb1246...90cC37<br/>(holds ETH, Poseidon deposit trees)"]
        RELAY["BitcoinLightRelay<br/>0x36358295...8Ca6D0<br/>(heaviest-chain header store)"]
        SP1V["SP1PoolRootVerifier<br/>0x42Ab3d9d...2a3CCb<br/>(acceptedBurns registry)"]
        GW["Succinct SP1VerifierGateway<br/>0x397A5f7f...48dA9B"]
        G16["Groth16 burn verifier<br/>0x031b22ba...7Ab2ca<br/>(unverified)"]
    end

    subgraph BTC["Bitcoin"]
        TACIT["Tacit confidential asset<br/>(Taproot envelopes)"]
    end

    User -->|"deposit ETH<br/>(commitment, denom)"| MIX
    MIX -.->|"leaf appears, user mints<br/>on Bitcoin"| TACIT
    TACIT -.->|"T_BRIDGE_BURN (0x61)<br/>Taproot reveal"| BTCBURN[["burn tx on Bitcoin"]]

    User -->|"withdrawFromBurn(rawBtcTx, headers, proof)"| MIX
    MIX -->|"verifyBlock (PoW + tip)"| RELAY
    MIX -->|"isAcceptedBurn(claimId)"| SP1V
    MIX -->|"verifyProof (bn254 pairing)"| G16
    MIX -->|"ETH denom"| Recip

    Relayer([anyone]) -->|"advanceTip(headers)"| RELAY
    Prover([anyone]) -->|"proveStateTransition(SP1 proof)"| SP1V
    SP1V -->|"verifyProof"| GW
    SP1V -->|"getRootAccumulator"| MIX
    BTCBURN -.->|"proven by SP1 guest"| SP1V

System Overview

The bridge has two directions and one shared escrow balance.

Into Tacit (deposit / batchDeposit). A user sends ETH equal to one of eight fixed denominations and supplies a 32-byte commitment (a Poseidon Hash of their secret note). The contract appends the commitment as a leaf in a per-denomination incremental Merkle Tree (depth 20, up to 1,048,576 leaves), records the new root, and increments totalBalance. Off-chain, the Tacit dApp watches for this leaf and lets the depositor mint the equivalent confidential asset on Bitcoin.

Out of Tacit (withdrawFromBurn). To redeem ETH, the user first burns their confidential balance on Bitcoin with a T_BRIDGE_BURN envelope (opcode 0x61), then submits the raw Bitcoin transaction plus a header chain to withdrawFromBurn. The contract verifies the burn is in Bitcoin's canonical chain via the relay, that the SP1 prover has accepted the exact burn claim, and that a Groth16 proof of note knowledge is valid, then releases the denomination to the recipient encoded in the burn envelope.

  • Escrowed ETH is released only against a stacked Bitcoin-origin proof — no signer can move funds.
  • The contract holds no admin, pause, fee, or upgrade capability. Once deployed, behavior is fixed.
  • Producing the SP1 proof and running the relay are off-chain jobs anyone can perform; both must stay advanced for withdrawals to process, which is a liveness dependency rather than a custody one.

Design Patterns Used

  • Fixed-denomination mixer: pools are keyed by keccak256(ASSET_ID, denomination); withdrawals are denomination-pure, which keeps each pool's anonymity set fungible. Mirrors Tornado Cash's pool design.
  • Incremental Merkle tree with cached filledSubtrees and precomputed zeros[]: O(20) hashing per deposit, no full-tree storage. Poseidon Hash is used as the tree hash because it is cheap inside the withdrawal SNARK.
  • Bitcoin Simplified Payment Verification (SPV) on Ethereum: a hand-rolled light client (BitcoinLightRelay) plus in-contract transaction parsing, txid recomputation, and Merkle inclusion — the contract reads Bitcoin without trusting any oracle.
  • Proof-carrying state bridge: the authoritative "which burns are valid" decision is delegated to an SP1 zero-knowledge proof of the Bitcoin-side pool state, recorded in acceptedBurns. The mixer never reconstructs Tacit semantics itself.
  • Transient reentrancy lock: inherits Solady's ReentrancyGuardTransient (EIP-1153 transient storage), guarding deposit, batchDeposit, and withdrawFromBurn.

Access Control

Roles & Permissions

There is no privileged role on this contract. No constructor argument assigns an owner, operator, or pauser, and no function checks msg.sender against a stored address. The table below reflects that everything is either open to anyone or driven by external contracts, whose risk surface is covered in the Potential Risks assessment.

ROLE ASSIGNED BY REVOKABLE CALL COUNT
User (depositor / withdrawer) Permissionless — anyone N/A Unlimited
Relayer / SP1 prover Permissionless — anyone (acts on external relay + verifier) N/A Unlimited
Admin / Owner ☒ Does not exist

Permission Matrix

FUNCTION OWNER USER ANYONE
deposit — (no owner)
batchDeposit
withdrawFromBurn
view functions
pause / upgrade / set-fee / rescue ☒ no such function exists

Time Locks & Delays

ACTION TIME LOCK CAN CANCEL PURPOSE
Withdrawal after burn Bitcoin confirmations (CONFIRMATION_DEPTH = 6) before the relay accepts the block No ◇ Finality — the burn block must be buried under 6 headers chaining to the relay tip
Deposit None — immediate No ◇ Funds escrow on the same transaction

Economic Model

The contract custodies native ETH and releases it only along the withdrawal path. Neither deposits nor withdrawals take a fee, and there is no protocol treasury.

Funding Sources & Sinks

  • Inflow: deposit / batchDeposit require msg.value to exactly equal the denomination(s). Any mismatch reverts (InvalidDenomination). The receive() function reverts, so ETH cannot enter except through a deposit.
  • Outflow: withdrawFromBurn sends exactly p.denomination to the recipient via Solady forceSafeTransferETH. There is no other path out: no sweep or owner-withdrawal function.

Economic Invariants

  • Aggregate solvency gate: withdrawals check totalBalance >= p.denomination and decrement totalBalance on each release. Because total redeemable value is capped at total deposits, the contract cannot pay out more ETH than was put in — even though pools are otherwise fungible (a burn in any denomination can be redeemed as long as aggregate backing exists).
  • Double-spend prevention: each pool tracks burnNullifiers; a nullifier can be consumed once. A replayed burn reverts NullifierAlreadySpent.
  • Denomination set — eight fixed values, in wei:
INDEX WEI ETH TACIT UNITS (8-DEC)
0 10,000,000,000,000 0.00001 1,000
1 100,000,000,000,000 0.0001 10,000
2 1,000,000,000,000,000 0.001 100,000
3 10,000,000,000,000,000 0.01 1,000,000
4 100,000,000,000,000,000 0.1 10,000,000
5 1,000,000,000,000,000,000 1 100,000,000
6 10,000,000,000,000,000,000 10 1,000,000,000
7 100,000,000,000,000,000,000 100 10,000,000,000

UNIT_SCALE = 10^10 converts between ETH's 18 decimals and Tacit's 8 decimals (TACIT_DECIMALS = 8). The constructor enforces every denomination is a whole multiple of UNIT_SCALE, so each maps to an exact integer number of Tacit units.

Observed Balance

As of analysis date totalBalance is 90,000,000,000,000 wei (0.00009 ETH), matching the contract's on-chain ETH balance. This is the residue of the deployer's own round-trip tests (see Summary of Observations).


Summary of Observations

TacitBridgeMixer is an immutable escrow that connects Ethereum ETH to Bitcoin-side Tacit confidential assets, with mixer-style unlinkability layered on top. The deposit side is self-contained: lock ETH, append a Poseidon commitment to a fixed-denomination tree. The withdrawal side carries the bulk of the logic and the trust assumptions. A single withdrawFromBurn call parses a raw Bitcoin transaction in Solidity, recomputes its txid, proves it is buried in Bitcoin's heaviest chain through the relay, extracts a Taproot script-path envelope, reconstructs a domain-separated bindHash over chainid and the contract's own address, confirms the SP1 prover has accepted the exact burn claim, and finally checks a Groth16 proof of note knowledge — all before a single wei moves.

On money: the contract holds only what was deposited, charges nothing, and gates every payout on an aggregate totalBalance that can never let withdrawals exceed deposits. There is no admin path to the funds. We traced a real withdrawal (tx 0x45f7ccbc...4491d9) end to end: it called the relay's verifyBlock, the SP1 verifier's isAcceptedBurn, then the Groth16 verifier's verifyProof (which executed genuine bn254 ecadd/ecmul/ecpairing precompiles and returned true), and released 0.001 ETH. The withdrawal path executed successfully in the one withdrawal we traced.

On concerns: the contract is one day old, unaudited, and its current anonymity set is a single user — the deployer, who performed two deposits and two withdrawals. The Groth16 burn verifier it depends on is unverified bytecode, so a reader cannot confirm on-chain which circuit it enforces. The SP1 guest program that decides which Bitcoin burns are valid lives off-chain; the contract pins only its verification-key hash (PROGRAM_VKEY), not its source. The Bitcoin relay's safety rests on a one-time genesis checkpoint set by the deployer and on Bitcoin not experiencing a deep reorg across a difficulty retarget (explicitly out of scope in the relay's own comments). And the whole withdrawal flow inherits the trusted-setup assumptions of two separate SNARK systems. These are the trust assumptions the design depends on, beyond the "no trusted roles" claim. See the Potential Risks assessment for the summarized findings.


References

RESOURCE NOTES
TacitBridgeMixer source (Etherscan) Verified Solidity source; primary input to this analysis
TacitBridgeMixer.sol @ z0r0z/tacit Upstream source at the analyzed commit
Tacit Project Overview DNZN research on the Bitcoin-side meta-protocol this bridge connects to
Tacit — Research Timeline Where this analysis is catalogued among DNZN's Tacit research (protocol, mechanisms, mixer, operations, risks)
Tacit — The Shielded Mixer The Bitcoin-side Groth16 mixer; same primitive family as this bridge's withdrawal proof
Succinct SP1 documentation The SP1 zkVM and on-chain verifier gateway used to prove Bitcoin pool state
Tornado Cash design Reference for the fixed-denomination Poseidon-tree mixer pattern

Change Log

DATE AUTHOR NOTES
2026-06-02 Artificial. Generated by robots. Gas: 130 tok
2026-07-03 Denizen. Reviewed, edited, and curated by humans.