Skip to content

Commit

Permalink
fix: support bytes array argument in solidity and vyper contracts (#2255
Browse files Browse the repository at this point in the history
)

Co-authored-by: slush <sam@apeworx.io>
Co-authored-by: El De-dog-lo <3859395+fubuloubu@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 3, 2024
1 parent 3603705 commit d97fff0
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 7 deletions.
19 changes: 17 additions & 2 deletions src/ape/managers/converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from collections.abc import Sequence
from collections.abc import Iterable, Sequence
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Union
Expand Down Expand Up @@ -70,6 +70,18 @@ def convert(self, value: Any) -> int:
return to_int(HexBytes(value))


class HexIterableConverter(ConverterAPI):
"""
Convert list of hex values to single concatenated ``HexBytes`` value.
"""

def is_convertible(self, value: Any) -> bool:
return isinstance(value, Iterable) and all(isinstance(v, bytes) or is_hex(v) for v in value)

def convert(self, value: Any) -> bytes:
return HexBytes(b"".join(HexBytes(v) for v in value))


class StringIntConverter(ConverterAPI):
def is_convertible(self, value: Any) -> bool:
return isinstance(value, str) and not is_0x_prefixed(value) and value.isnumeric()
Expand Down Expand Up @@ -263,7 +275,10 @@ def _converters(self) -> dict[type, list[ConverterAPI]]:
HexAddressConverter(),
IntAddressConverter(),
],
bytes: [HexConverter()],
bytes: [
HexConverter(),
HexIterableConverter(),
],
int: [
TimestampConverter(),
HexIntConverter(),
Expand Down
25 changes: 24 additions & 1 deletion tests/functional/conversion/test_hex.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import pytest
from eth_pydantic_types import HexBytes
from eth_utils import to_hex

from ape.exceptions import ConversionError
from ape.managers.converters import HexConverter, HexIntConverter
from ape.managers.converters import HexConverter, HexIntConverter, HexIterableConverter


@pytest.mark.parametrize("val", ("0xA100", "0x0A100", "0x00a100"))
Expand All @@ -23,3 +24,25 @@ def test_missing_prefix(convert):

with pytest.raises(ConversionError):
convert(hex_value, int)


@pytest.mark.parametrize(
"calldata,expected",
(
(
["0x123456", "0xabcd"],
"0x123456abcd",
),
(
[HexBytes("0x123456"), "0xabcd"],
"0x123456abcd",
),
(
("0x123456", "0xabcd"),
"0x123456abcd",
),
),
)
def test_hex_concat(calldata, expected, convert):
assert HexIterableConverter().is_convertible(calldata)
assert convert(calldata, bytes) == HexBytes(expected)

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/functional/data/sources/SolidityContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ contract SolidityContract {

}

function functionWithCalldata(bytes calldata data) public {

}

function setStruct(MyStruct memory _my_struct) public pure {

}
Expand Down
4 changes: 4 additions & 0 deletions tests/functional/data/sources/VyperContract.vy
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ def functionWithUniqueAmountOfArguments(
):
pass

@external
def functionWithCalldata(data: Bytes[1_024]=b""):
pass

@pure
@external
def setStruct(_my_struct: MyStruct):
Expand Down
35 changes: 33 additions & 2 deletions tests/functional/test_contract_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ def test_get_error_by_signature(error_contract):


def test_selector_identifiers(vyper_contract_instance):
assert len(vyper_contract_instance.selector_identifiers.keys()) == 52
assert len(vyper_contract_instance.selector_identifiers.keys()) == 54
assert vyper_contract_instance.selector_identifiers["balances(address)"] == "0x27e235e3"
assert vyper_contract_instance.selector_identifiers["owner()"] == "0x8da5cb5b"
assert (
Expand All @@ -829,7 +829,7 @@ def test_selector_identifiers(vyper_contract_instance):


def test_identifier_lookup(vyper_contract_instance):
assert len(vyper_contract_instance.identifier_lookup.keys()) == 52
assert len(vyper_contract_instance.identifier_lookup.keys()) == 54
assert vyper_contract_instance.identifier_lookup["0x27e235e3"].selector == "balances(address)"
assert vyper_contract_instance.identifier_lookup["0x8da5cb5b"].selector == "owner()"
assert (
Expand Down Expand Up @@ -990,3 +990,34 @@ def test_sending_funds_to_non_payable_constructor_by_accountDeploy(
def test_as_transaction(tx_type, vyper_contract_instance, owner, eth_tester_provider):
tx = vyper_contract_instance.setNumber.as_transaction(987, sender=owner, type=tx_type.value)
assert tx.gas_limit == eth_tester_provider.max_gas


@pytest.mark.parametrize(
"calldata,expected",
(
(
"0x123456",
"0x123456",
),
(
HexBytes("0x123456"),
"0x123456",
),
(
["0x123456", "0xabcd"],
"0x123456abcd",
),
(
[HexBytes("0x123456"), "0xabcd"],
"0x123456abcd",
),
(
("0x123456", "0xabcd"),
"0x123456abcd",
),
),
)
def test_calldata_arg(calldata, expected, contract_instance, owner):
tx = contract_instance.functionWithCalldata(calldata, sender=owner)
assert not tx.failed
assert HexBytes(expected) in tx.data

0 comments on commit d97fff0

Please sign in to comment.