Creating and Transferring Wrapped Objects in Move
In this tutorial, we will learn how to create and transfer wrapped objects in the Move programming language. We will use a Wrapper struct that allows any custom data with the store ability to be freely transferable.
- Define the
Wrapperstruct: First, we will create aWrapperstruct with thekeyandstoreabilities. Thestoreability allows the object to be transferred without a custom transfer implementation.
module examples::wrapper {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
struct Wrapper<T: store> has key, store {
id: UID,
contents: T
}
}
- Implement functions for the
Wrapperstruct: Now, we will implement functions to create, read, and destroy theWrapper.
// View function to read contents of a `Wrapper`
public fun contents<T: store>(c: &Wrapper<T>): &T {
&c.contents
}
// Anyone can create a new object
public fun create<T: store>(contents: T, ctx: &mut TxContext): Wrapper<T> {
Wrapper {
contents,
id: object::new(ctx),
}
}
// Destroy `Wrapper` and get T
public fun destroy<T: store>(c: Wrapper<T>): T {
let Wrapper { id, contents } = c;
object::delete(id);
contents
}
}
- Use the
Wrapperin another module: In this example, we will create aProfileInfostruct and use theWrapperfunctionality to make it transferable.
module examples::profile {
use sui::transfer;
use sui::url::{Self, Url};
use std::string::{Self, String};
use sui::tx_context::{Self, TxContext};
// using Wrapper functionality
use 0x0::wrapper;
struct ProfileInfo has store {
name: String,
url: Url
}
}
- Implement functions for the
ProfileInfostruct: Now, we will implement functions to read thenameandurlfields from theProfileInfostruct.
public fun name(info: &ProfileInfo): &String {
&info.name
}
public fun url(info: &ProfileInfo): &Url {
&info.url
}
- Create a new profile and wrap it into a
Wrapper: We will create a functioncreate_profileto create a newProfileInfoinstance, wrap it into aWrapper, and transfer it to the transaction sender.
public fun create_profile(
name: vector<u8>, url: vector<u8>, ctx: &mut TxContext
) {
// create a new container and wrap ProfileInfo into it
let container = wrapper::create(ProfileInfo {
name: string::utf8(name),
url: url::new_unsafe_from_bytes(url)
}, ctx);
// `Wrapper` type is freely transferable
transfer::public_transfer(container, tx_context::sender(ctx))
}
}
With this implementation, you can now create and transfer wrapped objects containing custom data in the Move programming language. Finally we get
/// A freely transfererrable Wrapper for custom data.
module examples::wrapper {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
/// An object with `store` can be transferred in any
/// module without a custom transfer implementation.
struct Wrapper<T: store> has key, store {
id: UID,
contents: T
}
/// View function to read contents of a `Container`.
public fun contents<T: store>(c: &Wrapper<T>): &T {
&c.contents
}
/// Anyone can create a new object
public fun create<T: store>(
contents: T, ctx: &mut TxContext
): Wrapper<T> {
Wrapper {
contents,
id: object::new(ctx),
}
}
/// Destroy `Wrapper` and get T.
public fun destroy<T: store> (c: Wrapper<T>): T {
let Wrapper { id, contents } = c;
object::delete(id);
contents
}
}
module examples::profile {
use sui::transfer;
use sui::url::{Self, Url};
use std::string::{Self, String};
use sui::tx_context::{Self, TxContext};
// using Wrapper functionality
use 0x0::wrapper;
/// Profile information, not an object, can be wrapped
/// into a transferable container
struct ProfileInfo has store {
name: String,
url: Url
}
/// Read `name` field from `ProfileInfo`.
public fun name(info: &ProfileInfo): &String {
&info.name
}
/// Read `url` field from `ProfileInfo`.
public fun url(info: &ProfileInfo): &Url {
&info.url
}
/// Creates new `ProfileInfo` and wraps into `Wrapper`.
/// Then transfers to sender.
public fun create_profile(
name: vector<u8>, url: vector<u8>, ctx: &mut TxContext
) {
// create a new container and wrap ProfileInfo into it
let container = wrapper::create(ProfileInfo {
name: string::utf8(name),
url: url::new_unsafe_from_bytes(url)
}, ctx);
// `Wrapper` type is freely transferable
transfer::public_transfer(container, tx_context::sender(ctx))
}
}