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
50 changes: 28 additions & 22 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
38 changes: 37 additions & 1 deletion crates/float/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockEnv, TxEnv, CfgEnv, InMemoryDB>;
type LocalEvm = Evm<EvmContext, (), EthInstructions<EthInterpreter, EvmContext>, EthPrecompiles>;

thread_local! {
pub(crate) static LOCAL_EVM: RefCell<LocalEvm> = {
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<F, T>(calldata: Bytes, process_output: F) -> Result<T, FloatError>
where
F: FnOnce(Bytes) -> Result<T, FloatError>,
{
execute_call_at_address(FLOAT_ADDRESS, calldata, process_output)
}

#[cfg(test)]
pub(crate) fn execute_test_call<F, T>(calldata: Bytes, process_output: F) -> Result<T, FloatError>
where
F: FnOnce(Bytes) -> Result<T, FloatError>,
{
execute_call_at_address(TEST_FLOAT_ADDRESS, calldata, process_output)
}

fn execute_call_at_address<F, T>(
address: Address,
calldata: Bytes,
process_output: F,
) -> Result<T, FloatError>
where
F: FnOnce(Bytes) -> Result<T, FloatError>,
{
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)
})??;
Expand Down
56 changes: 44 additions & 12 deletions crates/float/src/js_api.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -176,32 +175,65 @@ impl Float {
.map_err(|e| FloatError::JsSysError(e.to_string().into()))
}

/// Packs a coefficient and exponent into a `Float` in a lossless manner.
/// Converts a fixed-point decimal value to a `Float` using the specified number of decimals lossy.
///
/// # Arguments
///
/// * `coefficient` - The coefficient as an `string`.
/// * `exponent` - The exponent as an `number`.
/// * `value` - The fixed-point decimal value as a `string`.
/// * `decimals` - The number of decimals in the fixed-point representation.
///
/// # Returns
///
/// * `Ok(Float)` - The packed float.
/// * `Err(FloatError)` - If the packing fails (e.g., overflow).
/// * `Ok(Float)` - The resulting `Float` value.
/// * `Err(FloatError)` - If the conversion fails.
///
/// # Example
///
/// ```typescript
/// const floatResult = Float.packLossless("314", -2);
/// const floatResult = Float.fromFixedDecimalLossy("12345", 2);
/// if (floatResult.error) {
/// console.error(floatResult.error);
/// }
/// const float = floatResult.value;
/// assert(float.format() === "3.14");
/// 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<Float, FloatError> {
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.fromFixedDecimal(12345n, 3).value!;
/// const result = float.toFixedDecimalLossy(2);
/// if (result.error) {
/// console.error(result.error);
/// }
/// assert(result.value === "1234");
/// ```
#[wasm_export(js_name = "packLossless", preserve_js_class)]
pub fn pack_lossless_js(coefficient: String, exponent: i32) -> Result<Float, FloatError> {
let val = I224::from_str(&coefficient)?;
Self::pack_lossless(val, exponent)
#[wasm_export(
js_name = "toFixedDecimalLossy",
preserve_js_class,
unchecked_return_type = "bigint"
)]
pub fn to_fixed_decimal_lossy_js(&self, decimals: u8) -> Result<BigInt, FloatError> {
let fixed = self.to_fixed_decimal_lossy(decimals)?;
BigInt::from_str(&fixed.to_string())
.map_err(|e| FloatError::JsSysError(e.to_string().into()))
}

/// Parses a decimal string into a `Float`.
Expand Down
Loading
Loading