eth-brownie / brownie

A Python-based development and testing framework for smart contracts targeting the Ethereum Virtual Machine.
https://eth-brownie.readthedocs.io
MIT License
2.64k stars 553 forks source link

compiler remappings fails if same name path #893

Open eyooooo opened 3 years ago

eyooooo commented 3 years ago

Environment information

What was wrong?

  1. forked ESD
  2. init new brownie dir
  3. moved contracts
  4. ESD uses truffle npm style imports so need to perform remappings
  5. install packages: brownie pm install OpenZeppelin/openzeppelin-contracts@2.5.1 brownie pm install uniswap/uniswap-v2-core@1.0.1 brownie pm install uniswap/uniswap-lib@2.1.0
  6. set remappings:
        remappings: 
            - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@2.5.1"
            - "@uniswap/lib=uniswap/uniswap-lib@1.1.2"
            - "@uniswap/v2-core=uniswap/uniswap-v2-core@1.0.1"
    1. error on compile
      contracts/oracle/Oracle.sol:21:1: ParserError: Source "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol" not found: File outside of allowed directories.
      import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';

If I flip the two uniswap remappings, the 2nd one always fails. eg)

        remappings: 
            - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@2.5.1"
            - "@uniswap/v2-core=uniswap/uniswap-v2-core@1.0.1"
            - "@uniswap/lib=uniswap/uniswap-lib@1.1.2"

will produce errors

contracts/mock/MockUniswapV2PairTrade.sol:20:1: ParserError: Source "uniswap/uniswap-lib@1.1.2/contracts/libraries/FixedPoint.sol" not found: File not found.

How can it be fixed?

regex? it appears the remapping ignores the info after the / so uniswap/lib and uniswap/v2-core are just treated identically. the 2nd entry will always fail.

eyooooo commented 3 years ago

the issue is in /usr/local/lib/python3.6/dist-packages/brownie/project/compiler/__init__.py in func _get_solc_remappings. im seeing if i can patch it.

eyooooo commented 3 years ago

This is a hacky workaround

def _get_solc_remappings(remappings: Optional[list]) -> list:
    if remappings is None:
        remap_dict: Dict = {}
    elif isinstance(remappings, str):
        remap_dict = dict([remappings.split("=")])
    else:
        remap_dict = dict(i.split("=") for i in remappings)

    #hacky crap here
    for path in _get_data_folder().joinpath("packages").iterdir():
        for k,v in remap_dict.items():
            remap_dict[k] = path.parent.joinpath(v).as_posix()
    #print([f"{k}={v}" for k, v in remap_dict.items()])       

    for path in _get_data_folder().joinpath("packages").iterdir():
        key = next((k for k, v in remap_dict.items() if v.startswith(path.name)), None) 
        if key:
            remap_dict[key] = path.parent.joinpath(remap_dict[key]).as_posix()
        else:
            remap_dict[path.name] = path.as_posix()

    return [f"{k}={v}" for k, v in remap_dict.items()]
iamdefinitelyahuman commented 3 years ago

Thanks for the workaround, I'll have a look here shortly.

For reference: https://docs.soliditylang.org/en/latest/using-the-compiler.html#path-remapping

Pavel-Blaize commented 3 years ago

I have reproduced the same bug. I think can formalize it in the next way:

For example I have:

    remappings:
      - "@openzeppelinV3=OpenZeppelin/openzeppelin-contracts@3.1.0"
      - "@ozUpgradesV3=OpenZeppelin/openzeppelin-contracts-upgradeable@3.3.0"

So both packages has the same OpenZeppelin/ root. One contract of mine imports Ownable from the regular package, and another contract imports OwnableUpgradeable from the upgradable package. After the compilation I get an error:

ParserError: Source "OpenZeppelin/openzeppelin-contracts-upgradeable@3.3.0/contracts/access/OwnableUpgradeable.sol" not found: File not found.
import "@ozUpgradesV3/contracts/access/OwnableUpgradeable.sol";
^-------------------------------------------------------------^

Issue reproduces with different order of packages, different imports, different truffle or solc versions. Brownie version 1.12.2

Though, issue does not reproduce, when I manually clone all the packages except the first (brownie pm clone "OpenZeppelin/openzeppelin-contracts-upgradeable@3.3.0").

utgarda commented 3 years ago

Totally reproducing:

compiler:
  solc:
    version: 0.8.3
    remappings:
      - "@openzeppelin-upgradeable=/home/username/.brownie/packages/OpenZeppelin/openzeppelin-contracts-upgradeable@4.1.0"
      - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.1.0"

the above works, but if you remap as just

compiler:
  solc:
    version: 0.8.3
    remappings:
      - "@openzeppelin-upgradeable=OpenZeppelin/openzeppelin-contracts-upgradeable@4.1.0"
      - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.1.0"

i.e. if 2 remappings have the same path prefix, it's compiler errors "File not found" all over the place, like

ParserError: Source "OpenZeppelin/openzeppelin-contracts@4.1.0/contracts/access/Ownable.sol" not found: File not found.
veloboot commented 2 years ago

This worked for me:

  - "@openzeppelin/contracts=OpenZeppelin/openzeppelin-contracts@3.4.2/contracts"
  - "@openzeppelin/contracts-upgradeable=OpenZeppelin/openzeppelin-contracts-upgradeable@3.4.2/contracts"