Skip to content

property_map - [mainnet]

PropertyMap provides generic metadata support for AptosToken. It is a specialization of SimpleMap that enforces strict typing with minimal storage use by using constant u64 to represent types and storing values in bcs format.

use 0x1::bcs;
use 0x1::error;
use 0x1::from_bcs;
use 0x1::object;
use 0x1::simple_map;
use 0x1::string;
use 0x1::type_info;
use 0x1::vector;

Constants

Property value does not match expected type

const ETYPE_MISMATCH: u64 = 6;
const ADDRESS: u8 = 7;
const BOOL: u8 = 0;
const BYTE_VECTOR: u8 = 8;

The property key already exists

const EKEY_ALREADY_EXISTS_IN_PROPERTY_MAP: u64 = 2;

Property key and type counts do not match

const EKEY_TYPE_COUNT_MISMATCH: u64 = 5;

Property key and value counts do not match

const EKEY_VALUE_COUNT_MISMATCH: u64 = 4;

The property map does not exist

const EPROPERTY_MAP_DOES_NOT_EXIST: u64 = 1;

The key of the property is too long

const EPROPERTY_MAP_KEY_TOO_LONG: u64 = 8;

The number of properties exceeds the maximum

const ETOO_MANY_PROPERTIES: u64 = 3;

Invalid value type specified

const ETYPE_INVALID: u64 = 7;

Maximum number of items in a PropertyMap

const MAX_PROPERTY_MAP_SIZE: u64 = 1000;

Maximum number of characters in a property name

const MAX_PROPERTY_NAME_LENGTH: u64 = 128;
const STRING: u8 = 9;
const U128: u8 = 5;
const U16: u8 = 2;
const U256: u8 = 6;
const U32: u8 = 3;
const U64: u8 = 4;
const U8: u8 = 1;

Structs

PropertyValue

A typed value for the PropertyMap to ensure that typing is always consistent

struct PropertyValue has drop, store
Fields
type: u8
value: vector<u8>

MutatorRef

A mutator ref that allows for mutation of the property map

struct MutatorRef has drop, store
Fields
self: address

Resources

PropertyMap

A Map for typed key to value mapping, the contract using it should keep track of what keys are what types, and parse them accordingly.

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct PropertyMap has drop, key
Fields
inner: simple_map::SimpleMap<string::String, property_map::PropertyValue>

Functions

init

public fun init(ref: &object::ConstructorRef, container: property_map::PropertyMap)
Implementation
public fun init(ref: &ConstructorRef, container: PropertyMap) {
let signer = object::generate_signer(ref);
move_to(&signer, container);
}

extend

public fun extend(ref: &object::ExtendRef, container: property_map::PropertyMap)
Implementation
public fun extend(ref: &ExtendRef, container: PropertyMap) {
let signer = object::generate_signer_for_extending(ref);
move_to(&signer, container);
}

burn

Burns the entire property map

public fun burn(ref: property_map::MutatorRef)
Implementation
public fun burn(ref: MutatorRef) acquires PropertyMap {
move_from<PropertyMap>(ref.self);
}

prepare_input

Helper for external entry functions to produce a valid container for property values.

public fun prepare_input(keys: vector<string::String>, types: vector<string::String>, values: vector<vector<u8>>): property_map::PropertyMap
Implementation
public fun prepare_input(
keys: vector<String>,
types: vector<String>,
values: vector<vector<u8>>,
): PropertyMap {
let length = keys.length();
assert!(length <= MAX_PROPERTY_MAP_SIZE, error::invalid_argument(ETOO_MANY_PROPERTIES));
assert!(length == values.length(), error::invalid_argument(EKEY_VALUE_COUNT_MISMATCH));
assert!(length == types.length(), error::invalid_argument(EKEY_TYPE_COUNT_MISMATCH));
let container = simple_map::create<String, PropertyValue>();
while (!keys.is_empty()) {
let key = keys.pop_back();
assert!(
key.length() <= MAX_PROPERTY_NAME_LENGTH,
error::invalid_argument(EPROPERTY_MAP_KEY_TOO_LONG),
);
let value = values.pop_back();
let type = types.pop_back();
let new_type = to_internal_type(type);
validate_type(new_type, value);
container.add(key, PropertyValue { value, type: new_type });
};
PropertyMap { inner: container }
}

to_external_type

Maps String representation of types from their u8 representation

fun to_external_type(type: u8): string::String
Implementation
inline fun to_external_type(type: u8): String {
if (type == BOOL) {
string::utf8(b"bool")
} else if (type == U8) {
string::utf8(b"u8")
} else if (type == U16) {
string::utf8(b"u16")
} else if (type == U32) {
string::utf8(b"u32")
} else if (type == U64) {
string::utf8(b"u64")
} else if (type == U128) {
string::utf8(b"u128")
} else if (type == U256) {
string::utf8(b"u256")
} else if (type == ADDRESS) {
string::utf8(b"address")
} else if (type == BYTE_VECTOR) {
string::utf8(b"vector<u8>")
} else if (type == STRING) {
string::utf8(b"0x1::string::String")
} else {
abort (error::invalid_argument(ETYPE_INVALID))
}
}

to_internal_type

Maps the String representation of types to u8

fun to_internal_type(type: string::String): u8
Implementation
inline fun to_internal_type(type: String): u8 {
if (type == string::utf8(b"bool")) {
BOOL
} else if (type == string::utf8(b"u8")) {
U8
} else if (type == string::utf8(b"u16")) {
U16
} else if (type == string::utf8(b"u32")) {
U32
} else if (type == string::utf8(b"u64")) {
U64
} else if (type == string::utf8(b"u128")) {
U128
} else if (type == string::utf8(b"u256")) {
U256
} else if (type == string::utf8(b"address")) {
ADDRESS
} else if (type == string::utf8(b"vector<u8>")) {
BYTE_VECTOR
} else if (type == string::utf8(b"0x1::string::String")) {
STRING
} else {
abort (error::invalid_argument(ETYPE_INVALID))
}
}

type_info_to_internal_type

Maps Move type to u8 representation

fun type_info_to_internal_type<T>(): u8
Implementation
inline fun type_info_to_internal_type<T>(): u8 {
let type = type_info::type_name<T>();
to_internal_type(type)
}

validate_type

Validates property value type against its expected type

fun validate_type(type: u8, value: vector<u8>)
Implementation
inline fun validate_type(type: u8, value: vector<u8>) {
if (type == BOOL) {
from_bcs::to_bool(value);
} else if (type == U8) {
from_bcs::to_u8(value);
} else if (type == U16) {
from_bcs::to_u16(value);
} else if (type == U32) {
from_bcs::to_u32(value);
} else if (type == U64) {
from_bcs::to_u64(value);
} else if (type == U128) {
from_bcs::to_u128(value);
} else if (type == U256) {
from_bcs::to_u256(value);
} else if (type == ADDRESS) {
from_bcs::to_address(value);
} else if (type == BYTE_VECTOR) {
// nothing to validate...
} else if (type == STRING) {
from_bcs::to_string(value);
} else {
abort (error::invalid_argument(ETYPE_MISMATCH))
};
}

generate_mutator_ref

public fun generate_mutator_ref(ref: &object::ConstructorRef): property_map::MutatorRef
Implementation
public fun generate_mutator_ref(ref: &ConstructorRef): MutatorRef {
MutatorRef { self: object::address_from_constructor_ref(ref) }
}

contains_key

public fun contains_key<T: key>(object: &object::Object<T>, key: &string::String): bool
Implementation
public fun contains_key<T: key>(object: &Object<T>, key: &String): bool acquires PropertyMap {
assert_exists(object::object_address(object));
let property_map = &PropertyMap[object::object_address(object)];
property_map.inner.contains_key(key)
}

length

public fun length<T: key>(object: &object::Object<T>): u64
Implementation
public fun length<T: key>(object: &Object<T>): u64 acquires PropertyMap {
assert_exists(object::object_address(object));
let property_map = &PropertyMap[object::object_address(object)];
property_map.inner.length()
}

read

Read the property and get it’s external type in it’s bcs encoded format

The preferred method is to use read_<type> where the type is already known.

public fun read<T: key>(object: &object::Object<T>, key: &string::String): (string::String, vector<u8>)
Implementation
public fun read<T: key>(object: &Object<T>, key: &String): (String, vector<u8>) acquires PropertyMap {
assert_exists(object::object_address(object));
let property_map = &PropertyMap[object::object_address(object)];
let property_value = property_map.inner.borrow(key);
let new_type = to_external_type(property_value.type);
(new_type, property_value.value)
}

assert_exists

fun assert_exists(object: address)
Implementation
inline fun assert_exists(object: address) {
assert!(
exists<PropertyMap>(object),
error::not_found(EPROPERTY_MAP_DOES_NOT_EXIST),
);
}

read_typed

Read a type and verify that the type is correct

fun read_typed<T: key, V>(object: &object::Object<T>, key: &string::String): vector<u8>
Implementation
inline fun read_typed<T: key, V>(object: &Object<T>, key: &String): vector<u8> acquires PropertyMap {
let (type, value) = read(object, key);
assert!(
type == type_info::type_name<V>(),
error::invalid_argument(ETYPE_MISMATCH),
);
value
}

read_bool

public fun read_bool<T: key>(object: &object::Object<T>, key: &string::String): bool
Implementation
public fun read_bool<T: key>(object: &Object<T>, key: &String): bool acquires PropertyMap {
let value = read_typed<T, bool>(object, key);
from_bcs::to_bool(value)
}

read_u8

public fun read_u8<T: key>(object: &object::Object<T>, key: &string::String): u8
Implementation
public fun read_u8<T: key>(object: &Object<T>, key: &String): u8 acquires PropertyMap {
let value = read_typed<T, u8>(object, key);
from_bcs::to_u8(value)
}

read_u16

public fun read_u16<T: key>(object: &object::Object<T>, key: &string::String): u16
Implementation
public fun read_u16<T: key>(object: &Object<T>, key: &String): u16 acquires PropertyMap {
let value = read_typed<T, u16>(object, key);
from_bcs::to_u16(value)
}

read_u32

public fun read_u32<T: key>(object: &object::Object<T>, key: &string::String): u32
Implementation
public fun read_u32<T: key>(object: &Object<T>, key: &String): u32 acquires PropertyMap {
let value = read_typed<T, u32>(object, key);
from_bcs::to_u32(value)
}

read_u64

public fun read_u64<T: key>(object: &object::Object<T>, key: &string::String): u64
Implementation
public fun read_u64<T: key>(object: &Object<T>, key: &String): u64 acquires PropertyMap {
let value = read_typed<T, u64>(object, key);
from_bcs::to_u64(value)
}

read_u128

public fun read_u128<T: key>(object: &object::Object<T>, key: &string::String): u128
Implementation
public fun read_u128<T: key>(object: &Object<T>, key: &String): u128 acquires PropertyMap {
let value = read_typed<T, u128>(object, key);
from_bcs::to_u128(value)
}

read_u256

public fun read_u256<T: key>(object: &object::Object<T>, key: &string::String): u256
Implementation
public fun read_u256<T: key>(object: &Object<T>, key: &String): u256 acquires PropertyMap {
let value = read_typed<T, u256>(object, key);
from_bcs::to_u256(value)
}

read_address

public fun read_address<T: key>(object: &object::Object<T>, key: &string::String): address
Implementation
public fun read_address<T: key>(object: &Object<T>, key: &String): address acquires PropertyMap {
let value = read_typed<T, address>(object, key);
from_bcs::to_address(value)
}

read_bytes

public fun read_bytes<T: key>(object: &object::Object<T>, key: &string::String): vector<u8>
Implementation
public fun read_bytes<T: key>(object: &Object<T>, key: &String): vector<u8> acquires PropertyMap {
let value = read_typed<T, vector<u8>>(object, key);
from_bcs::to_bytes(value)
}

read_string

public fun read_string<T: key>(object: &object::Object<T>, key: &string::String): string::String
Implementation
public fun read_string<T: key>(object: &Object<T>, key: &String): String acquires PropertyMap {
let value = read_typed<T, String>(object, key);
from_bcs::to_string(value)
}

add

Add a property, already bcs encoded as a vector<u8>

public fun add(ref: &property_map::MutatorRef, key: string::String, type: string::String, value: vector<u8>)
Implementation
public fun add(ref: &MutatorRef, key: String, type: String, value: vector<u8>) acquires PropertyMap {
let new_type = to_internal_type(type);
validate_type(new_type, value);
add_internal(ref, key, new_type, value);
}

add_typed

Add a property that isn’t already encoded as a vector<u8>

public fun add_typed<T: drop>(ref: &property_map::MutatorRef, key: string::String, value: T)
Implementation
public fun add_typed<T: drop>(ref: &MutatorRef, key: String, value: T) acquires PropertyMap {
let type = type_info_to_internal_type<T>();
add_internal(ref, key, type, bcs::to_bytes(&value));
}

add_internal

fun add_internal(ref: &property_map::MutatorRef, key: string::String, type: u8, value: vector<u8>)
Implementation
inline fun add_internal(ref: &MutatorRef, key: String, type: u8, value: vector<u8>) acquires PropertyMap {
assert_exists(ref.self);
let property_map = &mut PropertyMap[ref.self];
property_map.inner.add(key, PropertyValue { type, value });
}

update

Updates a property in place already bcs encoded

public fun update(ref: &property_map::MutatorRef, key: &string::String, type: string::String, value: vector<u8>)
Implementation
public fun update(ref: &MutatorRef, key: &String, type: String, value: vector<u8>) acquires PropertyMap {
let new_type = to_internal_type(type);
validate_type(new_type, value);
update_internal(ref, key, new_type, value);
}

update_typed

Updates a property in place that is not already bcs encoded

public fun update_typed<T: drop>(ref: &property_map::MutatorRef, key: &string::String, value: T)
Implementation
public fun update_typed<T: drop>(ref: &MutatorRef, key: &String, value: T) acquires PropertyMap {
let type = type_info_to_internal_type<T>();
update_internal(ref, key, type, bcs::to_bytes(&value));
}

update_internal

fun update_internal(ref: &property_map::MutatorRef, key: &string::String, type: u8, value: vector<u8>)
Implementation
inline fun update_internal(ref: &MutatorRef, key: &String, type: u8, value: vector<u8>) acquires PropertyMap {
assert_exists(ref.self);
let property_map = &mut PropertyMap[ref.self];
let old_value = property_map.inner.borrow_mut(key);
*old_value = PropertyValue { type, value };
}

remove

Removes a property from the map, ensuring that it does in fact exist

public fun remove(ref: &property_map::MutatorRef, key: &string::String)
Implementation
public fun remove(ref: &MutatorRef, key: &String) acquires PropertyMap {
assert_exists(ref.self);
let property_map = &mut PropertyMap[ref.self];
property_map.inner.remove(key);
}

assert_end_to_end_input

fun assert_end_to_end_input(object: object::Object<object::ObjectCore>)
Implementation
fun assert_end_to_end_input(object: Object<ObjectCore>) acquires PropertyMap {
assert!(read_bool(&object, &string::utf8(b"bool")), 0);
assert!(read_u8(&object, &string::utf8(b"u8")) == 0x12, 1);
assert!(read_u16(&object, &string::utf8(b"u16")) == 0x1234, 2);
assert!(read_u32(&object, &string::utf8(b"u32")) == 0x12345678, 3);
assert!(read_u64(&object, &string::utf8(b"u64")) == 0x1234567812345678, 4);
assert!(read_u128(&object, &string::utf8(b"u128")) == 0x12345678123456781234567812345678, 5);
assert!(
read_u256(
&object,
&string::utf8(b"u256")
) == 0x1234567812345678123456781234567812345678123456781234567812345678,
6
);
assert!(read_bytes(&object, &string::utf8(b"vector<u8>")) == vector[0x01], 7);
assert!(read_string(&object, &string::utf8(b"0x1::string::String")) == string::utf8(b"a"), 8);
assert!(length(&object) == 9, 9);
}