eip712-structs-ng


Nameeip712-structs-ng JSON
Version 2.0.1 PyPI version JSON
download
home_page
SummaryA Python interface for EIP-712 struct construction.
upload_time2024-01-12 11:24:10
maintainer
docs_urlNone
authorAJ Grubbs
requires_python>=3.8
licenseMIT License Copyright (c) 2019 ConsenSys Copyright (c) 2024 Mickaël Schoentgen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords eip712 ethereum solidity
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # EIP-712 Structs

[![PyPI version](https://badge.fury.io/py/eip712-structs-ng.svg)](https://badge.fury.io/py/eip712-structs-ng)
[![Python versions](https://img.shields.io/pypi/pyversions/eip712-structs-ng.svg)](https://pypi.python.org/pypi/eip712-structs-ng)

A Python interface for [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) struct construction.

> Note: this is a **drop-in replacement** for the [eip712-structs](https://pypi.org/project/eip712-structs/) module, which is dead since 2019.
>
> It brings the same objects as its predecessor with some sugar:
> - the code is fully typed
> - dependencies were cleaned up
> - support for Python 3.8 and newer
> - code modernization, including Sourcery clean-up
> - 99% tests coverage
> - simplified testing (no more need for docker)

> **Warning**: Remove any installation of the old `eip712-structs` module to prevent import mismatches: `python -m pip uninstall -y eip712-structs`

In this module, a "struct" is structured data as defined in the standard.
It is not the same as the Python standard library's [struct](https://docs.python.org/3/library/struct.html).

## Supported Python Versions

Python 3.8 and newer.

## Install

```bash
python -m pip install -U eip712-structs-ng
```

## Usage

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

Examples & details below.

## Quickstart

Say we want to represent the following struct, convert it to a message and sign it:

```solidity
struct MyStruct {
    string some_string;
    uint256 some_number;
}
```

With this module, that would look like:

```python
from eip712_structs import make_domain, EIP712Struct, String, Uint


# Make a domain separator
domain = make_domain(name='Some name', version='1.0.0')

# Define your struct type
class MyStruct(EIP712Struct):
    some_string = String()
    some_number = Uint(256)

# Create an instance with some data
mine = MyStruct(some_string="hello world", some_number=1234)

# Values can be get/set dictionary-style:
mine["some_number"] = 4567
assert mine["some_string"] == "hello world"
assert mine["some_number"] == 4567

# Into a message dict - domain required
my_msg = mine.to_message(domain)

# Into message JSON - domain required.
# This method converts bytes types for you, which the default JSON encoder won't handle.
my_msg_json = mine.to_message_json(domain)

# Into signable bytes - domain required
my_bytes = mine.signable_bytes(domain)
```

See [Member Types](#member-types) for more information on supported types.

### Dynamic Construction

Attributes may be added dynamically as well.
This may be necessary if you want to use a reserved keyword like `from`:

```python
from eip712_structs import EIP712Struct, Address


class Message(EIP712Struct):
    pass

Message.to = Address()
setattr(Message, "from", Address())

# At this point, `Message` is equivalent to `struct Message { address to; address from; }`
```

### The Domain Separator

EIP-712 specifies a domain struct, to differentiate between identical structs that may be unrelated.
A helper method exists for this purpose.
All values to the `make_domain()` function are optional - but at least one must be defined.
If omitted, the resulting domain struct's definition leaves out the parameter entirely.

The full signature:

```python
make_domain(name: string, version: string, chainId: uint256, verifyingContract: address, salt: bytes32)
```

#### Setting a Default Domain

Constantly providing the same domain can be cumbersome. You can optionally set a default, and then forget it.
It is automatically used by `.to_message()` and `.signable_bytes()`:

```python
import eip712_structs


foo = SomeStruct()

my_domain = eip712_structs.make_domain(name="hello world")
eip712_structs.default_domain = my_domain

assert foo.to_message() == foo.to_message(my_domain)
assert foo.signable_bytes() == foo.signable_bytes(my_domain)
```

## Member Types

### Basic Types

EIP712's basic types map directly to solidity types:

```python
from eip712_structs import Address, Boolean, Bytes, Int, String, Uint


Address()  # Solidity's 'address'
Boolean()  # 'bool'
Bytes()    # 'bytes'
Bytes(N)   # 'bytesN' - N must be an int from 1 through 32
Int(N)     # 'intN' - N must be a multiple of 8, from 8 to 256
String()   # 'string'
Uint(N)    # 'uintN' - N must be a multiple of 8, from 8 to 256
```

Use like:

```python
from eip712_structs import EIP712Struct, Address, Bytes


class Foo(EIP712Struct):
    member_name_0 = Address()
    member_name_1 = Bytes(5)
    # etc.
```

### Struct References

In addition to holding basic types, EIP-712 structs may also hold other structs!
Usage is almost the same - the difference is you don't "instantiate" the class.

Example:

```python
from eip712_structs import EIP712Struct, String


class Dog(EIP712Struct):
    name = String()
    breed = String()

class Person(EIP712Struct):
    name = String()
    dog = Dog  # Take note - no parentheses!

# Dog "stands alone"
Dog.encode_type()  # Dog(string name,string breed)

# But Person knows how to include Dog
Person.encode_type()  # Person(string name,Dog dog)Dog(string name,string breed)
```

Instantiating the structs with nested values may be done a couple different ways:

```python
# Method one: set it to a struct
dog = Dog(name="Mochi", breed="Corgi")
person = Person(name="E.M.", dog=dog)

# Method two: set it to a dict - the underlying struct is built for you
person = Person(
    name="E.M.",
    dog={
        "name": "Mochi",
        "breed": "Corgi",
    }
)
```

### Arrays

Arrays are also supported for the standard:

```python
array_member = Array(<item_type>[, <optional_length>])
```

- `<item_type>` - The basic type or struct that will live in the array
- `<optional_length>` - If given, the array is set to that length.

For example:

```python
dynamic_array = Array(String())      # String[] dynamic_array
static_array  = Array(String(), 10)  # String[10] static_array
struct_array  = Array(MyStruct, 10)  # MyStruct[10] - again, don't instantiate structs like the basic types
```

## Development

Contributions always welcome.

Setup a development environment:

```bash
python -m venv venv
. venv/bin/activate
```

Install dependencies:

```bash
python -m pip install -U pip
python -m pip install -e '.[tests]'
```

Run tests:

```bash
python -m pytest
```

Run linters before submitting a PR:

```bash
./checks.sh
```

### Solidity Contract

When changing the code of the Solidity test contract, you will have to regenerate its Python data code:

```shell
cd src/tests/integration/contract_sources && ./compile.sh
```

That's it! Do not forget to commit those changes.

## Deploying a New Version

- Bump the version number in `__init__.py`, commit it into the `main` branch.
- Make a release tag on the `main` branch in GitHub.
- The CI will handle the PyPi publishing.

## Shameless Plug

Originally written by [ConsenSys](https://consensys.net) for the world! And continued by [BoboTiG](https://github.com/BoboTiG)! :heart:

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "eip712-structs-ng",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "Micka\u00ebl Schoentgen <contact@tiger-222.fr>",
    "keywords": "eip712,ethereum,solidity",
    "author": "AJ Grubbs",
    "author_email": "Micka\u00ebl Schoentgen <contact@tiger-222.fr>",
    "download_url": "https://files.pythonhosted.org/packages/3d/8b/b3b40f26d9cd51875da3229ab7a453c5c3497f329c7c6d8bd449d08b08ea/eip712_structs_ng-2.0.1.tar.gz",
    "platform": null,
    "description": "# EIP-712 Structs\n\n[![PyPI version](https://badge.fury.io/py/eip712-structs-ng.svg)](https://badge.fury.io/py/eip712-structs-ng)\n[![Python versions](https://img.shields.io/pypi/pyversions/eip712-structs-ng.svg)](https://pypi.python.org/pypi/eip712-structs-ng)\n\nA Python interface for [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) struct construction.\n\n> Note: this is a **drop-in replacement** for the [eip712-structs](https://pypi.org/project/eip712-structs/) module, which is dead since 2019.\n>\n> It brings the same objects as its predecessor with some sugar:\n> - the code is fully typed\n> - dependencies were cleaned up\n> - support for Python 3.8 and newer\n> - code modernization, including Sourcery clean-up\n> - 99% tests coverage\n> - simplified testing (no more need for docker)\n\n> **Warning**: Remove any installation of the old `eip712-structs` module to prevent import mismatches: `python -m pip uninstall -y eip712-structs`\n\nIn this module, a \"struct\" is structured data as defined in the standard.\nIt is not the same as the Python standard library's [struct](https://docs.python.org/3/library/struct.html).\n\n## Supported Python Versions\n\nPython 3.8 and newer.\n\n## Install\n\n```bash\npython -m pip install -U eip712-structs-ng\n```\n\n## Usage\n\nSee [API.md](API.md) for a succinct summary of available methods.\n\nExamples & details below.\n\n## Quickstart\n\nSay we want to represent the following struct, convert it to a message and sign it:\n\n```solidity\nstruct MyStruct {\n    string some_string;\n    uint256 some_number;\n}\n```\n\nWith this module, that would look like:\n\n```python\nfrom eip712_structs import make_domain, EIP712Struct, String, Uint\n\n\n# Make a domain separator\ndomain = make_domain(name='Some name', version='1.0.0')\n\n# Define your struct type\nclass MyStruct(EIP712Struct):\n    some_string = String()\n    some_number = Uint(256)\n\n# Create an instance with some data\nmine = MyStruct(some_string=\"hello world\", some_number=1234)\n\n# Values can be get/set dictionary-style:\nmine[\"some_number\"] = 4567\nassert mine[\"some_string\"] == \"hello world\"\nassert mine[\"some_number\"] == 4567\n\n# Into a message dict - domain required\nmy_msg = mine.to_message(domain)\n\n# Into message JSON - domain required.\n# This method converts bytes types for you, which the default JSON encoder won't handle.\nmy_msg_json = mine.to_message_json(domain)\n\n# Into signable bytes - domain required\nmy_bytes = mine.signable_bytes(domain)\n```\n\nSee [Member Types](#member-types) for more information on supported types.\n\n### Dynamic Construction\n\nAttributes may be added dynamically as well.\nThis may be necessary if you want to use a reserved keyword like `from`:\n\n```python\nfrom eip712_structs import EIP712Struct, Address\n\n\nclass Message(EIP712Struct):\n    pass\n\nMessage.to = Address()\nsetattr(Message, \"from\", Address())\n\n# At this point, `Message` is equivalent to `struct Message { address to; address from; }`\n```\n\n### The Domain Separator\n\nEIP-712 specifies a domain struct, to differentiate between identical structs that may be unrelated.\nA helper method exists for this purpose.\nAll values to the `make_domain()` function are optional - but at least one must be defined.\nIf omitted, the resulting domain struct's definition leaves out the parameter entirely.\n\nThe full signature:\n\n```python\nmake_domain(name: string, version: string, chainId: uint256, verifyingContract: address, salt: bytes32)\n```\n\n#### Setting a Default Domain\n\nConstantly providing the same domain can be cumbersome. You can optionally set a default, and then forget it.\nIt is automatically used by `.to_message()` and `.signable_bytes()`:\n\n```python\nimport eip712_structs\n\n\nfoo = SomeStruct()\n\nmy_domain = eip712_structs.make_domain(name=\"hello world\")\neip712_structs.default_domain = my_domain\n\nassert foo.to_message() == foo.to_message(my_domain)\nassert foo.signable_bytes() == foo.signable_bytes(my_domain)\n```\n\n## Member Types\n\n### Basic Types\n\nEIP712's basic types map directly to solidity types:\n\n```python\nfrom eip712_structs import Address, Boolean, Bytes, Int, String, Uint\n\n\nAddress()  # Solidity's 'address'\nBoolean()  # 'bool'\nBytes()    # 'bytes'\nBytes(N)   # 'bytesN' - N must be an int from 1 through 32\nInt(N)     # 'intN' - N must be a multiple of 8, from 8 to 256\nString()   # 'string'\nUint(N)    # 'uintN' - N must be a multiple of 8, from 8 to 256\n```\n\nUse like:\n\n```python\nfrom eip712_structs import EIP712Struct, Address, Bytes\n\n\nclass Foo(EIP712Struct):\n    member_name_0 = Address()\n    member_name_1 = Bytes(5)\n    # etc.\n```\n\n### Struct References\n\nIn addition to holding basic types, EIP-712 structs may also hold other structs!\nUsage is almost the same - the difference is you don't \"instantiate\" the class.\n\nExample:\n\n```python\nfrom eip712_structs import EIP712Struct, String\n\n\nclass Dog(EIP712Struct):\n    name = String()\n    breed = String()\n\nclass Person(EIP712Struct):\n    name = String()\n    dog = Dog  # Take note - no parentheses!\n\n# Dog \"stands alone\"\nDog.encode_type()  # Dog(string name,string breed)\n\n# But Person knows how to include Dog\nPerson.encode_type()  # Person(string name,Dog dog)Dog(string name,string breed)\n```\n\nInstantiating the structs with nested values may be done a couple different ways:\n\n```python\n# Method one: set it to a struct\ndog = Dog(name=\"Mochi\", breed=\"Corgi\")\nperson = Person(name=\"E.M.\", dog=dog)\n\n# Method two: set it to a dict - the underlying struct is built for you\nperson = Person(\n    name=\"E.M.\",\n    dog={\n        \"name\": \"Mochi\",\n        \"breed\": \"Corgi\",\n    }\n)\n```\n\n### Arrays\n\nArrays are also supported for the standard:\n\n```python\narray_member = Array(<item_type>[, <optional_length>])\n```\n\n- `<item_type>` - The basic type or struct that will live in the array\n- `<optional_length>` - If given, the array is set to that length.\n\nFor example:\n\n```python\ndynamic_array = Array(String())      # String[] dynamic_array\nstatic_array  = Array(String(), 10)  # String[10] static_array\nstruct_array  = Array(MyStruct, 10)  # MyStruct[10] - again, don't instantiate structs like the basic types\n```\n\n## Development\n\nContributions always welcome.\n\nSetup a development environment:\n\n```bash\npython -m venv venv\n. venv/bin/activate\n```\n\nInstall dependencies:\n\n```bash\npython -m pip install -U pip\npython -m pip install -e '.[tests]'\n```\n\nRun tests:\n\n```bash\npython -m pytest\n```\n\nRun linters before submitting a PR:\n\n```bash\n./checks.sh\n```\n\n### Solidity Contract\n\nWhen changing the code of the Solidity test contract, you will have to regenerate its Python data code:\n\n```shell\ncd src/tests/integration/contract_sources && ./compile.sh\n```\n\nThat's it! Do not forget to commit those changes.\n\n## Deploying a New Version\n\n- Bump the version number in `__init__.py`, commit it into the `main` branch.\n- Make a release tag on the `main` branch in GitHub.\n- The CI will handle the PyPi publishing.\n\n## Shameless Plug\n\nOriginally written by [ConsenSys](https://consensys.net) for the world! And continued by [BoboTiG](https://github.com/BoboTiG)! :heart:\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2019 ConsenSys Copyright (c) 2024 Micka\u00ebl Schoentgen  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "A Python interface for EIP-712 struct construction.",
    "version": "2.0.1",
    "project_urls": {
        "Released Versions": "https://github.com/BoboTiG/py-eip712-structs-ng/releases",
        "Source": "https://github.com/BoboTiG/py-eip712-structs-ng",
        "Tracker": "https://github.com/BoboTiG/py-eip712-structs-ng/issues"
    },
    "split_keywords": [
        "eip712",
        "ethereum",
        "solidity"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "064b56afce4a163d7fa4b179d178b450c1532a33cb28162cedda3285bcc49706",
                "md5": "d557b5adc465ab63f19786be38be4a03",
                "sha256": "53671ab49eee7f07fbb1d8f93900e7168a955f664527f8cac7ef146dd6576eec"
            },
            "downloads": -1,
            "filename": "eip712_structs_ng-2.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d557b5adc465ab63f19786be38be4a03",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 13706,
            "upload_time": "2024-01-12T11:24:09",
            "upload_time_iso_8601": "2024-01-12T11:24:09.173283Z",
            "url": "https://files.pythonhosted.org/packages/06/4b/56afce4a163d7fa4b179d178b450c1532a33cb28162cedda3285bcc49706/eip712_structs_ng-2.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3d8bb3b40f26d9cd51875da3229ab7a453c5c3497f329c7c6d8bd449d08b08ea",
                "md5": "4869734f56f6c6bb732728e635d5a549",
                "sha256": "40663777f770c8eb869ae0947702e8ce7077a552938c2d46ab856747673746cb"
            },
            "downloads": -1,
            "filename": "eip712_structs_ng-2.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "4869734f56f6c6bb732728e635d5a549",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 23626,
            "upload_time": "2024-01-12T11:24:10",
            "upload_time_iso_8601": "2024-01-12T11:24:10.982495Z",
            "url": "https://files.pythonhosted.org/packages/3d/8b/b3b40f26d9cd51875da3229ab7a453c5c3497f329c7c6d8bd449d08b08ea/eip712_structs_ng-2.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-12 11:24:10",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "BoboTiG",
    "github_project": "py-eip712-structs-ng",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "eip712-structs-ng"
}
        
Elapsed time: 0.18215s