Skip to content

Commit

Permalink
fix: compile contracts even when uninitialized
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickAlphaC committed Sep 20, 2024
1 parent 315400e commit 5871c8c
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 12 deletions.
12 changes: 9 additions & 3 deletions moccasin/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ def generate_main_parser_and_sub_parsers() -> (
# ------------------------------------------------------------------
# COMPILE COMMAND
# ------------------------------------------------------------------
sub_parsers.add_parser(
compile_parser = sub_parsers.add_parser(
"compile",
help="Compiles the project.",
description="""Compiles all Vyper contracts in the project. \n
This command will:
description="""Compiles a specific Vyper contract or all vyper contracts in the project. \n
If no contract or contract path is given, this command will:
1. Find all .vy files in the src/ directory
2. Compile each file using the Vyper compiler
3. Output the compiled artifacts to the out/ directory
Expand All @@ -130,6 +130,12 @@ def generate_main_parser_and_sub_parsers() -> (
parents=[parent_parser],
)

compile_parser.add_argument(
"contract_or_contract_path",
nargs="?",
help="Optional argument to compile a specific contract.",
)

# ------------------------------------------------------------------
# TEST COMMAND
# ------------------------------------------------------------------
Expand Down
30 changes: 21 additions & 9 deletions moccasin/commands/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@
from argparse import Namespace


def main(_: Namespace) -> int:
def main(args: Namespace) -> int:
initialize_global_config()
config = get_config()
project_path: Path = config.get_root()
compile_project(
project_path,
project_path.joinpath(config.out_folder),
project_path.joinpath(config.contracts_folder),
write_data=True,
)

if args.contract_or_contract_path:
contract_path = config._find_contract(args.contract_or_contract_path)
compile_(
contract_path, project_path.joinpath(config.out_folder), write_data=True
)
logger.info(f"Done compiling {contract_path.stem}")
else:
compile_project(
project_path,
project_path.joinpath(config.out_folder),
project_path.joinpath(config.contracts_folder),
write_data=True,
)
return 0


Expand All @@ -49,7 +57,11 @@ def compile_project(
logger.info(f"Compiling {len(contracts_to_compile)} contracts to {build_folder}...")

for contract_path in contracts_to_compile:
compile_(contract_path, build_folder, write_data=write_data)
try:
compile_(contract_path, build_folder, write_data=write_data)
except vyper.exceptions.InitializerException:
logger.info(f"Skipping contract {contract_path.stem} due to uninitialized.")
continue

logger.info("Done compiling project!")

Expand Down Expand Up @@ -104,6 +116,6 @@ def compile_(
json.dump(build_data, f, indent=4)
logger.debug(f"Compilation data saved to {build_file}")

logger.debug("Done compiling {contract_name}")
logger.debug(f"Done compiling {contract_name}")

return deployer
16 changes: 16 additions & 0 deletions tests/cli/test_cli_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@ def test_compile_alias_build_project(complex_cleanup_out_folder, mox_path):
os.chdir(current_dir)
assert "Running compile command" in result.stderr
assert result.returncode == 0


def test_compile_one(complex_cleanup_out_folder, mox_path):
current_dir = Path.cwd()
try:
os.chdir(current_dir.joinpath(COMPLEX_PROJECT_PATH))
result = subprocess.run(
[mox_path, "build", "BuyMeACoffee.vy"],
check=True,
capture_output=True,
text=True,
)
finally:
os.chdir(current_dir)
assert "Done compiling BuyMeACoffee" in result.stderr
assert result.returncode == 0
17 changes: 17 additions & 0 deletions tests/data/complex_project/contracts/Auth.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# pragma version ^0.4.0

# Not export to importing module?
owner: public(address)

@deploy
def __init__():
self.owner = msg.sender

def _check_owner():
assert self.owner == msg.sender

# Must be exported by importing module
@external
def set_owner(owner: address):
self._check_owner()
self.owner = owner
16 changes: 16 additions & 0 deletions tests/data/complex_project/contracts/InitializedAuth.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# pragma version ^0.4.0

import auth
import UninitializedAuth

initializes: auth
# auth is dependency of auth_2_step
initializes: UninitializedAuth[auth := auth]

# export all external functions
exports: UninitializedAuth.__interface__

@deploy
def __init__():
auth.__init__()
UninitializedAuth.__init__()
24 changes: 24 additions & 0 deletions tests/data/complex_project/contracts/UninitializedAuth.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# pragma version ^0.4.0
import auth

# This contract is not a valid contract. auth.__init__() must be called
# by a contract that imports and uses this contract

uses: auth

pending_owner: address

@deploy
def __init__():
pass

@external
def begin_transfer(new_owner: address):
auth._check_owner()
self.pending_owner = new_owner

@external
def accept_transfer():
assert msg.sender == self.pending_owner
auth.owner = msg.sender
self.pending_owner = empty(address)

0 comments on commit 5871c8c

Please sign in to comment.