From 307cde4369015c4cc364382f71ad726b588ba198 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:31:06 -0700 Subject: [PATCH] Fix dynamic fields --- crates/sui-graphql-client/src/lib.rs | 86 +++++++++++++------ .../src/query_types/dynamic_fields.rs | 18 +++- .../src/query_types/object.rs | 4 +- 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 64540d360..71aeb3098 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -51,6 +51,7 @@ use query_types::TransactionMetadata; use query_types::TransactionsFilter; use query_types::Validator; +use serde::de::DeserializeOwned; use serde::Serialize; use sui_types::types::framework::Coin; use sui_types::types::Address; @@ -104,6 +105,7 @@ pub struct DynamicFieldName { pub struct DynamicFieldOutput { pub name: DynamicFieldName, pub json: Option, + pub value: Option<(String, Vec)>, } /// Helper struct for passing a value that has a type that implements Serialize, for the dynamic @@ -149,6 +151,13 @@ impl From for Name { } } +impl DynamicFieldOutput { + pub fn deserialize(&self) -> Result { + let bcs = &self.name.bcs; + bcs::from_bytes::(&bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) + } +} + /// The GraphQL client for interacting with the Sui blockchain. /// By default, it uses the `reqwest` crate as the HTTP client. pub struct Client { @@ -878,33 +887,62 @@ impl Client { } } - /// Return the contents JSON of an object that is a Move object. + /// Return the contents' JSON of an object that is a Move object. /// /// If the object does not exist (e.g., due to prunning), this will return `Ok(None)`. /// Similarly, if this is not an object but an address, it will return `Ok(None)`. - // pub async fn object_move_contents( - // &self, - // address: Address, - // version: Option, - // ) -> Result, Error> { - // let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); - // - // let response = self.run_query(&operation).await?; - // - // if let Some(errors) = response.errors { - // return Err(Error::msg(format!("{:?}", errors))); - // } - // - // if let Some(object) = response.data { - // Ok(object - // .object - // .and_then(|o| o.as_move_object) - // .and_then(|o| o.contents) - // .and_then(|mv| mv.json)) - // } else { - // Ok(None) - // } - // } + pub async fn object_move_contents( + &self, + address: Address, + version: Option, + ) -> Result, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data { + Ok(object + .object + .and_then(|o| o.as_move_object) + .and_then(|o| o.contents) + .and_then(|mv| mv.json)) + } else { + Ok(None) + } + } + /// Return the BCS of an object that is a Move object. + /// + /// If the object does not exist (e.g., due to prunning), this will return `Ok(None)`. + /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + pub async fn object_move_contents_bcs( + &self, + address: Address, + version: Option, + ) -> Result>, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data { + object + .object + .and_then(|o| o.as_move_object) + .and_then(|o| o.contents) + .map(|bcs| base64ct::Base64::decode_vec(bcs.bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}"))) + } else { + Ok(None) + } + } // =========================================================================== // Dry Run API diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index da88d70b2..0848db8b2 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -130,6 +130,22 @@ impl DynamicFieldValue { _ => None, } } + + pub fn type_bcs(&self) -> Option<(String, Vec)> { + match self { + DynamicFieldValue::MoveObject(mo) => Some(( + mo.contents.as_ref().unwrap().__typename.clone(), + mo.contents.as_ref().unwrap().bcs.0.clone().into(), + // base64ct::Base64::decode_vec(mo.contents.as_ref().unwrap().bcs.0.as_ref()).unwrap(), + )), + DynamicFieldValue::MoveValue(mv) => Some(( + mv.__typename.clone(), + mv.bcs.0.clone().into(), + // base64ct::Base64::decode_vec(mv.bcs.0.as_ref()).unwrap(), + )), + _ => None, + } + } } impl DynamicField { @@ -157,8 +173,8 @@ impl From for DynamicFieldOutput { .unwrap(), json: val.name.as_ref().unwrap().json.clone(), }, - // object, json: val.field_value_json(), + value: val.value.and_then(|x| x.type_bcs()), } } } diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index bc10c6be3..deca2bca2 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -4,7 +4,7 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; -use crate::query_types::MoveObject; +use crate::query_types::MoveObjectContents; use crate::query_types::PageInfo; // =========================================================================== @@ -51,7 +51,7 @@ pub struct ObjectsQueryArgs<'a> { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Object")] pub struct Object { - pub as_move_object: Option, + pub as_move_object: Option, pub bcs: Option, }