Keep it shielded. Spend it in dollars.

Pay anyone an exact dollar amount straight from shielded ZEC. A lock on Zcash and a lock on the dollar chain open with the same key, so either both sides settle or both get their money back. No bridge, no custodian, no wrapped token.

Open source (MIT). Escrows live on six public testnets. Experimental and not audited.

Paying Acme Coffee25.00 USDC on Base
Settled
  1. 1You lock 0.019714 ZEC on Zcash7c1e…a90b
  2. 2The maker locks 25.00 USDC on Base0x4b2f…e1d7
  3. 3Acme Coffee is paid, revealing the key0x91ac…03fe
  4. 4The maker opens the ZEC lock with itd03a…5c7e
Illustration with example values. T is when the dollar lock expires and N is when the ZEC refund opens. T always comes long before N, so whoever learns the key has time to use it.

Pay with a link.

A shop shares a link with an address and a price. You pay from your shielded balance, and they receive exactly what they asked for, on the chain they chose. The ZEC rounds up, never the dollars.

Acme CoffeeSettled

25.00 USDC

received on Base, exactly as requested

You paid
0.019714 ZECfrom your shielded balance
Held by
Two on-chain locks. No custodian.
If it stalls
Both sides refund
Base claim
0x91ac…03fe

Example values

Two locks, one key.

Each chain holds one side of the trade, and taking your side hands the other party the key to theirs. The other party is a maker: it quotes a price, locks the stablecoins and earns the spread. Anyone can run one.

  1. 1Your device

    You pick a secret

    It stays on your device until you claim your dollars.

  2. 2Zcash

    You lock ZEC

    Only the secret lets the maker take it. If the maker never does, it's yours again at block N.

  3. 3EVM chain

    The maker locks dollars

    Once your ZEC is confirmed, the maker locks stablecoins under the same secret. The maker can refund them only after time T, long before N.

  4. 4EVM chain

    You claim the dollars

    Claiming reveals the secret. In a payment, the dollars go straight to the person you're paying.

  5. 5Zcash

    The maker claims the ZEC

    It uses the secret your claim revealed. Both sides are paid, and nobody held anyone else's money.

Standard mode shown. Private mode replaces the shared hash with adaptor signatures; the guarantees are the same.

Why nobody can cheat

Every outcome is enforced by the Zcash script and the EVM contract, not by a person. The ZEC refund window always outlasts the dollar lock by at least 2×, so whoever learns the secret has time to use it, and each side verifies the other's lock on-chain before committing anything more.

IfThen
The maker never locksYou refund your ZEC after block N.
You never claimThe secret stays hidden, so the maker can't touch the ZEC. The maker refunds after T, and you refund after N.
You claimThat reveals the secret, and the maker has until block N, long after T, to use it.
Someone copies the secret from the mempoolThere is nothing to steal. The dollar lock only pays the recipient set when it was created, and the ZEC lock also needs the maker's signature.

Privacy in three levels.

Removing the middleman is step one. The rest is about what the two chains can reveal about you, and each level reveals less.

LevelWhat it doesOn Zcash, observers seeValue linking the chains
1HygieneShippedShield before and after, a fresh recipient, a relayed claim, standard amounts and timing jitter.A transparent HTLCThe same sha256 hash on both chains
2Adaptor signaturesShipped, experimentalNo hash anywhere. The ZEC sits in a 2-of-2 multisig, and the dollar escrow releases on a one-time key's signature.An ordinary 2-of-2 multisig spendNone
3Fully shielded legIn researchFROST 2-of-2 shielded notes with Schnorr adaptor signatures, so the ZEC never leaves the shielded pool.A normal shielded transactionNone

At every level your wallet history stays out of the swap. Amounts and timing can still be correlated; standard amounts and jitter make that harder. Read the privacy design

Build on it.

Give your users dollar payments from shielded ZEC in a few lines of TypeScript. The SDK runs both chains, the timeouts and the recovery, and your app never touches their funds. It's MIT-licensed: embed the taker, or run a maker and earn the spread.

npm install @paralock/sdk
  • @paralock/sdk

    createTaker and createMaker: both modes, exact-amount payments, privacy options, crash-safe recovery.

  • @paralock/zcash

    HTLC and 2-of-2 locks, v5 transactions and ZIP 244 signing, in TypeScript. Runs in browsers.

  • @paralock/adaptor

    ECDSA adaptor signatures on secp256k1 with DLEQ proofs. Runs in browsers.

  • @paralock/evm

    viem clients for the hashlock and signature escrows on any EVM chain.

  • @paralock/lightwalletd

    Use a public lightwalletd server as the Zcash backend. No full node to run.

Run the whole stack locally

pnpm paralock is the CLI inside the repo, so there's nothing to install globally. You need Node 20+, pnpm, Docker or OrbStack, Foundry 1.8+ and ths (a one-command Zcash regtest).

  1. Get the code.

    git clone https://github.com/bitfalt/Paralock
  2. Install dependencies, then build the packages and the CLI.

    cd Paralock && pnpm install && pnpm build
  3. Check every prerequisite. Anything missing comes with its install command.

    pnpm paralock doctor
  4. Start a Zcash regtest, all six EVM chains, the contracts, a maker and this app at localhost:3000.

    pnpm paralock dev --web --evm all
pay.tsTypeScript
import { SwapFailedError, createTaker } from "@paralock/sdk";
import { LightwalletdClient } from "@paralock/lightwalletd";

const taker = createTaker({
  zcash: { network, key, node: new LightwalletdClient("zec.rocks:443") },
  evm: { base: { rpcUrl, chainId, htlc, adaptorEscrow, account } },
  maker: "https://maker.example",
  store: "./swaps", // secrets are saved before anything is broadcast
});

// Pay Acme Coffee exactly 25 USDC on Base, in private mode.
const quote = await taker.quoteExactOut(25_000_000n, "base", "adaptor");

try {
  await taker.swap(quote, { recipient: "0x5aE1…c9F2", label: "Acme Coffee" });
} catch (err) {
  // The maker stalled: take the ZEC back after the refund height.
  if (err instanceof SwapFailedError) await taker.refund(err.swap.id);
}
  • A few lines to pay

    createTaker, quoteExactOut, swap. The recipient gets exactly the amount you quoted, on the chain they chose.

  • Never custodial

    Your app never holds user funds. They sit in two on-chain locks and move only when the protocol says so.

  • Crash-safe

    Secrets and refund data are saved before anything is broadcast. resume() and refund() work from a fresh process.

  • One-command devnet

    pnpm paralock dev starts a Zcash regtest, six EVM chains, the contracts and a maker on your laptop.

  • No node to run

    @paralock/lightwalletd talks to public lightwalletd servers, and the escrows are live on six public testnets.

  • Agent-ready

    llms.txt, every docs page as Markdown, a read-only MCP server and an installable skill for coding agents.

What it's for.

Anywhere shielded ZEC needs to become exact dollars, without handing it to someone in the middle.

  • Private payroll

    Keep the treasury in shielded ZEC and pay each contributor an exact amount on the chain they choose. One devnet run paid $4,665.75 to four people on four chains.

    See the example
  • Sell a secret for ZEC

    Make the secret a decryption key. The seller is paid only by revealing it on-chain, and the buyer reads it from the claim. No escrow agent.

    See the example
  • Wallets and agents

    A Zcash wallet or an AI agent embeds the SDK and pays anywhere stablecoins work: exact amounts, no exchange account.

  • OTC trades

    Settle large ZEC trades through two on-chain locks instead of trusting the other desk. The price is fixed before anything locks.

Zcash to six dollar chains.

The same two escrow contracts on every chain: Tempo settles in OUSD, the rest in USDC. Both are deployed on all six public testnets, and the whole set runs locally with one command.

ZcashZEC, from the shielded pool
  • TempoOUSD
  • BaseUSDC
  • EthereumUSDC
  • ArbitrumUSDC
  • Robinhood ChainUSDC
  • HyperEVMUSDC

Questions

Is it really trustless?

Nobody but you and the maker ever holds the funds, and neither of you can take the other side without paying. The Zcash script and the EVM contract enforce it: either both sides settle or both refund.

What you still rely on: the code, which is experimental and not audited, and the maker's price, which you accept before anything locks. You also need to claim before T; the SDK watches both chains and does it for you.

Can the secret be front-run?

No. Anyone who copies the secret from the mempool can submit the claim, but the dollar lock only pays the recipient fixed when it was created, and the ZEC lock also requires the maker's signature. In private mode no secret is published at all: the claim publishes a signature that only the maker can turn into the key.

How is this different from CrossPay or NEAR Intents?

Zodl's CrossPay spends shielded ZEC through NEAR Intents market makers, and deposits move through NEAR's PoA bridge, which holds them in a treasury. THORChain's ZEC pool keeps funds in validator-held vaults. Both are good products, but an intermediary holds your money while the swap is in flight.

With Paralock, only the two parties ever hold funds, the SDK is self-hostable, and it settles natively on Tempo, which isn't a NEAR Intents destination.

Is the Zcash leg private?

Partly, depending on the mode. Your shielded balance never touches the swap: only the swap amount is unshielded, and change and refunds are shielded again.

In standard mode the Zcash lock is a transparent HTLC carrying the same hash as the dollar lock, so the two legs can be paired. Private mode removes that hash, and Zcash sees an ordinary 2-of-2 multisig. A fully shielded Zcash leg through FROST is still in research.

Can I use it with real money?

Not yet. The escrows are deployed on six public testnets, and the Zcash side runs on testnet through public lightwalletd servers, with no node to run. The hosted app uses a simulated maker. The code is experimental and has not been audited, so mainnet isn't supported.

Try a payment.

The hosted app runs against a simulated maker, so nothing real moves. Try all three endings (settled, the maker vanishes, you never claim) and watch both locks.

Launch app