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
420 changes: 212 additions & 208 deletions .gas-snapshot

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions .github/workflows/manual-sol-artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ jobs:
DEPLOY_BROADCAST: '1'
DEPLOYMENT_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRIVATE_KEY || secrets.PRIVATE_KEY_DEV }}
ETH_RPC_URL: ${{ secrets[env.rpc_secret_name] || vars[env.rpc_secret_name] || '' }}
CI_FORK_ETH_RPC_URL: ${{ secrets.RPC_URL_ETHEREUM_FORK || vars.RPC_URL_ETHEREUM_FORK || '' }}
CI_FORK_FLARE_RPC_URL: ${{ secrets.RPC_URL_FLARE_FORK || vars.RPC_URL_FLARE_FORK || '' }}
Comment on lines +75 to +76
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

🧩 Analysis chain

🏁 Script executed:

rg -n 'CI_FORK_(ETH|FLARE)_RPC_URL' --type yaml --type ts --type js --type sh

Repository: rainlanguage/rain.math.float

Length of output: 54


🏁 Script executed:

# Search more broadly for these env vars
rg -n 'CI_FORK_ETH_RPC_URL|CI_FORK_FLARE_RPC_URL'

Repository: rainlanguage/rain.math.float

Length of output: 289


🏁 Script executed:

# Also search for how RPC_URL is typically used in this repo
rg -n 'RPC.*URL' .github/workflows/ | head -30

Repository: rainlanguage/rain.math.float

Length of output: 912


🏁 Script executed:

cat -n test/src/lib/deploy/LibDecimalFloatDeploy.t.sol

Repository: rainlanguage/rain.math.float

Length of output: 1687


Tests will fail when fork RPC URLs are not configured. The env vars are set unconditionally with || '' fallback, causing vm.createSelectFork() calls to receive empty strings when secrets/vars are undefined. The tests at test/src/lib/deploy/LibDecimalFloatDeploy.t.sol:11 and line 28 will fail since Foundry requires non-empty RPC URLs.

Either remove the || '' fallback and let tests skip gracefully, or add conditional logic in tests to skip when these env vars are empty.

🤖 Prompt for AI Agents
In @.github/workflows/manual-sol-artifacts.yaml around lines 75 - 76, The
workflow currently forces CI_FORK_ETH_RPC_URL and CI_FORK_FLARE_RPC_URL to an
empty string via "|| ''", which passes empty values into vm.createSelectFork()
and makes Foundry tests fail; remove the "|| ''" fallbacks from the
CI_FORK_ETH_RPC_URL and CI_FORK_FLARE_RPC_URL assignments so the env vars are
truly undefined when not set, or alternatively add a guard in the Solidity tests
(e.g., in LibDecimalFloatDeploy.t.sol around the vm.createSelectFork() calls) to
detect an empty/undefined env var and skip the test instead; update either the
.github/workflows/manual-sol-artifacts.yaml lines containing CI_FORK_ETH_RPC_URL
/ CI_FORK_FLARE_RPC_URL or add the conditional skip logic in the test functions
that call vm.createSelectFork().

ETHERSCAN_API_KEY: ${{ secrets[env.etherscan_api_key_secret_name] || vars[env.etherscan_api_key_secret_name] || ''}}
DEPLOY_VERIFY: ${{ secrets[env.verify_secret_name] || vars[env.verify_secret_name] || '' }}
DEPLOY_VERIFIER: ${{ secrets[env.verifier_secret_name] || vars[env.verifier_secret_name] || '' }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/rainix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
- name: Run ${{ matrix.task }}
env:
ETH_RPC_URL: ${{ secrets.CI_DEPLOY_SEPOLIA_RPC_URL || vars.CI_DEPLOY_SEPOLIA_RPC_URL }}
CI_FORK_ARB_RPC_URL: ${{ secrets.RPC_URL_ARBITRUM_FORK || vars.RPC_URL_ARBITRUM_FORK || '' }}
CI_FORK_ETH_RPC_URL: ${{ secrets.RPC_URL_ETHEREUM_FORK || vars.RPC_URL_ETHEREUM_FORK || '' }}
CI_FORK_BASE_RPC_URL: ${{ secrets.RPC_URL_BASE_FORK || vars.RPC_URL_BASE_FORK || '' }}
CI_FORK_FLARE_RPC_URL: ${{ secrets.RPC_URL_FLARE_FORK || vars.RPC_URL_FLARE_FORK || '' }}
ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }}
DEPLOY_BROADCAST: ""
DEPLOY_VERIFIER: ""
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ target
temp
dist
node_modules
.env
59 changes: 38 additions & 21 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"lib/forge-std": {
"rev": "b8f065fda83b8cd94a6b2fec8fcd911dc3b444fd"
"rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6"
},
"lib/rain.datacontract": {
"rev": "061bf7cd63496ccbe4f74bcd0c48715883c1c3cb"
},
"lib/rain.math.fixedpoint": {
"rev": "1752e9fbd635901ac7eb177699681ed97290e12e"
"rev": "8308cbb6da0e231c6f3437f1861e66eff7ea2b00"
},
"lib/rain.sol.codegen": {
"rev": "bd7993b3f6b301e5a667ff687f25b80fdda878cd"
},
"lib/rain.string": {
"rev": "0b1ca08aed6d9c06b83fe127a7d20ee7002ead28"
"rev": "488f237cd59874e4eb91b5a4f747bd57578fec7f"
}
}
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 57 files
+14 −31 .github/workflows/ci.yml
+1 −1 .github/workflows/sync.yml
+2 −2 CONTRIBUTING.md
+11 −9 README.md
+3 −12 foundry.toml
+2 −2 package.json
+2 −12 scripts/vm.py
+2 −2 src/Base.sol
+1 −1 src/Config.sol
+2 −2 src/LibVariable.sol
+2 −2 src/Script.sol
+28 −13 src/StdAssertions.sol
+8 −5 src/StdChains.sol
+9 −13 src/StdCheats.sol
+24 −4 src/StdConfig.sol
+2 −2 src/StdConstants.sol
+2 −2 src/StdError.sol
+2 −4 src/StdInvariant.sol
+4 −12 src/StdJson.sol
+2 −2 src/StdMath.sol
+11 −9 src/StdStorage.sol
+2 −2 src/StdStyle.sol
+4 −12 src/StdToml.sol
+5 −13 src/StdUtils.sol
+2 −4 src/Test.sol
+26 −41 src/Vm.sol
+10 −19 src/console.sol
+2 −2 src/console2.sol
+2 −2 src/interfaces/IERC1155.sol
+2 −2 src/interfaces/IERC165.sol
+2 −2 src/interfaces/IERC20.sol
+2 −2 src/interfaces/IERC4626.sol
+2 −2 src/interfaces/IERC6909.sol
+2 −2 src/interfaces/IERC721.sol
+4 −10 src/interfaces/IERC7540.sol
+2 −2 src/interfaces/IERC7575.sol
+3 −8 src/interfaces/IMulticall3.sol
+691 −1,380 src/safeconsole.sol
+2 −2 test/CommonBase.t.sol
+34 −5 test/Config.t.sol
+19 −1 test/LibVariable.t.sol
+2 −2 test/StdAssertions.t.sol
+24 −24 test/StdChains.t.sol
+19 −20 test/StdCheats.t.sol
+2 −2 test/StdConstants.t.sol
+3 −4 test/StdError.t.sol
+2 −2 test/StdJson.t.sol
+6 −6 test/StdMath.t.sol
+24 −27 test/StdStorage.t.sol
+2 −2 test/StdStyle.t.sol
+2 −2 test/StdToml.t.sol
+13 −13 test/StdUtils.t.sol
+3 −3 test/Vm.t.sol
+2 −4 test/compilation/CompilationScript.sol
+2 −4 test/compilation/CompilationScriptBase.sol
+2 −4 test/compilation/CompilationTest.sol
+2 −4 test/compilation/CompilationTestBase.sol
7 changes: 5 additions & 2 deletions src/concrete/DecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,18 @@ contract DecimalFloat {
}

/// Exposes `LibFormatDecimalFloat.toDecimalString` for offchain use.
/// @param a The float to format.
/// @param a The float to format. The absolute value of `a` is used to
/// determine if scientific notation is used, this allows negative numbers to
/// be formatted consistently with their positive counterparts.
/// @param scientificMin The smallest number that won't be formatted in
/// scientific notation.
/// @param scientificMax The largest number that won't be formatted in
/// scientific notation.
/// @return The string representation of the float.
function format(Float a, Float scientificMin, Float scientificMax) public pure returns (string memory) {
require(scientificMin.lt(scientificMax), "scientificMin must be less than scientificMax");
return LibFormatDecimalFloat.toDecimalString(a, a.lt(scientificMin) || a.gt(scientificMax));
Float absA = a.abs();
return LibFormatDecimalFloat.toDecimalString(a, absA.lt(scientificMin) || absA.gt(scientificMax));
}

/// Exposes `LibFormatDecimalFloat.toDecimalString` for offchain use.
Expand Down
3 changes: 3 additions & 0 deletions src/error/ErrDecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ error DivisionByZero(int256 signedCoefficient, int256 exponent);

/// @dev Thrown when attempting to exponentiate a negative base.
error PowNegativeBase(int256 signedCoefficient, int256 exponent);

/// @dev Thrown if writing the data by creating the contract fails somehow.
error WriteError();
59 changes: 35 additions & 24 deletions src/lib/LibDecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type Float is bytes32;
library LibDecimalFloat {
using LibDecimalFloat for Float;

/// Address of the log tables data contract. Only valid if deployed using
/// the standard Rain deployment scripts that use Zoltu deterministic
/// deployment.
address constant LOG_TABLES_ADDRESS = 0x6421E8a23cdEe2E6E579b2cDebc8C2A514843593;

/// A zero valued float.
Expand Down Expand Up @@ -347,6 +350,11 @@ library LibDecimalFloat {
}
}

/// Lossless version of `packLossy`. This will revert if the conversion is
/// lossy.
/// @param signedCoefficient As per `packLossy`.
/// @param exponent As per `packLossy`.
/// @return float As per `packLossy`.
function packLossless(int256 signedCoefficient, int256 exponent) internal pure returns (Float) {
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
if (!lossless) {
Expand Down Expand Up @@ -573,28 +581,31 @@ library LibDecimalFloat {
/// @param float The float to frac.
function frac(Float float) internal pure returns (Float) {
(int256 signedCoefficient, int256 exponent) = float.unpack();
(int256 characteristic, int256 mantissa) =
LibDecimalFloatImplementation.characteristicMantissa(signedCoefficient, exponent);
(characteristic);
(Float result, bool lossless) = packLossy(mantissa, exponent);
(int256 integer, int256 fraction) = LibDecimalFloatImplementation.intFrac(signedCoefficient, exponent);
(integer);
(Float result, bool lossless) = packLossy(fraction, exponent);
// Frac is lossy by definition.
(lossless);
return result;
}

/// Integer component of a float.
/// Smallest integer value less than or equal to the float.
/// @param float The float to floor.
function floor(Float float) internal pure returns (Float) {
(int256 signedCoefficient, int256 exponent) = float.unpack();
// If the exponent is 0 or greater then the float is already an integer.
if (exponent >= 0) {
return float;
}
(int256 characteristic, int256 mantissa) =
LibDecimalFloatImplementation.characteristicMantissa(signedCoefficient, exponent);
(Float result, bool lossless) = packLossy(characteristic, exponent);
(int256 integer, int256 fraction) = LibDecimalFloatImplementation.intFrac(signedCoefficient, exponent);
if (signedCoefficient < 0 && fraction != 0) {
// If the float is negative and has a fractional part, we need to
// subtract 1 from the characteristic to floor it.
(integer, exponent) = LibDecimalFloatImplementation.sub(integer, exponent, 1e76, -76);
}
(Float result, bool lossless) = packLossy(integer, exponent);
// Flooring is lossy by definition.
(lossless, mantissa);
(lossless, fraction);
return result;
}

Expand All @@ -606,22 +617,21 @@ library LibDecimalFloat {
if (exponent >= 0) {
return float;
}
(int256 characteristic, int256 mantissa) =
LibDecimalFloatImplementation.characteristicMantissa(signedCoefficient, exponent);
(int256 integer, int256 fraction) = LibDecimalFloatImplementation.intFrac(signedCoefficient, exponent);

// If the mantissa is 0, then the float is already an integer.
if (mantissa == 0) {
// If the fraction is 0, then the float is already an integer.
if (fraction == 0) {
return float;
}
// Truncate the fractional part when exponent < 0:
// mantissa < 0 (input < 0) → truncation towards zero increases the value (correct ceil).
// mantissa == 0 → value is already an integer.
// mantissa > 0 (input > 0) → truncation decreases the value, so add 1 to round up.
else if (mantissa > 0) {
(characteristic, exponent) = LibDecimalFloatImplementation.add(characteristic, exponent, 1e76, -76);
// fraction < 0 (input < 0) → truncation towards zero increases the value (correct ceil).
// fraction == 0 → value is already an integer.
// fraction > 0 (input > 0) → truncation decreases the value, so add 1 to round up.
else if (fraction > 0) {
(integer, exponent) = LibDecimalFloatImplementation.add(integer, exponent, 1e76, -76);
}

(Float result, bool lossless) = packLossy(characteristic, exponent);
(Float result, bool lossless) = packLossy(integer, exponent);
(lossless);
return result;
}
Expand Down Expand Up @@ -700,11 +710,9 @@ library LibDecimalFloat {
}

(int256 signedCoefficientB, int256 exponentB) = b.unpack();
(int256 characteristicB, int256 mantissaB) =
LibDecimalFloatImplementation.characteristicMantissa(signedCoefficientB, exponentB);
(int256 integerB, int256 fractionB) = LibDecimalFloatImplementation.intFrac(signedCoefficientB, exponentB);

uint256 exponentBInteger =
uint256(LibDecimalFloatImplementation.withTargetExponent(characteristicB, exponentB, 0));
uint256 exponentBInteger = uint256(LibDecimalFloatImplementation.withTargetExponent(integerB, exponentB, 0));

// Exponentiation by squaring.
(int256 signedCoefficientResult, int256 exponentResult) = (1, 0);
Expand All @@ -725,7 +733,7 @@ library LibDecimalFloat {
LibDecimalFloatImplementation.log10(tablesDataContract, signedCoefficientA, exponentA);

(signedCoefficientC, exponentC) =
LibDecimalFloatImplementation.mul(signedCoefficientC, exponentC, mantissaB, exponentB);
LibDecimalFloatImplementation.mul(signedCoefficientC, exponentC, fractionB, exponentB);

(signedCoefficientC, exponentC) =
LibDecimalFloatImplementation.pow10(tablesDataContract, signedCoefficientC, exponentC);
Expand Down Expand Up @@ -772,6 +780,9 @@ library LibDecimalFloat {
return gt(a, b) ? a : b;
}

/// Returns true if the float is zero. Handles the case where the signed
/// coefficient is zero and exponent is potentially non zero.
/// @param a The float to check.
function isZero(Float a) internal pure returns (bool result) {
uint256 mask = type(uint224).max;
assembly ("memory-safe") {
Expand Down
Loading
Loading