use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::fmt;
use std::fmt::{Display, Formatter, Write};
use std::str::FromStr;
use af_sui_types::{
Address as SuiAddress,
Identifier,
MoveObjectType,
ObjectArg,
ObjectDigest,
ObjectId,
ObjectRef,
Owner,
StructTag,
TransactionDigest,
TypeOrigin,
UpgradeInfo,
};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::base64::Base64;
use serde_with::{serde_as, DisplayFromStr};
use sui_sdk_types::Version;
use super::{Page, SuiMoveStruct, SuiMoveValue};
use crate::serde::BigInt;
#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
#[error("Could not get object_id, something went wrong with SuiObjectResponse construction.")]
pub struct MissingObjectIdError;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct SuiObjectResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<SuiObjectData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<SuiObjectResponseError>,
}
impl SuiObjectResponse {
pub fn new(data: Option<SuiObjectData>, error: Option<SuiObjectResponseError>) -> Self {
Self { data, error }
}
pub fn new_with_data(data: SuiObjectData) -> Self {
Self {
data: Some(data),
error: None,
}
}
pub fn new_with_error(error: SuiObjectResponseError) -> Self {
Self {
data: None,
error: Some(error),
}
}
pub fn object(&self) -> Result<&SuiObjectData, SuiObjectResponseError> {
if let Some(data) = &self.data {
Ok(data)
} else if let Some(error) = &self.error {
Err(error.clone())
} else {
Err(SuiObjectResponseError::Unknown)
}
}
pub fn into_object(self) -> Result<SuiObjectData, SuiObjectResponseError> {
match self.object() {
Ok(data) => Ok(data.clone()),
Err(error) => Err(error),
}
}
pub fn move_object_bcs(&self) -> Option<&Vec<u8>> {
match &self.data {
Some(SuiObjectData {
bcs: Some(SuiRawData::MoveObject(obj)),
..
}) => Some(&obj.bcs_bytes),
_ => None,
}
}
pub fn owner(&self) -> Option<Owner> {
if let Some(data) = &self.data {
return data.owner.clone();
}
None
}
pub fn object_id(&self) -> Result<ObjectId, MissingObjectIdError> {
match (&self.data, &self.error) {
(Some(obj_data), None) => Ok(obj_data.object_id),
(None, Some(SuiObjectResponseError::NotExists { object_id })) => Ok(*object_id),
(
None,
Some(SuiObjectResponseError::Deleted {
object_id,
version: _,
digest: _,
}),
) => Ok(*object_id),
_ => Err(MissingObjectIdError),
}
}
pub fn object_ref_if_exists(&self) -> Option<ObjectRef> {
match (&self.data, &self.error) {
(Some(obj_data), None) => Some(obj_data.object_ref()),
_ => None,
}
}
}
impl Ord for SuiObjectResponse {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.data, &other.data) {
(Some(data), Some(data_2)) => {
if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Greater) {
return Ordering::Greater;
} else if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Less) {
return Ordering::Less;
}
Ordering::Equal
}
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
_ => Ordering::Equal,
}
}
}
impl PartialOrd for SuiObjectResponse {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
pub enum SuiObjectResponseError {
#[error("Object {:?} does not exist.", object_id)]
NotExists { object_id: ObjectId },
#[error("Cannot find dynamic field for parent object {:?}.", parent_object_id)]
DynamicFieldNotFound { parent_object_id: ObjectId },
#[error(
"Object has been deleted object_id: {:?} at version: {:?} in digest {:?}",
object_id,
version,
digest
)]
Deleted {
object_id: ObjectId,
version: Version,
digest: ObjectDigest,
},
#[error("Unknown Error.")]
Unknown,
#[error("Display Error: {:?}", error)]
DisplayError { error: String },
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
pub struct DisplayFieldsResponse {
pub data: Option<BTreeMap<String, String>>,
pub error: Option<SuiObjectResponseError>,
}
#[derive(thiserror::Error, Debug)]
pub enum SuiObjectDataError {
#[error("Missing object type")]
MissingObjectType,
#[error("Missing BCS encoding")]
MissingBcs,
#[error("Missing object owner")]
MissingOwner,
#[error("Not a Move object")]
NotMoveObject,
#[error("Not an immutable or owned object")]
NotImmOrOwned,
#[error("Not a shared object")]
NotShared,
#[error(transparent)]
ObjectType(#[from] NotMoveStructError),
}
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase", rename = "ObjectData")]
pub struct SuiObjectData {
pub object_id: ObjectId,
#[serde_as(as = "BigInt<u64>")]
pub version: Version,
pub digest: ObjectDigest,
#[serde_as(as = "Option<DisplayFromStr>")]
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_: Option<ObjectType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<Owner>,
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_transaction: Option<TransactionDigest>,
#[serde_as(as = "Option<BigInt<u64>>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub storage_rebate: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub display: Option<DisplayFieldsResponse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<SuiParsedData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcs: Option<SuiRawData>,
}
impl SuiObjectData {
pub fn object_ref(&self) -> ObjectRef {
(self.object_id, self.version, self.digest)
}
pub fn object_type(&self) -> Result<ObjectType, SuiObjectDataError> {
self.type_
.as_ref()
.ok_or(SuiObjectDataError::MissingObjectType)
.cloned()
}
pub fn is_gas_coin(&self) -> bool {
match self.type_.as_ref() {
Some(ObjectType::Struct(ty)) if ty.is_gas_coin() => true,
Some(_) => false,
None => false,
}
}
pub fn struct_tag(&self) -> Result<StructTag, SuiObjectDataError> {
Ok(self
.type_
.clone()
.ok_or(SuiObjectDataError::MissingObjectType)?
.try_into()?)
}
pub fn take_object_type(&mut self) -> Result<ObjectType, SuiObjectDataError> {
self.type_
.take()
.ok_or(SuiObjectDataError::MissingObjectType)
}
pub fn take_raw_object(&mut self) -> Result<SuiRawMoveObject, SuiObjectDataError> {
self.take_raw_data()?
.try_into_move()
.ok_or(SuiObjectDataError::NotMoveObject)
}
pub fn take_raw_data(&mut self) -> Result<SuiRawData, SuiObjectDataError> {
self.bcs.take().ok_or(SuiObjectDataError::MissingBcs)
}
pub fn shared_object_arg(&self, mutable: bool) -> Result<ObjectArg, SuiObjectDataError> {
let Owner::Shared {
initial_shared_version,
} = self.owner()?
else {
return Err(SuiObjectDataError::NotShared);
};
Ok(ObjectArg::SharedObject {
id: self.object_id,
initial_shared_version,
mutable,
})
}
pub fn imm_or_owned_object_arg(&self) -> Result<ObjectArg, SuiObjectDataError> {
use Owner::*;
if !matches!(self.owner()?, AddressOwner(_) | ObjectOwner(_) | Immutable) {
return Err(SuiObjectDataError::NotImmOrOwned);
};
let (i, v, d) = self.object_ref();
Ok(ObjectArg::ImmOrOwnedObject((i, v, d)))
}
pub fn owner(&self) -> Result<Owner, SuiObjectDataError> {
self.owner.clone().ok_or(SuiObjectDataError::MissingOwner)
}
}
impl Display for SuiObjectData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let type_ = if let Some(type_) = &self.type_ {
type_.to_string()
} else {
"Unknown Type".into()
};
let mut writer = String::new();
writeln!(
writer,
"{}",
format!("----- {type_} ({}[{}]) -----", self.object_id, self.version).bold()
)?;
if let Some(ref owner) = self.owner {
writeln!(writer, "{}: {}", "Owner".bold().bright_black(), owner)?;
}
writeln!(
writer,
"{}: {}",
"Version".bold().bright_black(),
self.version
)?;
if let Some(storage_rebate) = self.storage_rebate {
writeln!(
writer,
"{}: {}",
"Storage Rebate".bold().bright_black(),
storage_rebate
)?;
}
if let Some(previous_transaction) = self.previous_transaction {
writeln!(
writer,
"{}: {:?}",
"Previous Transaction".bold().bright_black(),
previous_transaction
)?;
}
if let Some(content) = self.content.as_ref() {
writeln!(writer, "{}", "----- Data -----".bold())?;
write!(writer, "{}", content)?;
}
write!(f, "{}", writer)
}
}
const PACKAGE: &str = "package";
#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
pub enum ObjectType {
Package,
Struct(MoveObjectType),
}
impl TryFrom<ObjectType> for StructTag {
type Error = NotMoveStructError;
fn try_from(o: ObjectType) -> Result<Self, Self::Error> {
match o {
ObjectType::Package => Err(NotMoveStructError),
ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
}
}
}
#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
#[error("Cannot create StructTag from Package")]
pub struct NotMoveStructError;
impl Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ObjectType::Package => write!(f, "{}", PACKAGE),
ObjectType::Struct(t) => write!(f, "{}", t),
}
}
}
impl FromStr for ObjectType {
type Err = <StructTag as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == PACKAGE {
Ok(ObjectType::Package)
} else {
let tag: StructTag = s.parse()?;
Ok(ObjectType::Struct(MoveObjectType::from(tag)))
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)]
pub struct SuiObjectDataOptions {
pub show_type: bool,
pub show_owner: bool,
pub show_previous_transaction: bool,
pub show_display: bool,
pub show_content: bool,
pub show_bcs: bool,
pub show_storage_rebate: bool,
}
impl SuiObjectDataOptions {
pub fn new() -> Self {
Self::default()
}
pub fn bcs_lossless() -> Self {
Self {
show_bcs: true,
show_type: true,
show_owner: true,
show_previous_transaction: true,
show_display: false,
show_content: false,
show_storage_rebate: true,
}
}
pub fn full_content() -> Self {
Self {
show_bcs: false,
show_type: true,
show_owner: true,
show_previous_transaction: true,
show_display: false,
show_content: true,
show_storage_rebate: true,
}
}
pub fn with_content(mut self) -> Self {
self.show_content = true;
self
}
pub fn with_owner(mut self) -> Self {
self.show_owner = true;
self
}
pub fn with_type(mut self) -> Self {
self.show_type = true;
self
}
pub fn with_display(mut self) -> Self {
self.show_display = true;
self
}
pub fn with_bcs(mut self) -> Self {
self.show_bcs = true;
self
}
pub fn with_previous_transaction(mut self) -> Self {
self.show_previous_transaction = true;
self
}
pub fn is_not_in_object_info(&self) -> bool {
self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
#[serde(rename_all = "camelCase", rename = "ObjectRef")]
pub struct SuiObjectRef {
pub object_id: ObjectId,
pub version: Version,
pub digest: ObjectDigest,
}
impl SuiObjectRef {
pub fn to_object_ref(&self) -> ObjectRef {
(self.object_id, self.version, self.digest)
}
}
impl Display for SuiObjectRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Object ID: {}, version: {}, digest: {}",
self.object_id, self.version, self.digest
)
}
}
impl From<ObjectRef> for SuiObjectRef {
fn from(oref: ObjectRef) -> Self {
Self {
object_id: oref.0,
version: oref.1,
digest: oref.2,
}
}
}
pub trait SuiData: Sized {
type ObjectType;
type PackageType;
fn try_as_move(&self) -> Option<&Self::ObjectType>;
fn try_into_move(self) -> Option<Self::ObjectType>;
fn try_as_package(&self) -> Option<&Self::PackageType>;
fn type_(&self) -> Option<&StructTag>;
}
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")]
pub enum SuiRawData {
MoveObject(SuiRawMoveObject),
Package(SuiRawMovePackage),
}
impl SuiData for SuiRawData {
type ObjectType = SuiRawMoveObject;
type PackageType = SuiRawMovePackage;
fn try_as_move(&self) -> Option<&Self::ObjectType> {
match self {
Self::MoveObject(o) => Some(o),
Self::Package(_) => None,
}
}
fn try_into_move(self) -> Option<Self::ObjectType> {
match self {
Self::MoveObject(o) => Some(o),
Self::Package(_) => None,
}
}
fn try_as_package(&self) -> Option<&Self::PackageType> {
match self {
Self::MoveObject(_) => None,
Self::Package(p) => Some(p),
}
}
fn type_(&self) -> Option<&StructTag> {
match self {
Self::MoveObject(o) => Some(&o.type_),
Self::Package(_) => None,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")]
pub enum SuiParsedData {
MoveObject(SuiParsedMoveObject),
Package(SuiMovePackage),
}
impl SuiData for SuiParsedData {
type ObjectType = SuiParsedMoveObject;
type PackageType = SuiMovePackage;
fn try_as_move(&self) -> Option<&Self::ObjectType> {
match self {
Self::MoveObject(o) => Some(o),
Self::Package(_) => None,
}
}
fn try_into_move(self) -> Option<Self::ObjectType> {
match self {
Self::MoveObject(o) => Some(o),
Self::Package(_) => None,
}
}
fn try_as_package(&self) -> Option<&Self::PackageType> {
match self {
Self::MoveObject(_) => None,
Self::Package(p) => Some(p),
}
}
fn type_(&self) -> Option<&StructTag> {
match self {
Self::MoveObject(o) => Some(&o.type_),
Self::Package(_) => None,
}
}
}
impl Display for SuiParsedData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut writer = String::new();
match self {
SuiParsedData::MoveObject(o) => {
writeln!(writer, "{}: {}", "type".bold().bright_black(), o.type_)?;
write!(writer, "{}", &o.fields)?;
}
SuiParsedData::Package(p) => {
write!(
writer,
"{}: {:?}",
"Modules".bold().bright_black(),
p.disassembled.keys()
)?;
}
}
write!(f, "{}", writer)
}
}
pub trait SuiMoveObject: Sized {
fn type_(&self) -> &StructTag;
}
#[serde_as]
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename = "MoveObject", rename_all = "camelCase")]
pub struct SuiParsedMoveObject {
#[serde(rename = "type")]
#[serde_as(as = "DisplayFromStr")]
pub type_: StructTag,
pub has_public_transfer: bool,
pub fields: SuiMoveStruct,
}
impl SuiMoveObject for SuiParsedMoveObject {
fn type_(&self) -> &StructTag {
&self.type_
}
}
impl SuiParsedMoveObject {
pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<SuiMoveValue> {
match &self.fields {
SuiMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
SuiMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(),
_ => None,
}
}
}
#[serde_as]
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename = "RawMoveObject", rename_all = "camelCase")]
pub struct SuiRawMoveObject {
#[serde(rename = "type")]
#[serde_as(as = "DisplayFromStr")]
pub type_: StructTag,
pub has_public_transfer: bool,
pub version: Version,
#[serde_as(as = "Base64")]
pub bcs_bytes: Vec<u8>,
}
impl SuiMoveObject for SuiRawMoveObject {
fn type_(&self) -> &StructTag {
&self.type_
}
}
impl SuiRawMoveObject {
pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result<T, bcs::Error> {
bcs::from_bytes(self.bcs_bytes.as_slice())
}
}
#[serde_as]
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename = "RawMovePackage", rename_all = "camelCase")]
pub struct SuiRawMovePackage {
pub id: ObjectId,
pub version: Version,
#[serde_as(as = "BTreeMap<_, Base64>")]
pub module_map: BTreeMap<String, Vec<u8>>,
pub type_origin_table: Vec<TypeOrigin>,
pub linkage_table: BTreeMap<ObjectId, UpgradeInfo>,
}
#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
pub enum SuiPastObjectResponseError {
#[error("Could not find the referenced object {object_id:?} at version {version:?}.")]
ObjectNotFound {
object_id: ObjectId,
version: Option<Version>,
},
#[error(
"Could not find the referenced object {object_id:?} \
as the asked version {asked_version:?} \
is higher than the latest {latest_version:?}"
)]
ObjectSequenceNumberTooHigh {
object_id: ObjectId,
asked_version: Version,
latest_version: Version,
},
#[error("Object deleted at reference {object_ref:?}.")]
ObjectDeleted { object_ref: ObjectRef },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(tag = "status", content = "details", rename = "ObjectRead")]
pub enum SuiPastObjectResponse {
VersionFound(SuiObjectData),
ObjectNotExists(ObjectId),
ObjectDeleted(SuiObjectRef),
VersionNotFound(ObjectId, Version),
VersionTooHigh {
object_id: ObjectId,
asked_version: Version,
latest_version: Version,
},
}
impl SuiPastObjectResponse {
pub fn object(&self) -> Result<&SuiObjectData, SuiPastObjectResponseError> {
match &self {
Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
object_ref: oref.to_object_ref(),
}),
Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
object_id: *id,
version: None,
}),
Self::VersionFound(o) => Ok(o),
Self::VersionNotFound(id, seq_num) => Err(SuiPastObjectResponseError::ObjectNotFound {
object_id: *id,
version: Some(*seq_num),
}),
Self::VersionTooHigh {
object_id,
asked_version,
latest_version,
} => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
object_id: *object_id,
asked_version: *asked_version,
latest_version: *latest_version,
}),
}
}
pub fn into_object(self) -> Result<SuiObjectData, SuiPastObjectResponseError> {
match self {
Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
object_ref: oref.to_object_ref(),
}),
Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
object_id: id,
version: None,
}),
Self::VersionFound(o) => Ok(o),
Self::VersionNotFound(object_id, version) => {
Err(SuiPastObjectResponseError::ObjectNotFound {
object_id,
version: Some(version),
})
}
Self::VersionTooHigh {
object_id,
asked_version,
latest_version,
} => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
object_id,
asked_version,
latest_version,
}),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename = "MovePackage", rename_all = "camelCase")]
pub struct SuiMovePackage {
pub disassembled: BTreeMap<String, Value>,
}
pub type QueryObjectsPage = Page<SuiObjectResponse, CheckpointedObjectId>;
pub type ObjectsPage = Page<SuiObjectResponse, ObjectId>;
#[serde_as]
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CheckpointedObjectId {
pub object_id: ObjectId,
#[serde_as(as = "Option<BigInt<u64>>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub at_checkpoint: Option<Version>,
}
#[serde_as]
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")]
pub struct SuiGetPastObjectRequest {
pub object_id: ObjectId,
#[serde_as(as = "BigInt<u64>")]
pub version: Version,
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SuiObjectDataFilter {
MatchAll(Vec<SuiObjectDataFilter>),
MatchAny(Vec<SuiObjectDataFilter>),
MatchNone(Vec<SuiObjectDataFilter>),
Package(ObjectId),
MoveModule {
package: ObjectId,
#[serde_as(as = "DisplayFromStr")]
module: Identifier,
},
StructType(#[serde_as(as = "DisplayFromStr")] StructTag),
AddressOwner(SuiAddress),
ObjectOwner(ObjectId),
ObjectId(ObjectId),
ObjectIds(Vec<ObjectId>),
Version(#[serde_as(as = "BigInt<u64>")] u64),
}
impl SuiObjectDataFilter {
pub fn gas_coin() -> Self {
Self::StructType(StructTag::gas_coin())
}
pub fn and(self, other: Self) -> Self {
Self::MatchAll(vec![self, other])
}
pub fn or(self, other: Self) -> Self {
Self::MatchAny(vec![self, other])
}
pub fn not(self, other: Self) -> Self {
Self::MatchNone(vec![self, other])
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
pub struct SuiObjectResponseQuery {
pub filter: Option<SuiObjectDataFilter>,
pub options: Option<SuiObjectDataOptions>,
}
impl SuiObjectResponseQuery {
pub fn new(filter: Option<SuiObjectDataFilter>, options: Option<SuiObjectDataOptions>) -> Self {
Self { filter, options }
}
pub fn new_with_filter(filter: SuiObjectDataFilter) -> Self {
Self {
filter: Some(filter),
options: None,
}
}
pub fn new_with_options(options: SuiObjectDataOptions) -> Self {
Self {
filter: None,
options: Some(options),
}
}
}