Skip to content

Commit

Permalink
[Cherry pick] View function Object<T> support (aptos-labs#8051)
Browse files Browse the repository at this point in the history
* [move-examples] Add view functions to Hero object demo

* [api] Allow Object<T> in view functions

The JSON parsing wasn't working properly for Object<T> so, it now
handles Object types as a one time check, and allows for types to
be provided as well
  • Loading branch information
gregnazario authored May 4, 2023
1 parent 5d087fe commit 8731f29
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 6 deletions.
24 changes: 22 additions & 2 deletions api/types/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ use aptos_types::{
use aptos_vm::move_vm_ext::MoveResolverExt;
use move_binary_format::file_format::FunctionHandleIndex;
use move_core_types::{
identifier::Identifier,
account_address::AccountAddress,
ident_str,
identifier::{IdentStr, Identifier},
language_storage::{ModuleId, StructTag, TypeTag},
value::{MoveStructLayout, MoveTypeLayout},
};
Expand All @@ -50,6 +52,9 @@ use std::{
sync::Arc,
};

const OBJECT_MODULE: &IdentStr = ident_str!("object");
const OBJECT_STRUCT: &IdentStr = ident_str!("Object");

/// The Move converter for converting Move types to JSON
///
/// This reads the underlying BCS types and ABIs to convert them into
Expand Down Expand Up @@ -754,7 +759,22 @@ impl<'a, R: MoveResolverExt + ?Sized> MoveConverter<'a, R> {
type_tag: &TypeTag,
val: Value,
) -> Result<move_core_types::value::MoveValue> {
let layout = self.inner.get_type_layout_with_types(type_tag)?;
let layout = match type_tag {
TypeTag::Struct(boxed_struct) => {
// The current framework can't handle generics, so we handle this here
if boxed_struct.address == AccountAddress::ONE
&& boxed_struct.module.as_ident_str() == OBJECT_MODULE
&& boxed_struct.name.as_ident_str() == OBJECT_STRUCT
{
// Objects are just laid out as an address
MoveTypeLayout::Address
} else {
// For all other structs, use their set layout
self.inner.get_type_layout_with_types(type_tag)?
}
},
_ => self.inner.get_type_layout_with_types(type_tag)?,
};

self.try_into_vm_value_from_layout(&layout, val)
}
Expand Down
1 change: 1 addition & 0 deletions api/types/src/move_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ impl TryFrom<MoveType> for TypeTag {
MoveType::Signer => TypeTag::Signer,
MoveType::Vector { items } => TypeTag::Vector(Box::new((*items).try_into()?)),
MoveType::Struct(v) => TypeTag::Struct(Box::new(v.try_into()?)),
MoveType::GenericTypeParam { index: _ } => TypeTag::Address, // Dummy type, allows for Object<T>
_ => {
return Err(anyhow::anyhow!(
"Invalid move type for converting into `TypeTag`: {:?}",
Expand Down
52 changes: 48 additions & 4 deletions aptos-move/move-examples/token_objects/sources/hero.move
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ module token_objects::hero {

use aptos_token_objects::collection;
use aptos_token_objects::token;
use aptos_std::string_utils;

const ENOT_A_HERO: u64 = 1;
const ENOT_A_WEAPON: u64 = 2;
const ENOT_A_GEM: u64 = 3;
const ENOT_CREATOR: u64 = 4;
const EINVALID_WEAPON_UNEQUIP: u64 = 5;
const EINVALID_GEM_UNEQUIP: u64 = 6;
const EINVALID_TYPE: u64 = 7;

struct OnChainConfig has key {
collection: String,
Expand Down Expand Up @@ -158,7 +160,7 @@ module token_objects::hero {
defense_modifier,
magic_attribute,
};
move_to(&token_signer, gem);
move_to(&token_signer, gem);

object::address_to_object(signer::address_of(&token_signer))
}
Expand Down Expand Up @@ -210,18 +212,60 @@ module token_objects::hero {
name: String,
description: String,
) acquires Hero {
let token_address = token::create_token_address(
let (hero_obj, hero) = get_hero(
&signer::address_of(creator),
&collection,
&name,
);
let hero_obj = object::address_to_object<Hero>(token_address);
let hero = borrow_global<Hero>(token_address);
let creator_addr = token::creator(hero_obj);
assert!(creator_addr == signer::address_of(creator), error::permission_denied(ENOT_CREATOR));
token::set_description(&hero.mutator_ref, description);
}

// View functions
#[view]
fun view_hero(creator: address, collection: String, name: String): Hero acquires Hero {
let token_address = token::create_token_address(
&creator,
&collection,
&name,
);
move_from<Hero>(token_address)
}

#[view]
fun view_hero_by_object(hero_obj: Object<Hero>): Hero acquires Hero {
let token_address = object::object_address(&hero_obj);
move_from<Hero>(token_address)
}

#[view]
fun view_object<T: key>(obj: Object<T>): String acquires Armor, Gem, Hero, Shield, Weapon {
let token_address = object::object_address(&obj);
if (exists<Armor>(token_address)) {
string_utils::to_string(borrow_global<Armor>(token_address))
} else if (exists<Gem>(token_address)) {
string_utils::to_string(borrow_global<Gem>(token_address))
} else if (exists<Hero>(token_address)) {
string_utils::to_string(borrow_global<Hero>(token_address))
} else if (exists<Shield>(token_address)) {
string_utils::to_string(borrow_global<Shield>(token_address))
} else if (exists<Weapon>(token_address)) {
string_utils::to_string(borrow_global<Weapon>(token_address))
} else {
abort EINVALID_TYPE
}
}

inline fun get_hero(creator: &address, collection: &String, name: &String): (Object<Hero>, &Hero) {
let token_address = token::create_token_address(
creator,
collection,
name,
);
(object::address_to_object<Hero>(token_address), borrow_global<Hero>(token_address))
}

#[test(account = @0x3)]
fun test_hero_with_gem_weapon(account: &signer) acquires Hero, OnChainConfig, Weapon {
init_module(account);
Expand Down

0 comments on commit 8731f29

Please sign in to comment.