Exports the sui_pkg_sdk! macro for generating Rust types from Move
source code and implementing relevant af_move_type traits.
Automates the conversion of Sui Move types to Rust. The goal is to extract as much information as possible at compile time about the Move types of a Sui package, generating equivalent Rust types that:
- are BCS-compatible with their on-chain counterparts, so that their contents can be deserialized from BCS bytes returned by RPCs
- embed type information based on their location (path) in a Move package + type parameters, so that a corresponding type tag can be easily constructed with just the missing information
- use the embedded type information when deserializing a
MoveInstanceto verify the type of incoming data, to avoid mistakenly deserializing a different type that has the same BCS bytes
See also:
af_move_typeaf_move_type_derivefor how the type tag information for a struct is derived from its declaration
Move types to Rust types
This macro allows callers to almost copy and paste Move struct declarations and get equivalent Rust types. Some additional steps may be necessary however:
- If
phantomkeywords are present, they must be substituted by!phantom - Struct fields should be Rust types. That means they must be in scope. Special Move types like
address,vector<T>andu256are automatically converted to equivalent Rust types.
The only requirement for a struct field type is that it has the same BCS representation as
the Move type for that field. You may use that to your advantage. For instance, if a
u256 is supposed to be interpreted as a fixed point number, you may define a custom
FixedP(U256) type that (de)serializes to/from u256 bytes but behaves like a fixed point
number.
Additionally, you may add any outter attributes, e.g. docs, to structs and their fields.
All MoveStructs created by this macro will have a pretty Display
using tabled as a backend.
Examples
use sui_pkg_sdk;
sui_pkg_sdk!;
Rust types clearing_house::{ClearingHouse, Vault} and keys::Position will be generated from
the macro call above.
Now suppose we have received a type tag and BCS contents of a Move object from an RPC call. We
can try deserializing it into a MoveInstance of one of these generated types
use ;
let type_tag: TypeTag;
let base64_bcs: String;
let instance = from_raw_type?;
println!;
A few things are happening here:
from_raw_typeis checking first thattype_tagmatches the declaration of the struct, i.e., it is of the form_::clearing_house::Vault<_::_::_>. Anything else will fail immediately- then, it tries to deserialize
clearing_house::Vaultfrom the BCS bytes
Finally, notice that we're accessing a type_ field in the Move instance. That's because a
VaultTypeTag was automatically generated:
Notice this contains information about the Vault's type tag that couldn't be derived at
compile time, namely, the address of the Move package defining the struct and the concrete type
of the generic type parameter.
One advantage of this type tag over carrying around the generic StructTag is that we can
access the type of the OTW directly, while to do so with the latter we'd have to check if
StructTag::type_params is not empty every time.
Generated code
Here's what the macro call above should generate.