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

feat: add conversion for fixed ABI types #1946

Merged
merged 8 commits into from Mar 7, 2024

Conversation

BowTiedDevil
Copy link
Contributor

What I did

Ape has no converter to handle Vyper's decimal type, which is translated to the fixed168x10 ABI type. Testing a contract function with a decimal type argument will fail with a ConversionError.

How I did it

ape_ethereum plugin:

  • Add Decimal as the native Python type for ABI types with "fixed" in the name.

ape base:

  • Add a converter to support using string-formatted float values instead of requiring a strictly Decimal value. This converter will translate a "1.0" string in-place. Did not add a float converter, because floating-point accuracy will cause subtle errors.

Tests:

  • Added simple string -> Decimal converter test.

Checklist:

  • All changes are completed
  • New test cases have been added
  • Documentation has been updated

fubuloubu
fubuloubu previously approved these changes Mar 5, 2024
def is_convertible(self, value: Any) -> bool:
# Matches only string-formatted floats with an optional sign character (+/-),
# leading and trailing zeros are optional.
return isinstance(value, str) and re.fullmatch("[+-]?[0-9]*[.][0-9]*", value)
Copy link
Member

Choose a reason for hiding this comment

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

Only nit is if theres a simpler regex to run here that picks up decimal values

Or even just trying to cast to decimal and catching exceptions

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've reworked the regex to require digits before and after the decimal, because a "." input was slipping through.

Copy link
Member

Choose a reason for hiding this comment

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

using \d might even be clearer (lots of alpha here)

Copy link
Member

Choose a reason for hiding this comment

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

also make sure to use \. and not just . (which is "any character")

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's interesting, I used \d and got a depreciation warning from the re module. I didn't look further into it, but will take another look now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Apparently they have already extensively documented the backslash plague 😂

I'll make some updates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved with ba341bb

…uts, add "is not None" check to satisfy mypy
@BowTiedDevil BowTiedDevil marked this pull request as ready for review March 5, 2024 07:21
@fubuloubu
Copy link
Member

Also, general testing advice, using a regex is usually great paired with fuzzing to find corner cases for your expression (given positive and negative examples of what you want it to handle)

fubuloubu
fubuloubu previously approved these changes Mar 6, 2024
def is_convertible(self, value: Any) -> bool:
# Matches only string-formatted floats with an optional sign character (+/-).
# Leading and trailing zeros are required.
return isinstance(value, str) and re.fullmatch(r"[+-]?\d+\.\d+", value) is not None
Copy link
Member

Choose a reason for hiding this comment

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

re.fullmatch is the same as checking the full string? That's helpful because we do already have conversion sequences like "1.0 ether" and "10.0 USDC"

Can you add a comment to this effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirmed, re.fullmatch will check the full string - it should be equivalent to sandwiching the above expression with ^...$ but I'm not experienced enough with regex to know for sure if that expression has edge cases.

Added a note to the comment with b31222a

antazoey
antazoey previously approved these changes Mar 6, 2024
import pytest


def test_converting_formatted_float_strings_to_decimal(convert):
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I tend to use -k to run multiple tests and it works great if following this pattern:

test_<method-name>_<rest>`
Suggested change
def test_converting_formatted_float_strings_to_decimal(convert):
def test_convert_formatted_float_strings_to_decimal(convert):

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 reformatted the test names per your suggestion, and added some more to check other bad strings per @fubuloubu's comment above.

@BowTiedDevil BowTiedDevil dismissed stale reviews from antazoey and fubuloubu via b31222a March 6, 2024 17:52
@antazoey antazoey enabled auto-merge (squash) March 7, 2024 01:26
@antazoey antazoey merged commit fedba85 into ApeWorX:main Mar 7, 2024
15 checks passed
@BowTiedDevil BowTiedDevil deleted the ape_fixed_decimal_conversion branch March 7, 2024 03:51
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.

None yet

3 participants