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);
);