From f9ba4270a957eaedfe6801862997f8d691aceca5 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Wed, 23 Jul 2025 23:49:41 +0000 Subject: [PATCH 01/10] init --- crates/float/src/js_api.rs | 61 +++++++++++++++++++++++++++++ crates/float/src/lib.rs | 73 +++++++++++++++++++++++++++++++++++ src/concrete/DecimalFloat.sol | 20 ++++++++++ 3 files changed, 154 insertions(+) diff --git a/crates/float/src/js_api.rs b/crates/float/src/js_api.rs index efdc0a4c..d70da23a 100644 --- a/crates/float/src/js_api.rs +++ b/crates/float/src/js_api.rs @@ -176,6 +176,67 @@ impl Float { .map_err(|e| FloatError::JsSysError(e.to_string().into())) } + /// Converts a fixed-point decimal value to a `Float` using the specified number of decimals lossy. + /// + /// # Arguments + /// + /// * `value` - The fixed-point decimal value as a `string`. + /// * `decimals` - The number of decimals in the fixed-point representation. + /// + /// # Returns + /// + /// * `Ok(Float)` - The resulting `Float` value. + /// * `Err(FloatError)` - If the conversion fails. + /// + /// # Example + /// + /// ```typescript + /// const floatResult = Float.fromFixedDecimalLossy("12345", 2); + /// if (floatResult.error) { + /// console.error(floatResult.error); + /// } + /// const float = floatResult.value; + /// assert(float.format() === "123.45"); + /// ``` + #[wasm_export(js_name = "fromFixedDecimalLossy", preserve_js_class)] + pub fn from_fixed_decimal_lossy_js(value: BigInt, decimals: u8) -> Result { + let value_str: String = value.to_string(10)?.into(); + let val = U256::from_str(&value_str)?; + Self::from_fixed_decimal_lossy(val, decimals) + } + + /// Converts a `Float` to a fixed-point decimal value using the specified number of decimals lossy. + /// + /// # Arguments + /// + /// * `decimals` - The number of decimals in the fixed-point representation. + /// + /// # Returns + /// + /// * `Ok(String)` - The resulting fixed-point decimal value as a string. + /// * `Err(FloatError)` - If the conversion fails. + /// + /// # Example + /// + /// ```typescript + /// const float = Float.parse("123.45").value!; + /// const result = float.toFixedDecimalLossy(2); + /// if (result.error) { + /// console.error(result.error); + /// } + /// assert(result.value === "12345"); + /// ``` + #[wasm_export( + js_name = "toFixedDecimalLossy", + preserve_js_class, + unchecked_return_type = "bigint" + )] + pub fn to_fixed_decimal_lossy_js(&self, decimals: u8) -> Result { + let fixed = self.to_fixed_decimal_lossy(decimals)?; + BigInt::from_str(&fixed.to_string()) + .map_err(|e| FloatError::JsSysError(e.to_string().into())) + } + /// Packs a coefficient and exponent into a `Float` in a lossless manner. /// /// # Arguments diff --git a/crates/float/src/lib.rs b/crates/float/src/lib.rs index fccbac87..5addd3cf 100644 --- a/crates/float/src/lib.rs +++ b/crates/float/src/lib.rs @@ -114,6 +114,79 @@ impl Float { }) } + /// Converts a fixed-point decimal value to a `Float` using the specified number of decimals lossy. + /// + /// # Arguments + /// + /// * `value` - The fixed-point decimal value as a `U256`. + /// * `decimals` - The number of decimals in the fixed-point representation. + /// + /// # Returns + /// + /// * `Ok(Float)` - The resulting `Float` value. + /// * `Err(FloatError)` - If the conversion fails. + /// + /// # Example + /// + /// ``` + /// use rain_math_float::Float; + /// use alloy::primitives::U256; + /// + /// // 123.45 with 2 decimals is represented as 12345 + /// let value = U256::from(12345u64); + /// let decimals = 2u8; + /// let float = Float::from_fixed_decimal_lossy(value, decimals)?; + /// assert_eq!(float.format()?, "123.45"); + /// + /// anyhow::Ok(()) + /// ``` + pub fn from_fixed_decimal_lossy(value: U256, decimals: u8) -> Result { + let calldata = + DecimalFloat::fromFixedDecimalLossyPackedCall { value, decimals }.abi_encode(); + + execute_call(Bytes::from(calldata), |output| { + let decoded = DecimalFloat::fromFixedDecimalLossyPackedCall::abi_decode_returns( + output.as_ref(), + )?; + Ok(Float(decoded._0)) + }) + } + + /// Converts a `Float` to a fixed-point decimal value using the specified number of decimals lossy. + /// + /// # Arguments + /// + /// * `decimals` - The number of decimals in the fixed-point representation. + /// + /// # Returns + /// + /// * `Ok(U256)` - The resulting fixed-point decimal value. + /// * `Err(FloatError)` - If the conversion fails. + /// + /// # Example + /// + /// ``` + /// use rain_math_float::Float; + /// use alloy::primitives::U256; + /// + /// // 123.45 with 2 decimals becomes 12345 + /// let float = Float::parse("123.45".to_string())?; + /// let fixed = float.to_fixed_decimal_lossy(2)?; + /// assert_eq!(fixed, U256::from(12345u64)); + /// + /// anyhow::Ok(()) + /// ``` + pub fn to_fixed_decimal_lossy(self, decimals: u8) -> Result { + let Float(float) = self; + let calldata = DecimalFloat::toFixedDecimalLossyCall { float, decimals }.abi_encode(); + + execute_call(Bytes::from(calldata), |output| { + let decoded = + DecimalFloat::toFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?; + Ok(decoded._0) + }) + } + /// Packs a coefficient and exponent into a `Float` in a lossless manner. /// /// # Arguments diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index 7e4d15e6..bdc2ae61 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -236,4 +236,24 @@ contract DecimalFloat { function toFixedDecimalLossless(Float float, uint8 decimals) external pure returns (uint256) { return LibDecimalFloat.toFixedDecimalLossless(float, decimals); } + + /// Exposes `LibDecimalFloat.fromFixedDecimalLossyPacked` for offchain + /// use. + /// @param value The fixed point decimal value to convert. + /// @param decimals The number of decimals in the fixed point + /// representation. e.g. If 1e18 represents 1 this would be 18 decimals. + /// @return float The Float struct containing the signed coefficient and + /// exponent. + function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) external pure returns (Float, bool) { + return LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); + } + + /// Exposes `LibDecimalFloat.toFixedDecimalLossy` for offchain use. + /// @param float The Float struct to convert. + /// @param decimals The number of decimals in the fixed point + /// representation. e.g. If 1e18 represents 1 this would be 18 decimals. + /// @return The fixed point decimal value as a uint256. + function toFixedDecimalLossy(Float float, uint8 decimals) external pure returns (uint256, bool) { + return LibDecimalFloat.toFixedDecimalLossy(float, decimals); + } } From c236a7181a59840dc417e3a6b85003fc636165c1 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Thu, 24 Jul 2025 00:32:57 +0000 Subject: [PATCH 02/10] tests --- crates/float/src/js_api.rs | 4 +-- crates/float/src/lib.rs | 61 ++++++++++++++++++++++++++++++++--- src/concrete/DecimalFloat.sol | 4 ++- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/crates/float/src/js_api.rs b/crates/float/src/js_api.rs index d70da23a..ee3b250e 100644 --- a/crates/float/src/js_api.rs +++ b/crates/float/src/js_api.rs @@ -219,12 +219,12 @@ impl Float { /// # Example /// /// ```typescript - /// const float = Float.parse("123.45").value!; + /// const float = Float.fromFixedDecimal(12345n, 3).value!; /// const result = float.toFixedDecimalLossy(2); /// if (result.error) { /// console.error(result.error); /// } - /// assert(result.value === "12345"); + /// assert(result.value === "1234"); /// ``` #[wasm_export( js_name = "toFixedDecimalLossy", diff --git a/crates/float/src/lib.rs b/crates/float/src/lib.rs index 5addd3cf..b1a36f0f 100644 --- a/crates/float/src/lib.rs +++ b/crates/float/src/lib.rs @@ -145,9 +145,8 @@ impl Float { DecimalFloat::fromFixedDecimalLossyPackedCall { value, decimals }.abi_encode(); execute_call(Bytes::from(calldata), |output| { - let decoded = DecimalFloat::fromFixedDecimalLossyPackedCall::abi_decode_returns( - output.as_ref(), - )?; + let decoded = + DecimalFloat::fromFixedDecimalLossyPackedCall::abi_decode_returns(output.as_ref())?; Ok(Float(decoded._0)) }) } @@ -170,9 +169,9 @@ impl Float { /// use alloy::primitives::U256; /// /// // 123.45 with 2 decimals becomes 12345 - /// let float = Float::parse("123.45".to_string())?; + /// let float = Float::from_fixed_decimal(U256::from(12345), 3)?; /// let fixed = float.to_fixed_decimal_lossy(2)?; - /// assert_eq!(fixed, U256::from(12345u64)); + /// assert_eq!(fixed, U256::from(1234u64)); /// /// anyhow::Ok(()) /// ``` @@ -1675,4 +1674,56 @@ mod tests { prop_assert!(gte); // gt } } + + #[test] + fn test_from_fixed_decimal_lossy() { + let cases = vec![ + (U256::from(0u128), 0u8, "0"), + (U256::from(0u128), 18u8, "0"), + (U256::from(1u128), 18u8, "1e-18"), + (U256::from(123456789u128), 0u8, "123456789"), + (U256::from(123456789u128), 2u8, "123456789e-2"), + (U256::from(1000000000000000000u128), 18u8, "1"), + ]; + + for (amount, decimals, expected) in cases { + let float = Float::from_fixed_decimal_lossy(amount, decimals).expect("should convert"); + let expected = Float::parse(expected.to_string()).unwrap(); + assert!(float.eq(expected).unwrap()); + } + } + + #[test] + fn test_to_fixed_decimal_lossy() { + let cases = vec![ + (U256::from(0), 0u8, 0u128), + (U256::from(0), 18u8, 0u128), + (U256::from(1), 18u8, 0u128), + (U256::from(123456789), 0u8, 12345678u128), + (U256::from(123456789), 2u8, 12345678u128), + ]; + + for (input, decimals, expected) in cases { + let float = Float::from_fixed_decimal(input, decimals + 1).unwrap(); + let fixed = float.to_fixed_decimal_lossy(decimals).unwrap(); + assert_eq!(fixed, U256::from(expected)); + } + } + + proptest! { + #[test] + fn test_from_to_fixed_decimal_lossy_valid_range(coeff in any::(), decimals in 0u8..=66u8) { + prop_assume!(coeff >= I224::ZERO); + + let exponent = -(decimals as i32 + 1); + let value = U256::from(coeff); + + let float = Float::from_fixed_decimal_lossy(value, decimals + 1).unwrap(); + let expected = Float::pack_lossless(coeff, exponent).unwrap(); + prop_assert!(float.eq(expected).unwrap()); + + let fixed = float.to_fixed_decimal_lossy(decimals).unwrap(); + assert_eq!(fixed, value / U256::from(10)); + } + } } diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index bdc2ae61..a1a0c521 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -244,6 +244,7 @@ contract DecimalFloat { /// representation. e.g. If 1e18 represents 1 this would be 18 decimals. /// @return float The Float struct containing the signed coefficient and /// exponent. + /// @return lossless True if the conversion was lossless, false otherwise. function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) external pure returns (Float, bool) { return LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); } @@ -252,7 +253,8 @@ contract DecimalFloat { /// @param float The Float struct to convert. /// @param decimals The number of decimals in the fixed point /// representation. e.g. If 1e18 represents 1 this would be 18 decimals. - /// @return The fixed point decimal value as a uint256. + /// @return value The fixed point decimal value as a uint256. + /// @return lossless True if the conversion was lossless, false otherwise. function toFixedDecimalLossy(Float float, uint8 decimals) external pure returns (uint256, bool) { return LibDecimalFloat.toFixedDecimalLossy(float, decimals); } From b70e267a0f5cb129b701787791730ca57382eb8d Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Thu, 24 Jul 2025 03:19:09 +0000 Subject: [PATCH 03/10] Update DecimalFloat.sol --- src/concrete/DecimalFloat.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index a1a0c521..4dc98eed 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -246,7 +246,8 @@ contract DecimalFloat { /// exponent. /// @return lossless True if the conversion was lossless, false otherwise. function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) external pure returns (Float, bool) { - return LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); + (Float float, bool lossless) = LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); + return (float, lossless); } /// Exposes `LibDecimalFloat.toFixedDecimalLossy` for offchain use. @@ -256,6 +257,7 @@ contract DecimalFloat { /// @return value The fixed point decimal value as a uint256. /// @return lossless True if the conversion was lossless, false otherwise. function toFixedDecimalLossy(Float float, uint8 decimals) external pure returns (uint256, bool) { - return LibDecimalFloat.toFixedDecimalLossy(float, decimals); + (uint256 value, bool lossless) = LibDecimalFloat.toFixedDecimalLossy(float, decimals); + return (value, lossless); } } From 440a7ebae84446b915c6d59253feff0835164971 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 16:34:07 +0400 Subject: [PATCH 04/10] slither false positive --- src/concrete/DecimalFloat.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index 4dc98eed..7ddbdff4 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -246,8 +246,8 @@ contract DecimalFloat { /// exponent. /// @return lossless True if the conversion was lossless, false otherwise. function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) external pure returns (Float, bool) { - (Float float, bool lossless) = LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); - return (float, lossless); + //slither-disable-next-line unused-return + return LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); } /// Exposes `LibDecimalFloat.toFixedDecimalLossy` for offchain use. @@ -257,7 +257,7 @@ contract DecimalFloat { /// @return value The fixed point decimal value as a uint256. /// @return lossless True if the conversion was lossless, false otherwise. function toFixedDecimalLossy(Float float, uint8 decimals) external pure returns (uint256, bool) { - (uint256 value, bool lossless) = LibDecimalFloat.toFixedDecimalLossy(float, decimals); - return (value, lossless); + //slither-disable-next-line unused-return + return LibDecimalFloat.toFixedDecimalLossy(float, decimals); } } From 7e9628be0629bc5d27d351e3c703d3a83d15bd7a Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 16:40:42 +0400 Subject: [PATCH 05/10] packlossless test --- .../concrete/DecimalFloat.packLossless.t.sol | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/src/concrete/DecimalFloat.packLossless.t.sol diff --git a/test/src/concrete/DecimalFloat.packLossless.t.sol b/test/src/concrete/DecimalFloat.packLossless.t.sol new file mode 100644 index 00000000..e4c8b01f --- /dev/null +++ b/test/src/concrete/DecimalFloat.packLossless.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatPackLosslessTest is Test { + function packLosslessExternal(int224 signedCoefficient, int32 exponent) external pure returns (Float) { + return LibDecimalFloat.packLossless(signedCoefficient, exponent); + } + + function testPackDeployed(int224 signedCoefficient, int32 exponent) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.packLosslessExternal(signedCoefficient, exponent) returns (Float packed) { + Float deployedPacked = deployed.packLossless(signedCoefficient, exponent); + + assertEq(Float.unwrap(packed), Float.unwrap(deployedPacked)); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.packLossless(signedCoefficient, exponent); + } + } +} From 44537c37db1dc41e8429bf41a564e65b9c651c8f Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 16:48:57 +0400 Subject: [PATCH 06/10] unpack test --- src/concrete/DecimalFloat.sol | 5 ++-- test/src/concrete/DecimalFloat.unpack.t.sol | 28 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test/src/concrete/DecimalFloat.unpack.t.sol diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index 7ddbdff4..3e803ee8 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -212,9 +212,8 @@ contract DecimalFloat { /// @param float The float to unpack. /// @return coefficient The coefficient of the float. /// @return exponent The exponent of the float. - function unpack(Float float) external pure returns (int224, int32) { - (int256 coefficient, int256 exponent) = LibDecimalFloat.unpack(float); - return (int224(coefficient), int32(exponent)); + function unpack(Float float) external pure returns (int256, int256) { + return LibDecimalFloat.unpack(float); } /// Exposes `LibDecimalFloat.fromFixedDecimalLosslessPacked` for offchain diff --git a/test/src/concrete/DecimalFloat.unpack.t.sol b/test/src/concrete/DecimalFloat.unpack.t.sol new file mode 100644 index 00000000..495bff31 --- /dev/null +++ b/test/src/concrete/DecimalFloat.unpack.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatUnpackTest is Test { + using LibDecimalFloat for Float; + + function unpackExternal(Float packed) external pure returns (int256 signedCoefficient, int256 exponent) { + return LibDecimalFloat.unpack(packed); + } + + function testUnpackDeployed(Float packed) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.unpackExternal(packed) returns (int256 signedCoefficient, int256 exponent) { + (int256 deployedSignedCoefficient, int256 deployedExponent) = deployed.unpack(packed); + + assertEq(signedCoefficient, deployedSignedCoefficient); + assertEq(exponent, deployedExponent); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.unpack(packed); + } + } +} From 2b080a13c28cf18409d0632a31348055dcd12e1c Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 16:59:55 +0400 Subject: [PATCH 07/10] tests for from fixed decimal lossless --- src/concrete/DecimalFloat.sol | 4 +-- ...ecimalFloat.fromFixedDecimalLossless.t.sol | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test/src/concrete/DecimalFloat.fromFixedDecimalLossless.t.sol diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index 3e803ee8..dc8d98e6 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -223,7 +223,7 @@ contract DecimalFloat { /// representation. e.g. If 1e18 represents 1 this would be 18 decimals. /// @return float The Float struct containing the signed coefficient and /// exponent. - function fromFixedDecimalLosslessPacked(uint256 value, uint8 decimals) external pure returns (Float) { + function fromFixedDecimalLossless(uint256 value, uint8 decimals) external pure returns (Float) { return LibDecimalFloat.fromFixedDecimalLosslessPacked(value, decimals); } @@ -244,7 +244,7 @@ contract DecimalFloat { /// @return float The Float struct containing the signed coefficient and /// exponent. /// @return lossless True if the conversion was lossless, false otherwise. - function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) external pure returns (Float, bool) { + function fromFixedDecimalLossy(uint256 value, uint8 decimals) external pure returns (Float, bool) { //slither-disable-next-line unused-return return LibDecimalFloat.fromFixedDecimalLossyPacked(value, decimals); } diff --git a/test/src/concrete/DecimalFloat.fromFixedDecimalLossless.t.sol b/test/src/concrete/DecimalFloat.fromFixedDecimalLossless.t.sol new file mode 100644 index 00000000..e2d2b6df --- /dev/null +++ b/test/src/concrete/DecimalFloat.fromFixedDecimalLossless.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatFromFixedDecimalLosslessTest is Test { + using LibDecimalFloat for Float; + + function fromFixedDecimalLosslessExternal(uint256 fixedDecimal, uint8 decimals) external pure returns (Float) { + return LibDecimalFloat.fromFixedDecimalLosslessPacked(fixedDecimal, decimals); + } + + function testFromFixedDecimalLosslessDeployed(uint256 fixedDecimal, uint8 decimals) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.fromFixedDecimalLosslessExternal(fixedDecimal, decimals) returns (Float packed) { + Float deployedPacked = deployed.fromFixedDecimalLossless(fixedDecimal, decimals); + + assertEq(Float.unwrap(packed), Float.unwrap(deployedPacked)); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.fromFixedDecimalLossless(fixedDecimal, decimals); + } + } +} From abfe62cf6313de6693e47356a5d56fb88bcae7de Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 17:19:10 +0400 Subject: [PATCH 08/10] fix tests --- .gas-snapshot | 50 +++++++++++-------- src/lib/LibDecimalFloat.sol | 1 + .../DecimalFloat.fromFixedDecimalLossy.t.sol | 28 +++++++++++ .../DecimalFloat.toFixedDecimalLossless.t.sol | 27 ++++++++++ .../DecimalFloat.toFixedDecimalLossy.t.sol | 28 +++++++++++ 5 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 test/src/concrete/DecimalFloat.fromFixedDecimalLossy.t.sol create mode 100644 test/src/concrete/DecimalFloat.toFixedDecimalLossless.t.sol create mode 100644 test/src/concrete/DecimalFloat.toFixedDecimalLossy.t.sol diff --git a/.gas-snapshot b/.gas-snapshot index 3eb4644d..f29bab8e 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,25 +1,31 @@ -DecimalFloatAbsTest:testAbsDeployed(bytes32) (runs: 5096, μ: 2379271, ~: 2379213) -DecimalFloatAddTest:testAddDeployed(bytes32,bytes32) (runs: 5096, μ: 2381906, ~: 2381585) -DecimalFloatConstantsTest:testEDeployed() (gas: 2378346) -DecimalFloatConstantsTest:testMaxValueDeployed() (gas: 2378358) -DecimalFloatConstantsTest:testMinNegativeValueDeployed() (gas: 2378400) -DecimalFloatDivTest:testDivDeployed(bytes32,bytes32) (runs: 5096, μ: 2381775, ~: 2381627) -DecimalFloatEqTest:testEqDeployed(bytes32,bytes32) (runs: 5096, μ: 2379590, ~: 2379516) -DecimalFloatFloorTest:testFloorDeployed(bytes32) (runs: 5096, μ: 2379387, ~: 2379371) -DecimalFloatFormatTest:testFormatDeployed(bytes32) (runs: 5096, μ: 2383231, ~: 2383074) -DecimalFloatFracTest:testFracDeployed(bytes32) (runs: 5096, μ: 2379436, ~: 2379419) -DecimalFloatGtTest:testGtDeployed(bytes32,bytes32) (runs: 5096, μ: 2379510, ~: 2379436) -DecimalFloatGteTest:testGteDeployed(bytes32,bytes32) (runs: 5096, μ: 2379583, ~: 2379509) -DecimalFloatInvTest:testInvDeployed(bytes32) (runs: 5096, μ: 2380819, ~: 2380741) -DecimalFloatIsZeroTest:testIsZeroDeployed(bytes32) (runs: 5096, μ: 2378745, ~: 2378745) -DecimalFloatLtTest:testLtDeployed(bytes32,bytes32) (runs: 5096, μ: 2379531, ~: 2379457) -DecimalFloatLteTest:testLteDeployed(bytes32,bytes32) (runs: 5096, μ: 2379540, ~: 2379466) -DecimalFloatMaxTest:testMaxDeployed(bytes32,bytes32) (runs: 5096, μ: 2379593, ~: 2379532) -DecimalFloatMinTest:testMinDeployed(bytes32,bytes32) (runs: 5096, μ: 2379590, ~: 2379530) -DecimalFloatMinusTest:testMinusDeployed(bytes32) (runs: 5096, μ: 2379305, ~: 2379305) -DecimalFloatMulTest:testMulDeployed(bytes32,bytes32) (runs: 5096, μ: 2381426, ~: 2382488) -DecimalFloatParseTest:testParseDeployed(string) (runs: 5096, μ: 2381982, ~: 2381851) -DecimalFloatSubTest:testSubDeployed(bytes32,bytes32) (runs: 5096, μ: 2382255, ~: 2381946) +DecimalFloatAbsTest:testAbsDeployed(bytes32) (runs: 5096, μ: 2425711, ~: 2425653) +DecimalFloatAddTest:testAddDeployed(bytes32,bytes32) (runs: 5096, μ: 2428475, ~: 2428157) +DecimalFloatConstantsTest:testEDeployed() (gas: 2424851) +DecimalFloatConstantsTest:testMaxValueDeployed() (gas: 2424885) +DecimalFloatConstantsTest:testMinNegativeValueDeployed() (gas: 2424883) +DecimalFloatDivTest:testDivDeployed(bytes32,bytes32) (runs: 5096, μ: 2428214, ~: 2428066) +DecimalFloatEqTest:testEqDeployed(bytes32,bytes32) (runs: 5096, μ: 2426116, ~: 2426043) +DecimalFloatFloorTest:testFloorDeployed(bytes32) (runs: 5096, μ: 2425915, ~: 2425898) +DecimalFloatFormatTest:testFormatDeployed(bytes32) (runs: 5096, μ: 2429752, ~: 2429579) +DecimalFloatFracTest:testFracDeployed(bytes32) (runs: 5096, μ: 2425963, ~: 2425946) +DecimalFloatFromFixedDecimalLosslessTest:testFromFixedDecimalLosslessDeployed(uint256,uint8) (runs: 5096, μ: 2426516, ~: 2426450) +DecimalFloatFromFixedDecimalLossyTest:testFromFixedDecimalLossyDeployed(uint256,uint8) (runs: 5096, μ: 2427004, ~: 2426920) +DecimalFloatGtTest:testGtDeployed(bytes32,bytes32) (runs: 5096, μ: 2426036, ~: 2425963) +DecimalFloatGteTest:testGteDeployed(bytes32,bytes32) (runs: 5096, μ: 2426109, ~: 2426036) +DecimalFloatInvTest:testInvDeployed(bytes32) (runs: 5096, μ: 2427304, ~: 2427226) +DecimalFloatIsZeroTest:testIsZeroDeployed(bytes32) (runs: 5096, μ: 2425228, ~: 2425228) +DecimalFloatLtTest:testLtDeployed(bytes32,bytes32) (runs: 5096, μ: 2426014, ~: 2425940) +DecimalFloatLteTest:testLteDeployed(bytes32,bytes32) (runs: 5096, μ: 2426067, ~: 2425993) +DecimalFloatMaxTest:testMaxDeployed(bytes32,bytes32) (runs: 5096, μ: 2426098, ~: 2426037) +DecimalFloatMinTest:testMinDeployed(bytes32,bytes32) (runs: 5096, μ: 2426096, ~: 2426035) +DecimalFloatMinusTest:testMinusDeployed(bytes32) (runs: 5096, μ: 2425788, ~: 2425788) +DecimalFloatMulTest:testMulDeployed(bytes32,bytes32) (runs: 5096, μ: 2427932, ~: 2428993) +DecimalFloatPackLosslessTest:testPackDeployed(int224,int32) (runs: 5096, μ: 2426066, ~: 2426066) +DecimalFloatParseTest:testParseDeployed(string) (runs: 5096, μ: 2428465, ~: 2428334) +DecimalFloatSubTest:testSubDeployed(bytes32,bytes32) (runs: 5096, μ: 2428809, ~: 2428495) +DecimalFloatToFixedDecimalLosslessTest:testToFixedDecimalLosslessDeployed(bytes32,uint8) (runs: 5096, μ: 2427141, ~: 2427033) +DecimalFloatToFixedDecimalLossyTest:testToFixedDecimalLossyDeployed(bytes32,uint8) (runs: 5096, μ: 2427285, ~: 2427562) +DecimalFloatUnpackTest:testUnpackDeployed(bytes32) (runs: 5096, μ: 2425741, ~: 2425741) LibDecimalFloatAbsTest:testAbsMinValue(int32) (runs: 5096, μ: 5121, ~: 5121) LibDecimalFloatAbsTest:testAbsNegative(int256,int32) (runs: 5096, μ: 10475, ~: 10702) LibDecimalFloatAbsTest:testAbsNonNegative(int256,int32) (runs: 5096, μ: 9641, ~: 9392) diff --git a/src/lib/LibDecimalFloat.sol b/src/lib/LibDecimalFloat.sol index dd80ed33..284d1e85 100644 --- a/src/lib/LibDecimalFloat.sol +++ b/src/lib/LibDecimalFloat.sol @@ -134,6 +134,7 @@ library LibDecimalFloat { /// e.g. If 1e18 represents 1 this would be 18 decimals. /// @return float The Float struct containing the signed coefficient and /// exponent. + /// @return lossless `true` if the conversion is lossless. function fromFixedDecimalLossyPacked(uint256 value, uint8 decimals) internal pure returns (Float, bool) { (int256 signedCoefficient, int256 exponent, bool lossless) = fromFixedDecimalLossy(value, decimals); (Float float, bool losslessPack) = packLossy(signedCoefficient, exponent); diff --git a/test/src/concrete/DecimalFloat.fromFixedDecimalLossy.t.sol b/test/src/concrete/DecimalFloat.fromFixedDecimalLossy.t.sol new file mode 100644 index 00000000..0f445685 --- /dev/null +++ b/test/src/concrete/DecimalFloat.fromFixedDecimalLossy.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatFromFixedDecimalLossyTest is Test { + using LibDecimalFloat for Float; + + function fromFixedDecimalLossyExternal(uint256 fixedDecimal, uint8 decimals) external pure returns (Float, bool) { + return LibDecimalFloat.fromFixedDecimalLossyPacked(fixedDecimal, decimals); + } + + function testFromFixedDecimalLossyDeployed(uint256 fixedDecimal, uint8 decimals) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.fromFixedDecimalLossyExternal(fixedDecimal, decimals) returns (Float packed, bool lossless) { + (Float deployedPacked, bool deployedLossless) = deployed.fromFixedDecimalLossy(fixedDecimal, decimals); + + assertEq(Float.unwrap(packed), Float.unwrap(deployedPacked)); + assertEq(lossless, deployedLossless); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.fromFixedDecimalLossy(fixedDecimal, decimals); + } + } +} diff --git a/test/src/concrete/DecimalFloat.toFixedDecimalLossless.t.sol b/test/src/concrete/DecimalFloat.toFixedDecimalLossless.t.sol new file mode 100644 index 00000000..47111352 --- /dev/null +++ b/test/src/concrete/DecimalFloat.toFixedDecimalLossless.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatToFixedDecimalLosslessTest is Test { + using LibDecimalFloat for Float; + + function toFixedDecimalLosslessExternal(Float packed, uint8 decimals) external pure returns (uint256) { + return LibDecimalFloat.toFixedDecimalLossless(packed, decimals); + } + + function testToFixedDecimalLosslessDeployed(Float packed, uint8 decimals) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.toFixedDecimalLosslessExternal(packed, decimals) returns (uint256 fixedDecimal) { + uint256 deployedFixedDecimal = deployed.toFixedDecimalLossless(packed, decimals); + + assertEq(fixedDecimal, deployedFixedDecimal); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.toFixedDecimalLossless(packed, decimals); + } + } +} diff --git a/test/src/concrete/DecimalFloat.toFixedDecimalLossy.t.sol b/test/src/concrete/DecimalFloat.toFixedDecimalLossy.t.sol new file mode 100644 index 00000000..b5059cbe --- /dev/null +++ b/test/src/concrete/DecimalFloat.toFixedDecimalLossy.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {Test} from "forge-std/Test.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +contract DecimalFloatToFixedDecimalLossyTest is Test { + using LibDecimalFloat for Float; + + function toFixedDecimalLossyExternal(Float packed, uint8 decimals) external pure returns (uint256, bool) { + return LibDecimalFloat.toFixedDecimalLossy(packed, decimals); + } + + function testToFixedDecimalLossyDeployed(Float packed, uint8 decimals) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.toFixedDecimalLossyExternal(packed, decimals) returns (uint256 fixedDecimal, bool lossless) { + (uint256 deployedFixedDecimal, bool deployedLossless) = deployed.toFixedDecimalLossy(packed, decimals); + + assertEq(fixedDecimal, deployedFixedDecimal); + assertEq(lossless, deployedLossless); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.toFixedDecimalLossy(packed, decimals); + } + } +} From 11fa42375370c4b29d961b79d071cd33a6afaddd Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 24 Jul 2025 17:23:55 +0400 Subject: [PATCH 09/10] move pack and unpack to test concrete --- src/concrete/DecimalFloat.sol | 16 ------------ .../concrete/DecimalFloat.packLossless.t.sol | 4 +-- test/concrete/TestDecimalFloat.sol | 26 +++++++++++++++++++ .../TestDecimalFloat.unpack.t.sol} | 6 ++--- 4 files changed, 31 insertions(+), 21 deletions(-) rename test/{src => }/concrete/DecimalFloat.packLossless.t.sol (88%) create mode 100644 test/concrete/TestDecimalFloat.sol rename test/{src/concrete/DecimalFloat.unpack.t.sol => concrete/TestDecimalFloat.unpack.t.sol} (84%) diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index dc8d98e6..367026aa 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -200,22 +200,6 @@ contract DecimalFloat { return a.isZero(); } - /// Exposes `LibDecimalFloat.packLossless` for offchain use. - /// @param coefficient The coefficient to pack. - /// @param exponent The exponent to pack. - /// @return The packed float. - function packLossless(int224 coefficient, int32 exponent) external pure returns (Float) { - return LibDecimalFloat.packLossless(coefficient, exponent); - } - - /// Exposes `LibDecimalFloat.unpack` for offchain use. - /// @param float The float to unpack. - /// @return coefficient The coefficient of the float. - /// @return exponent The exponent of the float. - function unpack(Float float) external pure returns (int256, int256) { - return LibDecimalFloat.unpack(float); - } - /// Exposes `LibDecimalFloat.fromFixedDecimalLosslessPacked` for offchain /// use. /// @param value The fixed point decimal value to convert. diff --git a/test/src/concrete/DecimalFloat.packLossless.t.sol b/test/concrete/DecimalFloat.packLossless.t.sol similarity index 88% rename from test/src/concrete/DecimalFloat.packLossless.t.sol rename to test/concrete/DecimalFloat.packLossless.t.sol index e4c8b01f..a26fcf35 100644 --- a/test/src/concrete/DecimalFloat.packLossless.t.sol +++ b/test/concrete/DecimalFloat.packLossless.t.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.25; import {Test} from "forge-std/Test.sol"; -import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {TestDecimalFloat} from "./TestDecimalFloat.sol"; import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; contract DecimalFloatPackLosslessTest is Test { @@ -11,7 +11,7 @@ contract DecimalFloatPackLosslessTest is Test { } function testPackDeployed(int224 signedCoefficient, int32 exponent) external { - DecimalFloat deployed = new DecimalFloat(); + TestDecimalFloat deployed = new TestDecimalFloat(); try this.packLosslessExternal(signedCoefficient, exponent) returns (Float packed) { Float deployedPacked = deployed.packLossless(signedCoefficient, exponent); diff --git a/test/concrete/TestDecimalFloat.sol b/test/concrete/TestDecimalFloat.sol new file mode 100644 index 00000000..2e709c8f --- /dev/null +++ b/test/concrete/TestDecimalFloat.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +/// Additional exposed functions for testing the internals of floats +/// from downstream environments, e.g. rust. +contract TestDecimalFloat { + using LibDecimalFloat for Float; + + /// Exposes `LibDecimalFloat.packLossless` for offchain use. + /// @param coefficient The coefficient to pack. + /// @param exponent The exponent to pack. + /// @return The packed float. + function packLossless(int224 coefficient, int32 exponent) external pure returns (Float) { + return LibDecimalFloat.packLossless(coefficient, exponent); + } + + /// Exposes `LibDecimalFloat.unpack` for offchain use. + /// @param float The float to unpack. + /// @return coefficient The coefficient of the float. + /// @return exponent The exponent of the float. + function unpack(Float float) external pure returns (int256, int256) { + return LibDecimalFloat.unpack(float); + } +} diff --git a/test/src/concrete/DecimalFloat.unpack.t.sol b/test/concrete/TestDecimalFloat.unpack.t.sol similarity index 84% rename from test/src/concrete/DecimalFloat.unpack.t.sol rename to test/concrete/TestDecimalFloat.unpack.t.sol index 495bff31..b074adeb 100644 --- a/test/src/concrete/DecimalFloat.unpack.t.sol +++ b/test/concrete/TestDecimalFloat.unpack.t.sol @@ -2,10 +2,10 @@ pragma solidity =0.8.25; import {Test} from "forge-std/Test.sol"; -import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; +import {TestDecimalFloat} from "./TestDecimalFloat.sol"; import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; -contract DecimalFloatUnpackTest is Test { +contract TestDecimalFloatUnpackTest is Test { using LibDecimalFloat for Float; function unpackExternal(Float packed) external pure returns (int256 signedCoefficient, int256 exponent) { @@ -13,7 +13,7 @@ contract DecimalFloatUnpackTest is Test { } function testUnpackDeployed(Float packed) external { - DecimalFloat deployed = new DecimalFloat(); + TestDecimalFloat deployed = new TestDecimalFloat(); try this.unpackExternal(packed) returns (int256 signedCoefficient, int256 exponent) { (int256 deployedSignedCoefficient, int256 deployedExponent) = deployed.unpack(packed); From 1e4e3082d41108f16f754b5a4ffb0cd3ec486438 Mon Sep 17 00:00:00 2001 From: 0xgleb Date: Thu, 24 Jul 2025 18:28:13 +0400 Subject: [PATCH 10/10] TestDecimalFloat bindings --- crates/float/src/evm.rs | 38 ++++++++++++++++++++++++++++++++- crates/float/src/js_api.rs | 29 ------------------------- crates/float/src/lib.rs | 43 +++++++++++++++++++++++--------------- test_js/float.test.ts | 4 ---- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/crates/float/src/evm.rs b/crates/float/src/evm.rs index aab41886..2867fecf 100644 --- a/crates/float/src/evm.rs +++ b/crates/float/src/evm.rs @@ -12,32 +12,68 @@ use std::cell::RefCell; use crate::{DecimalFloat, FloatError}; +#[cfg(test)] +use crate::TestDecimalFloat; + /// Fixed address where the DecimalFloat contract is deployed in the in-memory EVM. /// This arbitrary address is used consistently across all Calculator instances. pub(crate) const FLOAT_ADDRESS: Address = address!("00000000000000000000000000000000000f10a2"); +#[cfg(test)] +/// Fixed address where the TestDecimalFloat contract is deployed in the in-memory EVM. +pub(crate) const TEST_FLOAT_ADDRESS: Address = address!("00000000000000000000000000000000000f10a3"); + type EvmContext = Context; type LocalEvm = Evm, EthPrecompiles>; thread_local! { pub(crate) static LOCAL_EVM: RefCell = { let mut db = InMemoryDB::default(); + + // Deploy DecimalFloat contract let bytecode = revm::state::Bytecode::new_legacy(DecimalFloat::DEPLOYED_BYTECODE.clone()); let account_info = revm::state::AccountInfo::default().with_code(bytecode); db.insert_account_info(FLOAT_ADDRESS, account_info); + #[cfg(test)] + { + // Deploy TestDecimalFloat contract for testing + let test_bytecode = revm::state::Bytecode::new_legacy(TestDecimalFloat::DEPLOYED_BYTECODE.clone()); + let test_account_info = revm::state::AccountInfo::default().with_code(test_bytecode); + db.insert_account_info(TEST_FLOAT_ADDRESS, test_account_info); + } + let evm = Context::mainnet().with_db(db).build_mainnet(); RefCell::new(evm) }; } pub(crate) fn execute_call(calldata: Bytes, process_output: F) -> Result +where + F: FnOnce(Bytes) -> Result, +{ + execute_call_at_address(FLOAT_ADDRESS, calldata, process_output) +} + +#[cfg(test)] +pub(crate) fn execute_test_call(calldata: Bytes, process_output: F) -> Result +where + F: FnOnce(Bytes) -> Result, +{ + execute_call_at_address(TEST_FLOAT_ADDRESS, calldata, process_output) +} + +fn execute_call_at_address( + address: Address, + calldata: Bytes, + process_output: F, +) -> Result where F: FnOnce(Bytes) -> Result, { let result = LOCAL_EVM.try_with(|evm| { let evm = &mut *evm.borrow_mut(); - let result_and_state = evm.transact_system_call_finalize(FLOAT_ADDRESS, calldata)?; + let result_and_state = evm.transact_system_call_finalize(address, calldata)?; Ok::<_, FloatError>(result_and_state.result) })??; diff --git a/crates/float/src/js_api.rs b/crates/float/src/js_api.rs index ee3b250e..bc88a3fe 100644 --- a/crates/float/src/js_api.rs +++ b/crates/float/src/js_api.rs @@ -1,5 +1,4 @@ use crate::{Float, FloatError}; -use alloy::primitives::aliases::I224; use revm::primitives::{B256, U256}; use std::{ ops::{Add, Div, Mul, Neg, Sub}, @@ -237,34 +236,6 @@ impl Float { .map_err(|e| FloatError::JsSysError(e.to_string().into())) } - /// Packs a coefficient and exponent into a `Float` in a lossless manner. - /// - /// # Arguments - /// - /// * `coefficient` - The coefficient as an `string`. - /// * `exponent` - The exponent as an `number`. - /// - /// # Returns - /// - /// * `Ok(Float)` - The packed float. - /// * `Err(FloatError)` - If the packing fails (e.g., overflow). - /// - /// # Example - /// - /// ```typescript - /// const floatResult = Float.packLossless("314", -2); - /// if (floatResult.error) { - /// console.error(floatResult.error); - /// } - /// const float = floatResult.value; - /// assert(float.format() === "3.14"); - /// ``` - #[wasm_export(js_name = "packLossless", preserve_js_class)] - pub fn pack_lossless_js(coefficient: String, exponent: i32) -> Result { - let val = I224::from_str(&coefficient)?; - Self::pack_lossless(val, exponent) - } - /// Parses a decimal string into a `Float`. /// /// # Arguments diff --git a/crates/float/src/lib.rs b/crates/float/src/lib.rs index b1a36f0f..bc535490 100644 --- a/crates/float/src/lib.rs +++ b/crates/float/src/lib.rs @@ -1,5 +1,4 @@ use alloy::hex::FromHex; -use alloy::primitives::aliases::I224; use alloy::primitives::{B256, Bytes}; use alloy::{sol, sol_types::SolCall}; use revm::primitives::{U256, fixed_bytes}; @@ -7,6 +6,9 @@ use serde::{Deserialize, Serialize}; use std::ops::{Add, Div, Mul, Neg, Sub}; use wasm_bindgen_utils::prelude::*; +#[cfg(test)] +use alloy::primitives::aliases::I224; + pub mod error; mod evm; pub mod js_api; @@ -14,6 +16,8 @@ pub mod js_api; use error::DecimalFloatErrorSelector; pub use error::FloatError; use evm::execute_call; +#[cfg(test)] +use evm::execute_test_call; sol!( #![sol(all_derives)] @@ -21,6 +25,13 @@ sol!( "../../out/DecimalFloat.sol/DecimalFloat.json" ); +#[cfg(test)] +sol!( + #![sol(all_derives)] + TestDecimalFloat, + "../../out/TestDecimalFloat.sol/TestDecimalFloat.json" +); + #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, Hash)] #[wasm_bindgen] pub struct Float(B256); @@ -68,13 +79,11 @@ impl Float { /// anyhow::Ok(()) /// ``` pub fn from_fixed_decimal(value: U256, decimals: u8) -> Result { - let calldata = - DecimalFloat::fromFixedDecimalLosslessPackedCall { value, decimals }.abi_encode(); + let calldata = DecimalFloat::fromFixedDecimalLosslessCall { value, decimals }.abi_encode(); execute_call(Bytes::from(calldata), |output| { - let decoded = DecimalFloat::fromFixedDecimalLosslessPackedCall::abi_decode_returns( - output.as_ref(), - )?; + let decoded = + DecimalFloat::fromFixedDecimalLosslessCall::abi_decode_returns(output.as_ref())?; Ok(Float(decoded)) }) } @@ -141,12 +150,11 @@ impl Float { /// anyhow::Ok(()) /// ``` pub fn from_fixed_decimal_lossy(value: U256, decimals: u8) -> Result { - let calldata = - DecimalFloat::fromFixedDecimalLossyPackedCall { value, decimals }.abi_encode(); + let calldata = DecimalFloat::fromFixedDecimalLossyCall { value, decimals }.abi_encode(); execute_call(Bytes::from(calldata), |output| { let decoded = - DecimalFloat::fromFixedDecimalLossyPackedCall::abi_decode_returns(output.as_ref())?; + DecimalFloat::fromFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?; Ok(Float(decoded._0)) }) } @@ -212,29 +220,30 @@ impl Float { /// /// anyhow::Ok(()) /// ``` + #[cfg(test)] pub fn pack_lossless(coefficient: I224, exponent: i32) -> Result { - let calldata = DecimalFloat::packLosslessCall { + let calldata = TestDecimalFloat::packLosslessCall { coefficient, exponent, } .abi_encode(); - execute_call(Bytes::from(calldata), |output| { - let decoded = DecimalFloat::packLosslessCall::abi_decode_returns(output.as_ref())?; + execute_test_call(Bytes::from(calldata), |output| { + let decoded = TestDecimalFloat::packLosslessCall::abi_decode_returns(output.as_ref())?; Ok(Float(decoded)) }) } #[cfg(test)] - fn unpack(self) -> Result<(I224, i32), FloatError> { + fn unpack(self) -> Result<(alloy::primitives::I256, alloy::primitives::I256), FloatError> { let Float(float) = self; - let calldata = DecimalFloat::unpackCall { float }.abi_encode(); + let calldata = TestDecimalFloat::unpackCall { float }.abi_encode(); - execute_call(Bytes::from(calldata), |output| { - let DecimalFloat::unpackReturn { + execute_test_call(Bytes::from(calldata), |output| { + let TestDecimalFloat::unpackReturn { _0: coefficient, _1: exponent, - } = DecimalFloat::unpackCall::abi_decode_returns(output.as_ref())?; + } = TestDecimalFloat::unpackCall::abi_decode_returns(output.as_ref())?; Ok((coefficient, exponent)) }) diff --git a/test_js/float.test.ts b/test_js/float.test.ts index f1d6cbb1..54a09996 100644 --- a/test_js/float.test.ts +++ b/test_js/float.test.ts @@ -41,10 +41,6 @@ describe('Test Float Bindings', () => { expect(result).toBe(originalValue); }); - it('should test packLossless', () => { - const float = Float.packLossless('314', -2)?.value!; - expect(float.format()?.value!).toBe('3.14'); - }); it('should try from bigint', () => { const result = Float.tryFromBigint(5n);