Skip to content

Commit

Permalink
feat: add conversion for fixed ABI types (#1946)
Browse files Browse the repository at this point in the history
  • Loading branch information
BowTiedDevil authored Mar 7, 2024
1 parent 0279a84 commit fedba85
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/ape/managers/converters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Dict, List, Sequence, Tuple, Type, Union
Expand Down Expand Up @@ -185,6 +186,22 @@ def convert(self, value: Union[str, datetime, timedelta]) -> int:
raise ConversionError()


class StringDecimalConverter(ConverterAPI):
"""
Convert string-formatted floating point values to `Decimal` type.
"""

def is_convertible(self, value: Any) -> bool:
# Matches only string-formatted floats with an optional sign character (+/-).
# Leading and trailing zeros are required.
# NOTE: `re.fullmatch` will only match the full string, so "1.0 ether" and "10.0 USDC"
# will not be identified as convertible.
return isinstance(value, str) and re.fullmatch(r"[+-]?\d+\.\d+", value) is not None

def convert(self, value: str) -> Decimal:
return Decimal(value)


class ConversionManager(BaseManager):
"""
A singleton that manages all the converters.
Expand Down Expand Up @@ -219,7 +236,7 @@ def _converters(self) -> Dict[Type, List[ConverterAPI]]:
StringIntConverter(),
AccountIntConverter(),
],
Decimal: [],
Decimal: [StringDecimalConverter()],
bool: [],
str: [],
}
Expand Down
4 changes: 4 additions & 0 deletions src/ape_ethereum/ecosystem.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
from copy import deepcopy
from decimal import Decimal
from functools import cached_property
from typing import Any, ClassVar, Dict, Iterator, List, Optional, Sequence, Tuple, Type, Union, cast

Expand Down Expand Up @@ -582,6 +583,9 @@ def _python_type_for_abi_type(self, abi_type: ABIType) -> Union[Type, Sequence]:
elif "int" in abi_type.type:
return int

elif "fixed" in abi_type.type:
return Decimal

raise ConversionError(f"Unable to convert '{abi_type}'.")

def encode_calldata(self, abi: Union[ConstructorABI, MethodABI], *args) -> HexBytes:
Expand Down
83 changes: 83 additions & 0 deletions tests/functional/conversion/test_decimal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from decimal import Decimal

import pytest

from ape.exceptions import ConversionError


def test_convert_formatted_float_strings_to_decimal(convert):
test_strings = [
"1.000",
"1.00",
"1.0",
"0.1",
"0.01",
"0.001",
]
for test_string in test_strings:
actual = convert(test_string, Decimal)
assert actual == Decimal(test_string)


def test_convert_badly_formatted_float_strings_to_decimal(convert):
test_strings = [
".1",
"1.",
".",
]
for test_string in test_strings:
with pytest.raises(
ConversionError, match=f"No conversion registered to handle '{test_string}'"
):
convert(test_string, Decimal)


def test_convert_int_strings(convert):
test_strings = [
"1",
"10",
"100",
]
for test_string in test_strings:
with pytest.raises(
ConversionError, match=f"No conversion registered to handle '{test_string}'"
):
convert(test_string, Decimal)


def test_convert_alphanumeric_strings(convert):
test_strings = [
"a",
"H",
"XYZ",
]
for test_string in test_strings:
with pytest.raises(
ConversionError, match=f"No conversion registered to handle '{test_string}'"
):
convert(test_string, Decimal)


def test_convert_strings_with_token_names(convert):
test_strings = [
"0.999 DAI",
"10.0 USDC",
]
for test_string in test_strings:
with pytest.raises(
ConversionError, match=f"No conversion registered to handle '{test_string}'"
):
convert(test_string, Decimal)


def test_convert_strings_with_ether_alias(convert):
test_strings = [
"0 wei",
"999 gwei",
"1.0 ether",
]
for test_string in test_strings:
with pytest.raises(
ConversionError, match=f"No conversion registered to handle '{test_string}'"
):
convert(test_string, Decimal)

0 comments on commit fedba85

Please sign in to comment.