vyper icon indicating copy to clipboard operation
vyper copied to clipboard

Importing interfaces not as docs describe

Open PatrickAlphaC opened this issue 2 years ago • 3 comments

Version Information

  • vyper Version (output of vyper --version): 0.3.3
  • OS: osx
  • Python Version (output of python --version): 3.9.5

What's your issue about?

Please include information like:

  • full output of the error you received
  • what command you ran
  • the code that caused the failure (see this link for help with formatting code)
  • please try running your example with the --debug flag turned on

I have an interface that looks like such:

# @version ^0.3.3
interface IFace:
    def read_stuff() -> uint256: view

And a contract that looks as such:

# @version ^0.3.3
from interfaces.IFace import IFace

@external
@view
def read_contract(some_address: address) -> uint256:
    myContract: IFace = IFace(some_address)
    return myContract.read_stuff()

However, when I run vyper contracts/MyContract.vy I get:

Error compiling: contracts/MyContract.vy
vyper.exceptions.UnknownAttribute: IFace has no member 'read_stuff'. 
  contract "contracts/MyContract.vy", function "read_contract", line 11:11 
       10     myContract: IFace = IFace(some_address)
  ---> 11     return myContract.read_stuff()
  -------------------^
       12

According to the documentation, when importing interfaces, you need to use regular vyper syntax. However, adjusting my interface as such:

# @version ^0.3.3

interface IFace:
    @external
    def read_stuff() -> uint256: 
        pass

Results in this error:

vyper.exceptions.StructureException: Body must only contain state mutability label
  contract "contracts/interfaces/IFace.vy", function "read_stuff", line 6:8 
       5     def read_stuff() -> uint256:
  ---> 6         pass
  ---------------^
       7

How can it be fixed?

I'm not sure

PatrickAlphaC avatar Jun 07 '22 20:06 PatrickAlphaC

So, you can't import types from other Vyper contracts. What the docs mean by "regular vyper syntax" is that your file structure and internals should look like this:

interfaces/IFace.vy:

@view
@external
def read_stuff() -> uint256: 
    pass

Contract.vy:

# @version ^0.3.3
import interfaces.IFace as IFace

@view
@external
def read_contract(some_address: address) -> uint256:
    myContract: IFace = IFace(some_address)
    return myContract.read_stuff()

fubuloubu avatar Jun 07 '22 20:06 fubuloubu

Got it. We should probably update the docs to be more clear - been slamming my head.

PatrickAlphaC avatar Jun 07 '22 20:06 PatrickAlphaC

There's also a longer term fix with libraries that is coming in https://github.com/vyperlang/vyper/issues/2431

fubuloubu avatar Jun 07 '22 20:06 fubuloubu

I'm still so confused by this. I have this one file named contract1.vy

@external
def interface_func():
    pass

And then this other file named contract2.vy

import contract1 as Contract1

@external
def main_func(_to_call: address):
    other_contract: Contract1 = Contract1(_to_call)
    to_call.interface_func()

However, compilation of contract2.vy fails with

vyper.exceptions.UndeclaredDefinition: Unknown interface: Contract1. Did you mean 'ERC20Detailed', or maybe 'ERC721'?
            line 1:0
             ---> 1 import contract1 as Contract1
             -------^

kaimast avatar Feb 12 '23 20:02 kaimast

Try this

import .contract1 as Contract1

fubuloubu avatar Feb 12 '23 21:02 fubuloubu

I tried this before, but it yields in a syntax error:

vyper.exceptions.SyntaxException: invalid syntax (<unknown>, line 1)
line 1:8
---> 1 import .contract1 as Contract1
---------------^

I also tried putting a separate interface in a subfolder as people mentioned before, but that did not seem to work either.

kaimast avatar Feb 12 '23 21:02 kaimast

this should be closed by the language redesign done in #3663 and #3725. there are still two ways of importing interfaces, via importing .vyi interface files and also by interface Foo: declarations. however, the issue at hand should be fixed, see the following example works:

# ifaces.vy
interface IFoo:
    def foo(): nonpayable
# contract.vy
from ifaces import foo

@external
def call_foo():
    foo.IFoo(msg.sender).foo()

honestly now that you can import types from other modules, we could remove the .vyi file machinery entirely in favor of the in-line interface declarations. i'm going to close this issue for now as soon as #3725 is merged though.

charles-cooper avatar Jan 12 '24 14:01 charles-cooper