Module 0x4::property_map
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.
- Resource
PropertyMap - Struct
PropertyValue - Struct
MutatorRef - Constants
- Function
init - Function
extend - Function
burn - Function
prepare_input - Function
to_external_type - Function
to_internal_type - Function
type_info_to_internal_type - Function
validate_type - Function
generate_mutator_ref - Function
contains_key - Function
length - Function
read - Function
assert_exists - Function
read_typed - Function
read_bool - Function
read_u8 - Function
read_u16 - Function
read_u32 - Function
read_u64 - Function
read_u128 - Function
read_u256 - Function
read_address - Function
read_bytes - Function
read_string - Function
add - Function
add_typed - Function
add_internal - Function
update - Function
update_typed - Function
update_internal - Function
remove - Function
assert_end_to_end_input
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;
Resource 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
Struct 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>
Struct MutatorRef
A mutator ref that allows for mutation of the property map
struct MutatorRef has drop, store
Fields
-
self: address
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;
Function init
public fun init(ref: &object::ConstructorRef, container: property_map::PropertyMap)
Implementation
public fun init(ref: &ConstructorRef, container: PropertyMap) {
let signer = ref.generate_signer();
move_to(&signer, container);
}
Function extend
public fun extend(ref: &object::ExtendRef, container: property_map::PropertyMap)
Implementation
public fun extend(ref: &ExtendRef, container: PropertyMap) {
let signer = ref.generate_signer_for_extending();
move_to(&signer, container);
}
Function 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);
}
Function 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 }
}
Function 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))
}
}
Function 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))
}
}
Function 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)
}
Function 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))
};
}
Function 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: ref.address_from_constructor_ref() }
}
Function 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());
let property_map = &PropertyMap[object.object_address()];
property_map.inner.contains_key(key)
}
Function 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());
let property_map = &PropertyMap[object.object_address()];
property_map.inner.length()
}
Function 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());
let property_map = &PropertyMap[object.object_address()];
let property_value = property_map.inner.borrow(key);
let new_type = to_external_type(property_value.type);
(new_type, property_value.value)
}
Function 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),
);
}
Function 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> {
let (type, value) = read(object, key);
assert!(
type == type_info::type_name<V>(),
error::invalid_argument(ETYPE_MISMATCH),
);
value
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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)
}
Function 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);
}
Function 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));
}
Function 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>) {
assert_exists(ref.self);
let property_map = &mut PropertyMap[ref.self];
property_map.inner.add(key, PropertyValue { type, value });
}
Function 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);
}
Function 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));
}
Function 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>) {
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 };
}
Function 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);
}
Function 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);
}