npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zerocompress

v0.2.0-beta-2

Published

Compress calldata for reducing L1 gas use in L2 transactions

Downloads

7

Readme

zerocompress CircleCI

Compress Ethereum calldata then decompress on chain. For use in L2 applications.

Strategy

Calldata in Ethereum is priced at 4 gas per zero byte, and 16 gas per non-zero byte. As a result data can only be efficiently compressed if either:

  1. Zeroes are compressed to non-zeros with more than 75% efficiency
  2. Zeroes are not replaced with non-zeroes during compression

Single bit

Data is compressed by looking at each byte and storing a single bit indicating whether the byte is zero or non-zero. If the byte is non-zero it is added to a uniques array of bytes.

Each sequence of 8 consecutive zero bytes is compressed to a single zero byte.

Opcodes

A zero value in the uniques array indicates a special operation. The byte following a zero byte in the uniques array specifies an opcode for inflation. Any number of following bytes may be used as an argument for the opcode.

  • [x] 0x00() - zero insertion, insert a fixed number of zeroes specified by a number at register 1 (this number may be 0) (use this to make the total data length shorter to avoid padding)
  • [x] 0x01-0xE0 - Fixed length 0 insertion
  • [x] 0xE1-0xF1 - Fixed length 0xFF insertion
  • [x] 0xF2-0xF6 (uint40 id) - address insertion
  • [x] 0xF7-0xFB (uint40 id) - bls pubkey insertion (uint[4])
  • [ ] tbd(uint8 length) - insert a repeat string of bytes specified at register 2
  • [x] 0xFC-0xFF - for external use

Registers are bytes at the end of the data.

Use

npm i zerocompress

Contract

Implement a contract inheriting from the Decompressor contract.

import "zerocompress/contracts/Decompressor.sol";

contract Example is Decompressor {
  function testFunc(uint a, uint b) public {
    require(a != b);
  }
}

JS

Encode the data for the target function call using Ethers or Web3. Then compress the data using compressSingle or compressDouble. These functions will return a function to call and data to pass.

const { ethers } = require('ethers')
const { ExampleABI } = require('Example.sol.json')
const { compress } = require('zerocompress')

// deploy your contract that inherits Decompressor
const example = new ethers.Contract(
  '0xabcabcabc0abcabcabc0abcabcabc0abcabcabc0',
  ExampleABI
)

// Encode the function call
const calldata = example.interface.encodeFunctionData('testFunc', [ 20, 40 ])
// Compress the data and get a function to call and data to pass
const [ func, data ] = compress(calldata)
// Call the function with the data
const tx = await example[func](data)
// Wait for the transaction to complete
await tx.wait()

compress API

compress(calldata, options) returns [func, data]

  • calldata: A bytes string optionally prefixed with 0x. Should be an 8 byte function selector followed by abi encoded arguments.
  • options: An optional object specifying address and bls public key replacements.
    • addressSubs: An optional object mapping addresses to integers. Example: { '0xabcabcabc0abcabcabc0abcabcabc0abcabcabc0': 10029 }
    • blsPubkeySubs: An array of arrays mapping bls public keys to integers. Each item should be an array of length 2 containing the bls key (as an array of hex strings) as the first element, and an integer as the second element. Example:
[
  [ // the first replacement element
    [
      '0x02387ea12d645f4a3af6f974de1732c5d2a4469ddd14b74068f3f9ab71a3adf2',
      '0x2fb47daa10b6066a152832ce5275297424ef3b778f9853510cea6a79ab07bf42',
      '0x26d1389521121b21d7f6ee981f35ff627944b3a6877ecdc194b7b9ed6c1ec112',
      '0x0f4c29d245098298838b2e66de53d1d10482bddada6b4fc0d4b5b814fa2d5b16'
    ],
    41294
  ],
  [ // the second replacement element
    [
      '0x02186699ca6e1174566611699dd295af60434d60b0478a621e933789cf6c9574',
      '0x2f43e276298f9ecfc4c3925155fe852b94cfd191410831b051663fabf99ec2ce',
      '0x11bf07ff3b44f6f4ec24effc81be892097e591af6591481c6d84a99d0ece2f17',
      '0x28a14cf5fb0e62f55cba5048c3c6cfe53ad8eda449e7528abf30484eb7efdc64'
    ],
    20939
  ]
]
  • func: (return value) A fully formed function selector to call on the target contract. Example: decompress(bytes32[2])
  • data: (return value) Data to pass to the returned function.

Address Registry

To use address substitution each address must be registered with the Decompress contract. Registration will return an integer that can be substituted in compressed data.

const { decompressAddress, AddressRegistryABI } = require('zerocompress')
{
  const registry = new ethers.Contract(decompressAddress, AddressRegistryABI)
  await registry.bindAddress(myAddress).then(t => t.wait())
  const id = await registry.idByAddress(myAddress)
  // id is the address that can be used for compression now
  // e.g. compress(calldata, { addressSubs: { [myAddress]: id }})
}

BLS Public Key Registry

To use BLS public key substitution each public key must be registered with the Decompress contract before use. Registration will return an integer that can be substituted in compressed data.

const { decompressAddress, BLSPubkeyRegistryABI } = require('zerocompress')
{
  const registry = new ethers.Contract(decompressAddress, BLSPubkeyRegistryABI)
  await registry.bindPubkey(myPubkey).then(t => t.wait())
  const id = await registry.idByPubkey(myPubkey)
  // id is the pubkey that can be used for compression now
  // e.g. compress(calldata, { blsPubkeySubs: [ [ myPubkey, id ] ]})
}

Addresses

The Decompress contract (implementing the AddressRegistry and BLSPubkeyRegistry) is available on the following networks: