{"id":15016367,"url":"https://github.com/joejordan/crambit","last_synced_at":"2025-08-08T18:52:15.262Z","repository":{"id":65022822,"uuid":"580193126","full_name":"joejordan/CramBit","owner":"joejordan","description":"A Solidity library designed to cram as many arbitrary values into as small a space as possible.","archived":false,"fork":false,"pushed_at":"2023-01-06T05:18:30.000Z","size":1154,"stargazers_count":53,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-14T03:03:52.002Z","etag":null,"topics":["blockchain","compression","ethereum","evm","forge","foundry","solidity","solidity-library"],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joejordan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-20T00:10:37.000Z","updated_at":"2025-05-07T20:48:40.000Z","dependencies_parsed_at":"2023-02-05T10:01:54.448Z","dependency_job_id":null,"html_url":"https://github.com/joejordan/CramBit","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":"PaulRBerg/foundry-template","purl":"pkg:github/joejordan/CramBit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joejordan%2FCramBit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joejordan%2FCramBit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joejordan%2FCramBit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joejordan%2FCramBit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joejordan","download_url":"https://codeload.github.com/joejordan/CramBit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joejordan%2FCramBit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259752050,"owners_count":22905970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","compression","ethereum","evm","forge","foundry","solidity","solidity-library"],"created_at":"2024-09-24T19:48:45.109Z","updated_at":"2025-06-14T03:05:25.017Z","avatar_url":"https://github.com/joejordan.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CramBit [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license]\n\n[gitpod]: https://gitpod.io/#https://github.com/joejordan/CramBit\n[gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod\n[gha]: https://github.com/joejordan/CramBit/actions\n[gha-badge]: https://github.com/joejordan/CramBit/actions/workflows/ci.yml/badge.svg\n[foundry]: https://getfoundry.sh/\n[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg\n[license]: https://opensource.org/licenses/MIT\n[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg\n\n***A Solidity library designed to cram as many arbitrary values into as small a space as possible.***\n\nThis project is somewhat inspired by OpenZeppelin's [BitMaps](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol) solution, which allows a user to store up to 256 boolean values in a single `uint256` variable. \n\nI thought, *why just booleans?* and decided to cram multiple arbitrary values into the `bytes` container of your choice. It works by creating a list of packing instructions, i.e. an array of `PackBytes` structs, that tells the `pack` function\n\n1. the value (in byte format) that you want to store, and \n2. the maximum number of bits that that value could contain.\n\nUnpacking a packed variable just performs the reverse, using the packing instructions you already created to unpack your original values.\n\nWhy would you want to do something like this? Well, on-chain storage is pretty expensive, so if you can save space by fitting more information into a smaller container, it might be worth the effort.\n\n### Example Use Case\n\nOne immediate use case came to mind when building this, that being the efficient storage of NFT attributes, especially characters or inventory for Web3 gaming. Characters in games can have dozens, perhaps even hundreds of attributes, with each attribute representing for example 1 of 100 hairstyles, a 32-bit hair color, 1 of 12 face types, etc.\n\nRather than store all that information on-chain in a complex struct that may take up multiple storage slots, the data could instead be fit into a single 256 bit value or smaller, depending on your data needs.\n\n## Overview\n\n### Foundry\n\nFirst, run the install step:\n\n```sh\nforge install --no-commit joejordan/CramBit\n```\n\nThen, add this to your `remappings.txt` file:\n\n```text\ncrambit=lib/CramBit/src/\n```\n\n### Node.js\n\n```bash\nyarn add crambit\n# or\nnpm install crambit\n```\n\n## Getting Started\n\nImport the library into your Solidity contract, i.e.\n\n```solidity\nimport { CramBit } from \"crambit/CramBit.sol\";\n```\n\nPacking instructions can be created in several different ways. Ultimately, the `pack` function just needs an array of `PackBytes` structs so that it knows what values to pack and how much space to give each value. Here is one example that creates a PackBytes array and packs two values into a single `bytes1` variable:\n\n```solidity\n   CramBit.PackBytes1[] memory packInstructions = new CramBit.PackBytes1[](2);\n   bytes1 value1 = 0x0f; // 4 bytes max (1111)\n   bytes1 value2 = 0x0f; // 4 bytes max (1111)\n\n   // define our packing instructions\n   packInstructions[0] = CramBit.PackBytes1({ maxBits: 4, data: value1 });\n   packInstructions[1] = CramBit.PackBytes1({ maxBits: 4, data: value2 });\n\n   // pack our data into a bytes1 value\n   bytes1 packedBytes1 = CramBit.pack(packInstructions);\n```\n\nUnpacking is pretty easy as well. CramBit has a helper function called `packToUnpackMap` that will convert your pack instructions into an unpack map, essentially an array of values that represent the maxBits for each packed value. Unpacking can be done like this:\n\n```solidity\n   // generate an unpack map from our packInstructions\n   uint8[] memory unpackMap = CramBit.packToUnpackMap(packInstructions);\n\n   // unpack our packed values\n   uint8[] memory unpackedValues = CramBit.unpackBytes1(packedBytes1, unpackMap);\n\n   // assert that unpacked values match the original values\n   assertEq(unpackedValues[0], uint8(value1));\n   assertEq(unpackedValues[1], uint8(value2));\n```\n\nCheck out the [test directory](https://github.com/joejordan/CramBit/tree/main/test) for more examples.\n\n**One important caveat** when creating your packing instructions array is that the total `maxBits` for all of your entries must add up to the same number of bits as the container it's going in, or you're going to get weird numbers back when unpacking.\n\nFor example, if you were packing a number of values into in a `bytes1` variable, the total number of `maxBits` for all entries must add up to 8. If you end up with some space at the end, just remember to add an extra entry of any remaining `maxBits` you need to fill the container. Here's an example:\n\n```solidity\n   CramBit.PackBytes1[] memory packInstructions = new CramBit.PackBytes1[](4);\n   uint8 value1 = 7; // 3 bytes max (111)\n   uint8 value2 = 2; // 2 bytes max (10)\n   uint8 value3 = 1; // 1 bytes max (1)\n   bytes1 UNUSED = 0;\n   // packed binary value: 111_10_1_??\n\n   // define our packing instructions\n   packInstructions[0] = CramBit.PackBytes1({ maxBits: 3, data: bytes1(value1) });\n   packInstructions[1] = CramBit.PackBytes1({ maxBits: 2, data: bytes1(value2) });\n   packInstructions[2] = CramBit.PackBytes1({ maxBits: 1, data: bytes1(value3) });\n   /// @dev important! when your other values require less than the max of the bytes container,\n   /// you must include an UNUSED entry with the number of bytes remaining.\n   packInstructions[3] = CramBit.PackBytes1({ maxBits: 2, data: UNUSED });\n\n   // pack our data into a bytes1 value\n   bytes1 packedBytes1 = CramBit.pack(packInstructions);\n```\n\n\n## Contribute\n\nContributions are welcome! [Open](https://github.com/joejordan/CramBit/issues/new) an issue or submit a PR. There is always room for improvement. The instructions below will walk you through setting up for contributions.\n\n### Pre-Requisites\n\nYou will need the following software on your machine:\n\n- [Git](https://git-scm.com/downloads)\n- [Foundry](https://github.com/foundry-rs/foundry)\n- [Node.js](https://nodejs.org/en/download/)\n- [Yarn](https://yarnpkg.com/)\n\n### Set Up\n\nClone this repository:\n\n```bash\n$ git clone https://github.com/joejordan/CramBit.git\n```\n\nThen, inside the project's directory, run this to install dependencies:\n\n```bash\n$ yarn install\n```\n\nYour environment should now be ready for your improvements.\n\n## Security\n\nThis code has not been professionally audited by any third parties. If you use this library and include it in a professional audit, please let me know via [Twitter Direct Message](https://twitter.com/JJordan) for inclusion in this documentation.\n\nIf you discover any security issues with the library, please report them via [Twitter Direct Message](https://twitter.com/JJordan).\n\n### Disclaimer\n\nThis is experimental software and is provided on an \"as is\" basis. No expressed or implied warranties are granted of any kind. I will not be liable for any loss, direct or indirect, related to the use or misuse of this codebase.\n\n## Acknowledgements\n\n- To my loving and supportive wife who always has my back. ♥\n\n## License\n\nCramBit is released under the [MIT License](./LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoejordan%2Fcrambit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoejordan%2Fcrambit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoejordan%2Fcrambit/lists"}