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