diff --git a/packages/rpc-core/src/rpc-methods/index.ts b/packages/rpc-core/src/rpc-methods/index.ts index 2660814bcf16..f1426f1cd3a2 100644 --- a/packages/rpc-core/src/rpc-methods/index.ts +++ b/packages/rpc-core/src/rpc-methods/index.ts @@ -1,4 +1,5 @@ -import type { IRpcApi, RpcRequest } from '@solana/rpc-transport'; +import { createJsonRpcApi } from '@solana/rpc-transport'; +import { IRpcApi } from '@solana/rpc-transport'; import { patchParamsForSolanaLabsRpc } from '../params-patcher'; import { patchResponseForSolanaLabsRpc } from '../response-patcher'; @@ -115,37 +116,17 @@ export type SolanaRpcMethods = GetAccountInfoApi & SimulateTransactionApi; export function createSolanaRpcApi(config?: Config): IRpcApi { - return new Proxy({} as IRpcApi, { - defineProperty() { - return false; - }, - deleteProperty() { - return false; - }, - get>( - ...args: Parameters>['get']>> - ) { - const [_, p] = args; - const methodName = p.toString() as keyof SolanaRpcMethods as string; - return function ( - ...rawParams: Parameters< - SolanaRpcMethods[TMethodName] extends CallableFunction ? SolanaRpcMethods[TMethodName] : never - > - ): RpcRequest> { - const handleIntegerOverflow = config?.onIntegerOverflow; - const params = patchParamsForSolanaLabsRpc( - rawParams, - handleIntegerOverflow - ? (keyPath, value) => handleIntegerOverflow(methodName, keyPath, value) - : undefined, - ); - return { - methodName, - params, - responseTransformer: rawResponse => patchResponseForSolanaLabsRpc(rawResponse, methodName), - }; - }; - }, + const handleIntegerOverflow = config?.onIntegerOverflow; + return createJsonRpcApi({ + parametersTransformer: (rawParams: T, methodName: string) => + patchParamsForSolanaLabsRpc( + rawParams, + handleIntegerOverflow + ? (keyPath, value) => handleIntegerOverflow(methodName, keyPath, value) + : undefined, + ) as unknown[], + responseTransformer: (rawResponse: unknown, methodName: string): T => + patchResponseForSolanaLabsRpc(rawResponse, methodName as keyof SolanaRpcMethods), }); } diff --git a/packages/rpc-transport/src/__tests__/json-rpc-test.ts b/packages/rpc-transport/src/__tests__/json-rpc-test.ts index a33af0af0616..8fe0bda30ded 100644 --- a/packages/rpc-transport/src/__tests__/json-rpc-test.ts +++ b/packages/rpc-transport/src/__tests__/json-rpc-test.ts @@ -97,7 +97,7 @@ describe('JSON-RPC 2.0', () => { expect.assertions(1); (makeHttpRequest as jest.Mock).mockResolvedValueOnce({ result: 123 }); await rpc.someMethod().send(); - expect(responseTransformer).toHaveBeenCalledWith(123); + expect(responseTransformer).toHaveBeenCalledWith(123, 'someMethod'); }); it('returns the processed response', async () => { expect.assertions(1); diff --git a/packages/rpc-transport/src/apis/__typetests__/methods-api-typetest.ts b/packages/rpc-transport/src/apis/__typetests__/methods-api-typetest.ts new file mode 100644 index 000000000000..d8e10a2b5482 --- /dev/null +++ b/packages/rpc-transport/src/apis/__typetests__/methods-api-typetest.ts @@ -0,0 +1,23 @@ +import { IRpcApi } from '../../json-rpc-types'; +import { IRpcApiMethods } from '../api-types'; +import { createJsonRpcApi } from '../methods/methods-api'; + +type NftCollectionDetailsApiResponse = Readonly<{ + address: string; + circulatingSupply: number; + description: string; + erc721: boolean; + erc1155: boolean; + genesisBlock: string; + genesisTransaction: string; + name: string; + totalSupply: number; +}>; + +interface NftCollectionDetailsApi extends IRpcApiMethods { + qn_fetchNFTCollectionDetails(args: { contracts: string[] }): NftCollectionDetailsApiResponse; +} + +type QuickNodeRpcMethods = NftCollectionDetailsApi; + +createJsonRpcApi() satisfies IRpcApi; diff --git a/packages/rpc-transport/src/apis/methods/methods-api.ts b/packages/rpc-transport/src/apis/methods/methods-api.ts new file mode 100644 index 000000000000..6947dc03f06a --- /dev/null +++ b/packages/rpc-transport/src/apis/methods/methods-api.ts @@ -0,0 +1,36 @@ +import { IRpcApi, RpcRequest } from '../../json-rpc-types'; +import { IRpcApiMethods, RpcApiConfig } from '../api-types'; + +export function createJsonRpcApi(config?: RpcApiConfig): IRpcApi { + return new Proxy({} as IRpcApi, { + defineProperty() { + return false; + }, + deleteProperty() { + return false; + }, + get>( + ...args: Parameters>['get']>> + ) { + const [_, p] = args; + const methodName = p.toString() as keyof TRpcMethods as string; + return function ( + ...rawParams: Parameters< + TRpcMethods[TMethodName] extends CallableFunction ? TRpcMethods[TMethodName] : never + > + ): RpcRequest> { + const params = config?.parametersTransformer + ? config?.parametersTransformer(rawParams, methodName) + : rawParams; + const responseTransformer = config?.responseTransformer + ? config?.responseTransformer> + : (rawResponse: unknown) => rawResponse as ReturnType; + return { + methodName, + params, + responseTransformer, + }; + }; + }, + }); +} diff --git a/packages/rpc-transport/src/index.ts b/packages/rpc-transport/src/index.ts index 20084b8878c4..ba2a8c626d5d 100644 --- a/packages/rpc-transport/src/index.ts +++ b/packages/rpc-transport/src/index.ts @@ -1,4 +1,5 @@ export * from './apis/api-types'; +export * from './apis/methods/methods-api'; export * from './json-rpc'; export type { SolanaJsonRpcErrorCode } from './json-rpc-errors'; export * from './json-rpc-subscription'; diff --git a/packages/rpc-transport/src/json-rpc-types.ts b/packages/rpc-transport/src/json-rpc-types.ts index 8d841ea1b23f..0f8011d3d901 100644 --- a/packages/rpc-transport/src/json-rpc-types.ts +++ b/packages/rpc-transport/src/json-rpc-types.ts @@ -26,7 +26,7 @@ export type RpcSubscriptionConfig = Readonly<{ export type RpcRequest = { methodName: string; params: unknown[]; - responseTransformer?: (response: unknown) => TResponse; + responseTransformer?: (response: unknown, methodName: string) => TResponse; }; export type RpcSubscription = { params: unknown[]; diff --git a/packages/rpc-transport/src/json-rpc.ts b/packages/rpc-transport/src/json-rpc.ts index 14773e727ca8..142d1485e1b4 100644 --- a/packages/rpc-transport/src/json-rpc.ts +++ b/packages/rpc-transport/src/json-rpc.ts @@ -23,7 +23,9 @@ function createPendingRpcRequest( if ('error' in response) { throw new SolanaJsonRpcError(response.error); } else { - return (responseTransformer ? responseTransformer(response.result) : response.result) as TResponse; + return ( + responseTransformer ? responseTransformer(response.result, methodName) : response.result + ) as TResponse; } }, };