Solidity memory libraries — pointer arithmetic, byte-level copying, and dynamic
arrays/matrices built in raw assembly rather than through Solidity's
bounds-checked, zeroing array handling. Allocating functions still read and
update the free memory pointer at 0x40; the safety checks are skipped, the
allocator is not.
| Library | What it does |
|---|---|
LibPointer |
Raw memory pointer arithmetic, with explicit Pointer user-defined type. |
LibMemCpy |
Word-aligned and byte-aligned memcpy between memory pointers. |
LibBytes |
In-place truncate, plus start/data/end/allocated pointers. |
LibUint256Array |
arrayFrom literals, unsafeExtend, truncate, reverse, pointers. |
LibBytes32Array |
Dynamic bytes32[] mirror of LibUint256Array. |
LibUint256Matrix |
uint256[][] operations. |
LibBytes32Matrix |
bytes32[][] operations. |
LibStackSentinel |
Sentinel-terminated stack walks for unknown-length data. |
These libraries assume the caller knows what they're doing with memory. There is
no free in EVM memory, so the hazards are out-of-bounds reads and writes,
silent pointer wraparound, and aliasing.
- A
Pointeris a bareuint256.LibPointerarithmetic wraps silently and is checked against neither0x40nor the length prefix of the structure the pointer came from. unsafe*functions perform no bounds checks. The caller MUST establish, at its own call sites, that every read is in bounds and every write lands in memory the caller owns.- Functions that allocate read and update
0x40themselves, so they MUST NOT be interleaved with hand-written assembly that caches0x40across the call. unsafeExtendMAY mutate its base argument in place and MAY return a pointer to it. Use the returned array only.- Overlap between arguments is the caller's problem. Every array or
bytesargument MUST be a valid Solidity memory structure that owns the region its own length word describes. - Every assembly block in
src/is annotated("memory-safe"). That annotation is an assertion to the compiler, not a guarantee to the caller: it holds for these functions only while the obligations above hold. The functions areinternaland inline, so the assertion lands inside the consumer's own compilation unit.
| Error | Import from | Thrown by |
|---|---|---|
TruncateError |
src/error/ErrBytes.sol |
LibBytes.truncate |
OutOfBoundsTruncate |
src/error/ErrUint256Array.sol |
truncate on both array libs |
UnalignedStackPointer |
src/error/ErrStackPointer.sol |
LibStackSentinel.consumeSentinelTuples |
ZeroSentinelTupleSize |
src/lib/LibStackSentinel.sol |
LibStackSentinel.consumeSentinelTuples |
MissingSentinel |
src/lib/LibStackSentinel.sol |
LibStackSentinel.consumeSentinelTuples |
InvalidStackBounds |
src/lib/LibStackSentinel.sol |
LibStackSentinel.consumeSentinelTuples |
UnallocatedStack |
src/lib/LibStackSentinel.sol |
LibStackSentinel.consumeSentinelTuples |
itemCount and flatten on both matrix libs, and
LibStackSentinel.consumeSentinelTuples, also revert with Panic(uint256) code
0x11 on arithmetic overflow.
- solc
^0.8.25, which every source file declares. evm_version = "cancun"or later, and a chain with cancun activated. Every copy path compiles to themcopyopcode. Building below cancun fails at compile time; cancun bytecode on a chain without cancun reverts withinvalid opcodeat runtime.
Protofire reviewed rain.solmem in January 2026. The
report
covers two reviews — 228b35c6 on the 13th and 26bce619 on the 26th, the
latter being the end of the audited tree. Scope was all of src/ at those
commits, the twelve contracts the report's own scope table lists.
src/ today is not that tree: some of the audited files have since been
deleted, and some of what is there now the audit never saw. To see how far it
has moved:
git diff --stat 26bce6197383f193e35326bab4d4424cf6eafde7..HEAD -- src/Via soldeer (in your foundry project's root):
forge soldeer install rain-solmem~<version>Versioned remappings end up in dependencies/rain-solmem-<version>/. Add the
remapping to remappings.txt or foundry.toml.
This repo uses nix for its dev shell. The
default shell is the slim Solidity-only sol-shell from
rainix — no rust, node, or chromium.
nix develop # enter the shell
forge soldeer install # install dependencies declared in foundry.toml
forge testUse the nix-pinned forge for all development to keep versions consistent.
Publishing is merge-driven; there is no release tag to push. On every push to
main, Package Release calls
rainix's rainix-autopublish, which compares the package content against the
latest published revision. If it differs, it runs
forge soldeer push rain-solmem~<version>, tags sol-v<version>, and commits a
bump of [package] version back to main.
[package] version in foundry.toml is the next, unpublished version, not
the current one. It is always one ahead of the registry; do not "correct" it to
match.
Everything .soldeerignore does not exclude ships — src/**,
README.md, LICENSE, LICENSES/, REUSE.toml — and any change to it
publishes a new revision, including a comment-only edit. Soldeer revisions are
immutable and cannot be deleted.
DecentraLicense 1.0 (DCL-1.0) — full text in
LICENSES/. Roughly CAL-1.0
(opensource.org) plus user-data
disclosure obligations consistent with permissionless-blockchain assumptions.
"Not your keys, not your coins" aware, in legalese.
This repo is REUSE 3.2 compliant. Verify locally:
nix develop -c reuse lintWelcome under the same license. Contributors warrant that their contributions are compliant.