Skip to content
HimessPublic

About

Borrow against shielded ZEC, or swap USDC into it. No bridge, no custodian: only the proof crosses. ZECATHON 2026, Cross-Chain track (testnet).

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Zecured

Zecured: the shielded pool on the left, Base on the right, and a gold proof crossing the seam between them

Borrow against shielded ZEC. Only the proof crosses.

Borrow USDC on Base against ZEC that never leaves Zcash's shielded pool, or swap USDC into shielded ZEC. Base sees zero-knowledge proofs, never your Zcash address, transaction or balance.

ci

The site · the video · the docs page · the app as a guided replay (DEMO: our live run of 27 October, in your browser) · run it yourself · the same pages on GitHub Pages

For judges (5 minutes)

  1. Watch the video of the live run (3:55, English captions).

  2. Click through the app as a guided replay: the real app in your browser, replaying our live run of 27 October on Base Sepolia and Zcash testnet in place of our servers (DEMO), each on-chain step linked to its real transaction. Nothing to install.

  3. Check a real proof in one command (Foundry's cast, nothing to clone): RISC Zero's verifier router as deployed on Base Sepolia accepts the Groth16 proof of a real Zcash testnet deposit (the TEST-ONLY vault 9, a disposable test vault whose viewing key is public in the test fixtures; 0.01 TAZ at block 4,488,543):

    cast call 0x0b144E07A0826182B6b59788c34b32Bfa86Fb711 "verify(bytes,bytes32,bytes32)" \
      $(curl -sL https://zecured.vercel.app/evidence/deposit-test.seal) \
      0x406c6d268f29270226c775db1fe4bec04acf51ba919e250078125d71c10bff41 \
      0x1a9dcbc10182a251b4e24fdc01740df36a90f7eb9397cb89be1a9316d35867f4 \
      --rpc-url https://sepolia.base.org && echo "accepted by RISC Zero's router"

    The second hash is the deposit guest's image id, the third the SHA-256 of the proof's journal (_journal in contracts/test/fork/RealSealsV2.t.sol): change one digit and the router reverts. Every guest (a deposit and a vault spend, a payout's inclusion, a synthetic swap payment), with minting and slashing through the real router, on a fork: cd contracts && forge test --match-path test/fork/RealSealsV2.t.sol --isolate (Foundry 1.8.5).

  4. Real and mock, in one line: Zcash testnet transactions, the FROST vaults, the on-chain Zcash header relay, the RISC Zero proofs and Morpho Blue are real, and they ran live together on Base Sepolia on 27 October 2026, every proof checked by RISC Zero's deployed router; the ZEC price, the loan token (MockUSDC) and the signers' independence are not (the full list).

  5. On the explorers: every transaction of the live run, on Zcash and on Base, and every contract, verified on Basescan (the list).

  6. Read on: what ran, and how to reproduce it, the judges' FAQ, what is still open.

What it does

Using ZEC in DeFi today usually means taking it out of the shielded pool. Zecured doesn't. People already borrow against ZEC on Base: Morpho's cbZEC/USDC market, backed by Coinbase's custodial wrapped ZEC and public on-chain, had about USD 2.7 M of USDC borrowed (USD 3.0 M supplied) when we read it on 27 October 2026 (docs/ref/FACTS.md). Zecured is that market for shielded ZEC.

Two products, one Zcash light client on Base:

  • Borrow. Borrow USDC on Base against ZEC that stays in Ironwood, Zcash's newest shielded pool. The ZEC sits in a fresh 3-of-5 FROST threshold vault; on a zero-knowledge proof of the deposit, a non-transferable collateral receipt (zZEC) is minted straight into a Morpho Blue position of a fresh, gasless smart account. Close by repaying in USDC, or close from collateral: a market maker repays the debt and takes ZEC for it, and the rest returns to a new shielded address, so no outside funds ever touch your account. A liquidation, or a pre-liquidation the borrower opted into, pays in shielded ZEC too.
  • Swap. Swap USDC into shielded ZEC with no custodian, no signer and no wrapped ZEC. The USDC waits in escrow on Base and is released by a zero-knowledge proof, made by whoever paid, that a shielded payment for that very order is in the Zcash chain. Any Ironwood address can receive. Selling ZEC for USDC works the same way, from the CLI (docs/SWAP.md).

One loan seen from both chains: the five steps, then the borrower's viewing key, which reads its own payout but never the market maker's

The shared core is a Zcash light client on Base. ZcashHeaderRelay checks Zcash block headers on-chain, proof of work (Equihash) included. ZIP 221 history roots let a proof show that a block is in the chain without saying which one. RISC Zero zkVM programs ("guests") prove deposits, swap payments, payouts and violations, and ZecuredVersions lists the verifiers each proof is checked with. Any contract can use the same light client to check a shielded Zcash payment, with no bridge. The vault design is written up as a draft ZIP, Shielded Collateral Vaults, for any wallet or chain to build on.

What crosses from Zcash to Base: four zero-knowledge proofs (deposit, inclusion, violation, payment) and salted commitments; never a Zcash transaction, note or address

Who you have to trust. Zcash's shielded pool has no scripts, and a loan must stay liquidatable when its borrower is gone, so someone has to be able to spend the collateral: a fresh 3-of-5 FROST vault per loan, whose signers may pay only what Base has recorded. Three signers could still move a vault's ZEC: the design makes that provable and slashable, not impossible. The signers see the vault's notes and amounts, the Ironwood addresses it pays and the Base account it backs; never the depositor's wallet or identity. Base checks them instead of trusting them:

  • Deposits. zZEC is minted only on a zero-knowledge proof that the deposit is in a Zcash block whose header Base verified; no signer vouches for it, and all five together can only add a new verifier after a public delay (Tier 2).
  • Payouts. The signers must record every payout on Base before they can sign it, and a payout is final once its inclusion in a Zcash block is proven in zero knowledge, or once its dispute window closes undisputed.
  • Misbehaviour. Taking ZEC that backs loans, or paying a claim to anyone but its owner, is provable on Base without revealing any Zcash transaction, note or address; it slashes the signers' bonds, and the bonds compensate the victims: liquidators and lenders before the borrower's own equity (Tier 3).
  • Liveness. A claim left unpaid for 24 hours (72 on mainnet) after a signer acknowledged its destination is paid from the bonds. Three of five signers can pause new minting for up to 30 days at a time; withdrawals, repayments, liquidations and payouts never stop.
  • Swap. No signer and no bond take part in a trade: its USDC moves only on a proof of the payment.

Why stealing does not pay. On mainnet, new collateral is accepted only while all the ZEC locked is worth at most half of the signers' effective bond, and any three signers' bonds make up at least 3/5 of it: the three it takes to steal stand to lose at least 1.2 times what they could take, and since FROST hides which three signed, all five are slashed. What the bonds cover and what they do not: docs/ECONOMICS.md.

On Zcash testnet a single Equihash ASIC out-mines the whole network about 40 times over, so every proof-of-work-based guarantee here (Tier 2 minting and Tier 3 slashing) is demonstrative on testnet. On mainnet the same mechanisms inherit Zcash's proof of work, with confirmation depth chosen by the value at risk. The five signers of the demo are run by the team, and their bonds demonstrate the mechanism, not independent economic security.

Built by Himess for ZECATHON 2026, Cross-Chain track, from 10 October 2026. A testnet experiment, not a financial product.

What works today

Verified end to end on Zcash testnet (NU7) with five separate signer processes (all run by us). The Base side ran on local forks of Base Sepolia for the rows below, and live on Base Sepolia on 27 October 2026 for the last one, every proof of that run checked by RISC Zero's deployed router:

Labels: Tier 2 is minting on a deposit proof; Tier 3, every payout committed on Base before it is signed, with a theft provable and slashed. P0 to P6 are the signers' payout policies (docs/ARCHITECTURE.md); F1 to F3 the thefts a violation proof shows: a spend no commitment allowed, change withheld, a payment short of its claim.

Flow Result
Vault creation 5-party FROST DKG over HTTP (node-to-node encrypted), ZIP 2005 use_qsk keys, Ironwood-only address
P1 deposit classification Ironwood-only deposits of the exact denomination conform; a wrong amount is non-conforming
P5 refund Owner-signed request, PCZT verified by each signer, 3-of-5 FROST signature, ZEC back in the wallet
P2 repay + release zZEC burned on Base → payout to the borrower's new Ironwood address → confirmPayout
P6 close from collateral Market maker repays on Morpho; market maker and borrower paid in one Zcash transaction
P3 liquidation Bot liquidates after a price drop; liquidator paid with ovk = None; the remainder stays the borrower's and is released
Partial release (D14) Part of the ZEC paid out while the loan stays open (LTV after ≤ 50 %), the rest released later
Failure handling Payouts with two signers offline, a signer restarted mid-payout, and a payout that expired unmined and was rebuilt
Threshold demo Two signers switched to "compromised" sign a sweep, three refuse, nothing moves
Vault verification The borrower scans the vault with its viewing key (UFVK)
Tier 2 deposit proof The deposit statement on a real deposit: the zkVM's journal equals the native one (20.2 M cycles), the relay verified the anchor header's Equihash on-chain
Tier 3 commit before sign A real payout's SpendCommit signed by the three chosen signers in FROST round 1, recorded on Base, and checked by every signer three blocks deep before it released its share; then mined and confirmed
Tier 3 violation statements F1 holds on a real Testnet vault spend and the slasher's tests slash with exactly that journal; F1 and F3 run in the zkVM with the native journal
Real seals of the pinned guests Real Groth16 seals of deposit-test, violation-test (F1 and Included) and payment-test pass RISC Zero's router on Base Sepolia and Base mainnet forks; the STARKs take 4 to 37 s on an RTX 4090
Proven delivery A disputed confirmation made final by the payout's inclusion proof 2 min 51 s after the dispute; routine proofs inside the dispute window
Zecured Swap Buy and Sell orders settled on payment proofs (payment memo "ZSWP" || orderId, ovk none); an unpaid order refunded after its pay window
Theft demonstration Three compromised signers sweep a vault; the borrower's viewing key detects F1 two minutes later; all five bonds slashed; the victim's debt repaid and the rest of its ZEC's value paid in USDC (the timeline)
The app in a browser Gasless: open, deposit, borrow with pre-liquidation terms, deleverage and close from collateral (Safe signatures), repay and release, a claim paid from the bonds
Live on Base Sepolia (27 October) The core flows on a public chain: a deposit minted on its Groth16 proof (made on a rented RTX 4090) through RISC Zero's router, a sponsored borrow from an account that never held ETH, a close from collateral paid in one 3-of-5 FROST Zcash transaction whose delivery was then proven on Base, and a swap settled on the proof of its Zcash payment (docs/TESTING.md § 2c)

Evidence for every row (block heights, txids, timings) and how to reproduce it: docs/TESTING.md; the live run's transactions and contracts are below. No deployment is kept running through judging (D82, D112): the live run's servers and GPU stopped after it, and the whole system runs on a local fork (Running it).

The live run on the explorers

Every step of the recorded run of 27 October, on ZecBlock (Zcash testnet) and Basescan (Base Sepolia); its timings and the rehearsal before it are in docs/TESTING.md § 2c.

Step Chain Transaction
The vault's address: shielded only (the explorer shows its Orchard receiver, never a balance or a history) Zcash utest1cj0ssr88…fzjc29hjgv
The deposit, 0.05 TAZ, Ironwood → Ironwood (block 4,548,243) Zcash e5eaf50d…75825c
The deposit proven in zero knowledge, through RISC Zero's router Base 0x92af7a0f…64384b
zZEC minted into Morpho Base 0xbfe27bc5…f9e219
10 MockUSDC borrowed by a brand-new account, gas sponsored Base 0x0e17d8c2…469c9d
Closed from collateral Base 0xf9c5f4bc…2059e3
Both payouts in one 3-of-5 FROST transaction (block 4,548,312) Zcash 0e82a053…3e29fa
The payouts' delivery proven on Base: the claims final Base 0xee567c75…3edce9
Swap: the order opened from the buyer's own wallet, 5 MockUSDC locked Base 0x27198f05…180e1a
Swap: the maker paid 0.00391343 TAZ (block 4,548,320) Zcash fa6ce860…245ee0
Swap: the payment proven on Base Base 0xac5ad94f…c318fa
Swap: settled, the tokens released to the maker Base 0xd1c1989d…038d69

We publish both chains' transactions of our own demo vault as evidence; nothing on Base links them (0 of the run's 181 Zcash identifiers appear there).

The contracts, all verified on Basescan (contracts/deployments/base-sepolia{,.swap,.relay,.paymaster}.json):

Contract Address
ZecuredRegistry 0xDC9C7E6abeF85A7bE0C65e4208bC87D098b86479
ZecuredSpendLogic 0xaED9Fea923D8DD915f5A1367b9bc67DdC763e94a
ZecuredIntake 0x90e5F23645529e5009eD3179ae0D6D9F1D0FbF78
ZZEC (zZEC, the collateral receipt) 0xAa823B4A4339f371fd3BB3cd502e8D3feCEb2e83
SettlementAdapter 0xCFa5c24718Cc87C4731D1969Fd6a6EC88f3a74cb
SignerBondV2 (the signers' bonds) 0x58A1b34a9a641F03C62D5D3A7Fb3D3AD85269e1d
ZecuredSlasher 0x3983F1e1C7929A2208296895F01cE0d422917594
ZecuredCompensator 0x8F5cBBa2264D62ca16228eDd10253e3284bEB174
ZecuredVersions 0xae56343593A54C93043e0997c509607736EBEEA9
ZecuredSwap 0xb8Cc1E883FFF77c098802317cC98A966248948fC
ZcashHeaderRelay (Equihash on Base) 0xEc34aA21cDe1366b397a99A4d90fc6B2f02cFaEa
MockUSDC (MOCK: the loan token) 0x637BBA9C47190C7a1ab2412F72cF0A9c088dE58b
MockZecOracle (MOCK: the ZEC price) 0x5CF271B75eC511FB12C3667a1dfc69AD566A12a9
DemoShockOracle (DEMO: the market's oracle) 0x53a3D268683Cd748b3B71B55a202dF48B7cE633A
DemoCapPrice (DEMO: the coverage cap's price) 0xeC45AeDa5f0013fd37c9767eC79892Dd55834111
SingletonPaymasterV7 (our paymaster, Pimlico's contract) 0x86c16062083a64aDb333404204346C9e4E03bb1e

Used, not ours: Morpho Blue, RISC Zero's verifier router, ERC-4337 EntryPoint v0.7.

What is real and what is a mock

Real, as run on Zcash testnet and Base Sepolia (live on 27 October 2026), and on forks of Base Sepolia and Base mainnet:

  • Shielded Ironwood transactions on Zcash testnet (NU7).
  • 3-of-5 FROST threshold vaults (re-randomized FROST, RedPallas), keys per ZIP 2005, and every signer's per-payout policy checks (P0 to P6).
  • The Zcash header relay, which verifies Equihash on-chain; the deposit, violation and payment statements, run in the RISC Zero zkVM. In the live run, the deposit, inclusion and payment seals were made on a rented RTX 4090 and accepted by RISC Zero's verifier router on Base Sepolia: deposits minted on their proofs, a payout's delivery proven, a swap settled on its payment proof. Real seals of every pinned guest (statement v2: deposit, F1, Included, payment), and of a real deposit and a real F1 spend, also pass the router on Base Sepolia and Base mainnet forks (RealSealsV2.t.sol, RealGroth16Seal.t.sol).
  • Morpho Blue's own deployment on Base Sepolia (live, and on forks); the v3 contract suites (533 tests) also pass on a Base mainnet fork, against mainnet's Morpho Blue and RISC Zero router.
  • The contracts: ZZEC, ZecuredRegistry and ZecuredSpendLogic, ZecuredIntake, SettlementAdapter, SignerBondV2 (USDC bonds), ZecuredSlasher, ZecuredCompensator, ZecuredVersions and ZecuredSwap, deployed on Base Sepolia for the live run and verified on Basescan (contracts/deployments/base-sepolia.json, base-sepolia.swap.json); ZecuredPriceFeed for mainnet (tested against the live Chainlink and API3 feeds on a Base mainnet fork).
  • Liquidation, close from collateral and refunds of non-conforming deposits; Tier 3 (docs/TIER3.md): every payout committed on Base before it is signed, bonds, slashing, compensation, the liveness penalty and disputes of a confirmation, and the watch --tier3 watchtower.
  • Zecured Swap: ZecuredSwap, the payment statement in the RISC Zero zkVM and the market maker (the app's Buy page uses the user's own wallet; Sell is on the command line); the swap's runs paid real Testnet ZEC, and in the live run the order settled on a real proof through RISC Zero's router.
  • Our own RPC, bundler and paymaster proxy with a sponsorship allowlist; in the live run, our own paymaster contract (SingletonPaymasterV7) and alto bundler on Base Sepolia sponsored the loan account's UserOps.
  • The app's pages, the same in the live build and in the judges' replay.

Mock or demo, in the runs (labelled MOCK or DEMO in the code):

  • The ZEC/USD price on testnet: MockZecOracle (owner-set; the deploy script starts it at $1,265, near ZEC's price in October 2026) behind DemoShockOracle (DEMO: an owner-set shock, in public steps, to show a liquidation); the coverage cap reads it through DemoCapPrice (MOCK / DEMO: always fresh). Mainnet uses a ZecuredPriceFeed over Chainlink's ZEC/USD (Standard proxy) and API3's ZEC/USD instead, and the deploy script refuses the demo oracle there (docs/MAINNET.md).
  • The loan token on testnet: MockUSDC, a test token we mint (MOCK: no value). The contracts take any 6-decimal USDC; the mainnet configuration names Circle's USDC.
  • The market's liquidity and bots: our deployer supplied the 100,000 MockUSDC the loans borrow; the market makers and the liquidator are ours (anyone can run them).
  • The verifier in the local-fork runs: MockRiscZeroVerifier (MOCK: it accepts unproven journals, and also plays the router and its emergency stop for the version registry), fed by mock-host (MOCK, TEST ONLY), which checks each witness with the native statement and seals its journal where RISC Zero does not run (Windows). The live run on Base Sepolia used RISC Zero's router and real seals (above).
  • Sponsorship on the local fork: Pimlico's mock paymaster (tools/local-aa) behind our proxy's allowlist, with a local alto bundler.
  • The payment statement's test fixture is synthetic (D68); the swap's fork runs paid real Testnet ZEC and were proven with the MOCK verifier, the live run's with RISC Zero's router.
  • Signer independence: all five nodes are run by us; the Tier 3 bonds show the mechanism, not economic security.
  • DEMO: on testnet only, the operators' demo wallet pays a vault real TAZ for a visitor without testnet ZEC ("Pay it with demo TAZ"), takes back a closed loan's ZEC ("Send my ZEC back to the demo wallet") and can receive a swap ("DEMO: use the demo wallet's Ironwood address"). The recorded run's deposit was paid this way; its close and its swap paid our CLI wallet.
  • DEMO: the theft demonstration runs on its own deployment with a 2-minute challenge window, never on the lending deployment.
  • DEMO: the judges' replay of the app (app/lib/replay, a static site with no backend): an in-browser simulator plays the recording of our live run of 27 October (its real transactions, each linked to its explorer) in place of our servers, and it carries no private key, salt or viewing key (app/README.md).

Test-only mocks, never deployed:

  • Morpho in the symbolic checks and the compensator's unit tests (MockMorphoForHalmos, MockMorphoForCompensator): Halmos has no fork mode; the fork tests use the real Morpho deployment.
  • Unit-test peers: MockCompensatorRegistry, MockCompensatorSlasher, MockCompensatorBond, BondV2Mocks, MockSignerBond (the bond and slasher views set by hand), MockCapPrice, MockHeaderRelay, MockAggregator (a Chainlink-style source for the price feed's tests); IntakeHarness (TEST ONLY) activates a vault without a proof.

Test fixtures: tier2/statement/tests/fixtures/TEST-ONLY_* hold the viewing key and the deposit witness of one disposable Testnet vault, made only for the tests (never a demo vault), and tier2/payment-statement/tests/fixtures/TEST-ONLY_* a synthetic payment witness derived from it. The other keys in the repository are published test vectors (such as ZIP 2005's) and anvil's well-known development keys.

Repository layout

Path What
contracts/ Solidity + Foundry: ZZEC, ZecuredRegistry (vaults, claims, exits, Tier 3 lineage) with ZecuredSpendLogic (spend commitments, disputes), ZecuredIntake (deposit proofs and the gates of minting), SettlementAdapter, SignerBondV2, ZecuredSlasher, ZecuredCompensator, ZecuredVersions, ZecuredSwap, ZecuredPriceFeed (mainnet), mocks, and the Zcash header relay (src/relay/); 804 tests (6 skipped without a Base mainnet RPC), four invariant campaigns (35 invariants) and 22 symbolic checks (Halmos) (docs/INVARIANTS.md)
signer/crates/zcash-ops Ironwood scanning, PCZT build/prove/sign/extract, ZIP 2005 vault keys
signer/crates/threshold FROST DKG and re-randomized signing
signer/crates/policy P1 classification, payout ledger (I10), PCZT verification against the plan
signer/crates/node A signer node: DKG, owner-request checks, policy-gated signing
signer/crates/coordinator Keyless orchestrator: DKG relay, attestations, payouts, confirmations
signer/crates/base-client EIP-712 (cross-checked with Solidity), Registry events
signer/crates/deposit-witness Tier 2: the ZIP 221 history tree (V3, Ironwood) from public chain data, and the witness of a deposit proof
tier2/ Tier 2 and 3 and the swap: the deposit and violation statements (statement/), the payment statement (payment-statement/), their RISC Zero guests and the host (separate Cargo workspace)
ops/ zecured-ops: prepares, signs, collects and submits the signers' operator messages (docs/OPERATIONS.md § 8)
swap/ Zecured Swap: the shared swap crate and the market maker bot (zecured-swap-maker) (docs/SWAP.md)
tools/leak-scan Searches a deployment's events, calldata and storage for the Zcash identifiers of our own runs (docs/TESTING.md § 4)
tools/recipient Draws the salt of a destination that must be fixed before any backend runs (the treasury's FEE_COMMITMENT) and prints its commitment
tools/local-aa A local ERC-4337 bundler and Pimlico's mock paymaster for an anvil fork (MOCK, never on a public network)
backend/ Public API + privacy proxy (RPC, bundler, paymaster allowlist)
bots/ Liquidator and market maker
cli/ zecured-wallet: Ironwood CLI wallet and borrower actions
app/ Next.js app: borrow, positions, swap, evidence and privacy pages; it also builds as the judges' replay (app/README.md)
site/ The site: a landing page told by a 3D scene (three.js) and a docs page; static, its own fonts, no request to anyone else (docs/SITE.md)
docs/ Architecture, Interfaces, Threat model, Decisions, Invariants, Economics, Mainnet, Swap, Testing and evidence, Security review, Tier 2 design, Tier 3 design, Operators, Judges' FAQ, Draft ZIP: Shielded Collateral Vaults, spikes

Running it

Three ways, from nothing to install to the whole system.

1. The replay. Open the app as a guided replay (DEMO): the same pages, with a simulator that replays our live run of 27 October in place of our servers. To run it yourself: cd app && pnpm install && node scripts/build-replay.mjs --dev --port 3001 (app/README.md).

2. The tests. Prerequisites: Rust (the repo pins 1.99.0 via rust-toolchain.toml), Foundry 1.8.5 (foundryup -i 1.8.5, the version CI runs), jq, Python 3, and pnpm 10 for the app (app/package.json pins it).

cd contracts && forge test --isolate && cd ..   # 804 tests; the fork tests read BASE_SEPOLIA_RPC_URL,
                                                # or the public https://sepolia.base.org when it is unset
cargo test --workspace --locked                 # the Rust workspace: signers, coordinator, backend, bots, CLI
cd app && pnpm install && pnpm test && cd ..    # the app, offline

3. The whole system, on a local fork of Base Sepolia (below): five signer processes, the coordinator, the contracts, the bots and the app, paying real Zcash testnet notes. Plan an afternoon; our runs used one 16 GB desktop. Get TAZ from the Valar faucet (https://faucet.testnet.valargroup.dev, 0.125 TAZ a day per address); on 27 October it refused the Ironwood-only address zecured-wallet address prints ("the wallet cannot pay this address"), so if it still does, send TAZ from another testnet wallet. The Zcash side talks to https://testnet.zec.rocks:443 (the public light wallet server on NU7).

Local Base fork

Everything Base-related runs against a local fork of Base Sepolia, including the app's sponsored UserOps (a local alto bundler and Pimlico's mock paymaster replace the hosted ones, no API key). Minting needs a deposit proof (Tier 2) and all five signer bonds, so the fork always runs both:

cp .env.example .env          # first fill in DEPLOYER_PRIVATE_KEY, SIGNER_ADDRESSES and the other keys (never commit .env)
(cd tier2 && cargo build --release)           # RISC Zero guest and host (rzup toolchain; on Windows see below)
cargo build -p zecured-deposit-witness --bin update-headers && cargo build -p zecured-bots
mkdir -p data/tier2 && python3 -I contracts/test/relay/tools/zcash_p2p_headers.py --count 1 \
  --from-height 4465014 --from-hash 000008e7829f03777ab23991cc9dba1521a1cb3fe5ce2dffa6824c7d7f935ac9 \
  --out data/tier2/headers_nu7.json                         # first run only: a header just before the NU7 epoch
./target/debug/update-headers data/tier2/headers_nu7.json   # then every header since, over P2P
scripts/local-fork.sh start   # anvil fork, fresh deployment (contracts/deployments/anvil.json), the header relay fed
                              # from the Testnet, the guests' image ids, bundler :4337, paymaster :4338
export ZECURED_DEPLOYMENTS=$PWD/contracts/deployments/anvil.json BASE_SEPOLIA_RPC_URL=http://127.0.0.1:8545 \
       BUNDLER_URL=http://127.0.0.1:4337 PAYMASTER_URL=http://127.0.0.1:4338
scripts/signer-network.sh init && scripts/bond-signers.sh   # nothing is minted before all five bonds are in
DEPOSIT_PROOF_HOST=$PWD/tier2/target/release/zecured-tier2-host DEPOSIT_PROOF_MOCK=true \
  scripts/signer-network.sh start             # 5 signer nodes + backend; the coordinator proves each deposit
./target/debug/zecured-bots market-maker &    # and: zecured-bots liquidator &

The fork verifies proofs with MockRiscZeroVerifier (MOCK): with DEPOSIT_PROOF_MOCK=true the coordinator seals each deposit's journal for it instead of proving it, as in our runs. Real proofs need RISC Zero on a CUDA GPU (next section).

A borrower, from the command line (its Base commands are TEST ONLY: the loan owner's key acts as the account and pays its own fork gas; the app uses a fresh, gasless Safe instead):

./target/debug/zecured-wallet init                       # Ironwood wallet; fund it with testnet TAZ
./target/debug/zecured-wallet loan open --amount 0.01    # opens a vault, prints the ZIP-321 URI
./target/debug/zecured-wallet loan deposit <vault-id>
./target/debug/zecured-wallet loan status <vault-id>
./target/debug/zecured-wallet loan viewing-key <vault-id> > ufvk.txt   # keep it local
./target/debug/zecured-wallet watch --ufvk-file ufvk.txt --from <height> --tier3 --claims-of <vault-id>
# a finding prints the build-violation command that builds its proof's witness (F1, F2, F3)

Tier 2: minting on a deposit proof

On the local fork the proof is checked by MockRiscZeroVerifier (MOCK). Where RISC Zero does not run (Windows), target/debug/mock-host (MOCK) stands in for the zkVM host: it checks the witness with the native statement and seals its journal for the MOCK verifier, with the deployment's image ids in ZECURED_MOCK_DEPOSIT_IMAGE_ID, ZECURED_MOCK_VIOLATION_IMAGE_ID and ZECURED_MOCK_PAYMENT_IMAGE_ID. The real seal is a succinct receipt (zecured-tier2-host prove, 28.1 s for statement v2's deposit on a rented RTX 4090: scripts/gpu-prove.sh) wrapped in Groth16 (groth16, about a minute with Docker on x86), checked by RISC Zero's verifier router on Base Sepolia; RealGroth16Seal.t.sol runs a real deposit's seal through it, and RealSealsV2.t.sol the seals of the pinned guests (statement v2: deposit, F1, Included and the swap's payment).

Reproducing the guest image ids

The Intake accepts the deposit guest's image id and the slasher the violation guest's, as active versions of ZecuredVersions (adding one takes all five signers and a public delay). The network is a build parameter of the guests (design notes T-09), so each network has its own pair: deposit-test and violation-test (Testnet, the NU7 epoch), deposit-main and violation-main (Mainnet, the NU6.3 and NU7 epochs). All four come from a reproducible build: the guests are compiled in RISC Zero's pinned guest-builder image (risczero/risc0-guest-builder:r0.1.97.0) with tier2/ as the build context, as the guests workflow does on every change. With Docker and rzup's Rust toolchain (rzup install rust; risc0-build asks it for its version even when Docker compiles the guests):

cd tier2
ZECURED_DOCKER_GUESTS=1 cargo build --release -p zecured-tier2-host                     # Testnet
./target/release/zecured-tier2-host image-id              # deposit-test
./target/release/zecured-tier2-host violation image-id    # violation-test
ZECURED_DOCKER_GUESTS=1 cargo build --release -p zecured-tier2-host --features mainnet  # Mainnet
./target/release/zecured-tier2-host image-id              # deposit-main
./target/release/zecured-tier2-host violation image-id    # violation-main

The guests workflow's runs on statement v2 (2026-10-16) gave these, with every parity suite passing (docs/TIER2.md § 8.1 lists the ELFs' hashes):

Guest Image id
deposit-test 0x406c6d268f29270226c775db1fe4bec04acf51ba919e250078125d71c10bff41
violation-test 0x65705f75c5482b2b0cf6fee12760b2364d48213bb2933f3882dc2d92f824866e
deposit-main 0x53f8f8e3664ce908c4ea072f239e18b6fa8b01d68c01fc162b4ae2e31b2cd5c5
violation-main 0x24edd49e52447665146009687b6b85e3f0b6184883cbf635a762048cd1a5ce33
payment-test (swap, 2026-10-17) 0x199bf772d0780413a07081b4889f0473c8ae558c6d8c4dca96eef49b5e486134
payment-main (swap, 2026-10-17) 0x1c2def3eb121bd3240700ca554ab455e03420981856fda3f580b930f731038b2

The ids of a deployment's network must equal the deployed ones (the deployment's record in contracts/deployments/, such as anvil.json on a local fork). A build without ZECURED_DOCKER_GUESTS uses the local risc0 toolchain and gives machine-dependent ids; provers then run the pinned ELFs (ZECURED_DEPOSIT_ELF, ZECURED_VIOLATION_ELF, docs/TIER2.md § 8.1).

Security notes

This is a hackathon build. All five signers are run by the authors, key material is stored unencrypted on the signers' disks (files readable by their owner only), and the code has not been audited by a third party. An internal security review of the off-chain code, its fixes and the remaining risks are in SECURITY_REVIEW.md; see THREAT_MODEL.md for what leaks, what doesn't, and why.

License

MIT

About

Borrow against shielded ZEC, or swap USDC into it. No bridge, no custodian: only the proof crosses. ZECATHON 2026, Cross-Chain track (testnet).

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages