Skip to content

Commit

Permalink
feat[lang]: rename _abi_encode and _abi_decode (vyperlang#4097)
Browse files Browse the repository at this point in the history
rename to `abi_encode` and `abi_decode` respectively
leave the old ones in, but with deprecation warnings
  • Loading branch information
charles-cooper committed Jun 11, 2024
1 parent 4d1bacd commit 7c8862a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 48 deletions.
18 changes: 12 additions & 6 deletions docs/built-in-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui
x: uint256 = 123
success, response = raw_call(
_target,
_abi_encode(x, method_id=method_id("someMethodName(uint256)")),
abi_encode(x, method_id=method_id("someMethodName(uint256)")),
max_outsize=32,
value=msg.value,
revert_on_failure=False
Expand Down Expand Up @@ -1023,7 +1023,7 @@ Utilities
>>> ExampleContract.foo()
0xa9059cbb

.. py:function:: _abi_encode(*args, ensure_tuple: bool = True) -> Bytes[<depends on input>]
.. py:function:: abi_encode(*args, ensure_tuple: bool = True) -> Bytes[<depends on input>]
Takes a variable number of args as input, and returns the ABIv2-encoded bytestring. Used for packing arguments to raw_call, EIP712 and other cases where a consistent and efficient serialization method is needed.
Once this function has seen more use we provisionally plan to put it into the ``ethereum.abi`` namespace.
Expand All @@ -1041,7 +1041,7 @@ Utilities
def foo() -> Bytes[132]:
x: uint256 = 1
y: Bytes[32] = b"234"
return _abi_encode(x, y, method_id=method_id("foo()"))
return abi_encode(x, y, method_id=method_id("foo()"))
.. code-block:: vyper
Expand All @@ -1052,15 +1052,18 @@ Utilities
"0000000000000000000000000000000000000000000000000000000000000003"
"3233340000000000000000000000000000000000000000000000000000000000"
.. note::
Prior to v0.4.0, this function was named ``_abi_encode``.


.. py:function:: _abi_decode(b: Bytes, output_type: type_, unwrap_tuple: bool = True) -> Any
.. py:function:: abi_decode(b: Bytes, output_type: type_, unwrap_tuple: bool = True) -> Any
Takes a byte array as input, and returns the decoded values according to the specified output types. Used for unpacking ABIv2-encoded values.
Once this function has seen more use we provisionally plan to put it into the ``ethereum.abi`` namespace.

* ``b``: A byte array of a length that is between the minimum and maximum ABIv2 size bounds of the ``output type``.
* ``output_type``: Name of the output type, or tuple of output types, to be decoded.
* ``unwrap_tuple``: If set to True, the input is decoded as a tuple even if only one output type is specified. In other words, ``_abi_decode(b, Bytes[32])`` gets decoded as ``(Bytes[32],)``. This is the convention for ABIv2-encoded values generated by Vyper and Solidity functions. Except for very specific use cases, this should be set to True. Must be a literal.
* ``unwrap_tuple``: If set to True, the input is decoded as a tuple even if only one output type is specified. In other words, ``abi_decode(b, Bytes[32])`` gets decoded as ``(Bytes[32],)``. This is the convention for ABIv2-encoded values generated by Vyper and Solidity functions. Except for very specific use cases, this should be set to True. Must be a literal.

Returns the decoded value(s), with type as specified by `output_type`.

Expand All @@ -1071,9 +1074,12 @@ Utilities
def foo(someInput: Bytes[128]) -> (uint256, Bytes[32]):
x: uint256 = empty(uint256)
y: Bytes[32] = empty(Bytes[32])
x, y = _abi_decode(someInput, (uint256, Bytes[32]))
x, y = abi_decode(someInput, (uint256, Bytes[32]))
return x, y
.. note::
Prior to v0.4.0, this function was named ``_abi_decode``.


.. py:function:: print(*args, hardhat_compat=False) -> None
Expand Down
12 changes: 6 additions & 6 deletions tests/functional/builtins/codegen/test_abi_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def abi_decode(x: Bytes[160]) -> (address, int128, bool, decimal, bytes32):
c: bool = False
d: decimal = 0.0
e: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
a, b, c, d, e = _abi_decode(x, (address, int128, bool, decimal, bytes32))
a, b, c, d, e = abi_decode(x, (address, int128, bool, decimal, bytes32))
return a, b, c, d, e
@external
Expand All @@ -48,7 +48,7 @@ def abi_decode_struct(x: Bytes[544]) -> Human:
metadata=0x0000000000000000000000000000000000000000000000000000000000000000
)
)
human = _abi_decode(x, Human)
human = abi_decode(x, Human)
return human
"""

Expand Down Expand Up @@ -97,7 +97,7 @@ def test_abi_decode_single(get_contract, expected, input_len, output_typ, abi_ty
contract = f"""
@external
def foo(x: Bytes[{input_len}]) -> {output_typ}:
a: {output_typ} = _abi_decode(x, {output_typ}, unwrap_tuple={unwrap_tuple})
a: {output_typ} = abi_decode(x, {output_typ}, unwrap_tuple={unwrap_tuple})
return a
"""
c = get_contract(contract)
Expand Down Expand Up @@ -135,7 +135,7 @@ def test_abi_decode_double(
def foo(x: Bytes[{input_len}]) -> ({output_typ1}, {output_typ2}):
a: {output_typ1} = empty({output_typ1})
b: {output_typ2} = empty({output_typ2})
a, b = _abi_decode(x, ({output_typ1}, {output_typ2}), unwrap_tuple={unwrap_tuple})
a, b = abi_decode(x, ({output_typ1}, {output_typ2}), unwrap_tuple={unwrap_tuple})
return a, b
"""

Expand Down Expand Up @@ -173,7 +173,7 @@ def test_abi_decode_nested_dynarray(get_contract, args, unwrap_tuple):
@external
def abi_decode(x: Bytes[{len}]) -> DynArray[DynArray[uint256, 3], 3]:
a: DynArray[DynArray[uint256, 3], 3] = []
a = _abi_decode(x, DynArray[DynArray[uint256, 3], 3], unwrap_tuple={unwrap_tuple})
a = abi_decode(x, DynArray[DynArray[uint256, 3], 3], unwrap_tuple={unwrap_tuple})
return a
"""

Expand Down Expand Up @@ -213,7 +213,7 @@ def test_abi_decode_nested_dynarray2(get_contract, args, unwrap_tuple):
@external
def abi_decode(x: Bytes[{len}]) -> DynArray[DynArray[DynArray[uint256, 3], 3], 3]:
a: DynArray[DynArray[DynArray[uint256, 3], 3], 3] = []
a = _abi_decode(
a = abi_decode(
x,
DynArray[DynArray[DynArray[uint256, 3], 3], 3],
unwrap_tuple={unwrap_tuple}
Expand Down
62 changes: 31 additions & 31 deletions tests/functional/builtins/codegen/test_abi_encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,38 +48,38 @@ def abi_encode(
)
if ensure_tuple:
if not include_method_id:
return _abi_encode(human) # default ensure_tuple=True
return _abi_encode(human, method_id=0xdeadbeef)
return abi_encode(human) # default ensure_tuple=True
return abi_encode(human, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(human, ensure_tuple=False)
return _abi_encode(human, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(human, ensure_tuple=False)
return abi_encode(human, ensure_tuple=False, method_id=0xdeadbeef)
@external
def abi_encode2(name: String[32], ensure_tuple: bool, include_method_id: bool) -> Bytes[100]:
if ensure_tuple:
if not include_method_id:
return _abi_encode(name) # default ensure_tuple=True
return _abi_encode(name, method_id=0xdeadbeef)
return abi_encode(name) # default ensure_tuple=True
return abi_encode(name, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(name, ensure_tuple=False)
return _abi_encode(name, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(name, ensure_tuple=False)
return abi_encode(name, ensure_tuple=False, method_id=0xdeadbeef)
@external
def abi_encode3(x: uint256, ensure_tuple: bool, include_method_id: bool) -> Bytes[36]:
if ensure_tuple:
if not include_method_id:
return _abi_encode(x) # default ensure_tuple=True
return abi_encode(x) # default ensure_tuple=True
return _abi_encode(x, method_id=0xdeadbeef)
return abi_encode(x, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(x, ensure_tuple=False)
return abi_encode(x, ensure_tuple=False)
return _abi_encode(x, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(x, ensure_tuple=False, method_id=0xdeadbeef)
"""
c = get_contract(code)

Expand Down Expand Up @@ -129,7 +129,7 @@ def test_abi_encode_length_failing(get_contract, assert_compile_failed, type, va
@internal
def foo():
x: WrappedBytes = WrappedBytes(bs={value})
y: {type}[96] = _abi_encode(x, ensure_tuple=True) # should be Bytes[128]
y: {type}[96] = abi_encode(x, ensure_tuple=True) # should be Bytes[128]
"""

assert_compile_failed(lambda: get_contract(code))
Expand All @@ -141,12 +141,12 @@ def test_abi_encode_dynarray(get_contract):
def abi_encode(d: DynArray[uint256, 3], ensure_tuple: bool, include_method_id: bool) -> Bytes[164]:
if ensure_tuple:
if not include_method_id:
return _abi_encode(d) # default ensure_tuple=True
return _abi_encode(d, method_id=0xdeadbeef)
return abi_encode(d) # default ensure_tuple=True
return abi_encode(d, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(d, ensure_tuple=False)
return _abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(d, ensure_tuple=False)
return abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
"""
c = get_contract(code)

Expand Down Expand Up @@ -185,12 +185,12 @@ def abi_encode(
) -> Bytes[548]:
if ensure_tuple:
if not include_method_id:
return _abi_encode(d) # default ensure_tuple=True
return _abi_encode(d, method_id=0xdeadbeef)
return abi_encode(d) # default ensure_tuple=True
return abi_encode(d, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(d, ensure_tuple=False)
return _abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(d, ensure_tuple=False)
return abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
"""
c = get_contract(code)

Expand Down Expand Up @@ -236,12 +236,12 @@ def abi_encode(
) -> Bytes[1700]:
if ensure_tuple:
if not include_method_id:
return _abi_encode(d) # default ensure_tuple=True
return _abi_encode(d, method_id=0xdeadbeef)
return abi_encode(d) # default ensure_tuple=True
return abi_encode(d, method_id=0xdeadbeef)
else:
if not include_method_id:
return _abi_encode(d, ensure_tuple=False)
return _abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
return abi_encode(d, ensure_tuple=False)
return abi_encode(d, ensure_tuple=False, method_id=0xdeadbeef)
"""
c = get_contract(code)

Expand Down Expand Up @@ -281,7 +281,7 @@ def get_counter() -> (uint256, String[6]): nonpayable
@external
def foo(addr: address) -> Bytes[164]:
return _abi_encode(extcall Foo(addr).get_counter(), method_id=0xdeadbeef)
return abi_encode(extcall Foo(addr).get_counter(), method_id=0xdeadbeef)
"""

c2 = get_contract(contract_2)
Expand All @@ -300,7 +300,7 @@ def test_abi_encode_private(get_contract):
bytez: Bytes[96]
@internal
def _foo(bs: Bytes[32]):
self.bytez = _abi_encode(bs)
self.bytez = abi_encode(bs)
@external
def foo(bs: Bytes[32]) -> (uint256, Bytes[96]):
Expand All @@ -318,7 +318,7 @@ def test_abi_encode_private_dynarray(get_contract):
bytez: Bytes[160]
@internal
def _foo(bs: DynArray[uint256, 3]):
self.bytez = _abi_encode(bs)
self.bytez = abi_encode(bs)
@external
def foo(bs: DynArray[uint256, 3]) -> (uint256, Bytes[160]):
dont_clobber_me: uint256 = max_value(uint256)
Expand All @@ -335,7 +335,7 @@ def test_abi_encode_private_nested_dynarray(get_contract):
bytez: Bytes[1696]
@internal
def _foo(bs: DynArray[DynArray[DynArray[uint256, 3], 3], 3]):
self.bytez = _abi_encode(bs)
self.bytez = abi_encode(bs)
@external
def foo(bs: DynArray[DynArray[DynArray[uint256, 3], 3], 3]) -> (uint256, Bytes[1696]):
Expand All @@ -358,9 +358,9 @@ def test_abi_encode_empty_string(get_contract, empty_literal):
@external
def foo(ensure_tuple: bool) -> Bytes[96]:
if ensure_tuple:
return _abi_encode({empty_literal}) # default ensure_tuple=True
return abi_encode({empty_literal}) # default ensure_tuple=True
else:
return _abi_encode({empty_literal}, ensure_tuple=False)
return abi_encode({empty_literal}, ensure_tuple=False)
"""

c = get_contract(code)
Expand Down
2 changes: 1 addition & 1 deletion vyper/ast/grammar.lark
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ call: atom_expr "(" [arguments] ")"
empty: "empty" "(" type ")"

// special rule to handle types as "arguments" (for `_abi_decode` builtin)
abi_decode: "_abi_decode" "(" arg "," type ( "," kwarg )* ")"
abi_decode: ("_abi_decode" | "abi_decode") "(" arg "," type ( "," kwarg )* ")"

special_builtins: empty | abi_decode

Expand Down
32 changes: 28 additions & 4 deletions vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2331,7 +2331,7 @@ def build_IR(self, expr, args, kwargs, context):


class ABIEncode(BuiltinFunctionT):
_id = "_abi_encode" # TODO prettier to rename this to abi.encode
_id = "abi_encode"
# signature: *, ensure_tuple=<literal_bool> -> Bytes[<calculated len>]
# explanation of ensure_tuple:
# default is to force even a single value into a tuple,
Expand Down Expand Up @@ -2452,7 +2452,7 @@ def build_IR(self, expr, args, kwargs, context):


class ABIDecode(BuiltinFunctionT):
_id = "_abi_decode"
_id = "abi_decode"
_inputs = [("data", BytesT.any()), ("output_type", TYPE_T.any())]
_kwargs = {"unwrap_tuple": KwargSettings(BoolT(), True, require_literal=True)}

Expand Down Expand Up @@ -2541,6 +2541,28 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(ret)


class OldABIEncode(ABIEncode):
_warned = False
_id = "_abi_encode"

def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn(f"`{self._id}()` is deprecated! Please use `{super()._id}()` instead.", node)
self.__class__._warned = True
super()._try_fold(node)


class OldABIDecode(ABIDecode):
_warned = False
_id = "_abi_decode"

def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn(f"`{self._id}()` is deprecated! Please use `{super()._id}()` instead.", node)
self.__class__._warned = True
super()._try_fold(node)


class _MinMaxValue(TypenameFoldedFunctionT):
def _try_fold(self, node):
self._validate_arg_types(node)
Expand Down Expand Up @@ -2593,8 +2615,10 @@ def _try_fold(self, node):


DISPATCH_TABLE = {
"_abi_encode": ABIEncode(),
"_abi_decode": ABIDecode(),
"abi_encode": ABIEncode(),
"abi_decode": ABIDecode(),
"_abi_encode": OldABIEncode(),
"_abi_decode": OldABIDecode(),
"floor": Floor(),
"ceil": Ceil(),
"convert": Convert(),
Expand Down

0 comments on commit 7c8862a

Please sign in to comment.