77
88class EIP712Type :
99 """The base type for members of a struct.
10+
11+ Generally you wouldn't use this - instead, see the subclasses below. Or you may want an EIP712Struct instead.
1012 """
1113 def __init__ (self , type_name : str , none_val : Any ):
1214 self .type_name = type_name
1315 self .none_val = none_val
1416
1517 def encode_value (self , value ) -> bytes :
18+ """Given a value, verify it and convert into the format required by the spec.
19+
20+ :param value: A correct input value for the implemented type.
21+ :return: A 32-byte object containing encoded data
22+ """
1623 if value is None :
1724 return self ._encode_value (self .none_val )
1825 else :
1926 return self ._encode_value (value )
2027
2128 def _encode_value (self , value ) -> bytes :
22- """Given a value, verify it and convert into the format required by the spec .
29+ """Must be implemented by subclasses, handles value encoding on a case-by-case basis .
2330
24- :param value: A correct input value for the implemented type.
25- :return: A 32-byte object containing encoded data
31+ Don't call this directly - use ``.encode_value(value)`` instead.
2632 """
2733 pass
2834
@@ -38,6 +44,13 @@ def __hash__(self):
3844
3945class Array (EIP712Type ):
4046 def __init__ (self , member_type : Union [EIP712Type , Type [EIP712Type ]], fixed_length : int = 0 ):
47+ """Represents an array member type.
48+
49+ Example:
50+ a1 = Array(String()) # string[] a1
51+ a2 = Array(String(), 8) # string[8] a2
52+ a3 = Array(MyStruct) # MyStruct[] a3
53+ """
4154 fixed_length = int (fixed_length )
4255 if fixed_length == 0 :
4356 type_name = f'{ member_type .type_name } []'
@@ -48,31 +61,37 @@ def __init__(self, member_type: Union[EIP712Type, Type[EIP712Type]], fixed_lengt
4861 super (Array , self ).__init__ (type_name , [])
4962
5063 def _encode_value (self , value ):
64+ """Arrays are encoded by concatenating their encoded contents, and taking the keccak256 hash."""
5165 encoder = self .member_type
5266 encoded_values = [encoder .encode_value (v ) for v in value ]
5367 return keccak (b'' .join (encoded_values ))
5468
5569
5670class Address (EIP712Type ):
5771 def __init__ (self ):
72+ """Represents an ``address`` type."""
5873 super (Address , self ).__init__ ('address' , 0 )
5974
6075 def _encode_value (self , value ):
61- # Some smart conversions - need to get an address as an int
76+ """Addresses are encoded like Uint160 numbers."""
77+
78+ # Some smart conversions - need to get the address to a numeric before we encode it
6279 if isinstance (value , bytes ):
6380 v = to_int (value )
6481 elif isinstance (value , str ):
6582 v = to_int (hexstr = value )
6683 else :
67- v = value
84+ v = value # Fallback, just use it as-is.
6885 return Uint (160 ).encode_value (v )
6986
7087
7188class Boolean (EIP712Type ):
7289 def __init__ (self ):
90+ """Represents a ``bool`` type."""
7391 super (Boolean , self ).__init__ ('bool' , False )
7492
7593 def _encode_value (self , value ):
94+ """Booleans are encoded like the uint256 values of 0 and 1."""
7695 if value is False :
7796 return Uint (256 ).encode_value (0 )
7897 elif value is True :
@@ -83,6 +102,15 @@ def _encode_value(self, value):
83102
84103class Bytes (EIP712Type ):
85104 def __init__ (self , length : int = 0 ):
105+ """Represents a solidity bytes type.
106+
107+ Length may be used to specify a static ``bytesN`` type. Or 0 for a dynamic ``bytes`` type.
108+ Example:
109+ b1 = Bytes() # bytes b1
110+ b2 = Bytes(10) # bytes10 b2
111+
112+ ``length`` MUST be between 0 and 32, or a ValueError is raised.
113+ """
86114 length = int (length )
87115 if length == 0 :
88116 # Special case: Length of 0 means a dynamic bytes type
@@ -95,6 +123,7 @@ def __init__(self, length: int = 0):
95123 super (Bytes , self ).__init__ (type_name , b'' )
96124
97125 def _encode_value (self , value ):
126+ """Static bytesN types are encoded by right-padding to 32 bytes. Dynamic bytes types are keccak256 hashed."""
98127 if self .length == 0 :
99128 return keccak (value )
100129 else :
@@ -105,39 +134,58 @@ def _encode_value(self, value):
105134
106135
107136class Int (EIP712Type ):
108- def __init__ (self , length : int ):
137+ def __init__ (self , length : int = 256 ):
138+ """Represents a signed int type. Length may be given to specify the int length in bits. Default length is 256
139+
140+ Example:
141+ i1 = Int(256) # int256 i1
142+ i2 = Int() # int256 i2
143+ i3 = Int(128) # int128 i3
144+ """
109145 length = int (length )
110146 if length < 8 or length > 256 or length % 8 != 0 :
111147 raise ValueError (f'Int length must be a multiple of 8, between 8 and 256. Got: { length } ' )
112148 self .length = length
113149 super (Int , self ).__init__ (f'int{ length } ' , 0 )
114150
115151 def _encode_value (self , value : int ):
152+ """Ints are encoded by padding them to 256-bit representations."""
116153 value .to_bytes (self .length // 8 , byteorder = 'big' , signed = True ) # For validation
117154 return value .to_bytes (32 , byteorder = 'big' , signed = True )
118155
119156
120157class String (EIP712Type ):
121158 def __init__ (self ):
159+ """Represents a string type."""
122160 super (String , self ).__init__ ('string' , '' )
123161
124162 def _encode_value (self , value ):
163+ """Strings are encoded by taking the keccak256 hash of their contents."""
125164 return keccak (text = value )
126165
127166
128167class Uint (EIP712Type ):
129- def __init__ (self , length : int ):
168+ def __init__ (self , length : int = 256 ):
169+ """Represents an unsigned int type. Length may be given to specify the int length in bits. Default length is 256
170+
171+ Example:
172+ ui1 = Uint(256) # uint256 ui1
173+ ui2 = Uint() # uint256 ui2
174+ ui3 = Uint(128) # uint128 ui3
175+ """
130176 length = int (length )
131177 if length < 8 or length > 256 or length % 8 != 0 :
132178 raise ValueError (f'Uint length must be a multiple of 8, between 8 and 256. Got: { length } ' )
133179 self .length = length
134180 super (Uint , self ).__init__ (f'uint{ length } ' , 0 )
135181
136182 def _encode_value (self , value : int ):
183+ """Uints are encoded by padding them to 256-bit representations."""
137184 value .to_bytes (self .length // 8 , byteorder = 'big' , signed = False ) # For validation
138185 return value .to_bytes (32 , byteorder = 'big' , signed = False )
139186
140187
188+ # This helper dict maps solidity's type names to our EIP712Type classes
141189solidity_type_map = {
142190 'address' : Address ,
143191 'bool' : Boolean ,
@@ -156,21 +204,24 @@ def from_solidity_type(solidity_type: str):
156204 if match is None :
157205 return None
158206
159- type_name = match .group (1 )
160- opt_len = match .group (2 )
161- is_array = match .group (3 )
162- array_len = match .group (4 )
207+ type_name = match .group (1 ) # The type name, like the "bytes" in "bytes32"
208+ opt_len = match .group (2 ) # An optional length spec, like the "32" in "bytes32"
209+ is_array = match .group (3 ) # Basically just checks for square brackets
210+ array_len = match .group (4 ) # For fixed length arrays only, this is the length
163211
164212 if type_name not in solidity_type_map :
213+ # Only supporting basic types here - return None if we don't recognize it.
165214 return None
166215
216+ # Construct the basic type
167217 base_type = solidity_type_map [type_name ]
168218 if opt_len :
169219 type_instance = base_type (int (opt_len ))
170220 else :
171221 type_instance = base_type ()
172222
173223 if is_array :
224+ # Nest the aforementioned basic type into an Array.
174225 if array_len :
175226 result = Array (type_instance , int (array_len ))
176227 else :
0 commit comments