diff --git a/.github/workflows/manual-sol-artifacts.yaml b/.github/workflows/manual-sol-artifacts.yaml index 750c70f..e7e481c 100644 --- a/.github/workflows/manual-sol-artifacts.yaml +++ b/.github/workflows/manual-sol-artifacts.yaml @@ -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 || '' }} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b8a9fbc --- /dev/null +++ b/CLAUDE.md @@ -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. + +## 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. diff --git a/REUSE.toml b/REUSE.toml index da40ea3..f53a8f9 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -16,6 +16,7 @@ path = [ "scripts/**/", "test_js/**/", "tsconfig.json", + "CLAUDE.md", "README.md", "crates/**/", "flake.lock", diff --git a/foundry.lock b/foundry.lock index 7c0b688..5c0234a 100644 --- a/foundry.lock +++ b/foundry.lock @@ -6,7 +6,7 @@ "rev": "3bff30782e408386f73b9353c5122e2e0ab24df2" }, "lib/rain.deploy": { - "rev": "1af8ca2a981c2f67844f343e224f4c7b1969de1c" + "rev": "ccfdb378d2ab1ed95461ecf1355682250e487e4c" }, "lib/rain.math.fixedpoint": { "rev": "8308cbb6da0e231c6f3437f1861e66eff7ea2b00" diff --git a/foundry.toml b/foundry.toml index 3822d70..555cc84 100644 --- a/foundry.toml +++ b/foundry.toml @@ -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}" } flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" } polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" } diff --git a/lib/rain.deploy b/lib/rain.deploy index 1af8ca2..ccfdb37 160000 --- a/lib/rain.deploy +++ b/lib/rain.deploy @@ -1 +1 @@ -Subproject commit 1af8ca2a981c2f67844f343e224f4c7b1969de1c +Subproject commit ccfdb378d2ab1ed95461ecf1355682250e487e4c diff --git a/script/Deploy.sol b/script/Deploy.sol index d177062..4dc3fef 100644 --- a/script/Deploy.sol +++ b/script/Deploy.sol @@ -12,12 +12,14 @@ 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, @@ -25,12 +27,13 @@ contract Deploy is Script { "", 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, @@ -38,7 +41,8 @@ contract Deploy is Script { "src/concrete/DecimalFloat.sol:DecimalFloat", LibDecimalFloatDeploy.ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS, LibDecimalFloatDeploy.DECIMAL_FLOAT_CONTRACT_HASH, - decimalFloatDependencies + decimalFloatDependencies, + sDepCodeHashes ); } else { revert(