MCPcopy Index your code
hub / github.com/0xsequence/czip

github.com/0xsequence/czip @v0.2.4

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.2.4 ↗ · + Follow
86 symbols 268 edges 10 files 6 documented · 7% updated 4mo agov0.2.4 · 2024-02-13★ 106
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

CZIP: EVM Calldata Zip

CZIP 😈

czip is a utility 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 to addr with hex_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:

  • decode Generates a payload that, when sent to the decompressor.huff contract, will decompress the calldata and return the caller, without performing the call.
  • call Generates a payload that, when sent to the decompressor.huff contract, will decompress the calldata and perform the call, ignoring the return value.
  • call-return Generates a payload that, when sent to the decompressor.huff contract, 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:

  • decode Generates a payload that, when sent to the decompressor.huff contract, will decompress all calls and return them decompressed, without performing the calls.
  • call Generates a payload that, when sent to the decompressor.huff contract, 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 true Enables the use of storage indexes.
  • --contract <address> An instance of the decompressor.huff contract 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.

Sending ETH cost comparation

Send ETH uncompressed (1st):

0xa0efbb458309f1ccc14035a53e20c36155d722b1c5d991bfa7c43a21174ec468

Send ETH compressed + write storage (1st):

0x9a34d5787b0dd6fba248ebeb407d51526445b496f45f2b4f6ff1d56875f04f7c

Send ETH compressed (1st):

0x6197b0770cdb3efcdb252bf3932ff9964e3467906a7ac1de6361e2d9fe1bb84e

Send ETH uncompressed (2nd):

0x7b519df3f10a0e0ae507d6d18d775f1ed80e65c76df06e5632f402903cd9afb8

Send ETH compressed + write storage (2nd):

0x1680bae9b790bb54d522ebc033da92cb5261b73c27058767c55011957083ea41

Send ETH compressed + write storage (2nd):

0x1ccc93227065df0b9d6acc64504280ad7e55b5823b90111a0b6477c881291de4

Sending ETH cost comparation

Send ERC20 uncompressed:

0x0559ea8161e9cfed3298091d1f7626fe551bd40a38d2ff65d2340a52203e582a

Send ERC20 compressed + write storage:

0xbbf1d0250c37155f2da1d72cc01ff68fb5ccd4b4834a4accf53caf9374e64e3b

Send ERC20 compressed:

0x07e3b4de0b2cd3c8c531e90f32afc7a5dc3691b399ae4ab33d833b3e10741d05

Approve ERC20 uncompressed:

0x03c5f3d5c5a556439215c751a0d84b838266e9ec2481f862a912943e1bc309d6

Aprove ERC20 compressed:

0x7186dcf623d6bf5436691d28c649215900c4c71c2061863b0388786e07299428

Decompressor contract

The decompressor.huff serves as the decompressor for all data. It has been carefully designed to be as efficient as possible. It also serves the role of a "repository"; it can store address and bytes32 values that can later be referenced using a pointer.

The contract does not follow the Solidity ABI convention. The contract uses a single byte for the function selector. The following functions are available:

  • 0x00 Execute sequence transaction.
  • 0x01 Execute many sequence transactions.
  • 0x02 Fetch one address using a pointer.
  • 0x03 Fetch one bytes32 using a pointer.
  • 0x04 Fetch the total number of addresses and bytes32 stored.
  • 0x05 Fetch a list of storage slots.
  • 0x06 Decompress a sequence transaction and return the data.
  • 0x07 Decompress many sequence transactions and return the data.
  • 0x08 Execute a call and ignore the return value.
  • 0x09 Execute a call and return the return value.
  • 0x0a Execute many calls and ignore the return values.
  • 0x0b Decompress a call and return the data.
  • 0x0c Decompress many calls and return the data.
  • 0x0d Decompress any data and return the data.

The czip-compressor tool is designed to generate the payloads for the 0x08, 0x09, 0x0a, 0x0b, 0x0c, and 0x0d functions automatically; there is no need to manually prefix the payload with the function selector.

State machine

The state machine used by the decompressor.huff contract allows for a single-pass decompression of the calldata. It reads a single operation, but operations can be composed of multiple operations.

The operations are 1 byte long, they may contain any number of arguments, there are 89 operations available. The leftover space is used t

Core symbols most depended-on inside this repo

commitUint
called by 34
compressor/buffer.go
Allows
called by 31
compressor/buffer.go
end
called by 27
compressor/buffer.go
maxPriority
called by 18
compressor/utils.go
fail
called by 18
compressor/cmd/czip-compressor/main.go
WriteWord
called by 17
compressor/encode.go
commitByte
called by 15
compressor/buffer.go
commitBytes
called by 11
compressor/buffer.go

Shape

Function 46
Method 34
Struct 5
TypeAlias 1

Languages

Go100%

Modules by API surface

compressor/encode.go23 symbols
compressor/buffer.go17 symbols
compressor/utils.go11 symbols
compressor/cmd/czip-compressor/main.go11 symbols
compressor/contract.go9 symbols
compressor/cmd/czip-compressor/indexes.go5 symbols
compressor/cmd/czip-compressor/extras.go4 symbols
compressor/constants.go3 symbols
compressor/cmd/czip-compressor/utils.go3 symbols

For agents

$ claude mcp add czip \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact