EIP-712 Structs

A python interface for simple EIP-712 struct construction.
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 (e.g., import struct).
Read the proposal:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
Supported Python Versions
3.63.7
Install
pip install eip712-structs
Usage
See 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:
struct MyStruct {
string some_string;
uint256 some_number;
}
With this module, that would look like:
# Make a unique domain
from eip712_structs import make_domain
domain = make_domain(name='Some name', version='1.0.0') # Make a Domain Separator
# Define your struct type
from eip712_structs import EIP712Struct, String, Uint
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)
# Into a message dict (serializable to JSON) - domain required
my_msg = mine.to_message(domain)
# Into signable bytes - domain required
my_bytes = mine.signable_bytes(domain)
See 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.
from eip712_structs import EIP712Struct, Address
class Message(EIP712Struct):
pass
Message.to = Address()
setattr(Message, 'from', Address())
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:
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()
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.
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:
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, EIP712 structs may also hold other structs! Usage is almost the same - the difference is you don't "instantiate" the class.
Example:
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:
# 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.
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:
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.
Install dependencies:
pip install -r requirements.txt
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.
- 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. - Make a release tag on the master branch in Github. Travis should handle the rest.
Shameless Plug
Written by ConsenSys for ourselves and the community! ❤️
Metadata
Release files for eip712-structs 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| eip712-structs-1.1.0.tar.gz | 12.9 kB | Without provenance | Details for eip712-structs-1.1.0.tar.gz |
Release files / eip712-structs-1.1.0.tar.gz
| Download URL | eip712-structs-1.1.0.tar.gz |
|---|---|
| Size | 12.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b24400aef07b4d0287fb9bf8ce02b0abbe80c476d1b67222a7c5158df3a3e38d
|
|
BLAKE2b-256 checksum How to use checksums |
36e00c79d27da8918f7642cba8ad4b0e6176ff7a8b4774f363c6ceb2513474be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.1 CPython/3.7.1
|
Security for eip712-structs 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.