Czip
EVM Calldata Zip, aka czip
Install / Use
/learn @0xsequence/CzipREADME
CZIP: EVM Calldata Zip

czip is an engine for compressing and decompressing EVM calldata. It is designed to be used in L2s to trade off calldata size for computation cost.
The primary component of czip is the decompressor.huff contract, which is a Huff contract that inflates the calldata. It works by implementing a simple state machine that, in a single pass, decompresses the input. The operations of the state machine are specifically designed to work with EVM calldata.
A companion czip-compressor tool is provided to compress calldata. It is a simple command-line tool that does not perform perfect compression but provides a good starting point for starting to use the decompressor.huff contract.
Install
$ brew tap 0xsequence/tap
$ brew install czip-compressor
$ czip-compressor
or
$ docker run ghcr.io/0xsequence/czip-compressor
or
$ go install github.com/0xsequence/czip/compressor/cmd/czip-compressor@latest
Usage
The compressor has the following commands:
encode-call <decode/call/call-return> <hex_data> <addr>Compresses a call toaddrwithhex_data.encode-calls <decode/call> <hex_data_1> <addr_1> <hex_data_2> <addr_2> ...Compresses multiple calls into one payload.encode-any <data>Encodes any data into a compressed representation.encode-sequence-tx <decode/call> <sequence_tx> <sequence_wallet>Compresses a Sequence wallet transaction.
czip-compressor is a tool for compressing Ethereum calldata. The compressed data can be decompressed using the decompressor contract.
Usage:
czip-compressor [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
encode-any Compress any calldata: <hex>
encode-call Compress a call to a contract: <data> <to>
encode-calls Compress multiple calls to many contracts: <data> <to> <data> <to> ... <data> <to>
encode-sequence-tx Compress a Sequence Wallet transaction
extras Additional encoding methods, used for testing and debugging.
help Help about any command
Flags:
--allow-opcodes strings Will only encode using these operations, separated by commas.
--cache-dir string Path to the cache dir for indexes. (default "/tmp/czip-cache")
-c, --contract string Contract address of the decompressor contract.
--disallow-opcodes strings Will not encode using these operations, separated by commas.
-h, --help help for czip-compressor
-p, --provider string Ethereum RPC provider URL.
-s, --use-storage Use stateful read/write storage during compression.
Use "czip-compressor [command] --help" for more information about a command.
Encode call
It encodes a single call to a contract, the subcommands are:
decodeGenerates a payload that, when sent to thedecompressor.huffcontract, will decompress the calldata and return the caller, without performing the call.callGenerates a payload that, when sent to thedecompressor.huffcontract, will decompress the calldata and perform the call, ignoring the return value.call-returnGenerates a payload that, when sent to thedecompressor.huffcontract, will decompress the calldata and perform the call, returning the return value.
czip-compressor encode-call decode \
0xa9059cbb0000000000000000000000008bf74fb902cdad5d2d8ca0d3bbc7bb16894b9c350000000000000000000000000000000000000000000000000000000006052340 \
0xdAC17F958D2ee523a2206206994597C13D831ec7
> 0x0b3701148bf74fb902cdad5d2d8ca0d3bbc7bb16894b9c35332bf214dac17f958d2ee523a2206206994597c13d831ec7
Encode Calls
It encodes multiple calls to contracts, the subcommands are:
decodeGenerates a payload that, when sent to thedecompressor.huffcontract, will decompress all calls and return them decompressed, without performing the calls.callGenerates a payload that, when sent to thedecompressor.huffcontract, will decompress the calls and perform them, ignoring the return values.
Notice that the call-return subcommand is not available in this mode.
czip-compressor encode-calls decode \
0xa9059cbb0000000000000000000000009813d80d0686406b79c29b2b8a672a13725facb300000000000000000000000000000000000000000000000ae56f730e6d840000 \
0xdac17f958d2ee523a2206206994597c13d831ec7 \
0x095ea7b30000000000000000000000007c56be0ad3128acc33190484cd1badebc8c76240ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \
0xdac17f958d2ee523a2206206994597c13d831ec7
> 0x0c023701149813d80d0686406b79c29b2b8a672a13725facb3338fda14dac17f958d2ee523a2206206994597c13d831ec73702147c56be0ad3128acc33190484cd1badebc8c7624031ff3d001c
Compressing multiple calls into one payload is more efficient than compressing each call individually, as data can be de-duplicated and the overhead of the decompressor is amortized over multiple calls.
Encode Any
It encodes any data into a compressed representation. Sending the payload to the decompressor.huff contract will return the original data.
czip-compressor encode-any \
0x0000000000000000000000000000000000000000000000012a5f58168ee60000
> 0x0d3388d7
Encode Sequence Transaction
It works similarly to encode-calls, but it is specifically designed to compress a Sequence wallet transaction. It expects the data to be a Sequence Transaction ABI-encoded.
Using storage indexes
By default all commands run with --use-storage false, which means that the decompressor won't write any data to the storage, or read any addresses or bytes32 using indexes.
Storage indexes can be enabled using the following flags:
--use-storage trueEnables the use of storage indexes.--contract <address>An instance of thedecompressor.huffcontract to use for storage indexes.--provider <provider>The provider from which to fetch the pointers.
Notice that a cache on /tmp/czip-cache/czip-indexes-<chain-id>.json is automatically created to avoid fetching the same pointers multiple times. The cache dir can be changed using the --cache-dir flag.
Example
czip-compressor encode-call decode \
0xa9059cbb000000000000000000000000963752cac40e583dea143d6262e24f89c9e1f91100000000000000000000000000000000000000000000000000000000000003fc \
0x750ba8b76187092B0D1E87E28daaf484d1b5273b
> 0x0b370114963752cac40e583dea143d6262e24f89c9e1f9110203fc14750ba8b76187092b0d1e87e28daaf484d1b5273b
czip-compressor encode-call decode \
--contract 0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 \
--provider https://nodes.sequence.app/arbitrum-nova \
--use-storage \
0xa9059cbb000000000000000000000000963752cac40e583dea143d6262e24f89c9e1f91100000000000000000000000000000000000000000000000000000000000003fc \
0x750ba8b76187092B0D1E87E28daaf484d1b5273b
> 0x0b37012700010203fc270002
See it in action: https://nova.arbiscan.io/tx/0x86e7b4177c0d219a87cc58f93ae2ecf2f490a719119c283f61cdc88585cc7c7b
How to decompress
Sending the generated payload to the decompressor.huff will either return the decompressed data or perform the call (depending on the command used to generate the payload).
The decompressor.huff contract has no selectors; the data does not need to be re-encoded and can be sent directly to the contract.
Cast example
Try running the following command; it will inflate the data and return the original call data. You can do the same thing on-chain.
cast call \
--rpc-url https://nodes.sequence.app/arbitrum-nova \
0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 \
0x0b37012700010203fc270002
> 0xa9059cbb000000000000000000000000963752cac40e583dea143d6262e24f89c9e1f91100000000000000000000000000000000000000000000000000000000000003fc000000000000000000000000750ba8b76187092b0d1e87e28daaf484d1b5273b
Solidity example
Decompressing on-chain is as simple as calling the decompressor contract with the payload.
contract YourContract {
event WeGotData(bytes data);
function doSomething(bytes calldata _compressed) external {
(bool ok, bytes memory data) = address(0x8C5CF0a201C1F0C1517a23699BE48070724e7a70).call(_compressed);
require(ok, "Decompression failed");
emit WeGotData(data);
}
}
Notice that if the data was compressed using the call command, the compressor will not return the decompressed data; it will perform the call. In this example, we used the decode command, so the data will be returned.
Compression gains
The compression gains are highly dependent on the ratio of computation cost to calldata cost of a given network. It is most effective on "rollup" style L2s, but it can also achieve some small gains on some other networks.
| Network | Decompressor Address | Savings | |---------------|--------------------------------------------|----------| | Arbitrum | 0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 | ~50% | | Optimism | 0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 | ~50% | | Base | 0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 | ~50% | | Arbitrum Nova | 0x8C5CF0a201C1F0C1517a23699BE48070724e7a70 | ~15% | | Polygon | -- | Negative | | Ethereum | -- | Negative | | Polygon zkEVM | -- | Negative |
The following benchmarking transactions are from the Arbitrum network, they show savings of ~50% in gas costs. The savings account for the cost of the decompressor contract, notice that they use an older version of the compressor, but the inner workings are the same.
The transaction cost for sending ETH using a smart contract wallet, with a 2/2 configuration, goes from
