Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ target
temp
dist
node_modules
.env
.env
.fixes
12 changes: 12 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ npm test # TypeScript type check + vitest (tests in test_js/)
nix develop # Enter dev shell with all tooling
```

### Deployment
Contracts are deployed deterministically via the Zoltu proxy to the same address on all supported networks (Arbitrum, Base, Base Sepolia, Flare, Polygon). Two deployment suites (log-tables must be deployed first):
```bash
DEPLOYMENT_KEY=<key> DEPLOYMENT_SUITE=log-tables forge script script/Deploy.sol:Deploy --broadcast --verify
DEPLOYMENT_KEY=<key> DEPLOYMENT_SUITE=decimal-float forge script script/Deploy.sol:Deploy --broadcast --verify
```
Expected addresses and code hashes are in `src/lib/deploy/LibDecimalFloatDeploy.sol`. Network RPC URLs are configured in `foundry.toml` via `CI_DEPLOY_*_RPC_URL` env vars.

## Architecture

### Solidity Layer (`src/`)
Expand All @@ -51,6 +59,10 @@ nix develop # Enter dev shell with all tooling
- **`concrete/DecimalFloat.sol`** — Exposes library functions as contract methods (required for Rust/revm interop via ABI).
- **`error/`** — Custom error definitions (CoefficientOverflow, ExponentOverflow, DivisionByZero, etc.).

### Scripts (`script/`)
- **`Deploy.sol`** — Production deployment script using Zoltu deterministic proxy. Deploys log tables and DecimalFloat contract to all supported networks.
- **`BuildPointers.sol`** — Generates `src/generated/LogTables.pointers.sol` (committed to repo; must be regenerated if log table data changes).

### Rust Layer (`crates/float/`)
- **`lib.rs`** — `Float` struct wrapping `B256`, implements `Add`/`Sub`/`Mul`/`Div`/`Neg`. Uses `alloy::sol!` macro to generate bindings from Foundry JSON artifacts in `out/`.
- **`js_api.rs`** — `#[wasm_bindgen]` exports for JS consumption (parse, format, arithmetic, conversions).
Expand Down
39 changes: 39 additions & 0 deletions audit/2026-03-10-01/pass0/process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Audit Pass 0: Process Review

**Date:** 2026-03-10
**Files reviewed:** CLAUDE.md, README.md, REUSE.toml, foundry.toml

## Evidence of Reading

### CLAUDE.md
- Sections: Project Overview, Build Commands (Solidity, Rust, JavaScript/WASM, Nix), Architecture (Solidity Layer, Rust Layer, JavaScript Layer, Dependencies), Key Design Details, License
- Build commands: forge build/test, cargo build/test, npm install/build/test, nix develop
- Architecture references: LibDecimalFloat.sol, implementation/, parse/, format/, table/, DecimalFloat.sol, error/, lib.rs, js_api.rs, evm.rs, error.rs, build.js, test_js/, dist/
- Dependencies listed: forge-std, rain.string, rain.datacontract, rain.math.fixedpoint, rain.deploy, rain.sol.codegen

### README.md
- Sections: Context, Rounding vs. erroring vs. approximating (Rounding direction, Approach to preserving precision, Exponent underflow, Packing, Fixed decimal conversions, Exponent overflow, Other overflows, Uncalculable values, Unimplemented math, Parsing/formatting issues, Lossy conversions, Approximations)
- External reference: rainlanguage/rain.math.float#88

### REUSE.toml
- Single annotations block covering config/metadata files
- SPDX: LicenseRef-DCL-1.0

### foundry.toml
- Settings: solc 0.8.25, optimizer 1000000 runs, evm cancun, fuzz 5096 runs
- RPC endpoints: arbitrum, base, base_sepolia, flare, polygon
- Etherscan keys for same 5 networks

## Findings

### A01-1: CLAUDE.md omits `script/` directory from architecture (LOW)

The Architecture section documents `src/`, `crates/float/`, `scripts/`, `test_js/`, and `dist/` but does not mention `script/` which contains `Deploy.sol` and `BuildPointers.sol`. These are operationally critical — Deploy.sol is the production deployment script, and BuildPointers.sol generates committed source code (`src/generated/LogTables.pointers.sol`). A future session could be unaware of how deployment or pointer regeneration works.

### A01-2: CLAUDE.md omits deployment workflow documentation (LOW)

There is no mention of how to deploy contracts, what networks are supported, what environment variables are needed (`DEPLOYMENT_KEY`, `DEPLOYMENT_SUITE`, `CI_DEPLOY_*_RPC_URL`), or where deterministic addresses are defined (`LibDecimalFloatDeploy.sol`). A future session asked to deploy would need to reverse-engineer the workflow from `script/Deploy.sol` and the CI config.

### A01-3: CLAUDE.md omits test directory structure (INFO)

The architecture section doesn't describe the `test/` directory or its organization. Test files mirror the source tree under `test/src/` but this convention is undocumented.
113 changes: 113 additions & 0 deletions audit/2026-03-10-01/pass1/DecimalFloat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Audit Pass 1 (Security) -- `src/concrete/DecimalFloat.sol`

**Auditor:** A01
**Date:** 2026-03-10
**File:** `src/concrete/DecimalFloat.sol` (320 lines)

---

## Evidence of Reading

### Contract

- **`DecimalFloat`** (line 9) -- concrete contract that exposes `LibDecimalFloat`, `LibFormatDecimalFloat`, and `LibParseDecimalFloat` library functions as external/public methods for off-chain (Rust/revm) interop.

### Imports (lines 5-7)

| Import | Source |
|--------|--------|
| `LibDecimalFloat`, `Float` | `../lib/LibDecimalFloat.sol` |
| `LibFormatDecimalFloat` | `../lib/format/LibFormatDecimalFloat.sol` |
| `LibParseDecimalFloat` | `../lib/parse/LibParseDecimalFloat.sol` |

### Constants (lines 12-20)

| Name | Line | Description |
|------|------|-------------|
| `FORMAT_DEFAULT_SCIENTIFIC_MIN` | 14 | `1e-4`, default lower bound for scientific formatting |
| `FORMAT_DEFAULT_SCIENTIFIC_MAX` | 19 | `1e9`, default upper bound for scientific formatting |

### Functions

| Function | Line | Visibility | Mutability | Delegates to |
|----------|------|------------|------------|-------------|
| `maxPositiveValue()` | 24 | external | pure | `LibDecimalFloat.FLOAT_MAX_POSITIVE_VALUE` |
| `minPositiveValue()` | 30 | external | pure | `LibDecimalFloat.FLOAT_MIN_POSITIVE_VALUE` |
| `maxNegativeValue()` | 36 | external | pure | `LibDecimalFloat.FLOAT_MAX_NEGATIVE_VALUE` |
| `minNegativeValue()` | 42 | external | pure | `LibDecimalFloat.FLOAT_MIN_NEGATIVE_VALUE` |
| `zero()` | 48 | external | pure | `LibDecimalFloat.FLOAT_ZERO` |
| `e()` | 54 | external | pure | `LibDecimalFloat.FLOAT_E` |
| `parse(string)` | 64 | external | pure | `LibParseDecimalFloat.parseDecimalFloat` |
| `format(Float, Float, Float)` | 78 | public | pure | `LibFormatDecimalFloat.toDecimalString` |
| `format(Float, bool)` | 89 | external | pure | `LibFormatDecimalFloat.toDecimalString` |
| `format(Float)` | 97 | external | pure | calls `format(Float, Float, Float)` |
| `add(Float, Float)` | 105 | external | pure | `a.add(b)` |
| `sub(Float, Float)` | 113 | external | pure | `a.sub(b)` |
| `minus(Float)` | 120 | external | pure | `a.minus()` |
| `abs(Float)` | 127 | external | pure | `a.abs()` |
| `mul(Float, Float)` | 135 | external | pure | `a.mul(b)` |
| `div(Float, Float)` | 143 | external | pure | `a.div(b)` |
| `inv(Float)` | 150 | external | pure | `a.inv()` |
| `eq(Float, Float)` | 158 | external | pure | `a.eq(b)` |
| `lt(Float, Float)` | 166 | external | pure | `a.lt(b)` |
| `gt(Float, Float)` | 175 | external | pure | `a.gt(b)` |
| `lte(Float, Float)` | 184 | external | pure | `a.lte(b)` |
| `gte(Float, Float)` | 193 | external | pure | `a.gte(b)` |
| `integer(Float)` | 200 | external | pure | `a.integer()` |
| `frac(Float)` | 207 | external | pure | `a.frac()` |
| `floor(Float)` | 214 | external | pure | `a.floor()` |
| `ceil(Float)` | 221 | external | pure | `a.ceil()` |
| `pow10(Float)` | 228 | external | view | `a.pow10(LOG_TABLES_ADDRESS)` |
| `log10(Float)` | 235 | external | view | `a.log10(LOG_TABLES_ADDRESS)` |
| `pow(Float, Float)` | 243 | external | view | `a.pow(b, LOG_TABLES_ADDRESS)` |
| `sqrt(Float)` | 250 | external | view | `a.sqrt(LOG_TABLES_ADDRESS)` |
| `min(Float, Float)` | 258 | external | pure | `a.min(b)` |
| `max(Float, Float)` | 266 | external | pure | `a.max(b)` |
| `isZero(Float)` | 273 | external | pure | `a.isZero()` |
| `fromFixedDecimalLossless(uint256, uint8)` | 284 | external | pure | `LibDecimalFloat.fromFixedDecimalLosslessPacked` |
| `toFixedDecimalLossless(Float, uint8)` | 293 | external | pure | `LibDecimalFloat.toFixedDecimalLossless` |
| `fromFixedDecimalLossy(uint256, uint8)` | 305 | external | pure | `LibDecimalFloat.fromFixedDecimalLossyPacked` |
| `toFixedDecimalLossy(Float, uint8)` | 316 | external | pure | `LibDecimalFloat.toFixedDecimalLossy` |

### Types / Errors / Assembly

- No custom types or errors defined in this file.
- No assembly blocks.
- No state-modifying storage writes.
- No reentrancy vectors (all functions are `pure` or `view` with no external calls except reads from `LOG_TABLES_ADDRESS`).

---

## Security Findings

### A01-3: `require` uses string revert message instead of custom error [LOW]

**Location:** `src/concrete/DecimalFloat.sol`, line 79

**Description:**

```solidity
require(scientificMin.lt(scientificMax), "scientificMin must be less than scientificMax");
```

The entire codebase consistently uses custom errors (defined in `src/error/ErrDecimalFloat.sol` and `src/error/ErrFormat.sol`). This is the only location in the `src/` tree that uses a string-based `require`. String-based reverts:

1. Are more expensive at deployment and at revert time (ABI-encodes the string as `Error(string)`).
2. Are harder to catch and decode programmatically on-chain compared to a 4-byte custom error selector.
3. Break the project's own convention of using custom errors everywhere.

Because this contract is primarily used as an off-chain interop target (Rust/revm calls), the practical on-chain impact is low. However, the inconsistency is a code-quality concern and a minor gas inefficiency.

**Severity:** LOW

**Recommendation:** Define a custom error (e.g., `ScientificMinNotLessThanMax(Float scientificMin, Float scientificMax)`) in the appropriate error file and replace the `require` with a conditional revert.

---

### Summary

| ID | Severity | Title |
|----|----------|-------|
| A01-3 | LOW | `require` uses string revert message instead of custom error |

No MEDIUM, HIGH, or CRITICAL findings. The contract is a thin delegation layer with no storage, no assembly, no reentrancy surface, and no arithmetic of its own. All logic is delegated to the underlying library functions.
45 changes: 45 additions & 0 deletions audit/2026-03-10-01/pass1/LibDecimalFloat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Audit Pass 1: Security — `src/lib/LibDecimalFloat.sol` (A06)

**Date:** 2026-03-10 | **File:** 796 lines

## Evidence of Reading

**Library:** `LibDecimalFloat` (line 44) — Main public API library. Defines `Float` UDT wrapping `bytes32`.

**Type:** `Float` (line 16) — Upper 32 bits: int32 exponent, lower 224 bits: int224 coefficient.

**Constants (10):** `LOG_TABLES_ADDRESS` (L50), `FLOAT_ZERO` (L53), `FLOAT_ONE` (L56), `FLOAT_HALF` (L60), `FLOAT_TWO` (L64), `FLOAT_MAX_POSITIVE_VALUE` (L68), `FLOAT_MIN_POSITIVE_VALUE` (L73), `FLOAT_MAX_NEGATIVE_VALUE` (L79), `FLOAT_MIN_NEGATIVE_VALUE` (L85), `FLOAT_E` (L91).

**Functions (32):**
- Conversion: `fromFixedDecimalLossy` (L104), `fromFixedDecimalLossyPacked` (L132), `fromFixedDecimalLossless` (L144), `fromFixedDecimalLosslessPacked` (L158), `toFixedDecimalLossy` (L176, L254), `toFixedDecimalLossless` (L265, L286)
- Packing: `packLossy` (L299), `packLossless` (L358), `unpack` (L373)
- Arithmetic: `add` (L388), `sub` (L405), `minus` (L421), `abs` (L440), `mul` (L474), `div` (L491), `inv` (L507)
- Comparison: `eq` (L520), `lt` (L531), `gt` (L545), `lte` (L557), `gte` (L569)
- Rounding: `integer` (L582), `frac` (L593), `floor` (L603), `ceil` (L621)
- Transcendental: `pow10` (L652), `log10` (L668), `pow` (L690), `sqrt` (L764)
- Utility: `min` (L773), `max` (L781), `isZero` (L788)

**Assembly blocks (3):** `packLossy` (L347-349), `unpack` (L375-378), `isZero` (L790-793)

## Findings

No LOW+ security findings.

### A06-1 [INFO]: Silent precision loss in arithmetic operations without caller notification

All packed arithmetic functions discard the `lossless` bool from `packLossy`. By design and documented. The ~67 decimal digits of int224 precision makes packing-induced loss extremely rare.

### A06-2 [INFO]: `pow` exponentiation-by-squaring loop may consume excessive gas for large integer exponents

The loop iterates O(log2(exponentBInteger)) times with no explicit upper bound. In practice, intermediate overflows cause reverts before gas exhaustion.

## Verified as Correct

- All 3 assembly blocks: pure stack operations, correct signextend/sar usage, no memory safety issues
- Pack/unpack round-trip for edge cases (int224.min, int224.max, negative exponents)
- All 10 constants encode correct values
- `compareRescale` edge cases (zero, different signs, extreme exponent diffs) handled correctly
- `toFixedDecimalLossy` overflow checks correct
- `packLossy` truncation loop and exponent overflow handling correct
- `pow` function flow: negative b recursion, zero base, identity case all correct
- `floor`/`ceil` sign-aware rounding logic correct
94 changes: 94 additions & 0 deletions audit/2026-03-10-01/pass1/LibDecimalFloatDeploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Audit Pass 1 (Security) -- LibDecimalFloatDeploy.sol

**Agent:** A07
**File:** `src/lib/deploy/LibDecimalFloatDeploy.sol`
**Date:** 2026-03-10

---

## Evidence of Thorough Reading

### Library Name
- `LibDecimalFloatDeploy` (line 19)

### Functions
| Function | Line | Visibility | Mutability |
|---|---|---|---|
| `combinedTables()` | 42 | `internal` | `pure` |

### Constants
| Name | Line | Type | Value |
|---|---|---|---|
| `ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS` | 23 | `address` | `0xc51a14251b0dcF0ae24A96b7153991378938f5F5` |
| `LOG_TABLES_DATA_CONTRACT_HASH` | 27 | `bytes32` | `0x2573004ac3a9ee7fc8d73654d76386f1b6b99e34cdf86a689c4691e47143420f` |
| `ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS` | 32 | `address` | `0x12A66eFbE556e38308A17e34cC86f21DcA1CDB73` |
| `DECIMAL_FLOAT_CONTRACT_HASH` | 36 | `bytes32` | `0x705cdef2ed9538557152f86cd0988c748e0bd647a49df00b3e4f100c3544a583` |

### Types / Errors Defined
None defined in this file. `WriteError` is imported but unused.

### Imports
| Import | Source | Used in File Body? |
|---|---|---|
| `LOG_TABLES` | `LogTables.pointers.sol` | Yes (line 44) |
| `LOG_TABLES_SMALL` | `LogTables.pointers.sol` | Yes (line 45) |
| `LOG_TABLES_SMALL_ALT` | `LogTables.pointers.sol` | Yes (line 46) |
| `ANTI_LOG_TABLES` | `LogTables.pointers.sol` | Yes (line 47) |
| `ANTI_LOG_TABLES_SMALL` | `LogTables.pointers.sol` | Yes (line 48) |
| `LibDataContract`, `DataContractMemoryContainer` | `rain.datacontract` | No |
| `LibBytes` | `rain.solmem` | No |
| `LibMemCpy`, `Pointer` | `rain.solmem` | No |
| `DecimalFloat` | `concrete/DecimalFloat.sol` | No (re-exported for consumers) |
| `LOG_TABLE_DISAMBIGUATOR` | `LibLogTable.sol` | Yes (line 49) |
| `WriteError` | `ErrDecimalFloat.sol` | No |

---

## Security Analysis

### Hardcoded Addresses and Code Hashes

The file defines two hardcoded addresses and their expected codehashes for deterministic deployment via the Zoltu proxy. These constants are consumed by:

1. **`script/Deploy.sol`** -- The deployment script passes these constants to `LibRainDeploy.deployAndBroadcast`, which verifies the deployed codehash matches before proceeding.
2. **`test/src/lib/deploy/LibDecimalFloatDeploy.t.sol`** -- Tests verify that deploying via Zoltu produces the expected address and codehash.
3. **`test/src/lib/deploy/LibDecimalFloatDeployProd.t.sol`** -- Production fork tests verify that all supported networks (Arbitrum, Base, Base Sepolia, Flare, Polygon) have the contracts deployed at the expected addresses with correct codehashes.
4. **`test/abstract/LogTest.sol`** -- Test helper deploys combined tables and verifies codehash.

The addresses are deterministic (derived from Zoltu's deployment proxy with known creation code), so they are correctly constant across EVM-compatible chains. The codehash provides a second layer of verification that the deployed bytecode is exactly as expected.

The runtime library (`LibDecimalFloat.sol`) does NOT hardcode the tables address. Instead, `log10`, `pow10`, and `pow` accept `tablesDataContract` as an explicit parameter, so there is no risk of silently using the wrong tables at runtime -- the caller is responsible for providing the correct address.

### Data Integrity of Combined Tables

The `combinedTables()` function uses `abi.encodePacked` to concatenate five table byte constants and the `LOG_TABLE_DISAMBIGUATOR`. The use of `abi.encodePacked` on fixed-size `bytes` constants is safe here because there is no dynamic-length ambiguity -- each constant is a fixed hex literal. The disambiguator (`keccak256("LOG_TABLE_DISAMBIGUATOR_1")`) ensures the creation code is unique even if table contents happen to collide with other data contract deployments.

### Deployment to Wrong Address

The deployment flow in `Deploy.sol` passes both the expected address and expected codehash to `LibRainDeploy.deployAndBroadcast`. The Zoltu deterministic deployment proxy guarantees address determinism. If the creation code changes (e.g., table data modified), the address and codehash would both change, and the deployment script would catch the mismatch. This is well-guarded.

---

## Findings

### A07-1 [INFO] Unused Imports

**Lines:** 12-15, 17

Several imports are present in the file but never used in its body:
- `LibDataContract`, `DataContractMemoryContainer` (line 12)
- `LibBytes` (line 13)
- `LibMemCpy`, `Pointer` (line 14)
- `WriteError` (line 17)

`DecimalFloat` (line 15) is imported but only used in NatSpec comments; however, it serves the purpose of being re-exported to downstream consumers (e.g., `test/src/lib/deploy/LibDecimalFloatDeploy.t.sol` imports both `LibDecimalFloatDeploy` and `DecimalFloat` from this file's path).

**Severity:** Informational. Unused imports have no security impact but increase compile-time noise and may confuse auditors/reviewers. The Solidity compiler warns about these.

**Recommendation:** Remove `LibDataContract`, `DataContractMemoryContainer`, `LibBytes`, `LibMemCpy`, `Pointer`, and `WriteError` imports. Keep `DecimalFloat` only if it is intentionally re-exported.

---

## Summary

No security findings at LOW or above. The file is a well-structured deployment configuration library. The hardcoded addresses and codehashes are properly verified through both deterministic deployment mechanics and test coverage across multiple networks. The runtime library does not rely on these constants, requiring callers to explicitly provide the tables address.
Loading
Loading