Skip to content

Commit

Permalink
[rust/ts SDK] Adding query param onto get_owned_objects (MystenLabs#9399
Browse files Browse the repository at this point in the history
)

Adding query param onto get_owned_objects
## Description 

Modified unit tests in e2e to handle `ObjectsPage` response that is
returned by the SDK.
Adding query param onto get_owned_objects

## Test Plan 

How did you test the new or updated feature?
CI / Unit testing

Address page of explorer:

![image](https://user-images.githubusercontent.com/123408603/225946167-70760de1-62a5-4182-a61a-0989c32b3e09.png)

Txn page:

![image](https://user-images.githubusercontent.com/123408603/225946279-b5475f46-f506-4f5a-9421-2777020a434a.png)

Sui wallet: 

![image](https://user-images.githubusercontent.com/123408603/225977046-c375b3dd-9a32-4228-acbc-5243b10b0fff.png)
NFT on wallet


![image](https://user-images.githubusercontent.com/123408603/225979023-657516e1-c2b6-42b0-936d-b3e515375e4c.png)

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] user-visible impact
- [ x] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
healthydeve authored Mar 17, 2023
1 parent d5722bf commit 0f7aa65
Show file tree
Hide file tree
Showing 36 changed files with 622 additions and 186 deletions.
5 changes: 5 additions & 0 deletions .changeset/wild-points-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mysten/sui.js": minor
---

Switching the response type of the getOwnedObjects api to a paginatedObjects response, and also moving filtering to FN
12 changes: 9 additions & 3 deletions apps/explorer/src/components/ownedobjects/OwnedObjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
// SPDX-License-Identifier: Apache-2.0

import { useRpcClient } from '@mysten/core';
import { Coin, getObjectFields, getObjectId } from '@mysten/sui.js';
import {
Coin,
getObjectFields,
getObjectId,
PaginatedObjectsResponse,
is,
} from '@mysten/sui.js';
import { useEffect, useState } from 'react';

import {
Expand Down Expand Up @@ -43,8 +49,8 @@ function OwnedObject({ id, byAddress }: { id: string; byAddress: boolean }) {

req.then((objects) => {
let ids: string[];
if (objects instanceof Array) {
ids = objects.map(({ objectId }) => objectId);
if (is(objects, PaginatedObjectsResponse)) {
ids = objects.data.map((resp) => getObjectId(resp));
} else {
ids = objects.data.map(({ objectId }) => objectId);
}
Expand Down
14 changes: 11 additions & 3 deletions apps/wallet/src/ui/app/hooks/useObjectsOwnedByAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
import { useRpcClient } from '@mysten/core';
import { useQuery } from '@tanstack/react-query';

import type { SuiAddress } from '@mysten/sui.js';
import type { SuiAddress, SuiObjectResponseQuery } from '@mysten/sui.js';

export function useObjectsOwnedByAddress(address?: SuiAddress | null) {
export function useObjectsOwnedByAddress(
address?: SuiAddress | null,
query?: SuiObjectResponseQuery
) {
const rpc = useRpcClient();
return useQuery(
['objects-owned', address],
() => rpc.getOwnedObjects({ owner: address! }),
() =>
rpc.getOwnedObjects({
owner: address!,
filter: query?.filter,
options: query?.options,
}),
{
enabled: !!address,
}
Expand Down
18 changes: 14 additions & 4 deletions apps/wallet/src/ui/app/pages/home/nfts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,22 @@ import { NFTDisplayCard } from '_components/nft-display';
import { useObjectsOwnedByAddress } from '_hooks';
import PageTitle from '_src/ui/app/shared/PageTitle';

import type { SuiObjectResponse, SuiObjectData } from '@mysten/sui.js';

function NftsPage() {
const accountAddress = useActiveAddress();
const { data, isLoading, error, isError } =
useObjectsOwnedByAddress(accountAddress);
const nfts = data?.filter((obj) => !Coin.isCoin(obj));
const { data, isLoading, error, isError } = useObjectsOwnedByAddress(
accountAddress,
{ options: { showType: true } }
);
const sui_object_responses = data?.data as SuiObjectResponse[];
const nft_objects = sui_object_responses?.filter(
(obj) => !Coin.isCoin(obj)
);
const nfts = nft_objects?.map((nft) => {
const nft_details = nft.details as SuiObjectData;
return nft_details;
});

return (
<div className="flex flex-col flex-nowrap items-center gap-4 flex-1">
Expand All @@ -30,7 +41,6 @@ function NftsPage() {
<small>{(error as Error).message}</small>
</Alert>
) : null}

{nfts?.length ? (
<div className="grid grid-cols-2 gap-x-3.5 gap-y-4 w-full h-full">
{nfts.map(({ objectId }) => (
Expand Down
15 changes: 11 additions & 4 deletions crates/sui-core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use sui_config::genesis::Genesis;
use sui_config::node::{AuthorityStorePruningConfig, DBCheckpointConfig};
use sui_json_rpc_types::{
DevInspectResults, DryRunTransactionResponse, EventFilter, SuiEvent, SuiMoveValue,
SuiTransactionEvents,
SuiObjectDataFilter, SuiTransactionEvents,
};
use sui_macros::{fail_point, nondeterministic};
use sui_protocol_config::{ProtocolConfig, SupportedProtocolVersions};
Expand Down Expand Up @@ -2151,9 +2151,10 @@ impl AuthorityState {
owner: SuiAddress,
cursor: Option<ObjectID>,
limit: usize,
filter: Option<SuiObjectDataFilter>,
) -> SuiResult<Vec<ObjectInfo>> {
if let Some(indexes) = &self.indexes {
indexes.get_owner_objects(owner, cursor, limit)
indexes.get_owner_objects(owner, cursor, limit, filter)
} else {
Err(SuiError::IndexStoreNotAvailable)
}
Expand All @@ -2162,9 +2163,15 @@ impl AuthorityState {
pub fn get_owner_objects_iterator(
&self,
owner: SuiAddress,
cursor: Option<ObjectID>,
limit: Option<usize>,
filter: Option<SuiObjectDataFilter>,
) -> SuiResult<impl Iterator<Item = ObjectInfo> + '_> {
let cursor_u = cursor.unwrap_or(ObjectID::ZERO);
let count = limit.unwrap_or(MAX_GET_OWNED_OBJECT_SIZE);

if let Some(indexes) = &self.indexes {
indexes.get_owner_objects_iterator(owner, ObjectID::ZERO, MAX_GET_OWNED_OBJECT_SIZE)
indexes.get_owner_objects_iterator(owner, cursor_u, count, filter)
} else {
Err(SuiError::IndexStoreNotAvailable)
}
Expand All @@ -2179,7 +2186,7 @@ impl AuthorityState {
T: DeserializeOwned,
{
let object_ids = self
.get_owner_objects_iterator(owner)?
.get_owner_objects_iterator(owner, None, None, None)?
.filter(|o| match &o.type_ {
ObjectType::Struct(s) => type_.matches_type_fuzzy_generics(s),
ObjectType::Package => false,
Expand Down
10 changes: 5 additions & 5 deletions crates/sui-indexer/src/apis/read_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ use sui_json_rpc::SuiRpcModule;
use sui_json_rpc_types::{
Checkpoint, CheckpointId, DynamicFieldPage, MoveFunctionArgType, ObjectsPage, Page,
SuiGetPastObjectRequest, SuiMoveNormalizedFunction, SuiMoveNormalizedModule,
SuiMoveNormalizedStruct, SuiObjectDataOptions, SuiObjectResponse, SuiPastObjectResponse,
SuiTransactionResponse, SuiTransactionResponseOptions, SuiTransactionResponseQuery,
TransactionsPage,
SuiMoveNormalizedStruct, SuiObjectDataOptions, SuiObjectResponse, SuiObjectResponseQuery,
SuiPastObjectResponse, SuiTransactionResponse, SuiTransactionResponseOptions,
SuiTransactionResponseQuery, TransactionsPage,
};
use sui_open_rpc::Module;
use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress, TxSequenceNumber};
Expand Down Expand Up @@ -218,13 +218,13 @@ where
async fn get_owned_objects(
&self,
address: SuiAddress,
options: Option<SuiObjectDataOptions>,
query: Option<SuiObjectResponseQuery>,
cursor: Option<ObjectID>,
limit: Option<usize>,
at_checkpoint: Option<CheckpointId>,
) -> RpcResult<ObjectsPage> {
self.fullnode
.get_owned_objects(address, options, cursor, limit, at_checkpoint)
.get_owned_objects(address, query, cursor, limit, at_checkpoint)
.await
}

Expand Down
9 changes: 6 additions & 3 deletions crates/sui-indexer/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ mod pg_integration {
use sui_indexer::{new_pg_connection_pool, Indexer, IndexerConfig, PgPoolConnection};
use sui_json_rpc::api::{ReadApiClient, TransactionBuilderClient, WriteApiClient};
use sui_json_rpc_types::{
SuiMoveObject, SuiObjectDataOptions, SuiObjectResponse, SuiParsedMoveObject,
SuiTransactionResponseOptions, SuiTransactionResponseQuery, TransactionBytes,
SuiMoveObject, SuiObjectDataOptions, SuiObjectResponse, SuiObjectResponseQuery,
SuiParsedMoveObject, SuiTransactionResponseOptions, SuiTransactionResponseQuery,
TransactionBytes,
};
use sui_keys::keystore::{AccountKeystore, FileBasedKeystore, Keystore};
use sui_types::base_types::ObjectID;
Expand Down Expand Up @@ -72,7 +73,9 @@ mod pg_integration {
let gas_objects: Vec<ObjectID> = indexer_rpc_client
.get_owned_objects(
*address,
Some(SuiObjectDataOptions::new().with_type()),
Some(SuiObjectResponseQuery::new_with_options(
SuiObjectDataOptions::new().with_type(),
)),
None,
None,
None,
Expand Down
52 changes: 52 additions & 0 deletions crates/sui-json-rpc-types/src/sui_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::anyhow;
use colored::Colorize;
use fastcrypto::encoding::Base64;
use move_bytecode_utils::module_cache::GetModule;
use move_core_types::identifier::Identifier;
use move_core_types::language_storage::StructTag;
use move_core_types::value::{MoveStruct, MoveStructLayout};
use schemars::JsonSchema;
Expand Down Expand Up @@ -890,3 +891,54 @@ pub struct SuiGetPastObjectRequest {
/// the version of the queried object.
pub version: SequenceNumber,
}

#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum SuiObjectDataFilter {
/// Query by type a specified Package.
Package(ObjectID),
/// Query by type a specified Move module.
MoveModule {
/// the Move package ID
package: ObjectID,
/// the module name
#[schemars(with = "String")]
#[serde_as(as = "DisplayFromStr")]
module: Identifier,
},
/// Query by type
StructType(
#[schemars(with = "String")]
#[serde_as(as = "DisplayFromStr")]
StructTag,
),
}

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
pub struct SuiObjectResponseQuery {
/// If None, no filter will be applied
pub filter: Option<SuiObjectDataFilter>,
/// config which fields to include in the response, by default only digest is included
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),
}
}
}
10 changes: 5 additions & 5 deletions crates/sui-json-rpc/src/api/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use std::collections::BTreeMap;
use sui_json_rpc_types::{
Checkpoint, CheckpointId, DynamicFieldPage, MoveFunctionArgType, ObjectsPage,
SuiGetPastObjectRequest, SuiMoveNormalizedFunction, SuiMoveNormalizedModule,
SuiMoveNormalizedStruct, SuiObjectDataOptions, SuiObjectResponse, SuiPastObjectResponse,
SuiTransactionResponse, SuiTransactionResponseOptions, SuiTransactionResponseQuery,
TransactionsPage,
SuiMoveNormalizedStruct, SuiObjectDataOptions, SuiObjectResponse, SuiObjectResponseQuery,
SuiPastObjectResponse, SuiTransactionResponse, SuiTransactionResponseOptions,
SuiTransactionResponseQuery, TransactionsPage,
};
use sui_open_rpc_macros::open_rpc;
use sui_types::base_types::{
Expand All @@ -27,8 +27,8 @@ pub trait ReadApi {
&self,
/// the owner's Sui address
address: SuiAddress,
/// options for specifying the content to be returned
options: Option<SuiObjectDataOptions>,
/// the objects query criteria.
query: Option<SuiObjectResponseQuery>,
/// Optional paging cursor
cursor: Option<ObjectID>,
/// Max number of items returned per page, default to [MAX_GET_OWNED_OBJECT_SIZE] if not specified.
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-json-rpc/src/coin_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl CoinReadApi {
) -> Result<impl Iterator<Item = ObjectID> + '_, Error> {
Ok(self
.state
.get_owner_objects_iterator(owner)?
.get_owner_objects_iterator(owner, None, None, None)?
.filter(move |o| matches!(&o.type_, ObjectType::Struct(type_) if is_coin_type(type_, coin_type)))
.map(|info|info.object_id))
}
Expand Down
19 changes: 12 additions & 7 deletions crates/sui-json-rpc/src/read_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ use sui_json_rpc_types::{
BalanceChange, Checkpoint, CheckpointId, DynamicFieldPage, MoveFunctionArgType, ObjectChange,
ObjectValueKind, ObjectsPage, Page, SuiGetPastObjectRequest, SuiMoveNormalizedFunction,
SuiMoveNormalizedModule, SuiMoveNormalizedStruct, SuiMoveStruct, SuiMoveValue,
SuiObjectDataOptions, SuiObjectResponse, SuiPastObjectResponse, SuiTransactionEvents,
SuiTransactionResponse, SuiTransactionResponseOptions, SuiTransactionResponseQuery,
TransactionsPage,
SuiObjectDataOptions, SuiObjectResponse, SuiObjectResponseQuery, SuiPastObjectResponse,
SuiTransactionEvents, SuiTransactionResponse, SuiTransactionResponseOptions,
SuiTransactionResponseQuery, TransactionsPage,
};
use sui_open_rpc::Module;
use sui_types::base_types::{
Expand Down Expand Up @@ -118,23 +118,28 @@ impl ReadApiServer for ReadApi {
&self,
address: SuiAddress,
// exclusive cursor if `Some`, otherwise start from the beginning
options: Option<SuiObjectDataOptions>,
query: Option<SuiObjectResponseQuery>,
cursor: Option<ObjectID>,
limit: Option<usize>,
at_checkpoint: Option<CheckpointId>,
) -> RpcResult<ObjectsPage> {
if at_checkpoint.is_some() {
return Err(anyhow!("at_checkpoint param currently not supported").into());
return Err(anyhow!(UserInputError::Unsupported(
"at_checkpoint param currently not supported".to_string()
))
.into());
}
let limit = cap_page_objects_limit(limit)?;
let SuiObjectResponseQuery { filter, options } = query.unwrap_or_default();
let options = options.unwrap_or_default();

// MUSTFIXD(jian): multi-get-object for content/storage rebate if opt.show_content is true
let mut objects = self
.state
.get_owner_objects(address, cursor, limit + 1)
.get_owner_objects(address, cursor, limit + 1, filter)
.map_err(|e| anyhow!("{e}"))?;

// MUSTFIX(jian): multi-get-object for content/storage rebate if opt.show_content is true

// objects here are of size (limit + 1), where the last one is the cursor for the next page
let has_next_page = objects.len() > limit;
objects.truncate(limit);
Expand Down
10 changes: 7 additions & 3 deletions crates/sui-json-rpc/src/transaction_builder_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::sync::Arc;
use sui_core::authority::AuthorityState;
use sui_json_rpc_types::{
BigInt, CheckpointId, ObjectsPage, Page, SuiObjectDataOptions, SuiObjectResponse,
SuiTransactionBuilderMode, SuiTypeTag, TransactionBytes,
SuiObjectResponseQuery, SuiTransactionBuilderMode, SuiTypeTag, TransactionBytes,
};
use sui_open_rpc::Module;
use sui_transaction_builder::{DataReader, TransactionBuilder};
Expand Down Expand Up @@ -56,7 +56,7 @@ impl DataReader for AuthorityStateDataReader {
async fn get_owned_objects(
&self,
address: SuiAddress,
options: Option<SuiObjectDataOptions>,
query: Option<SuiObjectResponseQuery>,
cursor: Option<ObjectID>,
limit: Option<usize>,
at_checkpoint: Option<CheckpointId>,
Expand All @@ -66,9 +66,13 @@ impl DataReader for AuthorityStateDataReader {
}

let limit = cap_page_objects_limit(limit)?;
let SuiObjectResponseQuery { filter, options } = query.unwrap_or_default();

let options = options.unwrap_or_default();

let mut objects = self.0.get_owner_objects(address, cursor, limit + 1)?;
let mut objects = self
.0
.get_owner_objects(address, cursor, limit + 1, filter)?;

// objects here are of size (limit + 1), where the last one is the cursor for the next page
let has_next_page = objects.len() > limit;
Expand Down
Loading

0 comments on commit 0f7aa65

Please sign in to comment.