Macro ptb

Source
macro_rules! ptb {
    ($($tt:tt)*) => { ... };
}
Expand description

Build a programmable transaction using Move-like syntax.

§Overview

This automatically creates and finishes a ProgrammableTransactionBuilder and allows users to declare:

  • packages the transaction uses
  • type arguments for functions
  • object/pure inputs for the transaction
  • Move calls
  • Built-in PTB commands

Every Move call and built-in PTB command declared withing the macro’s scope can be thought of as happening in ‘programmable transaction time’. In this way, the macro also helps users more clearly separate what’s being executed at Rust’s runtime and chain’s runtime (once the transaction is execute by validators).

§Packages

Move functions expect the ObjectId of their package in the transaction payload (see MoveCall). One can declare the packages using the syntax

let package_name = ObjectId::new(rand::random());
let object_id = ObjectId::new(rand::random());
af_ptbuilder::ptb!(
    package package_name;
    package package_name: object_id;
// ...
);

Similar to struct initialization syntax;

§Type arguments

Move functions that have type arguments expect TypeTag arguments in the transaction payload (see MoveCall). One can declare these variables using the syntax

let T = TypeTag::U8;
let type_tag = TypeTag::U32;
af_ptbuilder::ptb!(
    type T;
    type T = type_tag;
// ...
);

§Object/Pure inputs

ProgrammableTransactions need all their inputs declared upfront. One can declare the two types of inputs using the syntax

let clock = ObjectArg::CLOCK_IMM;
let object = ObjectArg::SharedObject {
    id: ObjectId::new(rand::random()),
    initial_shared_version: 1,
    mutable: true
};
let count = &0_u64;
af_ptbuilder::ptb!(
    input obj clock;
    input obj another: object;
    input pure count;
    input pure more: &1_u32;
    // ...
);

Similar to struct initialization syntax. input objs expect ObjectArg values and become object Inputs in the transaction payload. input pures expect any type T that is Serialize + ?Sized (see ProgrammableTransactionBuilder::pure for the internals) and become Input::Pures in the transaction payload. Within the macro scope, both variables are Argument::Inputs and can be used in Move/built-in calls.

§Move calls

Use the syntax

    package::module::function<T>(arg);

To include a MoveCall in the transaction payload. package,T, and arg must have been declared earlier. module and function are simply pure identifiers1. One can of course declare more than one type argument if the function requires, or none if the function does not have type parameters.

Functions that return can have their results assigned to a value or unpacked into several ones:

let result = package::module::function(a, b);
let (a, b) = package::module::function(arg);

These, of course, happen at ‘programmable transaction time’ and the result are Argument::Results that can be passed to other functions.

§Built-in commands

Sui PTBs have access to some calls that do not declare a package, module and function. These use the syntax:

command! Variant(x, y, ...);

The result of the command can be optionally assigned or unpacked (let a = or let (a, b) =). Variant refers to the variant of Command to use. See its documentation for more information.

§Example

use af_ptbuilder::ptb;
use af_sui_types::{
    IdentStr,
    ObjectId,
    StructTag,
    Address,
    TypeTag
};
use af_sui_types::ObjectArg;

let foo = ObjectId::new(rand::random());
let otw = TypeTag::Struct(Box::new(StructTag {
    address: "0x2".parse()?,
    module: IdentStr::cast("sui").to_owned(),
    name: IdentStr::cast("SUI").to_owned(),
    type_params: vec![],
}));
let registry = ObjectArg::SharedObject {
    id: ObjectId::new(rand::random()),
    initial_shared_version: 1,
    mutable: true,
};
let sender = Address::new(rand::random());

ptb!(
    package foo;

    type T = otw;

    input obj registry;
    input pure sender: &sender;

    let account = foo::registry::create_account<T>(registry);
    command! TransferObjects(vec![account], sender);
);