Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vyper Interfaces Verification #334

Open
PatrickAlphaC opened this issue Nov 27, 2024 · 23 comments
Open

Vyper Interfaces Verification #334

PatrickAlphaC opened this issue Nov 27, 2024 · 23 comments
Assignees
Labels
bug Something isn't working

Comments

@PatrickAlphaC
Copy link

🐛 Bug Report

If a smart contract in vyper uses an interface, the ZKsync explorer will throw an error.

{'status': 'failed', 'error': 'zkvyper error: ./etc/vyper-bin/0.4.0/vyper error: vyper.exceptions.ModuleNotFound: interfaces.IERC20Permit\n\n  contract "/tmp/.tmp9OzJyE/lib/pypi/snekmate/tokens/erc20.vy:71", line 71:0 \n       70 # syntax.\n  ---> 71 import interfaces.IERC20Permit as IERC20Permit\n  --------^\n       72 implements: IERC20Permit\n\n[30563] Failed to execute script \'vyper_compile\' due to unhandled exception!\n'}

However, zkvyper compiles successfully. This is because the explorer verification process is not correctly understanding the python paths.

🔄 Reproduction Steps

  1. Download moccasin

Easiest way is to download uv, then download mox

curl -LsSf https://astral.sh/uv/install.sh | sh # downloads uv 
uv tool install moccasin # downloads moccasin/mox 

Make sure you have zkvyper and era_test_node also installed

  1. Create a new moccasin project
mkdir verify_erc20
cd verify_erc20
mox init
  1. Add erc20 details

First, install snekmate with:

mox install snekmate

Then create a file called snek_token.vy and add:

# pragma version 0.4.0
"""
@license MIT
@title snek_token
@author You!
@notice This is my ERC20 token!
"""

# ------------------------------------------------------------------
#                             IMPORTS
# ------------------------------------------------------------------
from ethereum.ercs import IERC20

implements: IERC20
from ethereum.ercs import IERC20Detailed

implements: IERC20Detailed
from snekmate.auth import ownable as ow

# from pcaversaccio.snekmate.src.snekmate.auth import ownable as ow

initializes: ow
from snekmate.tokens import erc20

initializes: erc20[ownable := ow]

exports: erc20.__interface__

# ------------------------------------------------------------------
#                         STATE VARIABLES
# ------------------------------------------------------------------
# Constants & Immutables
NAME: constant(String[25]) = "snek_token"
SYMBOL: constant(String[5]) = "SNEK"
DECIMALS: constant(uint8) = 18
EIP712_VERSOIN: constant(String[20]) = "1"

# Storage - none!

# ------------------------------------------------------------------
#                            FUNCTIONS
# ------------------------------------------------------------------
@deploy
def __init__(initial_supply: uint256):
    ow.__init__()
    erc20.__init__(NAME, SYMBOL, DECIMALS, NAME, EIP712_VERSOIN)
    erc20._mint(msg.sender, initial_supply)
  1. Add zksync network details

Add the following to your moccasin.toml

[networks.zksync]
url = "$ZKSYNC_RPC_URL"
is_zksync = true
explorer_uri = "https://zksync2-mainnet-explorer.zksync.io"
explorer_type = "zksyncexplorer"
prompt_live = false

Where ZKSYNC_RPC_URL is your zksync mainnet RPC URL.

  1. Create a file called verify.py in your script folder with this code:
from eth_utils import to_wei, to_bytes
from moccasin.boa_tools import VyperContract
from moccasin.config import get_active_network

from src import snek_token


def moccasin_main():
    contract_addy = "0x60074aD7A8e789d4fDeebB40Eb04B5000d6fE6eE"
    snek = snek_token.at(contract_addy)
    snek.constructor_calldata = to_bytes(
        hexstr="0x00000000000000000000000000000000000000000000003635c9adc5dea00000"
    )

    active_network = get_active_network()
    result = active_network.moccasin_verify(snek)
    result.wait_for_verification()
  1. Run it
mox run verify --network zksync

You'll get the error. This is an error on the explorer's side. This contract should be able to be installed.

@PatrickAlphaC PatrickAlphaC added the bug Something isn't working label Nov 27, 2024
@PatrickAlphaC
Copy link
Author

Or, more basically, the contract on zksync era mainnet at address: 0x60074aD7A8e789d4fDeebB40Eb04B5000d6fE6eE should verify. The code is here:

# pragma version 0.4.0
"""
@license MIT
@title snek_token
@author You!
@notice This is my ERC20 token!
"""

# ------------------------------------------------------------------
#                             IMPORTS
# ------------------------------------------------------------------
from ethereum.ercs import IERC20

implements: IERC20
from ethereum.ercs import IERC20Detailed

implements: IERC20Detailed
from snekmate.auth import ownable as ow

# from pcaversaccio.snekmate.src.snekmate.auth import ownable as ow

initializes: ow
from snekmate.tokens import erc20

initializes: erc20[ownable := ow]

exports: erc20.__interface__

# ------------------------------------------------------------------
#                         STATE VARIABLES
# ------------------------------------------------------------------
# Constants & Immutables
NAME: constant(String[25]) = "snek_token"
SYMBOL: constant(String[5]) = "SNEK"
DECIMALS: constant(uint8) = 18
EIP712_VERSOIN: constant(String[20]) = "1"

# Storage - none!

# ------------------------------------------------------------------
#                            FUNCTIONS
# ------------------------------------------------------------------
@deploy
def __init__(initial_supply: uint256):
    ow.__init__()
    erc20.__init__(NAME, SYMBOL, DECIMALS, NAME, EIP712_VERSOIN)
    erc20._mint(msg.sender, initial_supply)

This needs the snekmate package. I am successfully able to get my local zkvyper to compile it.

@popzxc
Copy link
Member

popzxc commented Nov 28, 2024

Hey @PatrickAlphaC

It feels like not all the files are being included into the compilation request. Could you please check what network request is being sent to the explorer? Does it include all the dependencies?
Also, if you try to verify the same contract with hardhat-zksync-verify-vyper, does it succeed or fail with the same error?

@PatrickAlphaC
Copy link
Author

@popzxc I think the issue is that the PATH that the zkvyper uses on the server is different from the users PATH.

Let me see if it makes sense to fix on the user's end...

@dimazhornyk
Copy link

@PatrickAlphaC hey, did you manage to fix this on the user's side?

@PatrickAlphaC
Copy link
Author

@dimazhornyk no. Here is where we are.

The (truncated) body of a request (for example, for this address live on ZKsync: 0x36266F07E40aeaFD5FBeaB736f85d2f1F1B4Bb06)

{
  "contractAddress": "0x36266F07E40aeaFD5FBeaB736f85d2f1F1B4Bb06",
  "sourceCode": {
    "lib/pypi/snekmate/tokens/interfaces/IERC721Metadata.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/tokens/interfaces/IERC721Enumerable.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/tokens/interfaces/IERC721Permit.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/tokens/interfaces/IERC4906.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/utils/interfaces/IERC5267.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/tokens/interfaces/IERC721Receiver.vyi": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/auth/ownable.vy": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/utils/ecdsa.vy": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/utils/message_hash_utils.vy": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/utils/eip712_domain_separator.vy": "# pragma version ~=0.4.0\n\"\"\"...",
    "lib/pypi/snekmate/tokens/erc721.vy": "# pragma version ~=0.4.0\n\"\"\"...",
    "birthday_coffee": "# pragma version 0.4.0\n\"\"\"...\""
  },
  "codeFormat": "vyper-multi-file",
  "contractName": "birthday_coffee",
  "compilerVyperVersion": "0.4.0",
  "compilerZkvyperVersion": "v1.5.7",
  "constructorArguments": "0x",
  "optimizationUsed": true
}

However, Vyper uses the Python Path to identify contracts. So instead of:

from lib.pypi.snekmate.tokens.interfaces import IERC721Metadata

We can just do:

from snekmate.tokens.interfaces import IERC721Metadata

Here are the options we can do to fix this:

  1. The user rewrites all their imports to be the exact path in the folders, instead of the python path
  2. The ZKsync explorer adds a field for python path

I think 1 is probably (sadly) the correct answer... So we'll have to do some work to do this automatically for users...

@PatrickAlphaC
Copy link
Author

Because otherwise you'll get:

ValueError: Verification failed: zkvyper error: ./etc/vyper-bin/0.4.0/vyper error: vyper.exceptions.ModuleNotFound: snekmate.tokens.erc721

  contract "/tmp/.tmp7HTS1k/birthday_coffee.vy:7", line 7:0 
       6
  ---> 7 from snekmate.tokens import erc721
  -------^
       8 from snekmate.auth import ownable as ow

[64919] Failed to execute script 'vyper_compile' due to unhandled exception!

@charles-cooper
Copy link

i think the issue here is that the search path (see "search_paths" key here: https://docs.vyperlang.org/en/latest/compiling-a-contract.html#input-json-description) is not being included. @dimazhornyk @popzxc what is the correct way to include the settings key in the json bundle?

@popzxc
Copy link
Member

popzxc commented Dec 4, 2024

@charles-cooper right now this setting is not passed, the list of files is just passed in the "sources" field (see the code).
I'm not sure if adding search_paths can be easily done, since (I assume) this field must have absoulte paths, while on the server everything is performed in temporary folders.

I see a few ways around it:

  • Add $base_dir/lib/pypi to the search_paths on the server in case there are contracts with lib/pypi in the sources. This will be a heuristic, but I assume it will work for most cases?
  • Add a configuration option to specify search_paths in verification request -- but it will likely require pre-processing on the plugin side, since you will need to make them relative for it to work.

WDYT?

@charles-cooper
Copy link

@charles-cooper right now this setting is not passed, the list of files is just passed in the "sources" field (see the code). I'm not sure if adding search_paths can be easily done, since (I assume) this field must have absoulte paths, while on the server everything is performed in temporary folders.

I see a few ways around it:

  • Add $base_dir/lib/pypi to the search_paths on the server in case there are contracts with lib/pypi in the sources. This will be a heuristic, but I assume it will work for most cases?
  • Add a configuration option to specify search_paths in verification request -- but it will likely require pre-processing on the plugin side, since you will need to make them relative for it to work.

WDYT?

i'm not sure how the verifier is calling vyper, but if it's passing standard json, then everything should "just work" so long as search_paths is correctly added to the standard json bundle

@charles-cooper
Copy link

'm not sure if adding search_paths can be easily done, since (I assume) this field must have absoulte paths, while on the server everything is performed in temporary folders.

Btw, the search paths do not need to be absolute.

@popzxc
Copy link
Member

popzxc commented Dec 6, 2024

I will try to implement the first proposal next week and let's see if it works.

@popzxc popzxc self-assigned this Dec 6, 2024
@charles-cooper
Copy link

I will try to implement the first proposal next week and let's see if it works.

I think it will work for a specific case, but the proper thing to do is to allow the settings to be properly set in the input bundle.

@popzxc
Copy link
Member

popzxc commented Dec 6, 2024

OK, I investigated it further, and looks like unfortunately this issue is not actionable right now 😓
zkvyper only supports combined JSON, so you cannot pass search_paths there at all.

There are some experiments with standard JSON going on there, but it's hard to say when it will reach production support.
For now, I guess, the only workaround is not to rely on the search_paths.

@charles-cooper
Copy link

OK, I investigated it further, and looks like unfortunately this issue is not actionable right now 😓 zkvyper only supports combined JSON, so you cannot pass search_paths there at all.

There are some experiments with standard JSON going on there, but it's hard to say when it will reach production support. For now, I guess, the only workaround is not to rely on the search_paths.

search paths can be set on the command line for combined_json with the -p flag

cc @hedgar2017

@hedgar2017
Copy link

Thanks @charles-cooper.
We can add its propagation to zkvyper v1.5.8 which is expected in a couple of days.

@popzxc, but full migration to standard JSON can only be done starting from the Hardhat plugin. Right now it relies on combined JSON output. The optimal path would be to test the plugin with vyper first.

@popzxc
Copy link
Member

popzxc commented Dec 6, 2024

I guess if -p works (I wasn't aware of it), we can live with combined JSON for a bit longer.

@hedgar2017
Copy link

I guess if -p works (I wasn't aware of it), we can live with combined JSON for a bit longer.

I will try now.

@charles-cooper is it safe to use with all versions of vyper?

@charles-cooper
Copy link

I guess if -p works (I wasn't aware of it), we can live with combined JSON for a bit longer.

I will try now.

@charles-cooper is it safe to use with all versions of vyper?

it is, but it has different semantics for < 0.4.0 (for <0.4.0, it is not as important, because prior to 0.4.0 multi-file contracts are not supported).

i would recommend guarding the feature to only apply for >=0.4.0, or more simply just propagate it to the compiler and let the compiler raise an error if the semantics are invalid, as the user would not really be setting search paths prior to 0.4.0.

@hedgar2017
Copy link

hedgar2017 commented Dec 6, 2024

@charles-cooper thanks, understood!

Could you also drop a quick comment on the status of combined JSON mode? I cannot find enough info on it in the docs.
Is it being deprecated in favor of standard JSON?

@charles-cooper
Copy link

@charles-cooper thanks, understood!

Could you also drop a quick comment on the status of combined JSON mode? I cannot find enough info on it in the docs? Is it being deprecated in favor of standard JSON?

it is not being deprecated, it is just the "json output" mode for standard cli so that the output is more structured than just text.

@hedgar2017
Copy link

hedgar2017 commented Dec 7, 2024

Hey @PatrickAlphaC @popzxc - could you try this build?
The paths can be added as zkvyper ... --search-paths <path1> <path2> ... <pathN>.

@PatrickAlphaC
Copy link
Author

@hedgar2017 so this will be passed to the explorer server? This fixes verification? I’m a bit confused

@hedgar2017
Copy link

@PatrickAlphaC sorry for the confusion!
We've just released the compiler part. @popzxc will look into the explorer part soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants