Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Create a wrap for eth::U256 in order to ensure correct serde #2086 #2416

Closed
wants to merge 1 commit into from

Conversation

m-lord-renkse
Copy link
Contributor

Description

Remove the eth::U256 type and create a new type U256 in crates/number. The idea is to enforce specific (de)serialization for for the U256 type as per #2086.

Changes

The new type as been converted into primitive_types::U256 when necessary when needed for the business logic. Ideal solution would be to propagate the new type not only as an API type, but also as an internal type for the business logic operations (but it requires many more changes, it could be potentially done in a followup issue).

How to test

#2086

@m-lord-renkse m-lord-renkse requested a review from a team as a code owner February 20, 2024 10:31
Copy link

github-actions bot commented Feb 20, 2024

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@m-lord-renkse m-lord-renkse force-pushed the implement-wrap-ethU256 branch 2 times, most recently from 7103de8 to d9f8c2c Compare February 20, 2024 10:48
@m-lord-renkse
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

Maybe I misunderstood the original issue but I thought we only want to have a new type that handles the (de)serialization without having to remember to use macros.

If we want to completely replace primitve_types::U256 with our new type I think we should look into implementing std::ops::Deref for the new type. That way we probably wouldn't have to reimplement a bunch of functions for the new type that we are currently using on the existing type.

Alternatively if we want to actually completely replace primitive_types::U256 with the new type it might also make sense to add a new crate that re-exports all the primtive_types but replaces the regular U256 with the new U256 type. But if we already go that far could also consider forking the primitive_types crate or even patching the behavior upstream.

crates/driver/src/boundary/mempool.rs Outdated Show resolved Hide resolved
crates/driver/src/boundary/mempool.rs Outdated Show resolved Hide resolved
crates/driver/src/domain/competition/auction.rs Outdated Show resolved Hide resolved
crates/driver/src/infra/config/file/mod.rs Outdated Show resolved Hide resolved
crates/driver/src/infra/solver/dto/solution.rs Outdated Show resolved Hide resolved
crates/driver/src/util/serialize/mod.rs Outdated Show resolved Hide resolved
crates/number/src/serialization.rs Outdated Show resolved Hide resolved
@@ -51,7 +52,7 @@ pub enum Kind {
/// The MEVBlocker private mempool.
MEVBlocker {
url: reqwest::Url,
max_additional_tip: eth::U256,
max_additional_tip: primitive_types::U256,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did this to keep the same behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

What same behaviour? Isn't eth::U256 just a re-export of primitive type?

@m-lord-renkse m-lord-renkse force-pushed the implement-wrap-ethU256 branch 2 times, most recently from 27d1049 to 4b639d5 Compare February 21, 2024 12:05
@squadgazzz
Copy link
Contributor

squadgazzz commented Feb 21, 2024

Would it be possible to split this activity into separate PRs moving from crate to crate(or any other separation kind)? Maybe, to also add tracking tasks to the original issue.

@m-lord-renkse m-lord-renkse force-pushed the implement-wrap-ethU256 branch 2 times, most recently from cd66af2 to 28c8265 Compare February 21, 2024 12:58
Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

The driver and solvers crate export a U256 type as part of the eth module (e.g. here). Might be a good idea to export the new number::U256 instead of the current primitive_types::U256 instead to keep people from using the old type.

Overall I'm still not sure what the best solution is for this. :/
Maybe @fleupold, @sunce86 and @squadgazzz have an opinion as well?

crates/number/src/serialization.rs Outdated Show resolved Hide resolved
crates/number/src/serialization.rs Outdated Show resolved Hide resolved
crates/autopilot/src/driver_model.rs Outdated Show resolved Hide resolved
@m-lord-renkse m-lord-renkse force-pushed the implement-wrap-ethU256 branch 2 times, most recently from 501c058 to ceaefb0 Compare February 22, 2024 15:10
@squadgazzz
Copy link
Contributor

Might be a good idea to export the new number::U256 instead of the current primitive_types::U256 instead to keep people from using the old type.

That looks convenient.

@m-lord-renkse
Copy link
Contributor Author

The driver and solvers crate export a U256 type as part of the eth module (e.g. here). Might be a good idea to export the new number::U256 instead of the current primitive_types::U256 instead to keep people from using the old type.

Overall I'm still not sure what the best solution is for this. :/ Maybe @fleupold, @sunce86 and @squadgazzz have an opinion as well?

It is a good idea, but I think the best is to do it in a followup issue, not to have a lot of more changes in this one, wdyt?

@MartinquaXD
Copy link
Contributor

It is a good idea, but I think the best is to do it in a followup issue, not to have a lot of more changes in this one, wdyt?

Makes sense. I keep thinking that the new type is equivalent to the old one so it shouldn't introduce more changes. But actually we'd have to dereference in a lot of places then.

Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

Looks alright to me. Let's give it a try.

Copy link
Contributor

@squadgazzz squadgazzz left a comment

Choose a reason for hiding this comment

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

LG. Do we have a follow-up issue? Could you link it here, please?

Copy link
Contributor

@fleupold fleupold left a comment

Choose a reason for hiding this comment

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

I don't want to be negative about this PR (I realize a lot of work went into making all these changes), but what are we really gaining here? Maybe we were a bit too optimistic about the original issue in the first place?

We have a new type with a bunch of boilerplate conversion but now have to remember instead of using #[serde_as(as = "HexOrDecimalU256")] to use number::256 (if most of other callsites continue using eth::U256 this may be as challenging). I assume we cannot get a clippy warning for this?

I guess the real win will only come when we replace the eth::U256 export with the new type (making confusion less likely)?

@@ -51,7 +52,7 @@ pub enum Kind {
/// The MEVBlocker private mempool.
MEVBlocker {
url: reqwest::Url,
max_additional_tip: eth::U256,
max_additional_tip: primitive_types::U256,
Copy link
Contributor

Choose a reason for hiding this comment

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

What same behaviour? Isn't eth::U256 just a re-export of primitive type?

@MartinquaXD
Copy link
Contributor

MartinquaXD commented Feb 23, 2024

I assume we cannot get a clippy warning for this?

Unfortunately not. There is an open issue for warning on trait implementation on specific types but and the open draft PR doesn't seem like it would get merged any time soon.
The options I see are:

  1. fork primitive types and patch it
  2. try to upstream that patch. They are aware of the issue but refuse to change the default behavior. I asked if they would be fine with a feature flag that switches to the saner logic and they are open to this option. So depending on how tricky that patch is (probably requires a patch to a macro) this might be the best way out.
  3. completely replace U256 type but I think it will not be possible to make it work seamlessly with the regular primitive_types during computations.
  4. push the clippy PR over the finish line

@m-lord-renkse
Copy link
Contributor Author

Unfortunately not. There is an open issue for warning on trait implementation on specific types but and the open draft PR doesn't seem like it would get merged any time soon. The options I see are:

  1. fork primitive types and patch it
  2. try to upstream that patch. They are aware of the issue but refuse to change the default behavior. I asked if they would be fine with a feature flag that switches to the saner logic and they are open to this option. So depending on how tricky that patch is (probably requires a patch to a macro) this might be the best way out.
  3. completely replace U256 type but I think it will not be possible to make it work seamlessly with the regular primitive_types during computations.
  4. push the clippy PR over the finish line

I think 1 may be complicated to maintain. We would need to rebase the fork every time we want to bump to the latest version of primitive_types.

The option 3 is not complicated, it should be a smaller PR than this one I believe. I could take care of that as a follow up issue (I'll create the issue once confirmed).

@sunce86
Copy link
Contributor

sunce86 commented Feb 26, 2024

The driver and solvers crate export a U256 type as part of the eth module (e.g. here). Might be a good idea to export the new number::U256 instead of the current primitive_types::U256 instead to keep people from using the old type.

Overall I'm still not sure what the best solution is for this. :/ Maybe @fleupold, @sunce86 and @squadgazzz have an opinion as well?

Long term, I would prefer to forbid using primitive_types::U256 in the domain, and instead have a wrapper around it for every object, because every U256 represents something meaningful to the domain. Not sure if it should be number::U256, because we don't need serde in domain.

Copy link
Contributor

@sunce86 sunce86 left a comment

Choose a reason for hiding this comment

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

Mostly nits.

@@ -6,7 +6,6 @@ use {
},
chrono::{DateTime, Utc},
itertools::Itertools,
number::serialization::HexOrDecimalU256,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we also make the line below this one primitive_types::H160, and explicitly use primitive_types::U256 where needed, so if we accidentally add a reference to this type next time, it's more obvious.

@@ -135,7 +135,7 @@ async fn eth_flow_tx_zero_fee(web3: Web3) {
.get_order(&ethflow_order.uid(onchain.contracts()).await)
.await
.unwrap();
order.metadata.executed_surplus_fee > U256::zero()
order.metadata.executed_surplus_fee > U256::zero().into()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
order.metadata.executed_surplus_fee > U256::zero().into()
order.metadata.executed_surplus_fee > number::U256::zero()

@@ -376,10 +376,10 @@ async fn mixed_limit_and_market_orders_test(web3: Web3) {

let order_b = OrderCreation {
sell_token: token_b.address(),
sell_amount: to_wei(5),
fee_amount: to_wei(1),
sell_amount: to_wei(5).into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: what happens if we change to_wei() to return number::U256?

fn try_from(value: crate::U256) -> Result<Self, Self::Error> {
ZeroU256::from(value).try_into()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be removed as not used.

impl U256 {
pub fn zero() -> Self {
Self(primitive_types::U256::zero())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Instead, how about implementing an existing trait for this:

impl num::Zero for U256 {
    fn zero() -> Self {
        Self(primitive_types::U256::zero())
    }

    fn is_zero(&self) -> bool {
        self.0.is_zero()
    }
}

impl std::ops::Add for U256 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self(self.0 + rhs.0)
    }
}

Copy link

github-actions bot commented Mar 5, 2024

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions bot added the stale label Mar 5, 2024
@github-actions github-actions bot closed this Mar 13, 2024
@m-lord-renkse m-lord-renkse deleted the implement-wrap-ethU256 branch September 19, 2024 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants