Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 47 additions & 81 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,49 @@
# CLAUDE.md

Guidance for Claude Code (claude.ai/code) working in this repository.

## Project

rain.math.float.deploy is the **deployment** half of `rain.math.float`: the
concrete `DecimalFloat` contract, its deterministic Zoltu deploy pins, the
frozen log-tables snapshot, and the deploy scripts + tests. The pure math —
`LibDecimalFloat`, `LibDecimalFloatImplementation`, `LibFormatDecimalFloat`,
`LibParseDecimalFloat`, `LibLogTable`, the errors — is NOT in this repo; it
arrives as the `rain-math-float` Soldeer package under
`dependencies/rain-math-float-<version>/src/`.

License: LicenseRef-DCL-1.0 (DecentraLicense). All source files carry SPDX
headers.

## Build & Test

Nix + Foundry, in the rainix `sol-shell` CI uses. `dependencies/` is gitignored,
so install before the first build.

```bash
nix develop -c forge soldeer install
nix develop -c forge build
nix develop -c forge test
nix develop -c forge fmt
nix develop -c slither .
nix develop -c reuse lint
```

## Layout

- `src/concrete/DecimalFloat.sol` — the deployed contract. Reads log tables at
`LibDecimalFloatDeploy.ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS`; its constructor
asserts they are present.
- `src/lib/deploy/LibDecimalFloatDeploy.sol` — the pins (log-tables +
DecimalFloat Zoltu addresses and codehashes) and the per-release frozen
suites the tagged-constants test enforces.
- `src/generated/LogTables.pointers.sol` — AOT log/anti-log table bytes,
regenerated by `script/Build.sol`. A pure function of `LibLogTable`,
so version-invariant. NEVER edit by hand.
- `script/Deploy.sol` — deploys the `log-tables` or `decimal-float` suite.
- `script/lib/LibEtchLogTables.sol` — plants the tables at the Zoltu address in
a forge VM (used by the deploy simulation pass and by tests).
- `test/abstract/LogTest.sol` — etches the tables at the Zoltu address so
`DecimalFloat` operations work without an on-chain deploy.
- `test/src/concrete/` — mirrors `src/` by subject path; files are named
`DecimalFloat.<functionName>.t.sol`.

## Conventions

- Concrete, scripts and tests pin `=0.8.25`; the shipped deploy lib floats
`^0.8.25`.
- solc 0.8.25, cancun, optimizer on at **1,000,000 runs**, no CBOR metadata.
The pins are a pure function of these AND the `rain-math-float` sources the
concrete compiles against. Do NOT change the run count to 100,000 to match
other deploy repos — it moves the pinned address.
- One contract per `.sol` file. No `@custom:` NatSpec. No skipped tests.
- Comments describe current behaviour only.
- `recursive_deps` is off, so every package an import resolves through is
declared in `foundry.toml`, including ones reached only via `rain-math-float`.

## Rust / npm bindings

`crates/float` is the `rain-math-float` Rust crate: `alloy::sol!` bindings over
the concrete `DecimalFloat` (its committed ABI + deployed bytecode), run in an
in-memory revm so off-chain results match on-chain exactly. `crates/float-wasm`
+ `scripts/` + `test_js/` build the `@rainlanguage/float` npm wasm wrapper.
`test/concrete/TestDecimalFloat.sol` exposes `packLossless`/`unpack` for the
crate's `#[cfg(test)]` path. `script/CopyArtifacts.sol` regenerates the
committed `crates/float/abi/*.json` from `out/`; `copy-artifacts.yaml` asserts
they are fresh. Crate + npm publish on merge via `crate-npm-release.yaml`
(rainix-autopublish); the Soldeer deploy package publishes only on tag.

## Deployment and releases

Deployed via the Zoltu deployer, so the address is a pure function of the
bytecode. A deploy is a human-dispatched run of `Manual sol artifacts`, never a
merge, and comes BEFORE the tag. Releases are manual `sol-v*` tags:
`package-release.yaml` runs `rainix-tag-release`, which regenerates the pointers
snapshot, verifies the live chains against the pins and publishes
`rain-math-float-deploy` to Soldeer. See README.md.
Only what a capable agent would get wrong from this repo alone. Layout, dev
shells, build/test commands and dependency lists are discoverable and
deliberately absent (rainlanguage/rainix#298).

## What this repo is

The **deploy half** of `rain.math.float`: the concrete `DecimalFloat`, its
generated deploy records and the deploy scripts + tests. The pure math
(`LibDecimalFloat*`, `LibLogTable`, the errors) is NOT here — it arrives as the
`rain-math-float` Soldeer package.

## Conventions an agent would get wrong

- Optimizer **1,000,000 runs**, NOT the 100,000 sibling deploy repos use.
Deterministic (Zoltu) deploy: the address is a pure function of the creation
bytecode, so this, solc `=0.8.25`, `evm_version = "cancun"`, no CBOR metadata,
and the pinned `rain-math-float` sources all move the pins if changed.
- Pragma: concretes, scripts and tests pin `=0.8.25`; shipped libs and generated
files float `^0.8.25`.
- All source files carry SPDX headers (LicenseRef-DCL-1.0).
- No skipped tests. Comments describe current behaviour only.
- `recursive_deps` is off: every package an import resolves through is declared
in `foundry.toml`, including ones reached only via `rain-math-float`.

## Deploy-pin invariants (the hazards)

- `src/generated/candidate/` is the rolling record, rewritten by
`script/Build.sol` and currency-checked by CI;
`src/lib/deploy/LibDecimalFloatDeploy.sol` aliases it and adds hand-written
helpers. `src/generated/<tag>/` snapshots are frozen: a release only ADDS one
(none exist yet — nothing is published). Never hand-edit generated files
(`src/generated/`, `src/lib/Lib*Released*.sol`).
- `[external.package].version` names the FIRST `sol-v*` tag until one exists; a
normal PR never bumps it.
- `DecimalFloat`'s constructor reverts unless the log tables are at their Zoltu
address: tables deploy/build/broadcast FIRST, always.
- `src/generated/LogTables.pointers.sol` is table BYTES (a pure function of
`LibLogTable`), not a deploy record — it stays at the generated root, never in
a tag dir.

## Release / deploy shape

- The on-chain deploy is a human-dispatched `Manual sol artifacts` run, BEFORE
tagging, never on merge: `log-tables` then `decimal-float`.
- A manual `sol-v*` tag is the sole Soldeer release trigger
(`rainix-tag-release`); the Rust crate + npm wrapper publish on merge instead
(`crate-npm-release.yaml`). See README.md.
71 changes: 47 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,72 @@

The **deployment** half of
[`rain.math.float`](https://github.com/rainlanguage/rain.math.float): the
concrete `DecimalFloat` contract, its deterministic Zoltu deploy pins
(`src/lib/deploy/LibDecimalFloatDeploy.sol`), the frozen log-tables snapshot
concrete `DecimalFloat` contract, the rolling `src/generated/candidate/`
snapshots of its deterministic Zoltu deploy records (address, codehash, creation
and runtime bytecode), the alias lib `src/lib/deploy/LibDecimalFloatDeploy.sol`
over those pins, the generated log-tables bytes
(`src/generated/LogTables.pointers.sol`), and the deploy scripts + tests.

The **library** half — `LibDecimalFloat`, `LibDecimalFloatImplementation`,
`LibFormatDecimalFloat`, `LibParseDecimalFloat`, `LibLogTable` and the errors —
lives in `rain.math.float` and is imported here as the `rain-math-float`
Soldeer package. Consumers that need only the pure math depend on
`rain-math-float`; consumers that need the deployed address, codehash or the
deploy pins (`LibDecimalFloatDeploy.ZOLTU_DEPLOYED_*`) depend on
`rain-math-float-deploy`.
lives in `rain.math.float` and is imported here as the `rain-math-float` Soldeer
package. Consumers that need only the pure math depend on `rain-math-float`;
consumers that need the deployed address, codehash or the deploy pins
(`LibDecimalFloatDeploy.ZOLTU_DEPLOYED_*`) depend on `rain-math-float-deploy`.

## The deploy surface

- `src/concrete/DecimalFloat.sol` — the deployed contract. Wraps the library
and reads the log tables from the Zoltu address; its constructor asserts the
- `src/concrete/DecimalFloat.sol` — the deployed contract. Wraps the library and
reads the log tables from the Zoltu address; its constructor asserts the
tables are present via `LibDecimalFloatDeploy.checkLogTablesDeployed()`.
- `src/lib/deploy/LibDecimalFloatDeploy.sol` — the pins: the log-tables and
DecimalFloat Zoltu addresses + codehashes, and the per-release frozen suites
the tagged-constants test enforces.
- `src/abstract/DecimalFloatDeploySuites.sol` — everything this repo deploys,
declared once: the `log-tables` and `decimal-float` candidate suites that
`script/Build.sol`, `script/Deploy.sol` and the deploy tests all bind to.
- `src/generated/candidate/{LogTables,DecimalFloat}.sol` — the rolling deploy
records (address, codehash, creation and runtime bytecode), rewritten from
what source compiles to by `script/Build.sol` and currency-checked by CI. A
release cut freezes them into `src/generated/<tag>/` (none exist yet — nothing
is published). NEVER edit by hand.
- `src/lib/deploy/LibDecimalFloatDeploy.sol` — the stable import path over the
candidate pins (the `ZOLTU_DEPLOYED_*` addresses + codehashes are aliases of
the generated constants), plus the hand-written `combinedTables()` and
`checkLogTablesDeployed()`.
- `src/lib/LibReleasedSuites.sol` (+ the per-contract `Lib*Released.sol`) — the
generated record of every released suite, currently empty because nothing is
released yet. NEVER edit by hand.
- `src/generated/LogTables.pointers.sol` — the AOT-compiled log/anti-log table
bytes, regenerated (not hand-written) by `script/Build.sol`. The
bytes are a pure function of `LibLogTable`, so this snapshot is
version-invariant. NEVER edit by hand.
bytes, regenerated (not hand-written) by `script/Build.sol`. The bytes are a
pure function of `LibLogTable`, so this snapshot is version-invariant. NEVER
edit by hand.
- `script/Deploy.sol` — deploys either the `log-tables` suite or the
`decimal-float` suite via the Zoltu deterministic deployer.

## Conventions

- Concrete, scripts and tests pin `=0.8.25`; the shipped deploy lib floats
`^0.8.25`. Optimizer on at **1,000,000 runs** (see `foundry.toml`) — this
is what the live deployment used, and the pins move if it changes.
`^0.8.25`. Optimizer on at **1,000,000 runs** (see `foundry.toml`) — this is
what the live deployment used, and the pins move if it changes.
- Cancun, no CBOR metadata. Soldeer deps carry the version in the import path;
`recursive_deps` is off, so every transitively reached package is declared.

## Releases

Releases are manual `sol-v*` tags, never merges. `package-release.yaml` runs
`rainix-tag-release`, which regenerates the pointers snapshot, verifies the
live chains against the pins and publishes `rain-math-float-deploy` to Soldeer.
The on-chain deploy is separate and human-dispatched, run BEFORE tagging: the
`Manual sol artifacts` workflow runs `script/Deploy.sol`. The addresses already
exist on-chain (deterministic Zoltu), so the first release attests existing
pins rather than deploying fresh.
Releases are manual `sol-v*` tags, never merges. Nothing is published yet:
`src/generated/` holds no frozen tag directory until the first tag cuts one.

The on-chain deploy comes first and is human-dispatched: the
`Manual sol artifacts` workflow runs `script/Deploy.sol` for the `log-tables`
suite, then again for `decimal-float` (its constructor reverts unless the tables
are already on-chain). Where the addresses already exist (deterministic Zoltu),
a deploy attests existing code rather than deploying fresh.

Cutting the release is then a PR that runs
`forge script ./script/Build.sol --sig 'cutRelease()'` + `forge fmt`, freezing
the candidate snapshots into `src/generated/<tag>/` and regenerating the
released-suites libs, with `[external.package].version` naming that tag. After
merge, pushing the `sol-v*` tag runs `rainix-tag-release`
(`package-release.yaml`), which re-runs the non-freezing build, requires a clean
tree with the frozen snapshot byte-identical to the candidate, verifies the live
chains against the record and publishes `rain-math-float-deploy` to Soldeer.

See rainlanguage/rain.factory#46 for the library/deploy split rationale.
30 changes: 20 additions & 10 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
# never warns about it.
[external.package]
name = "rain-math-float-deploy"
# Deploy repo: this is the version of the LAST Soldeer publish, not a
# next-version slot. A normal PR does not bump it; only a release moves it.
# Deploy repo: a normal PR does not bump this; only a release moves it, in
# lockstep with the frozen `src/generated/<tag>/` snapshot it names. Nothing
# is published yet, so this names the version the FIRST `sol-v*` tag will
# cut, and `src/generated/` holds no frozen tag directory until it does.
version = "0.1.0"

[profile.default]
Expand All @@ -25,31 +27,39 @@ optimizer = true
# bytecode, which is a pure function of these settings AND the library sources
# it compiles against (the `rain-math-float` package). These match the exact
# settings the live deployment was produced with in the pre-split hybrid
# `rain.math.float` repo; changing the run count moves the address and breaks
# every pin in `src/lib/deploy/LibDecimalFloatDeploy.sol`.
# `rain.math.float` repo; changing the run count regenerates the
# `src/generated/candidate/` pins at a different address, off the live
# deployment that `src/lib/deploy/LibDecimalFloatDeploy.sol` aliases them as.
optimizer_runs = 1000000

evm_version = "cancun"

bytecode_hash = "none"
cbor_metadata = false

# `LibDecimalFloatDeployTaggedConstantsTest` runs
# `script/check-published-deploy-constants.sh` via `vm.ffi`.
# `script/lib/LibCopyArtifacts.sol` extracts each committed ABI from the forge
# artifact via `jq` over `vm.ffi`.
ffi = true

libs = ["dependencies"]

# `script/Build.sol` regenerates `src/generated/LogTables.pointers.sol`
# under `src/`. The generated log-table bytes are a pure function of
# `LibLogTable` (mathematical tables), version-invariant, so this file is a
# single snapshot rather than a per-tag directory.
# Write access is scoped to exactly the files `script/Build.sol` generates:
# everything under `src/generated/` (the candidate snapshots, the frozen tag
# dirs `cutRelease()` adds, `LogTables.pointers.sol`) and the three generated
# released-suites libs in `src/lib/` — never hand-written source. It reads the
# release version from this file when `cutRelease()` runs, and
# `DecimalFloatDeploySnapshotTest`'s inherited frozen-record walk reads
# `src/generated/` too.
#
# `script/CopyArtifacts.sol` reads the freshly built forge artifacts under
# `./out` and writes the committed ABI copies the Rust crate compiles against
# into `./crates/float/abi`; `test/script/CopyArtifacts.t.sol` reads them back.
fs_permissions = [
{ access = "read", path = "./foundry.toml" },
{ access = "read-write", path = "./src/generated" },
{ access = "read-write", path = "./src/lib/LibLogTablesReleased.sol" },
{ access = "read-write", path = "./src/lib/LibDecimalFloatReleased.sol" },
{ access = "read-write", path = "./src/lib/LibReleasedSuites.sol" },
{ access = "read", path = "./out" },
{ access = "read-write", path = "./crates/float/abi" },
]
Expand Down
Loading
Loading