Automatic code generation
Factori's first and foremost feature is the automatic generation of a static interface, or SDK (Software Development Kit), from a smart contract. This smart contract can be retrieved from a Tezos blockchain (mainnet, ghostnet, flextesa, etc...) or directly from its Michelson code. The interface may be generated once and for all, for instance if you are trying to interact with a fixed, on-chain contract, or it can be generated on the fly as many times as needed, e.g. during the development of a smart contract. If you have done any significant smart contract development on Tezos, it is very likely that you will have written, by hand, some or all of the code that Factori will generate for you in one second. It is even likely that you have rewritten it several times over, and taken significant time to debug your code when your smart contract inevitably changed and the data structures evolved. At least, at Functori, we found ourselves writing and debugging such tedious, boilerplate code over and over until we wrote this tool.
These interfaces can (currently) be generated in four distinct languages: Typescript, Python, C# and OCaml. There are specific subsections for each target programming language, listed below.
When you import a contract into your factori project, the SDK will be
found in a subfolder of
List of supported languages:
Note that each interface involves some amount of static typing (to the level permitted by each language). The next section goes into why that is a good idea.
Why static interfaces?
Why do we generate static code in Factori? What are the advantages over a dynamic interface?
First, note that although the interface generated by Factori is static, it can be re-generated instantly, either because Factori has been updated or because the contract we are working on is evolving (we are developing it, or it has been "updated" on-chain in one of the limited ways this is even possible). It is static in the sense that we don't discover the interface at (contract-interacting, not entrypoint-inferring) runtime.
By contrast, a dynamic interface would typically generate an object, at runtime, containing various methods to interact with the contract (this is the case, for instance, of Pytezos and Taquito).
Typically, when we interact with a smart contract, we want to define, in advance, a storage and some parameters of entrypoint calls. With a dynamic interface and in a dynamic language, we could do this by creating values belonging to the type generated by the framework for the storage and parameters. As a consequence, this type must already exist before runtime.
- First: not all languages are dynamic, and this would not work in e.g. OCaml. If the type of the storage is a record, then we can't possibly write OCaml code which will accept this record before it has been defined.
- Second objection: even if we are working in a dynamic language, we are not certain that, when the contract or the framework changes, these dynamically-generated types are not going to change as well. We will discover this when trying to run our code interacting with the blockchain. Or maybe we won't discover it immediately because duck typing will coerce the previous type into the new one in unpredictable ways. To protect against this, a natural response is to use static typing.
With a dynamic interface, we need either to trust the interface not to change, or to add static type annotations to our types ourselves. And of course, these static type annotations will need to change every time the contract changes, that is to say: some of what Factori does automatically will have to be done by hand.
All this being said, we make extensive use of the existing dynamic tools mentioned above: what we do is add a layer of static typing to protect the programmer against uncaught interface changes. Our work would not be possible without theirs, so shout out to Taquito, Netezos, and Pytezos.