Porównaj commity

...

2 Commity

Autor SHA1 Wiadomość Data
Neeraj Kashyap 05274f355d
Merge pull request #121 from moonstream-to/foundry-projects
Generate Brownie interface for Foundry project
2023-11-08 12:12:17 -08:00
Neeraj Kashyap ec3e5bccb7 Generate Brownie interface for Foundry project
It is now possible to call `moonworm generate-brownie` with the
`--foundry` argument.

If it is called this way, the Brownie interface is generated with
reference to a Foundry project.

Rough around the edges, and may not play nice with `--prod`, which is
anyway not working well for deployments (even before the addition of
`--foundry`).

The same scheme we use to remap the build object for `--foundry` should
be generalized across both `--foundry` and `--prod`.
2023-11-07 11:14:26 -08:00
4 zmienionych plików z 143 dodań i 8 usunięć

Wyświetl plik

@ -106,16 +106,34 @@ def handle_brownie_generate(args: argparse.Namespace):
project_directory = args.project
build_directory = os.path.join(project_directory, "build", "contracts")
intermediate_dirs: List[str] = []
if args.foundry:
build_directory = os.path.join(project_directory, "out")
build_file_path = os.path.join(build_directory, f"{args.name}.json")
if not os.path.isfile(build_file_path):
raise IOError(
f"File does not exist: {build_file_path}. Maybe you have to compile the smart contracts?"
)
if args.foundry:
if args.sol_filename is not None:
build_file_path = os.path.join(
build_directory, args.sol_filename, f"{args.name}.json"
)
intermediate_dirs.append(args.sol_filename)
else:
build_file_path = os.path.join(
build_directory, f"{args.name}.sol", f"{args.name}.json"
)
intermediate_dirs.append(f"{args.name}.sol")
else:
if not os.path.isfile(build_file_path):
raise IOError(
f"File does not exist: {build_file_path}. Maybe you have to compile the smart contracts?"
)
with open(build_file_path, "r") as ifp:
build = json.load(ifp)
if args.foundry:
build["contractName"] = args.name
relpath = os.path.relpath(project_directory, args.outdir)
splitted_relpath = [
f'"{item}"' for item in relpath.split(os.sep)
@ -129,6 +147,8 @@ def handle_brownie_generate(args: argparse.Namespace):
args.name,
splitted_relpath_string,
prod=args.prod,
foundry=args.foundry,
intermediate_dirs=intermediate_dirs,
)
write_file(interface, os.path.join(args.outdir, args.name + ".py"))
@ -355,12 +375,22 @@ def generate_argument_parser() -> argparse.ArgumentParser:
"-p",
"--project",
required=True,
help=f"Path to brownie project directory",
help=f"Path to brownie/foundry project directory",
)
generate_brownie_parser.add_argument(
"--foundry",
action="store_true",
help="Project is using Foundry (if not specified, the assumption is that the project uses brownie)",
)
generate_brownie_parser.add_argument(
"--sol-filename",
required=False,
help="Name of solidity file containing your contract; required if --foundry, moonworm will look for build artifacts in out/<this filename>, defaults to the contract name if not provided",
)
generate_brownie_parser.add_argument(
"--prod",
action="store_true",
help="Generate shippable python interface, in which abi and bytecode will be included inside the generated file",
help="Generate self-contained python interface, in which ABI and bytecode will be included inside the generated file",
)
generate_brownie_parser.set_defaults(func=handle_brownie_generate)

Wyświetl plik

@ -21,14 +21,19 @@ BROWNIE_INTERFACE_TEMPLATE_PATH = os.path.join(
BROWNIE_INTERFACE_PROD_TEMPLATE_PATH = os.path.join(
os.path.dirname(__file__), "brownie_contract_prod.py.template"
)
BROWNIE_INTERFACE_FOUNDRY_TEMPLATE_PATH = os.path.join(
os.path.dirname(__file__), "brownie_contract_foundry.py.template"
)
try:
with open(BROWNIE_INTERFACE_TEMPLATE_PATH, "r") as ifp:
BROWNIE_INTERFACE_TEMPLATE = ifp.read()
with open(BROWNIE_INTERFACE_PROD_TEMPLATE_PATH, "r") as ifp:
BROWNIE_INTERFACE_PROD_TEMPLATE = ifp.read()
with open(BROWNIE_INTERFACE_FOUNDRY_TEMPLATE_PATH, "r") as ifp:
BROWNIE_INTERFACE_FOUNDRY_TEMPLATE = ifp.read()
except Exception as e:
logging.warn(
f"WARNING: Could not load cli template from ({BROWNIE_INTERFACE_TEMPLATE_PATH})/({BROWNIE_INTERFACE_PROD_TEMPLATE_PATH}):"
f"WARNING: Could not load cli template from ({BROWNIE_INTERFACE_TEMPLATE_PATH})/({BROWNIE_INTERFACE_PROD_TEMPLATE_PATH})/({BROWNIE_INTERFACE_FOUNDRY_TEMPLATE_PATH}):"
)
logging.warn(e)
@ -962,6 +967,8 @@ def generate_brownie_interface(
cli: bool = True,
format: bool = True,
prod: bool = False,
foundry: bool = True,
intermediate_dirs: Optional[List[str]] = None,
) -> str:
"""
Generates Python code which allows you to interact with a smart contract with a given ABI, build data, and a given name.
@ -988,6 +995,11 @@ def generate_brownie_interface(
7. `prod`: If True, creates a self-contained file. Generated code will not require reference to an
existing brownie project at its runtime.
8. `foundry`: If True, assumes a Foundry project structure.
9. intermediate_dirs: Currently only used for Foundry projects. Path to build file via intermediate
build subdirectory which takes the name of the Solidity file that the contract is implemented in.
## Outputs
The generated code as a string.
@ -1011,6 +1023,14 @@ def generate_brownie_interface(
contract_body=contract_body,
moonworm_version=MOONWORM_VERSION,
)
elif foundry:
content = BROWNIE_INTERFACE_FOUNDRY_TEMPLATE.format(
contract_body=contract_body,
moonworm_version=MOONWORM_VERSION,
relative_path=relative_path,
build_subdir=intermediate_dirs[0],
)
else:
content = BROWNIE_INTERFACE_TEMPLATE.format(
contract_body=contract_body,

Wyświetl plik

@ -0,0 +1,85 @@
# Code generated by moonworm : https://github.com/moonstream-to/moonworm
# Moonworm version : {moonworm_version}
import argparse
import json
import os
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from brownie import Contract, network, project
from brownie.network.contract import ContractContainer
from eth_typing.evm import ChecksumAddress
PROJECT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), {relative_path}))
BUILD_DIRECTORY = os.path.join(PROJECT_DIRECTORY, "out", "{build_subdir}")
def boolean_argument_type(raw_value: str) -> bool:
TRUE_VALUES = ["1", "t", "y", "true", "yes"]
FALSE_VALUES = ["0", "f", "n", "false", "no"]
if raw_value.lower() in TRUE_VALUES:
return True
elif raw_value.lower() in FALSE_VALUES:
return False
raise ValueError(
f"Invalid boolean argument: {{raw_value}}. Value must be one of: {{','.join(TRUE_VALUES + FALSE_VALUES)}}"
)
def bytes_argument_type(raw_value: str) -> str:
return raw_value
def get_abi_json(abi_name: str) -> List[Dict[str, Any]]:
abi_full_path = os.path.join(BUILD_DIRECTORY, f"{{abi_name}}.json")
if not os.path.isfile(abi_full_path):
raise IOError(
f"File does not exist: {{abi_full_path}}. Maybe you have to compile the smart contracts?"
)
with open(abi_full_path, "r") as ifp:
build = json.load(ifp)
abi_json = build.get("abi")
if abi_json is None:
raise ValueError(f"Could not find ABI definition in: {{abi_full_path}}")
return abi_json
def contract_from_build(abi_name: str) -> ContractContainer:
# This is workaround because brownie currently doesn't support loading the same project multiple
# times. This causes problems when using multiple contracts from the same project in the same
# python project.
PROJECT = project.main.Project("moonworm", Path(PROJECT_DIRECTORY))
abi_full_path = os.path.join(BUILD_DIRECTORY, f"{{abi_name}}.json")
if not os.path.isfile(abi_full_path):
raise IOError(
f"File does not exist: {{abi_full_path}}. Maybe you have to compile the smart contracts?"
)
with open(abi_full_path, "r") as ifp:
foundry_build = json.load(ifp)
build = {{
"type": "contract",
"ast": foundry_build["ast"],
"abi": foundry_build["abi"],
"contractName": abi_name,
"compiler": {{
"version": foundry_build["metadata"]["compiler"]["version"],
}},
"language": foundry_build["metadata"]["language"],
"bytecode": foundry_build["bytecode"]["object"],
"sourceMap": foundry_build["bytecode"]["sourceMap"],
"deployedBytecode": foundry_build["deployedBytecode"]["object"],
"deployedSourceMap": foundry_build["deployedBytecode"]["sourceMap"],
"pcMap": {{}},
}}
return ContractContainer(PROJECT, build)
{contract_body}

Wyświetl plik

@ -1 +1 @@
MOONWORM_VERSION = "0.7.2"
MOONWORM_VERSION = "0.8.0"