Skip to content

JumpCrypto/podite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Podite

Serializing and Deserializing "Plain Old Data"

Podite makes interacting with on-chain programs easy - if a Podite dataclass looks exactly like its on-chain rust equivalent, then you know it serializes the same way too. No more arcane serialization libraries, no more surprises - get exactly what you expect.

Podite was developed alongside Solmate, a client code generator for Solana smart-contracts using the Anchor Framework in rust.

Key Principles

Simple - python types visually mirror on-chain rust types

Pythonic - follows conventions from native dataclass

Smart Defaults - automatically detects Borsh and ZeroCopy (bytemuck) account formats during deserialization, but allows manually specifying format

Extensible - provide custom serialization to support non-standard account layouts or optimize in performance-critical components

Quick Start

Defining a message is as simple as:

from podite import pod, U8, Str


@pod
class Message:
    message: Str[1000]  # max_length of string
    likes: U8


original = Message("Serialization doesn't have to be painful", 19020)
Message.to_bytes(original)

This has a byte representation of:

| length of string | utf-8 string contents | likes  | 
-----------------------------------------------------
| 4 bytes          | str-len bytes         | 1 byte |

Here is a corresponding rust struct that can deserialize this message:

use borsh::BorshDeserialize;

#[derive(BorshDeserialize)]
struct Message {
    message: String,
    likes: u8,
}

Pod was originally developed to interact with rust smart-contracts on solana where compact, quick and consistent ser( de) (serialization and deserialization) are crucial. Pod by default serializes to borsh format used by most solana programs, but also supports zero-copy ser(de) to C format structs.

from podite import FORMAT_ZERO_COPY

# ... previous 

_bytes = Message.to_bytes(original, format=FORMAT_ZERO_COPY)
round_tripped = Message.from_bytes(_bytes)
use bytemuck::{Pod, ZeroCopy};

#[derive(Pod, ZeroCopy, Clone, Copy, Debug)]
struct Message {
    message: String,
    likes: u8,
}

Notice that to deserialize, the format did not need to be specified. As there are currently only 2
binary formats supported by pod, we can compare the number of bytes we are given to the fixed size of a zero-copy serialized object. If they are the same, we can deserialize using zero-copy and if they are different, we can attempt to deserialize using the borsh format.

If you do not want to rely on this implicit format recognition, you can explicitly write the format like this:

Message.from_bytes(_bytes, format=...)

What's with the name?

During development, the rust side used the Pod (short for plain-old-data) trait from bytemuck to read/write the in-memory format, so for the python side we originally used the same name. Podite means the leg of a crustacean which we liked as an homage to rustaceans (aka rust users) and, like the library itself, adds some spice to an otherwise bland topic.

Installation

Requires python >= 3.9

poetry install podite

or

pip install podite

Development Setup

If you want to contribute to Solmate, follow these steps to get set up:

  1. Install poetry
  2. Install dev dependencies:
poetry install
  1. Code your change and add tests
  2. Verify tests
poetry run pytest
  1. Open Pr!