Build
Connected Chains
Bitcoin

Interacting with universal contracts on ZetaChain from Bitcoin happens through the Bitcoin Gateway, a threshold signature scheme (TSS) address. The private key to this address is distributed among ZetaChain's validator set using MPC.

The Bitcoin Gateway supports the following operations:

  • Deposit: Send BTC to a ZetaChain account or contract.
  • Call: Trigger a smart contract on ZetaChain using a BTC transaction.
  • Deposit and Call: Deposit BTC and immediately invoke a contract.

There are two ways to interact with the Bitcoin Gateway:

MethodMax PayloadCostRevert AddressBest For
Inscriptions400 KB*Higher (2 tx)CustomizableStructured cross-chain calls, custom logic
OP_RETURN60 bytes**Lower (1 tx)Matches senderSimple deposits and small data payloads

* Limited only by Bitcoin's transaction and witness size limits. Typical payloads range from 1–30 KB; larger payloads may not relay through standard nodes.

** Not counting the required 20 bytes for the universal contract address.

📝 Recommended Usage

For most call and deposit and call operations, use inscriptions with ABI encoding. This supports structured data, complex contract interactions, and custom revert behavior.

For simple deposit operations, especially to EOAs, you can use OP_RETURN, which is lower cost and easier to construct.

Inscriptions enable rich interaction between Bitcoin and ZetaChain by embedding structured metadata into Bitcoin transactions using a commit-reveal flow. This method encodes ABI data and optional Bitcoin revert logic into the Bitcoin blockchain.

Commit and Reveal Each interaction consists of two Bitcoin transactions:

  • Commit: Encodes the payload as a Taproot-inscribed output. It commits to the data but doesn't reveal it yet.
  • Reveal: Broadcasts the actual data that was committed to, including logic for contract interaction on ZetaChain.

✉️ Envelope Format (Witness Script)

OP_PUSHBYTES_32 <32-byte public key> OP_CHECKSIG
OP_FALSE
OP_IF
    OP_PUSH 0x...
    OP_PUSH 0x...
OP_ENDIF

🧩 Payload Format

The inscription data consists of: a 4-byte ZetaChain header and ABI- or Compact-encoded fields (depending on format).

Header

Byte IndexDescription
0Fixed identifier: 0x5a (ASCII 'Z') for ZetaChain inscriptions
1Encoding format (lower nibble). Example: 0x00 = ABI, 0x01 = CompactShort, 0x02 = CompactLong
2Operation code (upper nibble). Example: 0x10 for Call = 0x02 << 4
3Flags bitmask. Indicates which fields are set. Common value: 0x07 (receiver + payload + revert)

Fields

Fields are encoded differently based on the encoding format.

FormatValue
EncodingFmtABI0b0000
EncodingFmtCompactShort0b0001
EncodingFmtCompactLong0b0010

Compact encoding is space-efficient and can be useful when optimizing transaction size. Use CompactShort when all dynamic fields (payload and revert address) are under 255 bytes. Use CompactLong when any field may exceed that threshold.

ABI encoding

For calls involving structured input, ZetaChain uses Ethereum-style ABI encoding. This allows full compatibility with Solidity contracts. You can pass complex types like address, bytes, uint256[], etc., and encode them client-side before embedding them in the inscription.

  • Receiver address: A 20-byte Ethereum-style address of the ZetaChain account or universal contract.
  • Payload: Optional encoded data (e.g., an ABI-encoded function call) for use in the contract’s onCall handler.
  • Revert address (optional): A Bitcoin address to return funds if the cross-chain call fails.
FieldValue
Header4 bytes
ABI dataabi.encode(receiver, payload, revertAddress)

Note: the ABI-encoded data must exclude the 4-byte function selector. Only the packed argument values should be included in the payload.

Compact encoding

Each field is encoded in a more concise form:

[receiver (20 bytes)] + [len][payload bytes] + [len][revert address bytes]
  • Receiver is raw 20 bytes
  • Payload and Revert Address are length-prefixed:
    • CompactShort: 1-byte length prefix (max 255 bytes)
    • CompactLong: 2-byte length prefix (max 65535 bytes)
FieldValue
Header4 bytes
Receiver20 bytes
Payload[len:1 or 2] + bytes
Revert[len:1 or 2] + address bytes

🔁 Operation Types (OpCode)

OperationCodeDescription
Deposit0b0010Only receiver, no payload. Optional revert address
DepositAndCall0b0000Transfers BTC and invokes onCall() with payload. Revert required
Call0b0001Sends no BTC, invokes onCall() with payload. Revert optional
Invalid0b0011Reserved
  • No call data is included.
  • BTC is deposited to the ZRC-20 equivalent on ZetaChain.
  • Useful when transferring BTC as ZRC-20 BTC to an EOA on ZetaChain

📌 Example:

  • The encoded data includes:
    • Contract address (as receiver)
    • Payload
  • No BTC is transferred to the contract — it's a logic-only interaction.
  • Ideal for triggering universal contract execution that does not require BTC

📌 Example:

  • Combines the previous two:
    • BTC is transferred and
    • A contract function is invoked with encoded parameters.
  • Enables rich interactions like "send BTC and trigger a swap", "deposit and mint", or any other cross-chain composable logic.

📌 Example:

Use inscriptions when:

  • Your payload exceeds 60 bytes (which OP_RETURN can’t handle)
  • You need to encode structured ABI arguments
  • You want to specify a custom revert address for fail-safety
  • You’re triggering logic, not just transferring BTC

This method involves sending a standard Bitcoin transaction with an OP_RETURN output that encodes the recipient (universal contract or EOA) and an optional short message.

To initiate a cross-chain transaction from Bitcoin, the transaction must have at least two outputs:

  1. First output: BTC amount sent to the Bitcoin Gateway (TSS) address.
  2. Second output: OP_RETURN PUSH_x [DATA]

⚠️ If the transaction does not include both required outputs in the correct order, ZetaChain will not initiate the cross-chain transaction. The BTC will still be sent to the Gateway address, but no smart contract call or token minting will occur.

To deposit BTC as ZRC-20 BTC to an EOA or a universal contract on ZetaChain:

[DATA] = [EOA or contract address (20 bytes)]

📌 Example:

To call a universal contract on ZetaChain:

[DATA] = [contract address (20 bytes)] + [call payload (max 60 bytes)]

This will execute the onCall method on the target contract.

⚠️ If your payload is larger than 60 bytes consider using inscriptions instead.

To deposit BTC and call a universal contract on ZetaChain:

[DATA] = [contract address (20 bytes)] + [call payload (max 60 bytes)]

Unlike EVM-based chains, each deposited Bitcoin output incurs a fee when it is spent. To address this, both the depositor and the withdrawer share the cost of the spend. This fee is charged in advance as a deposit fee.

The Bitcoin deposit fee is calculated with the following formula:

depositFee = (txFee / txVsize) * 68 vB * 2

Where:

  • txFee = totalInputValue - totalOutputValue
  • txVsize is the virtual size of the Bitcoin transaction.