Skip to content
New issue

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

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

Already on GitHub? # to your account

update to work with python 3.10-3.12 #22

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
71 changes: 71 additions & 0 deletions .github/workflows/jobs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: lint

on:
push:
branches:
- main
- update
pull_request:

jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
with:
token: ${{github.token}}

- name: set up python
uses: actions/setup-python@v5
with:
python-version: "3.12"
token: ${{github.token}}

- name: install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh

- name: install project
run: |
uv venv .venv -p 3.12
source .venv/bin/activate
uv pip install -e .[all]

- name: lint with ruff
run: |
source .venv/bin/activate
python -m ruff check .
test:
name: test
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: checkout
uses: actions/checkout@v4
with:
token: ${{github.token}}

- name: set up python
uses: actions/setup-python@v5
with:
python-version: ${{matrix.python-version}}
token: ${{github.token}}

- name: install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh

- name: install project
run: |
uv venv .venv -p ${{matrix.python-version}}
source .venv/bin/activate
uv pip install -e .[all]

- name: test
run: |
source .venv/bin/activate
docker-compose up -d
python -m pytest .
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ build/
.coverage

tests/contracts/build
__pycache__
.vscode
22 changes: 0 additions & 22 deletions .travis.yml

This file was deleted.

17 changes: 0 additions & 17 deletions API.md

This file was deleted.

1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2019 ConsenSys
Copyright (c) 2024 Mihai Cosma

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@ Read the proposal:<br/>
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md

#### Supported Python Versions
- `3.6`
- `3.7`

## Install
```bash
pip install eip712-structs
```
- 3.10 and up, tested up to 3.12

## Usage
See [API.md](API.md) for a succinct summary of available methods.

### `class EIP712Struct`
#### Important methods
- `.to_message(domain: EIP712Struct)` - Convert the struct (and given domain struct) into the standard EIP-712 message structure.
- `.signable_bytes(domain: EIP712Struct)` - Get the standard EIP-712 bytes hash, suitable for signing.
- `.from_message(message_dict: dict)` **(Class method)** - Given a standard EIP-712 message dictionary (such as produced from `.to_message`), returns a NamedTuple containing the `message` and `domain` EIP712Structs.

#### Other stuff
- `.encode_value()` - Returns a `bytes` object containing the ordered concatenation of each members bytes32 representation.
- `.encode_type()` **(Class method)** - Gets the "signature" of the struct class. Includes nested structs too!
- `.type_hash()` **(Class method)** - The keccak256 hash of the result of `.encode_type()`.
- `.hash_struct()` - Gets the keccak256 hash of the concatenation of `.type_hash()` and `.encode_value()`
- `.get_data_value(member_name: str)` - Get the value of the given struct member
- `.set_data_value(member_name: str, value: Any)` - Set the value of the given struct member
- `.data_dict()` - Returns a dictionary with all data in this struct. Includes nested struct data, if exists.
- `.get_members()` **(Class method)** - Returns a dictionary mapping each data member's name to it's type.

Examples/Details below.

Expand Down Expand Up @@ -193,22 +202,32 @@ struct_array = Array(MyStruct, 10) # MyStruct[10] - again, don't instantiate s
## Development
Contributions always welcome.

Install dependencies:
- `pip install -r requirements.txt`
Install test dependencies:
- `uv pip install -e ".[test]"`

Run tests:
- `python setup.py test`
- Some tests expect an active local ganache chain on http://localhost:8545. Docker will compile the contracts and start the chain for you.
- `pytest`
- Some tests expect an active local anvil chain on http://localhost:11111. Docker will compile the contracts and start the chain for you.
- Docker is optional, but useful to test the whole suite. If no chain is detected, chain tests are skipped.
- Usage:
- `docker-compose up -d` (Starts containers in the background)
- Note: Contracts are compiled when you run `up`, but won't be deployed until the test is run.
- Cleanup containers when you're done: `docker-compose down`

Deploying a new version:
- Bump the version number in `setup.py`, commit it into master.
- Bump the version number in `pyproject.toml`, commit it into master.
- Make a release tag on the master branch in Github. Travis should handle the rest.

### Changes in 1.2
- Switch from ganache to anvil
- Remove pysha3 dependency
- Remove python2 style super() call
- Remove OrderedAttributesMeta. From version 3.7 onward, dictionaries maintain the insertion order of their items.
- Require python >= 3.10 as the lowest version to install with [uv](https://github.com/astral-sh/uv)
- Switch from Sphinx to Google docstring format for readability
- Lint with [ruff](https://github.com/astral-sh/ruff)
- Add Github workflows (lint and test)
- Add pyproject.toml

## Shameless Plug
Written by [ConsenSys](https://consensys.net) for the world! :heart:
13 changes: 5 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
---
version: "3"
services:
ganache:
image: "trufflesuite/ganache-cli:latest"
command: "--account=\"0x3660582119566511de16f4dcf397fa324b27bd6247f653cf0298a0993f3432ed,100000000000000000000\""
anvil:
image: "ghcr.io/foundry-rs/foundry:latest"
command: "'anvil --host 0.0.0.0 --port=11111'"
ports:
- "8545:8545"
- "11111:11111"
depends_on:
- compiler
labels:
net.consensys.description: >
Starts an instance of ganache-cli for chain parity tests.
Unlocks address 0xD68D840e1e971750E6d45049ff579925456d5893"
Starts an instance of anvil on port 11111 for chain parity tests.

compiler:
image: "ethereum/solc:stable"
Expand Down
9 changes: 7 additions & 2 deletions eip712_structs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
"""EIP712 data structure management for python."""

# required before imports to avoid circular dependency
# pylint: disable=wrong-import-position
# pylint: disable=invalid-name
default_domain = None

from eip712_structs.domain_separator import make_domain
from eip712_structs.struct import EIP712Struct
from eip712_structs.types import Address, Array, Boolean, Bytes, Int, String, Uint

default_domain = None
34 changes: 20 additions & 14 deletions eip712_structs/domain_separator.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
"""EIP-712 Domain Separator."""

import eip712_structs

# allow camelCase
# ruff: noqa: N803
# pylint: disable=invalid-name
# allow missing class docstring
# pylint: disable=missing-class-docstring

def make_domain(name=None, version=None, chainId=None, verifyingContract=None, salt=None):
"""Helper method to create the standard EIP712Domain struct for you.
"""Create the standard EIP712Domain struct.

Per the standard, if a value is not used then the parameter is omitted from the struct entirely.
"""

if all(i is None for i in [name, version, chainId, verifyingContract, salt]):
raise ValueError('At least one argument must be given.')
raise ValueError("At least one argument must be given.")

class EIP712Domain(eip712_structs.EIP712Struct):
pass

kwargs = dict()
kwargs = {}
if name is not None:
EIP712Domain.name = eip712_structs.String()
kwargs['name'] = str(name)
EIP712Domain.name = eip712_structs.String() # type: ignore
kwargs["name"] = str(name)
if version is not None:
EIP712Domain.version = eip712_structs.String()
kwargs['version'] = str(version)
EIP712Domain.version = eip712_structs.String() # type: ignore
kwargs["version"] = str(version)
if chainId is not None:
EIP712Domain.chainId = eip712_structs.Uint(256)
kwargs['chainId'] = int(chainId)
EIP712Domain.chainId = eip712_structs.Uint(256) # type: ignore
kwargs["chainId"] = int(chainId)
if verifyingContract is not None:
EIP712Domain.verifyingContract = eip712_structs.Address()
kwargs['verifyingContract'] = verifyingContract
EIP712Domain.verifyingContract = eip712_structs.Address() # type: ignore
kwargs["verifyingContract"] = verifyingContract
if salt is not None:
EIP712Domain.salt = eip712_structs.Bytes(32)
kwargs['salt'] = salt
EIP712Domain.salt = eip712_structs.Bytes(32) # type: ignore
kwargs["salt"] = salt

return EIP712Domain(**kwargs)
Loading