From 55c295917e2a4c915202a285611bd8aa92635728 Mon Sep 17 00:00:00 2001 From: Chris Li <76067158+666lcz@users.noreply.github.com> Date: Thu, 5 May 2022 11:42:24 -0700 Subject: [PATCH] [explorer] implement batching requests (#1836) --- .../components/ownedobjects/OwnedObjects.tsx | 7 ++-- .../src/providers/json-rpc-provider.ts | 18 +++++++++ sdk/typescript/src/rpc/client.guard.ts | 12 +++++- sdk/typescript/src/rpc/client.ts | 40 +++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/explorer/client/src/components/ownedobjects/OwnedObjects.tsx b/explorer/client/src/components/ownedobjects/OwnedObjects.tsx index c0fd8c0233065..5143ac24fbb9a 100644 --- a/explorer/client/src/components/ownedobjects/OwnedObjects.tsx +++ b/explorer/client/src/components/ownedobjects/OwnedObjects.tsx @@ -73,9 +73,8 @@ function OwnedObjectAPI({ id }: { id: string }) { // getOwnedObjectRefs will need to return id, type and balance for each owned object useEffect(() => { rpc.getOwnedObjectRefs(id).then((objects) => { - Promise.all( - objects.map(({ objectId }) => rpc.getObjectInfo(objectId)) - ).then((results) => { + const ids = objects.map(({ objectId }) => objectId); + rpc.getObjectInfoBatch(ids).then((results) => { setResults( results .filter(({ status }) => status === 'Exists') @@ -98,7 +97,7 @@ function OwnedObjectAPI({ id }: { id: string }) { balance: balanceValue, }; } - //TO DO - add back display and version + //TO DO - add back version ) ); setIsLoaded(true); diff --git a/sdk/typescript/src/providers/json-rpc-provider.ts b/sdk/typescript/src/providers/json-rpc-provider.ts index 22433215e2b3d..ce9454547b972 100644 --- a/sdk/typescript/src/providers/json-rpc-provider.ts +++ b/sdk/typescript/src/providers/json-rpc-provider.ts @@ -64,6 +64,24 @@ export class JsonRpcProvider extends Provider { } } + async getObjectInfoBatch( + objectIds: string[] + ): Promise { + const requests = objectIds.map(id => ({ + method: 'sui_getObjectTypedInfo', + args: [id], + })); + try { + const responses = await this.client.batchRequestWithType( + requests, + isGetObjectInfoResponse + ); + return responses.map(r => transformGetObjectInfoResponse(r)); + } catch (err) { + throw new Error(`Error fetching object info: ${err} for id ${objectIds}`); + } + } + // Transactions async getTransactionWithEffects( digest: TransactionDigest diff --git a/sdk/typescript/src/rpc/client.guard.ts b/sdk/typescript/src/rpc/client.guard.ts index a1324f63a9d3d..a31b1226759db 100644 --- a/sdk/typescript/src/rpc/client.guard.ts +++ b/sdk/typescript/src/rpc/client.guard.ts @@ -5,7 +5,7 @@ * Generated type guards for "client.ts". * WARNING: Do not manually change this file. */ -import { HttpHeaders, ValidResponse, ErrorResponse } from "./client"; +import { HttpHeaders, RpcParams, ValidResponse, ErrorResponse } from "./client"; import { isTransactionResponse } from "../index.guard"; export function isHttpHeaders(obj: any, _argumentName?: string): obj is HttpHeaders { @@ -16,6 +16,16 @@ export function isHttpHeaders(obj: any, _argumentName?: string): obj is HttpHead ) } +export function isRpcParams(obj: any, _argumentName?: string): obj is RpcParams { + return ( + (obj !== null && + typeof obj === "object" || + typeof obj === "function") && + isTransactionResponse(obj.method) as boolean && + Array.isArray(obj.args) + ) +} + export function isValidResponse(obj: any, _argumentName?: string): obj is ValidResponse { return ( (obj !== null && diff --git a/sdk/typescript/src/rpc/client.ts b/sdk/typescript/src/rpc/client.ts index e5f35f97038cd..0347fecd2a5de 100644 --- a/sdk/typescript/src/rpc/client.ts +++ b/sdk/typescript/src/rpc/client.ts @@ -10,6 +10,14 @@ import { isErrorResponse, isValidResponse } from './client.guard'; */ export type HttpHeaders = { [header: string]: string }; +/** + * @internal + */ +export type RpcParams = { + method: string; + args: Array; +}; + export class JsonRpcClient { private rpcClient: RpcClient; @@ -83,6 +91,38 @@ export class JsonRpcClient { }); }); } + + async batchRequestWithType( + requests: RpcParams[], + isT: (val: any) => val is T + ): Promise { + const responses = await this.batchRequest(requests); + // TODO: supports other error modes such as throw or return + const validResponses = responses.filter( + (response: any) => isValidResponse(response) && isT(response.result) + ); + + return validResponses.map((response: ValidResponse) => response.result); + } + + async batchRequest(requests: RpcParams[]): Promise { + return new Promise((resolve, reject) => { + // Do nothing if requests is empty + if (requests.length === 0) resolve([]); + + const batch = requests.map(params => { + return this.rpcClient.request(params.method, params.args); + }); + + this.rpcClient.request(batch, (err: any, response: any) => { + if (err) { + reject(err); + return; + } + resolve(response); + }); + }); + } } export type ValidResponse = {