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
115 changes: 115 additions & 0 deletions .github/workflows/npm-package-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: NPM Packages Release
on:
push:
branches:
- main

jobs:
release:
if: ${{ github.ref == 'refs/heads/main' && !startsWith(github.event.head_commit.message, 'NPM Package Release') }}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
outputs:
version: ${{ env.NEW_VERSION }}
steps:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.PUBLISH_PRIVATE_KEY }}
submodules: recursive
fetch-depth: 0

- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: DeterminateSystems/flakehub-cache-action@main

- name: Install NodeJS v22
uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"

- run: nix develop -c rainix-sol-prelude

- name: Test JS/TS Binding
run: nix develop -c test-js-bindings

- name: Git Config
run: |
git config --global user.email "${{ secrets.CI_GIT_EMAIL }}"
git config --global user.name "${{ secrets.CI_GIT_USER }}"

# get hash of latest published pkgs from npm and concat them
- name: Get Old Hash
run: |
OLD_HASH=$(npm view @rainlanguage/float@latest dist.shasum 2>/dev/null || echo "none")
echo "OLD_HASH=$OLD_HASH" >> $GITHUB_ENV
echo "old hash: $OLD_HASH"

# calc hash of current workspace pkgs by packing them and concat them
- name: Get New Hash
run: |
NEW_HASH=$(npm pack --silent | xargs shasum | cut -c1-40)
echo "NEW_HASH=$NEW_HASH" >> $GITHUB_ENV
echo "new hash: $NEW_HASH"
rm -f *.tgz

# from here on, we'll skip if OLD_HASH and NEW_HASH are the same (ie no publish)
# this means we need to skip every step by using an if statement.
# set npm version
- name: Set Version
if: ${{ env.OLD_HASH != env.NEW_HASH }}
run: |
NEW_VERSION=$(npm version prerelease --preid alpha --no-git-tag-version)
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV

# Commit changes and tag
- name: Commit And Tag
if: ${{ env.OLD_HASH != env.NEW_HASH }}
run: |
git add "package.json"
git add "package-lock.json"
git commit -m "NPM Package Release v${{ env.NEW_VERSION }}"
git tag npm-v${{ env.NEW_VERSION }}

# Push the commit to remote
- name: Push Changes To Remote
if: ${{ env.OLD_HASH != env.NEW_HASH }}
run: |
git push origin
git push -u origin npm-v${{ env.NEW_VERSION }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Create float npm package tarball
- name: Create float NPM Package Tarball
if: ${{ env.OLD_HASH != env.NEW_HASH }}
run: echo "NPM_PACKAGE=$(npm pack --silent)" >> $GITHUB_ENV

- name: Rename float NPM Package Tarball
if: ${{ env.OLD_HASH != env.NEW_HASH }}
run: mv ${{ env.NPM_PACKAGE }} float_npm_package_${{ env.NEW_VERSION }}.tgz

# publish float pkg to npm
- name: Publish float pkg To NPM
if: ${{ env.OLD_HASH != env.NEW_HASH }}
uses: JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_TOKEN }}
access: public
package: float_npm_package_${{ env.NEW_VERSION }}.tgz

# Create gitHub release with tarballs
- name: Create GitHub Release with float pkg
if: ${{ env.OLD_HASH != env.NEW_HASH }}
id: gh_release
uses: softprops/action-gh-release@v2
with:
tag_name: npm-v${{ env.NEW_VERSION }}
name: NPM Package Release v${{ env.NEW_VERSION }}
files: |
float_npm_package_${{ env.NEW_VERSION }}.tgz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
out
cache
.direnv
target
target
temp
dist
node_modules
6 changes: 2 additions & 4 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion crates/float/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ edition.workspace = true
license.workspace = true
homepage.workspace = true

[lib]
crate-type = ["rlib", "cdylib"]

[dependencies]
alloy.workspace = true
thiserror.workspace = true
serde.workspace = true
wasm-bindgen-utils = "0.0.10"
wasm-bindgen-utils = { git = "https://github.com/rainlanguage/rain.wasm", rev = "06990d85a0b7c55378a1c8cca4dd9e2bc34a596a" }

[target.'cfg(not(target_family = "wasm"))'.dependencies]
revm = { workspace = true, default-features = false, features = [
Expand Down
35 changes: 33 additions & 2 deletions crates/float/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::DecimalFloat;
use alloy::hex::FromHexError;
use alloy::primitives::{Bytes, FixedBytes};
use alloy::sol_types::SolError;
use revm::context::result::{EVMError, HaltReason, Output, SuccessReason};
use std::thread::AccessError;
use thiserror::Error;

use crate::DecimalFloat;
use wasm_bindgen_utils::prelude::js_sys::{Error as JsError, RangeError};
use wasm_bindgen_utils::result::WasmEncodedError;

#[derive(Debug, Error)]
pub enum FloatError {
Expand All @@ -26,6 +28,14 @@ pub enum FloatError {
Access(#[from] AccessError),
#[error("Invalid hex string: {0}")]
InvalidHex(String),
#[error(transparent)]
AlloyFromHexError(#[from] FromHexError),
#[error(transparent)]
AlloyParseError(#[from] alloy::primitives::ruint::ParseError),
#[error(transparent)]
AlloyParseSignedError(#[from] alloy::primitives::ParseSignedError),
#[error("Wasm bindgen js_sys threw error: {0}")]
JsSysError(String),
}

#[derive(Debug)]
Expand Down Expand Up @@ -64,3 +74,24 @@ impl TryFrom<FixedBytes<4>> for DecimalFloatErrorSelector {
}
}
}

impl From<FloatError> for WasmEncodedError {
fn from(value: FloatError) -> Self {
WasmEncodedError {
msg: value.to_string(),
readable_msg: value.to_string(), // todo: add detailed readable msg for errors
}
}
}
Comment on lines +78 to +85
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Jul 17, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

WasmEncodedError conversion needs improvement.

The TODO comment indicates that detailed readable messages are needed. Consider implementing more specific error descriptions for better user experience.

 impl From<FloatError> for WasmEncodedError {
     fn from(value: FloatError) -> Self {
+        let readable_msg = match &value {
+            FloatError::InvalidHex(hex) => format!("Invalid hexadecimal string: {}", hex),
+            FloatError::JsSysError(msg) => format!("JavaScript error: {}", msg),
+            FloatError::DecimalFloat(_) => "Decimal float operation failed".to_string(),
+            _ => value.to_string(),
+        };
         WasmEncodedError {
             msg: value.to_string(),
-            readable_msg: value.to_string(), // todo: add detailed readable msg for errors
+            readable_msg,
         }
     }
 }

Do you want me to generate a more comprehensive error message mapping implementation?

📝 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
impl From<FloatError> for WasmEncodedError {
fn from(value: FloatError) -> Self {
WasmEncodedError {
msg: value.to_string(),
readable_msg: value.to_string(), // todo: add detailed readable msg for errors
}
}
}
impl From<FloatError> for WasmEncodedError {
fn from(value: FloatError) -> Self {
let readable_msg = match &value {
FloatError::InvalidHex(hex) => format!("Invalid hexadecimal string: {}", hex),
FloatError::JsSysError(msg) => format!("JavaScript error: {}", msg),
FloatError::DecimalFloat(_) => "Decimal float operation failed".to_string(),
_ => value.to_string(),
};
WasmEncodedError {
msg: value.to_string(),
readable_msg,
}
}
}
🤖 Prompt for AI Agents
In crates/float/src/error.rs around lines 78 to 85, the From<FloatError>
implementation for WasmEncodedError currently uses the same generic message for
both msg and readable_msg fields. To improve this, create a mapping from
different FloatError variants to more detailed, user-friendly readable_msg
strings that explain the error clearly. Update the from function to match on the
FloatError variants and assign appropriate descriptive messages to readable_msg
while keeping msg as the original error string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will be added in followups

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rouzwelt Sounds good! Deferring the detailed error message improvements to follow-up PRs is a reasonable approach to keep this PR focused on the core JavaScript/TypeScript bindings functionality.


🧠 Learnings used
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#57
File: crates/float/src/lib.rs:316-328
Timestamp: 2025-06-18T09:10:41.740Z
Learning: In the rain.math.float codebase, the user prefers implementing standard Rust traits (like Neg) rather than creating redundant public methods when the trait already provides the needed functionality. Float implements Copy, so reference usage with operators is not a concern.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#46
File: crates/float/src/lib.rs:112-153
Timestamp: 2025-06-11T09:07:00.591Z
Learning: Alloy generates helpers to decode full ABI-encoded error payloads, but it doesn’t generate utilities to map raw 4-byte error selectors to error types; manual mapping is required when a contract returns only the selector.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#59
File: crates/float/src/lib.rs:447-461
Timestamp: 2025-06-17T10:11:32.740Z
Learning: In the rainlanguage/rain.math.float repository, the user 0xgleb prefers using .unwrap() over ? in tests because it provides clearer stack traces showing the exact line where panics occurred, making debugging easier.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: crates/float/src/lib.rs:201-232
Timestamp: 2025-06-16T13:14:38.431Z
Learning: In the rain.math.float Rust crate, the `execute_call` function already provides sufficient abstraction for EVM contract calls by handling execution boilerplate, error handling, and result processing. Individual methods like `lt`, `eq`, `gt` only need to handle their specific call encoding and result decoding, making further abstraction unnecessary.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#59
File: crates/float/src/lib.rs:233-242
Timestamp: 2025-06-17T10:17:56.205Z
Learning: 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.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#68
File: crates/float/src/lib.rs:134-135
Timestamp: 2025-06-21T11:07:19.190Z
Learning: In Alloy primitives library, B256 is a type alias for FixedBytes<32> (pub type B256 = FixedBytes<32>), making them completely interchangeable in type usage. Changing between these types is not a breaking change.

Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#30
File: test/src/lib/LibDecimalFloat.gt.t.sol:33-36
Timestamp: 2025-04-25T03:58:01.307Z
Learning: In the rain.math.float library, all values of `Float` (which is a type alias for bytes32) are considered valid and can be safely used with methods like gt(), lt(), or eq() without causing reverts.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#46
File: .github/workflows/rainix.yaml:50-50
Timestamp: 2025-06-11T10:19:16.675Z
Learning: In the rain.math.float CI workflow, the `nix develop -c rainix-sol-prelude` step is required for Rust jobs as well, because Rust code uses JSON ABI artifacts generated during the Solidity prelude to drive `alloy::sol!` type generation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rouzwelt do we have an issue for that?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


impl From<JsError> for FloatError {
fn from(value: JsError) -> Self {
FloatError::JsSysError(value.to_string().into())
}
}

impl From<RangeError> for FloatError {
fn from(value: RangeError) -> Self {
FloatError::JsSysError(value.to_string().into())
}
}
Loading
Loading