Interface type returns implicitly but not explicitly type `address` at assignment
Version Information
- vyper Version (output of
vyper --version):0.3.7+commit.6020b8b - OS: linux
- Python Version (output of
python --version):3.9.2
What's your issue about?
Interface type returns implicitly but not explicitly type address at assignment.
Let me explain what I exactly mean. Let's start with the following snippet (let's call it contract.vy):
# @version 0.3.7
from vyper.interfaces import ERC20
asset: public(ERC20)
@external
def __init__(asset_: ERC20):
self.asset = asset_
Using Titanoboa, we can simply do the following:
>>> import boa
>>> contract = boa.load("./contract.vy", "0xdAC17F958D2ee523a2206206994597C13D831ec7")
>>> contract.asset()
'0xdac17f958d2ee523a2206206994597c13d831ec7'
So the compiler returns implicitly the result of asset_.address at construction time. However, the compiler will throw with vyper.exceptions.TypeMismatch: Given reference has type ERC20, expected address if I make an explicit address type assignment:
# @version 0.3.7
from vyper.interfaces import ERC20
asset: public(address)
@external
def __init__(asset_: ERC20):
self.asset = asset_
Ok, so far so good, but if I add a conversion to address it will throw with vyper.exceptions.TypeMismatch: Can't convert address to address
# @version 0.3.7
from vyper.interfaces import ERC20
asset: public(address)
@external
def __init__(asset_: ERC20):
self.asset = convert(asset_, address)
So under the hood, the compiler already assigns the type address but does still not allow for an explicit address assignment, but requires the type ERC20 (or fix it via asset_.address). At least for me the feels kinda inconsistent.
How can it be fixed?
Allow for the following and the compiler automatically handles the assignment of asset_.address:
# @version 0.3.7
from vyper.interfaces import ERC20
asset: public(address)
@external
def __init__(asset_: ERC20):
self.asset = asset_
Another example, where this behavior would be useful:
@external
def example_fn(asset_: ERC20):
success: bool = empty(bool)
return_data: Bytes[max_value(uint8)] = b""
success, return_data = raw_call(asset_, ...)
To fix it currently, I must use .address again:
@external
def example_fn(asset_: ERC20):
success: bool = empty(bool)
return_data: Bytes[max_value(uint8)] = b""
success, return_data = raw_call(asset_.address, ...)
sorry i think i forgot to comment on this -- i think the issue here is the error message is confusing, but there are not implicit type conversions going on. there is no convert() role between interfaces and addresses, you just use <some contract>.address
sorry i think i forgot to comment on this -- i think the issue here is the error message is confusing, but there are not implicit type conversions going on. there is no
convert()role between interfaces and addresses, you just use<some contract>.address
as per offline discussion, it's worth adding that the Vyper convention in the ABI encoding is to represent an interface as address. We should get the confusing error message fixed.