tacitdocs

The Tacit chain

The Tacit chain lets you hold and send TAC privately. It runs on Ethereum: every block is proven and checked there. Payments go through in seconds, and no one else can see who paid whom or how much.

Your wallet Public TAC on Ethereum
ShieldPublic: your address and the amount
Tacit chain Send and receive. Who and how much stay hidden.
WithdrawPublic: the amount and where it goes
  • Your walletpublic TAC
  • DeFi poolswap, farm, borrow
  • Bitcoinyour address
TAC goes in from your wallet, moves privately on the chain, and comes out where you choose. Only the way in and the way out are public.

For everyone

Use it

Does
Hold TAC privately, pay anyone, get paid
Needs
TAC in an Ethereum wallet. No ETH.
Costs
A small fee in TAC, shown before you press
Speed
Sent in seconds, settled in about a minute
Where
tacit.finance/tac

For builders

How it works

Proofs
Each payment is proven on the payer’s device. Each block is proven once and checked on Ethereum.
Data
Stored on Ethereum. Anyone can rebuild the chain from it.
Control
No owner, no upgrade key, no pause
Leaders
Anyone who locks 1,000 TAC
Contract
0x00000000000a9147ffd8d41d152d730ca5f84e6c

Use it

Get started

Go to tacit.finance/tac and sign in with your Ethereum wallet. Signing in creates your Tacit key. The same wallet always creates the same key, so you can sign in again on any device.

  1. Shield. Pick an amount of TAC and press Shield. If you have no ETH, you sign once and the leader sends it in for a small fee in TAC. If you hold ETH, you sign and send one transaction yourself, with no fee. Your TAC then shows as private TAC on the Tacit chain. For now, each shield has a limit, which the page shows.
  2. Send. Enter a tacit1… address, a name.wei or an 0x… address, and an amount. You see “Sent” within seconds. The payment settles on Ethereum about a minute later. A payment to a tacit1 address or a name stays private. A payment to an 0x address arrives as ordinary public TAC, but no one can see who sent it.
  3. Get paid. Open the Receive tab. Share your tacit1 address, or a pay link such as tacit.finance/to/yourname/25. You can also show a code to scan. It turns to “Paid” when the money arrives.
  4. Withdraw. Move TAC out to your wallet as public TAC, into the DeFi pool to swap, farm or borrow privately, or to Bitcoin.

People can pay your tacit1 address on the chain once you have opened your Tacit wallet. If you already have private TAC in the DeFi pool, you can move it onto the chain from the same page.

Use it

Fees

Each action costs a small fee in TAC. The page shows it before you press. Most of the fee pays the leader for Ethereum gas, so it goes up and down with gas prices. A small part stays in the chain’s pool.

ActionStays in the poolPays for gas
Send0.002 TACThe leader’s fee
ShieldA small margin, now 0.02%The leader’s fee, plus sending it in if you signed once
Withdraw0.1%, at least 0.002 TACThe leader’s fee

Your private balance is counted in shares of this pool. The part of each fee that stays in the pool makes every share worth a little more. So when other people pay, what you hold grows slightly. A share never loses value in TAC.

fees fees add TAC no new shares BEFORE 100 TAC · 100 shares 1 share = 1.00 TAC AFTER 110 TAC · 100 shares 1 share = 1.10 TAC
The pool holds the TAC, and you hold shares of it. Fees add TAC but no shares, so each share is worth more. The numbers are an example; real fees are much smaller.

Use it

What is public

The chain hides what happens inside it. What crosses its edges is public, as the picture at the top shows.

PublicHidden
ShieldYour Ethereum address and the amountWho holds it afterwards
SendThat a block landed, and how many notes it madeWho paid whom, and how much
WithdrawThe amount and the address it goes toWho it came from
BalancesThe total on the chainWhat anyone holds

Because the edges are public, a withdrawal of exactly the amount you shielded can be linked to it. Wait a while between the two, and use different amounts.

Use it

If something goes wrong

No single party can freeze your money. Here is what happens in each case.

  • The leader stops. If no block lands for 10 minutes, anyone may land the next one.
  • Your payment is left out. Press “Post it to Ethereum yourself” in the app. Blocks must include it within an hour. A posted payment shows on Ethereum which notes it spends and creates, but not the amounts.
  • A shield never goes in. After 7 days, press “Take back” in the app. The TAC goes back to your wallet, or into the DeFi pool as private TAC.
  • You lose your device. Sign in with the same wallet somewhere else. You get the same key, and the app finds your notes again from the data on Ethereum.

How it works

A payment’s path

  1. Your deviceProves the payment. Your keys and amounts stay here.
  2. LeaderChecks the proof and adds it to the next block.You see “Sent”
  3. ProverProves the whole block at once, on a GPU.
  4. EthereumChecks the block’s proof and stores its data.You see “Settled”

Your device makes a zero-knowledge proof. The proof shows that you own the notes you are spending and that the amounts add up, without revealing either. It uses Halo2, the same circuit as the Bitcoin shielded pool.

The leader checks the proof and adds the payment to the next block. A prover then runs the block program in SP1. The program checks every payment proof in the block in one step and produces a single Groth16 proof for the whole block. The TacitBlocks contract checks that proof on Ethereum and records the block.

Checking a block’s proof costs the same on Ethereum whether the block holds one payment or thirty. The payments in a block share that cost, which keeps fees low.

How it works

Notes and shares

SPENT note••• note••• proof on your device NEW them••• change••• fee••• published nullifiersmark spent new notesjoin tree
One payment: two notes in, three out. It publishes the old notes’ nullifiers and the new notes. The amounts (•••) stay sealed.

Your balance is made of notes. A note is a sealed record of an amount and its owner. The chain stores only the seal, called a commitment, so no one can read the amount or the owner from it.

To spend notes, your wallet publishes their nullifiers. A nullifier is a tag that only the owner can compute. It marks a note as spent, so the note can’t be spent twice, but it doesn’t reveal which note it was.

A payment spends up to two notes and creates up to three new ones: one for the person you pay, one for your change, and one for the leader’s fee.

Each new note comes with an encrypted memo of 1,152 bytes. Your wallet finds your notes by trying to open the memos with your key. The memos use ML-KEM-768 together with secp256k1, so even a future quantum computer couldn’t read one recorded today.

Notes count shares of the pool. One share is worth (pool + 1) / (shares + 1) TAC. The extra 1 on each side is a virtual TAC that no note holds, so no single deposit can set the price. Shielding buys shares at this rate. Withdrawing sells them back, less the fee.

How it works

Blocks and leaders

A leader collects payments and lands blocks on Ethereum. The TacitSeats contract decides who leads. It has no owner.

ONE SEAT LEADS EACH SLOT · ABOUT 10 MIN A B A C B A C A now IF THINGS STALL 10 min with no block anyone may land 1 h after posting must be in a block
Each letter is a seat. Sixteen slots make an epoch, and Ethereum’s randomness picks which seat leads each slot. Two timers keep blocks coming if a leader goes quiet.

Anyone can take a seat by locking 1,000 TAC. If you leave, you get the TAC back 7 days later.

Time is split into slots of 50 Ethereum blocks, about 10 minutes each. In each slot, one seat leads. While no one holds a seat, the bootstrap leader leads.

Every payment includes a fee note for its leader, which pays the gas to land the block. Provers are paid in points.

Two rules keep the chain moving. If no block lands for 10 minutes, anyone may land one. And anything posted to the forced queue on Ethereum must be in a block within an hour.

To lead, run the node and leader from tacit-leader ↗ and call announce(url). To prove blocks with your GPU, see tacit.finance/prove.

How it works

What you trust

ForYou rely onNot on
Your fundsThe proofs and the contract’s codeThe leader or the provers
Staying liveAny leader, or anyone after 10 minutesAny single leader
Your dataEthereum, which stores every block’s notes, memos and nullifiersAny indexer. Anyone can rebuild the chain’s state.
Your privacyYour own device, which makes every proofThe leader or the provers

The contract has no owner, no upgrade key and no pause. Improvements come as a new chain. Each person moves their own notes to it and keeps their privacy on the way.

A block’s prover can see which nullifiers and new notes belong to the same payment within that block. It can’t see keys, amounts or owners.

The proofs use pairings, like most zk-rollups on Ethereum. The memos are already safe against quantum computers.

How it works

Build on it

The leader’s API is public and needs no key.

curl https://tacit-chain-leader.onrender.com/state   # height, roots, pool and shares
curl https://tacit-chain-leader.onrender.com/fee     # what a payment costs now
RouteReturns
GET /stateHeight, note root, counts, the forced queue, pool and shares
GET /feeThe fee, the shield’s sending fee and limit, and the leader’s chain address
GET /notes?from=N&limit=MNew notes and their memos, to scan
GET /path/NA note’s Merkle path
GET /spent/0x…Whether a nullifier is spent
POST /spendTakes a proved payment. Follow it at /receipt/ID.
POST /forcedThe bytes to post with TacitBlocks.force

In JavaScript, dapp/tacit-chain-client.js is the app’s complete chain wallet. Amounts are in units of 10−8 TAC.

import { makeChainClient } from './dapp/tacit-chain-client.js';
import { tacitChain } from './dapp/tacit-chain-config.js';
import { makeHalo2System } from './dapp/btc-pool-halo2-prover.js';

// rpcCall(method, params): an Ethereum JSON-RPC call. priv: the Tacit key
const system = makeHalo2System({ wasm, params, vk, pinnedVkHash });
const c = makeChainClient({ cfg: tacitChain(), rpcCall, priv, system: () => system });
await c.ready();
await c.scan();                     // find your notes
await c.worth();                    // your balance, in units
await c.pay(chainAddress, units);   // proved here, sent to the leader
await c.withdraw('0x…', units);
ContractAddress
TacitBlocks0x00000000000a9147ffd8d41d152d730ca5f84e6c
TacitSeats0x00000000001f703eae70de3b528213e4802790fb
TAC0xA1313eb9f3A445606D9583bcAc3ebeB56a858279
SP1 verifier0xb69f2584CBcFf99a58C4e7002E8b89Af54a6f4e2
Block program key0x00610fb0fa1b5d800e548c3cdcdf98ceb4288828be1cec1fb81e39004bad6ed6