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
2 changes: 2 additions & 0 deletions .github/workflows/manual-sol-artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ jobs:

CI_DEPLOY_ARBITRUM_RPC_URL: ${{ secrets.CI_DEPLOY_ARBITRUM_RPC_URL || vars.CI_DEPLOY_ARBITRUM_RPC_URL || '' }}
CI_DEPLOY_BASE_RPC_URL: ${{ secrets.CI_DEPLOY_BASE_RPC_URL || vars.CI_DEPLOY_BASE_RPC_URL || '' }}
CI_DEPLOY_BASE_SEPOLIA_RPC_URL: ${{ secrets.CI_DEPLOY_BASE_SEPOLIA_RPC_URL || vars.CI_DEPLOY_BASE_SEPOLIA_RPC_URL || '' }}
CI_DEPLOY_FLARE_RPC_URL: ${{ secrets.CI_DEPLOY_FLARE_RPC_URL || vars.CI_DEPLOY_FLARE_RPC_URL || '' }}
CI_DEPLOY_POLYGON_RPC_URL: ${{ secrets.CI_DEPLOY_POLYGON_RPC_URL || vars.CI_DEPLOY_POLYGON_RPC_URL || '' }}

CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY || vars.CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_BASE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_BASE_ETHERSCAN_API_KEY || vars.CI_DEPLOY_BASE_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY || vars.CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_FLARE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_FLARE_ETHERSCAN_API_KEY || vars.CI_DEPLOY_FLARE_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY || vars.CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY || '' }}
78 changes: 78 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Decimal floating-point math library for Rainlang/DeFi. The `Float` type packs a 224-bit signed coefficient and 32-bit signed exponent into a single `bytes32`. Decimal (not binary) representation ensures exact decimal values (e.g., `0.1`). No NaN, Infinity, or negative zero — operations error on nonsense rather than producing special values.

Dual implementation: Solidity for on-chain, Rust/WASM for off-chain JS/TS consumption. The Rust crate uses revm to execute Solidity via an in-memory EVM, ensuring identical behavior.

## Build Commands

### Solidity (Foundry)
```bash
forge build # Compile contracts
forge test # Run all Solidity tests (5096 fuzz runs)
forge test --mt testFunctionName # Run specific test by name
forge test -vvvv # Verbose trace output for debugging
```

### Rust
```bash
cargo build # Build native
cargo build --target wasm32-unknown-unknown --lib -r # Build WASM
cargo test # Run Rust tests
cargo test test_name # Run specific test
```

Rust tests depend on Foundry build artifacts (`out/`). Run `forge build` before `cargo test` if artifacts are missing.

### JavaScript/WASM
```bash
npm install
npm run build # Full pipeline: Rust WASM → wasm-bindgen → base64 embed → CJS/ESM dist
npm test # TypeScript type check + vitest (tests in test_js/)
```

### Nix
```bash
nix develop # Enter dev shell with all tooling
```

## Architecture

### Solidity Layer (`src/`)
- **`lib/LibDecimalFloat.sol`** — Public API: arithmetic, comparison, conversion, formatting, parsing. User-defined type `Float` wrapping `bytes32`.
- **`lib/implementation/`** — Internal arithmetic (512-bit intermediates for mul/div), normalization, packing.
- **`lib/parse/`** — String-to-Float parsing.
- **`lib/format/`** — Float-to-string formatting.
- **`lib/table/`** — Log lookup tables (deployed as a data contract at a deterministic address).
- **`concrete/DecimalFloat.sol`** — Exposes library functions as contract methods (required for Rust/revm interop via ABI).
- **`error/`** — Custom error definitions (CoefficientOverflow, ExponentOverflow, DivisionByZero, etc.).

### 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).
- **`evm.rs`** — In-memory EVM setup via revm. All Rust float operations delegate to Solidity through this.
- **`error.rs`** — Maps Solidity error selectors to Rust error types.

### JavaScript Layer
- **`scripts/build.js`** — Build pipeline: compiles WASM, runs wasm-bindgen, base64-encodes WASM into JS modules for both CJS and ESM.
- **`test_js/`** — Vitest tests for the WASM bindings.
- **`dist/`** — Generated output (CJS + ESM with embedded WASM).

### Dependencies (`lib/`)
Git submodules: forge-std, rain.string, rain.datacontract, rain.math.fixedpoint, rain.deploy, rain.sol.codegen.

Comment on lines +11 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the markdownlint spacing violations.

MD022 and MD031 are already firing across the new headings and fenced blocks, so this file will keep the docs lint noisy until blank lines are added around those sections.

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 13-13: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 14-14: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 22-22: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 31-31: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 32-32: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38-38: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 39-39: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 45-45: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 60-60: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 65-65: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 11 - 67, The README has markdownlint violations
MD022/MD031 due to missing blank lines around headings and fenced code blocks
(e.g., "## Build Commands", "### Solidity (Foundry)", the triple-backtick code
blocks under Rust/JavaScript/WASM/Nix, and other "###" section headers); fix by
adding a single blank line before and after each heading and ensuring there is a
blank line above and below every fenced code block (the ```bash blocks) so that
headings like "## Build Commands", "### Solidity (Foundry)", "### Rust", "###
JavaScript/WASM", "### Nix" and their adjacent code fences conform to
markdownlint rules MD022/MD031.

## Key Design Details

- 512-bit intermediate values in multiply/divide to preserve precision.
- Exponent underflow silently rounds toward zero; exponent overflow reverts.
- Log/power use lookup table approximations with linear interpolation (table deployed as a data contract).
- Two packing modes: lossless (reverts on precision loss) and lossy (returns bool flag).
- Solidity compiler: 0.8.25, EVM target: Cancun, optimizer: 1,000,000 runs.

## License

LicenseRef-DCL-1.0 (Rain Decentralized Computer License). All source files require SPDX headers per REUSE.toml.
Comment on lines +1 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Split this agent guide into a docs-only follow-up.

This file is orthogonal to the Base Sepolia / deploy-path change, so keeping it in the same PR makes the deployment review harder to reason about.

Based on learnings, in the rainlanguage/rain.math.float repository, the maintainer 0xgleb prefers to handle documentation additions and improvements in separate issues rather than inline with feature PRs.

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 13-13: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 14-14: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 22-22: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 31-31: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 32-32: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38-38: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 39-39: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 45-45: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 60-60: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 65-65: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 1 - 78, This PR includes a docs-only file (CLAUDE.md)
unrelated to the Base Sepolia / deploy-path change; remove CLAUDE.md from this
branch and open a separate docs-only follow-up PR (or issue) that contains the
same content, referencing the rainlanguage/rain.math.float repo and maintainer
0xgleb's preference for separate documentation changes. Specifically: revert or
remove the CLAUDE.md addition from the current commit/branch, update the PR
description to note the removal, and create a new issue/PR titled "Add CLAUDE.md
agent guide" containing the file content so reviewers can evaluate docs
separately.

1 change: 1 addition & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ path = [
"scripts/**/",
"test_js/**/",
"tsconfig.json",
"CLAUDE.md",
"README.md",
"crates/**/",
"flake.lock",
Expand Down
2 changes: 1 addition & 1 deletion foundry.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"rev": "3bff30782e408386f73b9353c5122e2e0ab24df2"
},
"lib/rain.deploy": {
"rev": "1af8ca2a981c2f67844f343e224f4c7b1969de1c"
"rev": "ccfdb378d2ab1ed95461ecf1355682250e487e4c"
},
"lib/rain.math.fixedpoint": {
"rev": "8308cbb6da0e231c6f3437f1861e66eff7ea2b00"
Expand Down
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ runs = 5096
[rpc_endpoints]
arbitrum = "${CI_DEPLOY_ARBITRUM_RPC_URL}"
base = "${CI_DEPLOY_BASE_RPC_URL}"
base_sepolia = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"

[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
base_sepolia = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
Comment on lines +42 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden -S '\bbase[-_]sepolia\b|supportedNetworks\s*\('

Repository: rainlanguage/rain.math.float

Length of output: 1822


🌐 Web query:

Foundry Alloy chain aliases base-sepolia documentation

💡 Result:

In Foundry, the chain keys you use in TOML config files (e.g., deployments.toml) must be either a numeric chain id ([84532]) or a valid Alloy chain alias (like [base-sepolia]). If you use a custom name that isn’t an Alloy alias, you’ll hit InvalidChainKey. [1]

Where it’s documented / defined

  • Foundry docs (chain keys + Alloy aliases + InvalidChainKey): “Scripting with config” guide. [1]
  • Alloy alias exists: alloy_chains::Chain::base_sepolia() (shows Base Sepolia is a known chain in Alloy’s chain list). [2]

Base Sepolia network details (for your endpoint_url)

Example

# deployments.toml
[base-sepolia]
endpoint_url = "https://sepolia.base.org"

(Or use [84532] instead of [base-sepolia] for maximum compatibility.) [1][3]


Use the canonical Base Sepolia alias spelling in foundry.toml.

The base_sepolia keys will not be resolved by vm.rpcUrl(...) or etherscan lookups. Foundry's documented Alloy chain alias for Base Sepolia is base-sepolia (with hyphens), not underscores. TOML keys with hyphens must be quoted.

Suggested change
 [rpc_endpoints]
 arbitrum = "${CI_DEPLOY_ARBITRUM_RPC_URL}"
 base = "${CI_DEPLOY_BASE_RPC_URL}"
-base_sepolia = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
+"base-sepolia" = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
 flare = "${CI_DEPLOY_FLARE_RPC_URL}"
 polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"
 
 [etherscan]
 arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
 base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
-base_sepolia = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
+"base-sepolia" = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
 flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" }
 polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
base_sepolia = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
base_sepolia = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
base_sepolia = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
"base-sepolia" = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
Suggested change
base_sepolia = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
base_sepolia = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
[rpc_endpoints]
arbitrum = "${CI_DEPLOY_ARBITRUM_RPC_URL}"
base = "${CI_DEPLOY_BASE_RPC_URL}"
"base-sepolia" = "${CI_DEPLOY_BASE_SEPOLIA_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
"base-sepolia" = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@foundry.toml` around lines 42 - 49, The TOML uses the noncanonical alias
base_sepolia (underscores), which Foundry/Alloy expects as "base-sepolia"
(hyphenated and quoted for TOML keys); change the top-level RPC key base_sepolia
and the [etherscan] table entry base_sepolia to the hyphenated quoted key
"base-sepolia" so vm.rpcUrl(...) and etherscan lookups resolve correctly,
ensuring you update both the RPC variable line (base_sepolia = ...) and the
etherscan mapping arbitrum/base/base_sepolia = ... to use "base-sepolia" instead
of base_sepolia.

flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" }
polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" }
12 changes: 8 additions & 4 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,37 @@ bytes32 constant DEPLOYMENT_SUITE_TABLES = keccak256("log-tables");
bytes32 constant DEPLOYMENT_SUITE_CONTRACT = keccak256("decimal-float");

contract Deploy is Script {
mapping(string => mapping(address => bytes32)) internal sDepCodeHashes;

function run() external {
uint256 deployerPrivateKey = vm.envUint("DEPLOYMENT_KEY");

bytes32 suite = keccak256(bytes(vm.envOr("DEPLOYMENT_SUITE", string("decimal-float"))));
if (suite == DEPLOYMENT_SUITE_TABLES) {
LibRainDeploy.deployAndBroadcastToSupportedNetworks(
LibRainDeploy.deployAndBroadcast(
vm,
LibRainDeploy.supportedNetworks(),
deployerPrivateKey,
LibDataContract.contractCreationCode(LibDecimalFloatDeploy.combinedTables()),
"",
LibDecimalFloatDeploy.ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS,
LibDecimalFloatDeploy.LOG_TABLES_DATA_CONTRACT_HASH,
new address[](0)
new address[](0),
sDepCodeHashes
);
} else if (suite == DEPLOYMENT_SUITE_CONTRACT) {
address[] memory decimalFloatDependencies = new address[](1);
decimalFloatDependencies[0] = LibDecimalFloatDeploy.ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS;
LibRainDeploy.deployAndBroadcastToSupportedNetworks(
LibRainDeploy.deployAndBroadcast(
vm,
LibRainDeploy.supportedNetworks(),
deployerPrivateKey,
type(DecimalFloat).creationCode,
"src/concrete/DecimalFloat.sol:DecimalFloat",
LibDecimalFloatDeploy.ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS,
LibDecimalFloatDeploy.DECIMAL_FLOAT_CONTRACT_HASH,
decimalFloatDependencies
decimalFloatDependencies,
sDepCodeHashes
);
} else {
revert(
Expand Down