You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our contract has a very large and complex storage type, and a large number of entrypoints, with a variety of parameter types. When built monolithically, the resulting compiled contract size is prohibitively large, ~60kb. As a result, we have been exploring storing entrypoint code as serialised lambdas in a big_map, borrowing techniques from Dani's excellent article on the topic.
Naïve serialisation of endpoint functions
Our starting point was deserialising each lazy endpoint function into its exact differentiated function type in the contract's main (i.e. state -> specific_params -> (ops, state)), but this leads to lots of local duplication of the storage and parameter types, such that the main contract size ballooned to 97kb: the entrypoint code had been removed from the main contract, but additional type costs dwarf the cost of the code.
Monomorphisation of serialized functions
Presumably to address this issue, Dani's technique involves monomorphising the deserialised endpoint functions by making them all take their parameters in Bytes-serialised form (i.e. state -> bytes -> (ops, state)). This does not work directly for our use case because some of our endpoint parameters involve tickets, which are not serializable.
We have instead tried an approach of monomorphisation via a sum type which combines the possible params of all the entrypoints: the serialised entrypoints then accept the large sum type and internally match partially on their corresponding constructor (i.e. state -> all_possible_params -> (ops, state)). In this approach, the act of typechecking the unpacked function then becomes spectacularly expensive, exceeding all reasonable gas limits: both the large storage and large parameter sum type appear in the signature of the serialised function, and unification of this with the call site appears to make Michelson explode.
This approach is therefore not viable, but it has yielded a code generation facility that makes it easier to experiment with the packing scheme.
Proposed approach
Our next approach is to read (and thereby consume) all incoming tickets in the main contract, so that the endpoint implementation functions themselves take parameters that are entirely serializable, then we can wrap them into lazy serialized functions and apply Dani's monomorphisation technique. This should allow us to avoid referencing any large parameter sum type in individual lazy functions.
The text was updated successfully, but these errors were encountered:
This is now done: the core contract weighs in at 10kb, and the endpoints are packed as functions state -> bytes -> (ops, state). Tickets supplied by users are read and verified at the boundary inside the core contract, and their contents are included in the payloads serialised as bytes.
Example cost of calling touch (with an empty checker):
Fee to the baker: ꜩ0.045499
Expected counter: 8
Gas limit: 452271
Background
Our contract has a very large and complex storage type, and a large number of entrypoints, with a variety of parameter types. When built monolithically, the resulting compiled contract size is prohibitively large, ~60kb. As a result, we have been exploring storing entrypoint code as serialised lambdas in a
big_map
, borrowing techniques from Dani's excellent article on the topic.Naïve serialisation of endpoint functions
Our starting point was deserialising each lazy endpoint function into its exact differentiated function type in the contract's
main
(i.e.state -> specific_params -> (ops, state)
), but this leads to lots of local duplication of the storage and parameter types, such that the main contract size ballooned to 97kb: the entrypoint code had been removed from the main contract, but additional type costs dwarf the cost of the code.Monomorphisation of serialized functions
Presumably to address this issue, Dani's technique involves monomorphising the deserialised endpoint functions by making them all take their parameters in
Bytes
-serialised form (i.e.state -> bytes -> (ops, state)
). This does not work directly for our use case because some of our endpoint parameters involve tickets, which are not serializable.We have instead tried an approach of monomorphisation via a sum type which combines the possible params of all the entrypoints: the serialised entrypoints then accept the large sum type and internally match partially on their corresponding constructor (i.e.
state -> all_possible_params -> (ops, state)
). In this approach, the act of typechecking the unpacked function then becomes spectacularly expensive, exceeding all reasonable gas limits: both the large storage and large parameter sum type appear in the signature of the serialised function, and unification of this with the call site appears to make Michelson explode.This approach is therefore not viable, but it has yielded a code generation facility that makes it easier to experiment with the packing scheme.
Proposed approach
Our next approach is to read (and thereby consume) all incoming tickets in the main contract, so that the endpoint implementation functions themselves take parameters that are entirely serializable, then we can wrap them into lazy serialized functions and apply Dani's monomorphisation technique. This should allow us to avoid referencing any large parameter sum type in individual lazy functions.
The text was updated successfully, but these errors were encountered: