Skip to content

Conversation

@squadgazzz
Copy link
Contributor

@squadgazzz squadgazzz commented Jan 6, 2026

Description

As noted in #4010, several U256Ext utility implementations are scattered across different crates in the codebase, some of which are duplicated. This PR moves all of them into a single U256Ext trait under the numbers crate. No logic changes. As a result, easier to keep everything in one place, no duplicated logic.

@squadgazzz squadgazzz requested a review from a team as a code owner January 6, 2026 17:43
@squadgazzz squadgazzz changed the title Rename Participant to Bid Extract U256Ext implementations into the numbers crate Jan 6, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello @squadgazzz, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the handling of U256Ext utility implementations and related number conversions. Despite the misleading title "Rename Participant to Bid", the actual purpose is to centralize these utilities into the crates/number crate. This addresses the issue of scattered and duplicated utility code, providing a single, consistent source for U256 extensions and conversions across the codebase, thereby improving maintainability and reducing redundancy.

Highlights

  • U256Ext Trait Consolidation: The U256Ext trait and its utility implementations, previously scattered across various util/conv modules in autopilot, driver, and shared crates, have been centralized into a single location within the crates/number crate.
  • Number Conversion Utilities Centralization: Related number conversion functions, such as rational_to_big_decimal and big_decimal_to_big_rational, have also been moved to the crates/number crate, establishing a single source of truth for these operations.
  • Redundant Code Removal: Duplicate U256Ext trait definitions and their implementations have been removed from the autopilot, driver, and shared crates, streamlining the codebase.
  • Expanded U256Ext Functionality: The U256Ext trait in crates/number has been enhanced to include comprehensive conversion methods (e.g., to_big_int, from_big_uint) and an explicit ceil_div method, with implementations provided for both alloy::primitives::U256 and primitive_types::U256.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a good refactoring that centralizes the U256Ext trait and its implementations into the numbers crate, reducing code duplication. However, I've identified a significant issue with potential precision loss and overflow in the checked_mul_f64 method, which I've flagged as high severity. I've also included a medium-severity suggestion to improve code consistency. Overall, the direction is great, but these points should be addressed to ensure correctness and maintainability.

Comment on lines +100 to +108
fn checked_mul_f64(&self, factor: f64) -> Option<Self> {
const CONVERSION_FACTOR: f64 = 1_000_000_000_000_000_000.;
let factor_as_u256 = PrimitiveU256::from((factor * CONVERSION_FACTOR) as u128);
let conversion_factor_u256 = PrimitiveU256::from(CONVERSION_FACTOR as u128);
let multiplied = self
.checked_mul(factor_as_u256)?
.checked_div(conversion_factor_u256)?;
Some(multiplied)
}

Choose a reason for hiding this comment

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

high

Similar to the implementation for AlloyU256, this implementation of checked_mul_f64 can lead to precision loss or overflow. The explicit cast as u128 can be lossy or overflow if factor * CONVERSION_FACTOR exceeds u128::MAX. Also, negative factors will be cast to 0. Using BigRational is a much safer approach and was suggested in a TODO in the original code.

    fn checked_mul_f64(&self, factor: f64) -> Option<Self> {
        if factor.is_sign_negative() {
            return None;
        }
        let self_rational = self.to_big_rational();
        let factor_rational = BigRational::from_f64(factor)?;
        let result_rational = self_rational * factor_rational;
        Self::from_big_rational(&result_rational).ok()
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That probably makes sense. But I will address it in a separate PR to avoid any logic changes here.

Comment on lines +110 to +112
fn to_big_int(&self) -> BigInt {
crate::conversions::u256_to_big_int(self)
}

Choose a reason for hiding this comment

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

medium

For consistency with the AlloyU256 implementation and to make the impl block more self-contained, consider implementing to_big_int directly instead of delegating to crate::conversions::u256_to_big_int. The logic is simple and can be inlined here.

    fn to_big_int(&self) -> BigInt {
        BigInt::from_biguint(num::bigint::Sign::Plus, self.to_big_uint())
    }

Copy link
Contributor

@jmg-duarte jmg-duarte left a comment

Choose a reason for hiding this comment

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

LGTM, we don't need to change the div_ceil because I'm not 100% sure they match, though I'd like it very much

Comment on lines +95 to +98
fn checked_ceil_div(&self, other: &Self) -> Option<Self> {
self.checked_add(other.checked_sub(1.into())?)?
.checked_div(*other)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like div_ceil but implemented in a weird way
https://docs.rs/ruint/1.17.2/ruint/struct.Uint.html#method.div_ceil

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it is just a copy-paste from the old code. Will try to address it in a separate PR.

Comment on lines +76 to +85
fn from_big_int(input: &BigInt) -> Result<Self> {
anyhow::ensure!(input.sign() != num::bigint::Sign::Minus, "negative");
Self::from_big_uint(input.magnitude())
}

fn from_big_uint(input: &BigUint) -> Result<Self> {
let bytes = input.to_bytes_be();
anyhow::ensure!(bytes.len() <= 32, "too large");
Ok(AlloyU256::from_be_slice(&bytes))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Eventually, we could replace this with the official implementation
https://github.com/recmo/uint/blob/5bd4cff6ae3960591d750cdd7356e24aa086b67a/src/support/num_bigint.rs

Copy link
Contributor

@m-sz m-sz left a comment

Choose a reason for hiding this comment

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

LGTM, thanks. This is a very welcome change.

Base automatically changed from rename-participant-to-bid to main January 7, 2026 15:38
# Conflicts:
#	crates/autopilot/src/domain/competition/winner_selection.rs
@squadgazzz squadgazzz enabled auto-merge January 7, 2026 15:40
@squadgazzz squadgazzz added this pull request to the merge queue Jan 7, 2026
Merged via the queue into main with commit bae34b6 Jan 7, 2026
19 checks passed
@squadgazzz squadgazzz deleted the extract-u256-ext-into-numbers branch January 7, 2026 16:05
@github-actions github-actions bot locked and limited conversation to collaborators Jan 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants