> ## Documentation Index
> Fetch the complete documentation index at: https://docs.li.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Zcash Providers

> Zcash Architecture

export const SupportedTools = ({chainId}) => {
  const [chains, setChains] = useState(null);
  const [tools, setTools] = useState(null);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchChains = async () => {
      try {
        const response = await fetch('https://li.quest/v1/chains?chainTypes=EVM,SVM,UTXO,MVM,TVM,STL');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const jsonData = await response.json();
        setChains(jsonData.chains);
      } catch (err) {
        setError(err.message);
      }
    };
    fetchChains();
  }, []);
  useEffect(() => {
    const fetchTools = async () => {
      try {
        const response = await fetch('https://li.quest/v1/tools');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const jsonData = await response.json();
        setTools(jsonData);
      } catch (err) {
        setError(err.message);
      }
    };
    fetchTools();
  }, []);
  const parseBridges = (bridges, selectedChainId) => bridges.map(bridge => {
    const fromChainIds = bridge.supportedChains.filter(connection => connection.toChainId === selectedChainId).map(connection => connection.fromChainId);
    const toChainIds = bridge.supportedChains.filter(connection => connection.fromChainId === selectedChainId).map(connection => connection.toChainId);
    const connectedChainIds = [...new Set([...fromChainIds, ...toChainIds])];
    return {
      ...bridge,
      fromChainIds,
      toChainIds,
      connectedChainIds
    };
  }).filter(bridge => bridge.connectedChainIds.length).sort((a, b) => b.connectedChainIds.length - a.connectedChainIds.length);
  const parseExchanges = (exchanges, selectedChainId) => exchanges.filter(exchange => exchange.supportedChains.includes(selectedChainId));
  const renderChains = chains => <div className="p-2">
      <div className="flex flex-wrap gap-4">
        {chains.map(chain => <div key={chain.key} className="relative group flex-shrink-0">
            <img src={chain.logoURI} alt={chain.name} className="w-10 h-10 rounded-full object-cover not-prose" />
            <div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 whitespace-nowrap z-10">
              {chain.name}
            </div>
          </div>)}
      </div>
    </div>;
  const renderTools = (tools, chains) => {
    const bridges = parseBridges(tools.bridges, Number(chainId));
    const exchanges = parseExchanges(tools.exchanges, Number(chainId));
    return <div>
      <h2>Supported Bridges</h2>
      <ul>
        {bridges.map(bridge => <li>
            {bridge.name} (<code>{bridge.key}</code>) connects to:
            {renderChains(chains.filter(chain => bridge.connectedChainIds.includes(chain.id)))}
          </li>)}
      </ul>

      <h2>Supported Exchanges</h2>
      <ul>
        {exchanges.map(exchange => <li>{exchange.name} (<code>{exchange.key}</code>)</li>)}
        {exchanges.length === 0 ? '-' : ''}
      </ul>
    </div>;
  };
  if (error) return <div>Error: {error}</div>; else if (chains && tools) return renderTools(tools, chains); else return <div>Loading...</div>;
};

LI.FI offers bridging of native ZEC in both directions between Zcash and EVM chains, Solana, Bitcoin, Tron, Stellar and Hyperliquid. Zcash carries a single asset, so there are no same-chain swaps and every Zcash route is a bridge.

<Note>
  Zcash chainId in LI.FI is `20000000000005`. The chain key is `zec` and the chain type is `UTXO`.

  `GET /v1/chains` returns EVM chains only by default. Pass `chainTypes` to include Zcash, for example `GET /v1/chains?chainTypes=EVM,UTXO`.

  Native ZEC is addressed as `zcash` with 8 decimals. Amounts are always expressed in zatoshi (1 ZEC = 100,000,000 zatoshi).
</Note>

<SupportedTools chainId="20000000000005" />

## Requesting a quote

A quote for Zcash can be requested using the same endpoints as EVM. Only the chain ids, the token address and the transaction data differ.

### Addresses

LI.FI sends from transparent Zcash addresses only. It pays out to a transparent address, or to a unified address that carries an Orchard receiver.

| Address form | Prefix | As `fromAddress` | As `toAddress` |
| - | - | - | - |
| Transparent, pay-to-public-key-hash | `t1` | Yes | Yes, on both bridges |
| Transparent, pay-to-script-hash | `t3` | Yes | Yes, on both bridges |
| Unified, with an Orchard receiver | `u1` | No | NearIntents only |
| Unified, without an Orchard receiver | `u1` | No | No |
| Unified, with a receiver type other than transparent, Sapling or Orchard | `u1` | No | No |
| Sapling | `zs1` | No | No |

<Note>
  `fromAddress` when the source chain is Zcash accepts:

  * **A single transparent address**: `t1cSswzBCTiKZ9XwEUCurkfGSQFGunU5CDM`
  * **Several addresses**, separated by a semicolon: `t1address1;t1address2;t3address3`

  Extended public keys (xpub) are **not** supported on Zcash. Pass explicit addresses.

  `toAddress` when Zcash is the destination chain accepts a `t1` or `t3` address, or a `u1` unified address with an Orchard receiver. When bridging out of Zcash, `toAddress` is a regular address on the destination chain.
</Note>

<Note>
  **Unified destinations.** A unified address bundles several receivers into one address. NearIntents pays the Orchard receiver, even when the address also carries a transparent receiver. A unified address without an Orchard receiver is refused, because a bridge would pay its transparent receiver and the payout would not be shielded.

  Unit pays out to transparent addresses only. For a unified `toAddress`, `/v1/quote` returns no Unit route, and `/v1/advanced/routes` lists the Unit route in `unavailableRoutes.filteredOut` with the reason. A step posted to `/v1/advanced/stepTransaction` that pays a unified address through Unit is refused.
</Note>

<Note>
  **Refused addresses.** A refused Zcash address returns HTTP 400 with error code `1011` (`ValidationError`). The message names the field, the reason and the accepted forms, for example:

  ```
  Invalid toAddress: Sapling (zs1) addresses are deprecated on Zcash and no bridge pays out to or accepts one; use a transparent (t1 or t3) address, or a unified (u1) address with an Orchard receiver
  ```
</Note>

<Note>
  **UTXO collection.** LI.FI pools the UTXOs of every address in `fromAddress` and selects them largest-first until they cover the transfer plus the network fee. The pooled set must cover both, otherwise no quote is returned.

  Two kinds of UTXO are never selected:

  * A UTXO with **zero confirmations**. Wait for one confirmation.
  * A **coinbase** (mining reward) output. Zcash requires a coinbase spend to move the value into a shielded pool, which a transparent transaction cannot do.

  **Change** goes back to the address that contributed the largest value among the selected inputs.

  **Signing.** Every address that contributes an input must sign the transaction.
</Note>

## Token approvals

Zcash needs no token approval. Zcash is a UTXO chain with one asset, so a transfer is authorized by the signature on the transaction itself. Ignore `approvalAddress` on a Zcash step.

## NearIntents integration

Registered as the tool key `near` ("NearIntents"). It bridges native ZEC between Zcash and 15 chains: Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, BNB Smart Chain, Gnosis, Berachain, Monad, X Layer, Bitcoin, Solana, Tron, and Stellar.

<Note>
  14 of those chains route in both directions. **Stellar is a destination only**: LI.FI can bridge ZEC to Stellar, but not from Stellar to Zcash.
</Note>

NearIntents is the only bridge that pays out to a unified (`u1...`) address. The payout goes to the address's Orchard receiver, so the ZEC arrives in a shielded balance.

<Note>
  **Contract receiver for a unified address.** A unified address does not fit in 32 bytes. When the source chain is an EVM chain, the LI.FI contract call therefore carries `sha256(toAddress)` as the non-EVM receiver, and the `BridgeToNonEVMChainBytes32` event emits that digest. NearIntents pays the full `toAddress`, which the quote binds to the deposit address.
</Note>

## Unit integration

Registered as the tool key `unit` ("Unit"). It bridges native ZEC in both directions between Zcash and Hyperliquid.

<Note>
  Unit enforces a minimum transfer of **0.07 ZEC** (`7000000` zatoshi) on both sides. A smaller amount returns no quote.

  Unit pays out to transparent (`t1` or `t3`) addresses only. A Hyperliquid to Zcash transfer to a unified `toAddress` gets no Unit route.
</Note>

## Executing a transaction

### Transaction data

`transactionRequest.data` is **not** a PSBT. Zcash transparent transactions are signed with the ZIP-243 signature hash, which is personalized by the consensus branch id and by the transaction's `versionGroupId` and `expiryHeight`. No Bitcoin PSBT library can produce it, so LI.FI returns a structured unsigned transaction instead.

The payload is a JSON object describing a v4 (ZIP-243) transaction:

```json theme={"system"}
{
  "version": 4,
  "versionGroupId": 2301567109,
  "consensusBranchId": 933566043,
  "expiryHeight": 3475269,
  "lockTime": 0,
  "inputs": [
    {
      "txid": "c56bbe3e...5ade",
      "vout": 0,
      "valueZat": "403860",
      "address": "t1cSswzBCTiKZ9XwEUCurkfGSQFGunU5CDM"
    }
  ],
  "outputs": [
    { "address": "t1SdGayz4D5oEbc9wm5RLTP6Do8TRMLdHrf", "valueZat": "388860" },
    { "scriptHex": "6a0a3d7c6c69666920e6552d", "valueZat": "0" },
    { "address": "t1cSswzBCTiKZ9XwEUCurkfGSQFGunU5CDM", "valueZat": "..." }
  ]
}
```

Field notes:

* **`consensusBranchId`** is read live from the node for every quote. It changes at every Zcash network upgrade and it personalizes the signature hash, so never cache it and never substitute a constant.
* **`inputs[].address`** is the address the UTXO belongs to. Derive that input's previous output script from it. **`inputs[].valueZat`** is required by the ZIP-243 signature hash, so keep it.
* **`outputs[].address`** is set on a payment output (transfer, fee collection, change). Build the standard P2PKH or P2SH script for it yourself.
* **`outputs[].scriptHex`** is set on the `OP_RETURN` output instead, and its `valueZat` is always `"0"`. Encode that script verbatim.
* **All amounts are zatoshi strings.** Parse them as big integers, never as floating-point numbers.

Output order is fixed: the transfer to the bridge, then the `OP_RETURN` tracking memo, then any fee-collection outputs, then the change output when there is one.

To execute the transaction:

1. Request the transaction data through the regular quote flow.
2. Serialize the transaction as Zcash v4 (ZIP-243) from the fields above.
3. Sign every input with the key of its `address`.
4. Broadcast the signed transaction to the Zcash network.

<Warning>
  **Risk of modifying Zcash transaction data**

  Do not add, remove, reorder or re-value any input or output, and do not edit the `OP_RETURN` script. The amounts balance to an exact ZIP-317 fee, and the `OP_RETURN` memo is how LI.FI identifies your transfer. A modified transaction can lose the funds, or complete on chain while `/status` never resolves it.
</Warning>

<Warning>
  **Envelope validity window**

  `expiryHeight` is set about 40 blocks ahead of the chain tip, which is roughly 50 minutes. The network drops the transaction once that height passes.

  * Sign and broadcast promptly after receiving the transaction data.
  * Request a new quote if the transaction is stale.
  * Request a new quote if any selected UTXO was spent in the meantime.
</Warning>

### Gas

The network fee is returned in the step's `estimate.gasCosts` in zatoshi (ZEC, 8 decimals).

Zcash has **no fee market**. The fee follows the ZIP-317 formula:

```
fee = 5000 zatoshi x max(2, logicalActions)
```

`logicalActions` is derived from the shape of the transaction, so the network minimum is 10,000 zatoshi (0.0001 ZEC) and there is no fee rate to bid. `gasCosts[0].price` is reported as `0` for that reason, and paying more does not confirm the transaction faster.

Change below 5,000 zatoshi is added to the fee rather than paid out, because such an output costs more to spend later than it is worth.

## Status tracking

Transaction status can be tracked using the regular [status tracking](/introduction/user-flows-and-examples/status-tracking) flow.

<Note>
  When polling `/status` for a transaction that originates on Zcash, always pass `fromChain=zec` (or the Zcash chain id `20000000000005`). Zcash transaction hashes are 64-character hex strings and are otherwise indistinguishable from Bitcoin transaction hashes.
</Note>

<Note>
  **Shielded payouts.** A payout to a unified address goes into the shielded pool, and the amount of a shielded output is not public. For that payout, `receiving.amount` is the amount that NearIntents reports.

  `receiving.txLink` points to [CipherScan](https://cipherscan.app), which shows the shielded (Orchard) part of a payout. Some other Zcash explorers show a shielded payout as 0 ZEC.
</Note>
