Skip to content

Commit da1aa2f

Browse files
Support string revision in TypedData (#1383)
1 parent 2ad5d45 commit da1aa2f

File tree

3 files changed

+95
-36
lines changed

3 files changed

+95
-36
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"types": {
3+
"StarknetDomain": [
4+
{ "name": "name", "type": "shortstring" },
5+
{ "name": "version", "type": "shortstring" },
6+
{ "name": "chainId", "type": "shortstring" },
7+
{ "name": "revision", "type": "shortstring" }
8+
],
9+
"Example": [
10+
{ "name": "n0", "type": "felt" },
11+
{ "name": "n1", "type": "bool" },
12+
{ "name": "n2", "type": "string" },
13+
{ "name": "n3", "type": "selector" },
14+
{ "name": "n4", "type": "u128" },
15+
{ "name": "n5", "type": "i128" },
16+
{ "name": "n6", "type": "ContractAddress" },
17+
{ "name": "n7", "type": "ClassHash" },
18+
{ "name": "n8", "type": "timestamp" },
19+
{ "name": "n9", "type": "shortstring" }
20+
]
21+
},
22+
"primaryType": "Example",
23+
"domain": {
24+
"name": "StarkNet Mail",
25+
"version": "1",
26+
"chainId": "1",
27+
"revision": "1"
28+
},
29+
"message": {
30+
"n0": "0x3e8",
31+
"n1": true,
32+
"n2": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
33+
"n3": "transfer",
34+
"n4": "0x3e8",
35+
"n5": "-170141183460469231731687303715884105727",
36+
"n6": "0x3e8",
37+
"n7": "0x3e8",
38+
"n8": 1000,
39+
"n9": "transfer"
40+
}
41+
}

starknet_py/utils/typed_data.py

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from starknet_py.net.client_utils import _to_rpc_felt
1313
from starknet_py.net.models.typed_data import DomainDict, Revision, TypedDataDict
1414
from starknet_py.net.schemas.common import RevisionField
15+
from starknet_py.serialization.data_serializers import ByteArraySerializer
1516
from starknet_py.utils.merkle_tree import MerkleTree
1617

1718

@@ -91,40 +92,6 @@ class BasicType(Enum):
9192
TIMESTAMP = "timestamp"
9293

9394

94-
def _encode_value_v1(basic_type: BasicType, value: Union[int, str]) -> Optional[int]:
95-
if basic_type in (
96-
BasicType.FELT,
97-
BasicType.SHORT_STRING,
98-
BasicType.CONTRACT_ADDRESS,
99-
BasicType.CLASS_HASH,
100-
) and isinstance(value, (int, str)):
101-
return parse_felt(value)
102-
103-
if basic_type in (
104-
BasicType.U128,
105-
BasicType.TIMESTAMP,
106-
) and isinstance(value, (int, str)):
107-
return encode_u128(value)
108-
109-
if basic_type == BasicType.I128 and isinstance(value, (int, str)):
110-
return encode_i128(value)
111-
112-
return None
113-
114-
115-
def _encode_value_v0(
116-
basic_type: BasicType,
117-
value: Union[int, str],
118-
) -> Optional[int]:
119-
if basic_type in (
120-
BasicType.FELT,
121-
BasicType.STRING,
122-
) and isinstance(value, (int, str)):
123-
return parse_felt(value)
124-
125-
return None
126-
127-
12895
@dataclass(frozen=True)
12996
class TypedData:
13097
"""
@@ -167,6 +134,45 @@ def to_dict(self) -> dict:
167134
def _is_struct(self, type_name: str) -> bool:
168135
return type_name in self.types
169136

137+
def _encode_value_v1(
138+
self, basic_type: BasicType, value: Union[int, str, dict, list]
139+
) -> Optional[int]:
140+
if basic_type in (
141+
BasicType.FELT,
142+
BasicType.SHORT_STRING,
143+
BasicType.CONTRACT_ADDRESS,
144+
BasicType.CLASS_HASH,
145+
) and isinstance(value, (int, str)):
146+
return parse_felt(value)
147+
148+
if basic_type in (
149+
BasicType.U128,
150+
BasicType.TIMESTAMP,
151+
) and isinstance(value, (int, str)):
152+
return encode_u128(value)
153+
154+
if basic_type == BasicType.I128 and isinstance(value, (int, str)):
155+
return encode_i128(value)
156+
157+
if basic_type == BasicType.STRING and isinstance(value, str):
158+
return self._encode_long_string(value)
159+
160+
return None
161+
162+
# pylint: disable=no-self-use
163+
def _encode_value_v0(
164+
self,
165+
basic_type: BasicType,
166+
value: Union[int, str, dict, list],
167+
) -> Optional[int]:
168+
if basic_type in (
169+
BasicType.FELT,
170+
BasicType.STRING,
171+
) and isinstance(value, (int, str)):
172+
return parse_felt(value)
173+
174+
return None
175+
170176
def _encode_value(
171177
self,
172178
type_name: str,
@@ -190,11 +196,11 @@ def _encode_value(
190196
if self.domain.resolved_revision == Revision.V0 and isinstance(
191197
value, (str, int)
192198
):
193-
encoded_value = _encode_value_v0(basic_type, value)
199+
encoded_value = self._encode_value_v0(basic_type, value)
194200
elif self.domain.resolved_revision == Revision.V1 and isinstance(
195201
value, (str, int)
196202
):
197-
encoded_value = _encode_value_v1(basic_type, value)
203+
encoded_value = self._encode_value_v1(basic_type, value)
198204

199205
if encoded_value is not None:
200206
return encoded_value
@@ -353,6 +359,11 @@ def _get_merkle_tree_leaves_type(self, context: TypeContext) -> str:
353359

354360
return target_type.contains
355361

362+
def _encode_long_string(self, value: str) -> int:
363+
byte_array_serializer = ByteArraySerializer()
364+
serialized_values = byte_array_serializer.serialize(value)
365+
return self._hash_method.hash_many(serialized_values)
366+
356367

357368
def parse_felt(value: Union[int, str]) -> int:
358369
if isinstance(value, int):

starknet_py/utils/typed_data_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class CasesRev0(Enum):
3333
class CasesRev1(Enum):
3434
TD = "typed_data_rev_1_example.json"
3535
TD_FELT_MERKLE_TREE = "typed_data_rev_1_felt_merkletree_example.json"
36+
TD_BASIC_TYPES = "typed_data_rev_1_basic_types_example.json"
3637

3738

3839
def load_typed_data(file_name: str) -> TypedData:
@@ -68,6 +69,8 @@ def test_parse_felt(value, result):
6869
"Mail(from:Person,to:Person,posts_len:felt,posts:Post*)Person(name:felt,wallet:felt)Post(title:felt,content:felt)"),
6970
(CasesRev1.TD.value, "Mail",
7071
""""Mail"("from":"Person","to":"Person","contents":"felt")"Person"("name":"felt","wallet":"felt")"""),
72+
(CasesRev1.TD_BASIC_TYPES.value, "Example",
73+
""""Example"("n0":"felt","n1":"bool","n2":"string","n3":"selector","n4":"u128","n5":"i128","n6":"ContractAddress","n7":"ClassHash","n8":"timestamp","n9":"shortstring")"""),
7174
(CasesRev1.TD_FELT_MERKLE_TREE.value, "Example", """"Example"("value":"felt","root":"merkletree")""")
7275
],
7376
)
@@ -96,6 +99,8 @@ def test_encode_type(example, type_name, encoded_type):
9699
(CasesRev1.TD.value, "StarknetDomain", "0x1ff2f602e42168014d405a94f75e8a93d640751d71d16311266e140d8b0a210"),
97100
(CasesRev1.TD.value, "Person", "0x30f7aa21b8d67cb04c30f962dd29b95ab320cb929c07d1605f5ace304dadf34"),
98101
(CasesRev1.TD.value, "Mail", "0x560430bf7a02939edd1a5c104e7b7a55bbab9f35928b1cf5c7c97de3a907bd"),
102+
(
103+
CasesRev1.TD_BASIC_TYPES.value, "Example", "0x1f94cd0be8b4097a41486170fdf09a4cd23aefbc74bb2344718562994c2c111"),
99104
(CasesRev1.TD_FELT_MERKLE_TREE.value, "Example",
100105
"0x160b9c0e8a7c561f9c5d9e3cc2990a1b4d26e94aa319e9eb53e163cd06c71be"),
101106
],
@@ -150,6 +155,8 @@ def test_struct_hash(example, type_name, attr_name, struct_hash):
150155
"0x5d28fa1b31f92e63022f7d85271606e52bed89c046c925f16b09e644dc99794"),
151156
(CasesRev1.TD.value, "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
152157
"0x7f6e8c3d8965b5535f5cc68f837c04e3bbe568535b71aa6c621ddfb188932b8"),
158+
(CasesRev1.TD_BASIC_TYPES.value, "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
159+
"0x2d80b87b8bc32068247c779b2ef0f15f65c9c449325e44a9df480fb01eb43ec"),
153160
(CasesRev1.TD_FELT_MERKLE_TREE.value, "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
154161
"0x4f706783e0d7d0e61433d41343a248a213e9ab341d50ba978dfc055f26484c9")
155162
],

0 commit comments

Comments
 (0)