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

feat(BACK-8208): tooling: Allow generation from string inputs #177

Merged
merged 2 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
name: check yaml files (yamllint)

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v5.0.0
hooks:
- id: check-ast
- id: check-builtin-literals
Expand Down Expand Up @@ -51,7 +51,7 @@ repos:
- --py310-plus

- repo: https://github.com/hadialqattan/pycln
rev: v2.4.0
rev: v2.5.0
hooks:
- id: pycln
name: remove unused imports (pycln)
Expand Down
35 changes: 16 additions & 19 deletions src/erc7730/generate/generate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections.abc import Generator
from pathlib import Path
from typing import Any, assert_never

from caseswitcher import to_title
Expand Down Expand Up @@ -47,29 +46,29 @@
def generate_descriptor(
chain_id: int,
contract_address: Address,
abi_file: Path | None = None,
eip712_schema_file: Path | None = None,
abi: str | bytes | None = None,
ckorchane-ledger marked this conversation as resolved.
Show resolved Hide resolved
eip712_schema: str | bytes | None = None,
owner: str | None = None,
legal_name: str | None = None,
url: HttpUrl | None = None,
) -> InputERC7730Descriptor:
"""
Generate an ERC-7730 descriptor.

If an EIP-712 schema file is provided, an EIP-712 descriptor is generated for this schema, otherwise a calldata
descriptor. If no ABI file is supplied, the ABIs are fetched from Etherscan using the chain id / contract address.
If an EIP-712 schema is provided, an EIP-712 descriptor is generated for this schema, otherwise a calldata
descriptor. If no ABI is supplied, the ABIs are fetched from Etherscan using the chain id / contract address.

:param chain_id: contract chain id
:param contract_address: contract address
:param abi_file: path to a JSON ABI file (to generate a calldata descriptor)
:param eip712_schema_file: path to an EIP-712 schema (to generate an EIP-712 descriptor)
:param abi: JSON ABI string or buffer representation (to generate a calldata descriptor)
:param eip712_schema: JSON EIP-712 schema string or buffer representation (to generate an EIP-712 descriptor)
:param owner: the display name of the owner or target of the contract / message to be clear signed
:param legal_name: the full legal name of the owner if different from the owner field
:param url: URL with more info on the entity the user interacts with
:return: a generated ERC-7730 descriptor
"""

context, trees = _generate_context(chain_id, contract_address, abi_file, eip712_schema_file)
context, trees = _generate_context(chain_id, contract_address, abi, eip712_schema)
metadata = _generate_metadata(legal_name, owner, url)
display = _generate_display(trees)

Expand All @@ -82,18 +81,17 @@ def _generate_metadata(owner: str | None, legal_name: str | None, url: HttpUrl |


def _generate_context(
chain_id: int, contract_address: Address, abi_file: Path | None, eip712_schema_file: Path | None
chain_id: int, contract_address: Address, abi: str | bytes | None, eip712_schema: str | bytes | None
) -> tuple[InputContractContext | InputEIP712Context, dict[str, SchemaTree]]:
if eip712_schema_file is not None:
return _generate_context_eip712(chain_id, contract_address, eip712_schema_file)
return _generate_context_calldata(chain_id, contract_address, abi_file)
if eip712_schema is not None:
return _generate_context_eip712(chain_id, contract_address, eip712_schema)
return _generate_context_calldata(chain_id, contract_address, abi)


def _generate_context_eip712(
chain_id: int, contract_address: Address, eip712_schema_file: Path
chain_id: int, contract_address: Address, eip712_schema: str | bytes
) -> tuple[InputEIP712Context, dict[str, SchemaTree]]:
with open(eip712_schema_file, "rb") as f:
schemas = TypeAdapter(list[EIP712Schema]).validate_json(f.read())
schemas = TypeAdapter(list[EIP712Schema]).validate_json(eip712_schema)

context = InputEIP712Context(
eip712=InputEIP712(schemas=schemas, deployments=[InputDeployment(chainId=chain_id, address=contract_address)])
Expand All @@ -105,11 +103,10 @@ def _generate_context_eip712(


def _generate_context_calldata(
chain_id: int, contract_address: Address, abi_file: Path | None
chain_id: int, contract_address: Address, abi: str | bytes | None
) -> tuple[InputContractContext, dict[str, SchemaTree]]:
if abi_file is not None:
with open(abi_file, "rb") as f:
abis = TypeAdapter(list[ABI]).validate_json(f.read())
if abi is not None:
abis = TypeAdapter(list[ABI]).validate_json(abi)

elif (abis := get_contract_abis(chain_id, contract_address)) is None:
raise Exception("Failed to fetch contract ABIs")
Expand Down
17 changes: 15 additions & 2 deletions src/erc7730/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,24 @@ def command_generate(
legal_name: Annotated[str | None, Option(help="The full legal name of the owner")] = None,
url: Annotated[str | None, Option(help="URL with more info on the entity interacted with")] = None,
) -> None:
if schema is not None and abi is not None:
print("Cannot specify both ABI and schema.")
raise Exit(1)
schema_buffer = None
abi_buffer = None

if schema is not None:
with open(schema, "rb") as f:
schema_buffer = f.read()
elif abi is not None:
with open(abi, "rb") as f:
abi_buffer = f.read()

descriptor = generate_descriptor(
chain_id=chain_id,
contract_address=address,
abi_file=abi,
eip712_schema_file=schema,
abi=abi_buffer,
eip712_schema=schema_buffer,
owner=owner,
legal_name=legal_name,
url=HttpUrl(url) if url is not None else None,
Expand Down
38 changes: 32 additions & 6 deletions tests/generate/test_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,49 @@ def test_generate_from_contract_address(label: str, chain_id: int, contract_addr


@pytest.mark.parametrize("test_file", sorted(glob(str(DATA / "abis*.json"))), ids=lambda f: Path(f).stem)
def test_generate_from_abis(test_file: str) -> None:
def test_generate_from_abis_buffer(test_file: str) -> None:
"""Generate descriptor using a provided ABI file."""
_assert_descriptor_valid(
generate_descriptor(
chain_id=1, contract_address=Address("0x0000000000000000000000000000000000000000"), abi_file=Path(test_file)
with open(test_file, "rb") as f:
_assert_descriptor_valid(
generate_descriptor(
chain_id=1, contract_address=Address("0x0000000000000000000000000000000000000000"), abi=f.read()
)
)


@pytest.mark.parametrize("test_file", sorted(glob(str(DATA / "abis*.json"))), ids=lambda f: Path(f).stem)
def test_generate_from_abis_string(test_file: str) -> None:
"""Generate descriptor using a provided ABI file."""
with open(test_file, "rb") as f:
abi = f.read().decode("utf-8")
_assert_descriptor_valid(
generate_descriptor(chain_id=1, contract_address=Address("0x0000000000000000000000000000000000000000"), abi=abi)
)


@pytest.mark.parametrize("test_file", sorted(glob(str(DATA / "schemas*.json"))), ids=lambda f: Path(f).stem)
def test_generate_from_eip712_schemas(test_file: str) -> None:
def test_generate_from_eip712_schemas_buffer(test_file: str) -> None:
"""Generate descriptor using a provided EIP-712 file."""
with open(test_file, "rb") as f:
_assert_descriptor_valid(
generate_descriptor(
chain_id=1,
contract_address=Address("0x0000000000000000000000000000000000000000"),
eip712_schema=f.read(),
)
)


@pytest.mark.parametrize("test_file", sorted(glob(str(DATA / "schemas*.json"))), ids=lambda f: Path(f).stem)
def test_generate_from_eip712_schemas_string(test_file: str) -> None:
"""Generate descriptor using a provided EIP-712 file."""
with open(test_file, "rb") as f:
schema = f.read().decode("utf-8")
_assert_descriptor_valid(
generate_descriptor(
chain_id=1,
contract_address=Address("0x0000000000000000000000000000000000000000"),
eip712_schema_file=Path(test_file),
eip712_schema=schema,
)
)

Expand Down
Loading