Kinobi
Generating Umi clients via Kinobi
Last updated
Generating Umi clients via Kinobi
Last updated
The Umi framework provides the basis for building Solana clients in JavaScript. It becomes a lot more powerful when programs offer Umi-compatible libraries as it allows end-users to simply plug their Umi instance into whichever helper functions they provide. To simplify and automate the process of creating Umi-compatible libraries, Umi provides a powerful code generator called Kinobi.
Kinobi introduces a language-agnostic representation of Solana clients which can be composed of one or several programs. It does this by using a tree of nodes that can be visited by Visitor
classes. Visitors can be used to update any aspect of the tree allowing developers to tailor the client to their needs. Once the tree is to the developer's liking, language-specific visitors can be used to generate the code for the target language or framework.
The good news is Kinobi ships with a RenderJavaScriptVisitor
that generates Umi-compatible libraries for us.
Here's a quick overview of how to use Kinobi and Umi to create JavaScript clients for Solana programs. Note that you might be interested in this thread that goes through this diagram step by step.
You may want to check the Kinobi documentation for more details but here's a quick overview of how to get started with Kinobi.
First, you need to install Kinobi:
Then, you need to create a JavaScript file — e.g. kinobi.js
— that creates and renders a Kinobi tree. This is done by creating a Kinobi
instance and passing it an array of paths to IDL files. You may want to check the Shank JS library to generate your IDL files. You can then use visitors to update the tree and render it as a Umi-compatible library via the RenderJavaScriptVisitor
. Here's an example.
Now, all you need to do is run this file with Node.js like so.
The first time you are generating your JS client, make sure to prepare the library as needed. You'll need to at least create its package.json
file, install its dependencies and provide a top-level index.ts
file that imports the generated folder.
Now that we know how to generate Umi-compatible libraries via Kinobi, let's take a look at what they can do.
Kinobi-generated libraries provide a serializer for each type, account and instruction defined on the program. It also exports the two TypeScript types required to create the serializer — i.e. its From
and To
type parameters. It will suffix the From
type with Args
to distinguish the two. For instance, if you have a MyType
type defined in your IDL, you can use the following code to serialize and deserialize it.
For instructions, the name of the type is suffixed with InstructionData
and, for accounts, it is suffixed with AccountData
. This allows the unsuffixed account name to be used as an Account<T>
type. For example, if you have a Token
account and a Transfer
instruction on your program, you will get the following types and serializers.
If a generated type is identified as a data enum, additional helper methods will be created to help improve the developer experience. For instance, say you have the following data enum type generated.
Then, on top of generating the types and getMessageSerializer
function, it will also generate a message
and isMessage
function that can be used to create a new data enum and check the type of its variant respectively.
Kinobi will also provide additional helper methods for accounts, providing us with an easy way to fetch and deserialize them. Assuming the account name is Metadata
here are the additional helper methods available to you.
You may want to check the documentation on GpaBuilder
s to learn more about what they can do.
Each generated instruction will also have its own function that can be used to create a transaction builder containing the instruction. For instance, if you have a Transfer
instruction, it will generate a transfer
function returning a TransactionBuilder
.
Because transaction builders can be combined together, this allows us to easily create transactions that contain multiple instructions like so.
Kinobi will also generate a function that returns a Program
type for each program defined in the client as well as some helpers to access them. For instance, say your client defines a MplTokenMetadata
program, then the following helpers will be generated.
Note that Kinobi does not auto-generate a Umi plugin for your client allowing you to customize it however you want. That means you'll need to create a plugin yourself and, at the very least, register the programs defined by your client. Here's an example using the MplTokenMetadata
program.
Additionally, each program generates a custom ProgramError
for each error it may throw. For instance, if your program defines a UpdateAuthorityIncorrect
error, it will generate the following class.
Each generated error is also registered in a codeToErrorMap
and a nameToErrorMap
allowing the library to provide two helper methods that can find any error class from its name or code.
Note that these methods are used by the createMplTokenMetadataProgram
function to fill the getErrorFromCode
and getErrorFromName
functions of the Program
object.