Skip to main content
Apophis allows integrating new types for de/serialization with standard encodings (currently ProtoBuf and Amino) by following a specific pattern. Generally, the default encodings expect your messages to be implemented as classes. The ProtoBuf and Amino encoding subsystems define the following types, as well as these methods for registration of new default types:
@apophis-sdk/core/encoding/protobuf/any.ts
@apophis-sdk/cosmos/encoding/amino.ts
With v0.3.1, a new solution for protobufs is introduced and will eventually supersede the existing in v0.4.0:
This new method leverages a new in-house protobuf abstraction hiproto which uses a zod-like API to define protobuf schemas. Correspondingly, the Cosmos Bank Send message is implemented as follows:
As the naming suggests, these are merely “default” types. Using the Middleware Subsystem, nearly every single aspect of the Apophis SDK can be fine-tuned to the requirements of a specific blockchain, with these default types used as fallbacks. The beauty of this approach is that the same message class can be used for a variety of blockchains, and can even be somewhat standardized across entirely different blockchains. Likewise, it allows deserializing transactions into semantic objects, rather than just raw bytes or plain data objects.

Amino

Amino is a legacy JSON sub-specification for blockchains that use the Cosmos SDK. It is superceded by ProtoBuf, but is still widely used, and the only format Ledger hardware wallets support in Cosmos. Amino is defined in the @apophis-sdk/cosmos package, but listed here for completeness.

ProtoBuf

ProtoBuf is a widely adopted data transmission format developed and primarily maintained by Google. Unlike Amino, it is a size-optimized binary format, and requires more complex de/serialization. Formerly, you had to provide the correct de/serialization for your protobuf types yourself. With v0.3.1, we are now migrating to hiproto. Following is an example of how I’ve implemented the Coin schema:
You can then reference this schema in your own messages:
./protobuf/schema.ts
And de/serialize it as follows:
You can then register your message class with the registerDefaultProtobufSchema function:
./messages/bank.ts
Note that this is currently in a transitional state. For now, Apophis’ Cosmos module still uses the cosmjs-types package for its standard messages, but these will be gradually migrated to the new hiproto-based approach. The existing methodology is now marked as deprecated and will be removed in v0.4.0.

Custom Amino Marshaller

You can further apply a custom marshaller for Amino types. This is useful where data is no longer reliably reconstructible from serialized form. For example the StoreCode message of the CosmWasm module:

Required Fields

By default, Protobuf message fields are all optional. Even so, clients may require certain fields be present or otherwise transactions are rejected. Cross-compatibility with Amino also may require certain fields to be present, even if empty, such as funds on a Contracts.Execute message. Unfortunately, for legacy Amino support, you must pay attention to required and optional fields yourself. Otherwise, the full node may reconstruct the signature payload differently from the data you have provided to the signer, causing the transaction to be rejected immediately. The Contracts.Execute schema may look like this:
Given this schema, you shouldn’t need any further changes to the default pattern.

Transforms

There are a few general-purpose transforms for protobuf fields available in @apophis-sdk/cosmos/encoding/protobuf/core.js:
  • bigintTransform: Transforms a string to a bigint and vice versa.
  • aminoTransform: Applies Amino.normalize to an arbitrary value, including sorting the object’s keys. In general, whenever you use hpb.json, you should also apply this transform.

Other encodings

Other blockchains such as Solana and Ethereum use different encodings. When the time comes to develop integrations for these ecosystems, I will add corresponding encoding subsystems to this SDK. If you are looking to integrate your own encoding, please follow the above pattern, and develop a corresponding middleware. Your middleware may use the ["core", "init"] hook to register existing messages (if applicable, e.g. Bank.Send) for your new encoding so SDK consumers may seamlessly integrate your blockchain.