Skip to content

Commit

Permalink
Render display templates and output with PRC
Browse files Browse the repository at this point in the history
  • Loading branch information
pause125 committed Feb 25, 2024
1 parent a53297b commit b8726d5
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 35 deletions.
7 changes: 7 additions & 0 deletions crates/rooch-rpc-api/src/jsonrpc_types/rpc_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
pub struct StateOptions {
/// If true, the state is decoded and the decoded value is returned in the response.
pub decode: bool,
/// If true, result with display rendered is returned
pub show_display: bool,
}

impl StateOptions {
Expand All @@ -20,6 +22,11 @@ impl StateOptions {
self.decode = decode;
self
}

pub fn show_display(mut self, show_display: bool) -> Self {
self.show_display = show_display;
self
}
}

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Default)]
Expand Down
21 changes: 21 additions & 0 deletions crates/rooch-rpc-api/src/jsonrpc_types/state_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,30 @@ use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::str::FromStr;

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct DisplayFieldsView {
pub fields: BTreeMap<String, String>,
}

impl DisplayFieldsView {
pub fn new(fields: BTreeMap<String, String>) -> Self {
Self { fields }
}
}

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct StateView {
pub value: BytesView,
pub value_type: TypeTagView,
pub decoded_value: Option<AnnotatedMoveValueView>,
pub display_fields: Option<DisplayFieldsView>,
}

impl StateView {
pub fn with_display_fields(mut self, display_fields: Option<DisplayFieldsView>) -> Self {
self.display_fields = display_fields;
self
}
}

impl From<State> for StateView {
Expand All @@ -36,6 +55,7 @@ impl From<State> for StateView {
value: StrView(state.value),
value_type: state.value_type.into(),
decoded_value: None,
display_fields: None,
}
}
}
Expand All @@ -46,6 +66,7 @@ impl From<AnnotatedState> for StateView {
value: StrView(state.state.value),
value_type: state.state.value_type.into(),
decoded_value: Some(state.decoded_value.into()),
display_fields: None,
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions crates/rooch-rpc-client/src/rooch_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ impl RoochRpcClient {
.await?)
}

pub async fn get_decoded_states_with_display(
&self,
access_path: AccessPath,
) -> Result<Vec<Option<StateView>>> {
Ok(self
.http
.get_states(
access_path.into(),
Some(StateOptions::default().decode(true).show_display(true)),
)
.await?)
}

pub async fn get_transactions_by_order(
&self,
cursor: Option<u64>,
Expand Down
111 changes: 94 additions & 17 deletions crates/rooch-rpc-server/src/server/rooch_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ use jsonrpsee::{
RpcModule,
};
use move_core_types::account_address::AccountAddress;
use moveos_types::h256::H256;
use moveos_types::state::KeyState;
use moveos_types::{
access_path::AccessPath,
h256::H256,
moveos_std::{
display::{get_display_object_id, RawDisplay},
object::ObjectEntity,
},
state::{AnnotatedKeyState, AnnotatedState, KeyState},
};
use rooch_rpc_api::jsonrpc_types::event_view::{EventFilterView, EventView, IndexerEventView};
use rooch_rpc_api::jsonrpc_types::transaction_view::TransactionFilterView;
use rooch_rpc_api::jsonrpc_types::{
Expand All @@ -21,7 +28,7 @@ use rooch_rpc_api::jsonrpc_types::{
};
use rooch_rpc_api::jsonrpc_types::{transaction_view::TransactionWithInfoView, EventOptions};
use rooch_rpc_api::jsonrpc_types::{
AccessPathView, AccountAddressView, BalanceInfoPageView, EventPageView,
AccessPathView, AccountAddressView, BalanceInfoPageView, DisplayFieldsView, EventPageView,
ExecuteTransactionResponseView, FunctionCallView, H256View, StatePageView, StateView, StrView,
StructTagView, TransactionWithInfoPageView,
};
Expand Down Expand Up @@ -55,6 +62,45 @@ impl RoochServer {
aggregate_service,
}
}

async fn get_display_fields_and_render(
&self,
states: Vec<&AnnotatedState>,
) -> Result<Vec<Option<DisplayFieldsView>>> {
let mut display_ids = vec![];
for s in &states {
// TODO: handle Move resources
let struct_tag = s.state.get_object_struct_tag();
if let Some(tag) = struct_tag {
display_ids.push(get_display_object_id(tag.into()));
};
}
// get display fields
let path = AccessPath::objects(display_ids);
let display_fields = self
.rpc_service
.get_states(path)
.await?
.into_iter()
.map(|option_s| {
option_s
.map(|s| s.as_object_uncheck::<RawDisplay>())
.transpose()
})
.collect::<Result<Vec<Option<ObjectEntity<RawDisplay>>>>>()?;

let mut display_field_views = vec![];
for (annotated_s, option_display_obj) in states.into_iter().zip(display_fields) {
let struct_tag = annotated_s.state.get_object_struct_tag();
display_field_views.push(match struct_tag {
Some(_tag) => option_display_obj.map(|obj| {
DisplayFieldsView::new(obj.value.render(&annotated_s.decoded_value))
}),
_ => None,
});
}
Ok(display_field_views)
}
}

#[async_trait]
Expand Down Expand Up @@ -105,23 +151,43 @@ impl RoochAPIServer for RoochServer {
state_option: Option<StateOptions>,
) -> RpcResult<Vec<Option<StateView>>> {
let state_option = state_option.unwrap_or_default();
if state_option.decode {
Ok(self

let state_views = if state_option.decode || state_option.show_display {
let states = self
.rpc_service
.get_annotated_states(access_path.into())
.await?
.into_iter()
.map(|s| s.map(StateView::from))
.collect())
.await?;

if state_option.show_display {
let valid_states = states.iter().filter_map(|s| s.as_ref()).collect::<Vec<_>>();
let mut valid_display_field_views =
self.get_display_fields_and_render(valid_states).await?;
valid_display_field_views.reverse();
states
.into_iter()
.map(|option_annotated_s| {
option_annotated_s.map(|annotated_s| {
debug_assert!(
valid_display_field_views.len() > 0,
"display fields should not be empty"
);
let display_view = valid_display_field_views.pop().unwrap();
StateView::from(annotated_s).with_display_fields(display_view)
})
})
.collect()
} else {
states.into_iter().map(|s| s.map(StateView::from)).collect()
}
} else {
Ok(self
.rpc_service
self.rpc_service
.get_states(access_path.into())
.await?
.into_iter()
.map(|s| s.map(StateView::from))
.collect())
}
.collect()
};
Ok(state_views)
}

async fn list_states(
Expand All @@ -140,13 +206,24 @@ impl RoochAPIServer for RoochServer {
Some(key_state_str) => Some(KeyState::from_str(key_state_str.as_str())?),
None => None,
};
let mut data: Vec<StateKVView> = if state_option.decode {
self.rpc_service
let mut data: Vec<StateKVView> = if state_option.decode || state_option.show_display {
let (key_states, states): (Vec<AnnotatedKeyState>, Vec<AnnotatedState>) = self
.rpc_service
.list_annotated_states(access_path.into(), cursor_of, limit_of + 1)
.await?
.into_iter()
.map(|(key_state, state)| {
StateKVView::new(KeyStateView::from(key_state), StateView::from(state))
.unzip();
let state_refs: Vec<&AnnotatedState> = states.iter().collect();
let display_field_views = self.get_display_fields_and_render(state_refs).await?;
key_states
.into_iter()
.zip(states.into_iter())
.zip(display_field_views.into_iter())
.map(|((key_state, state), display_field_view)| {
StateKVView::new(
KeyStateView::from(key_state),
StateView::from(state).with_display_fields(display_field_view),
)
})
.collect::<Vec<_>>()
} else {
Expand Down
25 changes: 18 additions & 7 deletions crates/rooch/src/commands/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub struct ObjectCommand {

#[clap(flatten)]
pub(crate) context_options: WalletContextOptions,

#[clap(long)]
pub show_display: bool,
}

#[async_trait]
Expand All @@ -26,13 +29,21 @@ impl CommandAction<Option<StateView>> for ObjectCommand {
let mapping = context.address_mapping();
let id = self.id.into_object_id(&mapping)?;
let client = context.get_client().await?;
let resp = client
.rooch
.get_decoded_states(AccessPath::object(id))
.await?
.pop()
.flatten();

let resp = if self.show_display {
client
.rooch
.get_decoded_states_with_display(AccessPath::object(id))
.await?
.pop()
.flatten()
} else {
client
.rooch
.get_decoded_states(AccessPath::object(id))
.await?
.pop()
.flatten()
};
Ok(resp)
}
}
25 changes: 18 additions & 7 deletions examples/nft/sources/collection.move
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

module nft::collection{
use std::option;
use std::option::Option;
use std::option::{Self, Option};
use std::string::{Self, String};
use moveos_std::display;
use moveos_std::object_id::{ObjectID};
Expand All @@ -19,6 +18,7 @@ module nft::collection{
name: String,
creator: address,
supply: Supply,
description: String,
}

struct Supply has store{
Expand All @@ -36,11 +36,11 @@ module nft::collection{

fun init(ctx: &mut Context){
let collection_display_obj = display::object_display<Collection>(ctx);
display::set_value(collection_display_obj, string::utf8(b"name"), string::utf8(b"{ value.name }"));
display::set_value(collection_display_obj, string::utf8(b"uri"), string::utf8(b"https:://base_url/{ id }"));
display::set_value(collection_display_obj, string::utf8(b"description"), string::utf8(b"{ value.description }"));
display::set_value(collection_display_obj, string::utf8(b"creator"), string::utf8(b"{ creator }"));
display::set_value(collection_display_obj, string::utf8(b"supply"), string::utf8(b"{ value.supply }"));
display::set_value(collection_display_obj, string::utf8(b"name"), string::utf8(b"{value.name}"));
display::set_value(collection_display_obj, string::utf8(b"uri"), string::utf8(b"https:://base_url/{id}"));
display::set_value(collection_display_obj, string::utf8(b"description"), string::utf8(b"{value.description}"));
display::set_value(collection_display_obj, string::utf8(b"creator"), string::utf8(b"{value.creator}"));
display::set_value(collection_display_obj, string::utf8(b"supply"), string::utf8(b"{value.supply}"));
}

/// Create a new collection Object
Expand All @@ -59,6 +59,7 @@ module nft::collection{
current: 0,
maximum: max_supply,
},
description,
};

let collection_obj = context::new_object(
Expand All @@ -79,6 +80,16 @@ module nft::collection{
collection_id
}

entry fun create_collection_entry(
ctx: &mut Context,
name: String,
creator: address,
description: String,
max_supply: u64,
) {
create_collection(ctx, name, creator, description, option::some(max_supply));
}

public(friend) fun increment_supply(collection: &mut Collection): Option<u64>{
collection.supply.current = collection.supply.current + 1;
if(option::is_some(&collection.supply.maximum)){
Expand Down
8 changes: 4 additions & 4 deletions examples/nft/sources/nft.move
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ module nft::nft {

fun init(ctx: &mut Context){
let nft_display_object = display::object_display<NFT>(ctx);
display::set_value(nft_display_object, string::utf8(b"name"), string::utf8(b"{ value.name }"));
display::set_value(nft_display_object, string::utf8(b"owner"), string::utf8(b"{ owner }"));
display::set_value(nft_display_object, string::utf8(b"creator"), string::utf8(b"{ value.creator }"));
display::set_value(nft_display_object, string::utf8(b"uri"), string::utf8(b"https://base_url/{ collection }/{ id }"));
display::set_value(nft_display_object, string::utf8(b"name"), string::utf8(b"{value.name}"));
display::set_value(nft_display_object, string::utf8(b"owner"), string::utf8(b"{owner}"));
display::set_value(nft_display_object, string::utf8(b"creator"), string::utf8(b"{value.creator}"));
display::set_value(nft_display_object, string::utf8(b"uri"), string::utf8(b"https://base_url/{value.collection}/{id}"));
}

/// Mint a new NFT,
Expand Down
Loading

0 comments on commit b8726d5

Please sign in to comment.