I have extended EIP-7495 with a section describing an additional type safety layer built on top of StableContainer
: PR
# Serialization and merkleization format
class Shape(StableContainer[4]):
side: Optional[uint16]
color: uint8
radius: Optional[uint16]
# Valid variants
class Square(Variant[Shape]):
side: uint16
color: uint8
class Circle(Variant[Shape]):
radius: uint16
color: uint8
class AnyShape(OneOf[Shape]):
@classmethod
def select_variant(cls, value: Shape, circle_allowed = True) -> Type[Shape]:
if value.radius is not None:
assert circle_allowed
return Circle
if value.side is not None:
return Square
assert False
In code, could pass around Square
/ Circle
for type-safety. While both of them would serialize / merkleize as Shape
. Example usage
Why should we care about stable serialization / merkleization?
- Someone may create a client today that is solely interested in the
color
of the shapes. They use a hypothetical API that provides them with just thecolor
+ Merkle proof that it is actually the shape’s color. - This client should not break, just because a future software upgrade adds rectangles with a width/height instead of side.
In context of Ethereum: A decentralized staking pool should not have to continuously issue software updates just because the location where information about slashing status of a validator and their balance is stored may change between forks. It should be possible to build forward-compatible clients that only require an update if actually used functionality changes. Likewise, a smart contract validating that someone paid their bill using a SSZ transaction / receipt merkle proof shouldn’t have to upgrade their contract just because a future transaction type adds an access list or a priority fee field – both those features are irrelevant for the smart contract’s purpose.