From ab8b14906242863326ef8bd0c085476200a7a3d8 Mon Sep 17 00:00:00 2001 From: Joe C Date: Wed, 30 Aug 2023 01:25:30 -0600 Subject: [PATCH] refactor(experimental): add opaque type for serialized message (#1543) --- .../src/rpc-methods/__tests__/get-fee-for-message-test.ts | 8 ++++---- packages/rpc-core/src/rpc-methods/getFeeForMessage.ts | 7 ++++--- packages/transactions/src/serializers/message.ts | 7 ++++--- packages/transactions/src/types.ts | 4 ++++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/rpc-core/src/rpc-methods/__tests__/get-fee-for-message-test.ts b/packages/rpc-core/src/rpc-methods/__tests__/get-fee-for-message-test.ts index f3b2a5aaa9f2..0eb8267c08a6 100644 --- a/packages/rpc-core/src/rpc-methods/__tests__/get-fee-for-message-test.ts +++ b/packages/rpc-core/src/rpc-methods/__tests__/get-fee-for-message-test.ts @@ -2,10 +2,10 @@ import { base58, base64, fixSerializer } from '@metaplex-foundation/umi-serializ import { createHttpTransport, createJsonRpc } from '@solana/rpc-transport'; import type { SolanaJsonRpcErrorCode } from '@solana/rpc-transport/dist/types/json-rpc-errors'; import type { Rpc } from '@solana/rpc-transport/dist/types/json-rpc-types'; -import { Blockhash } from '@solana/transactions'; +import { Blockhash, SerializedMessageBytesBase64 } from '@solana/transactions'; import fetchMock from 'jest-fetch-mock-fork'; -import { Base64EncodedBytes, Commitment } from '../common'; +import { Commitment } from '../common'; import { createSolanaRpcApi, SolanaRpcMethods } from '../index'; // See scripts/fixtures/send-transaction-fee-payer.json @@ -50,7 +50,7 @@ function getMockTransactionMessage(blockhash: Blockhash) { 0x00, // Number of address table lookups ]); const messageBase64 = base64.deserialize(message)[0]; - return messageBase64 as Base64EncodedBytes; + return messageBase64 as SerializedMessageBytesBase64; } describe('getFeeForMessage', () => { @@ -114,7 +114,7 @@ describe('getFeeForMessage', () => { describe('when called with an invalid message', () => { it('throws an error', async () => { expect.assertions(1); - const sendPromise = rpc.getFeeForMessage('someInvalidMessage' as Base64EncodedBytes).send(); + const sendPromise = rpc.getFeeForMessage('someInvalidMessage' as SerializedMessageBytesBase64).send(); await expect(sendPromise).rejects.toMatchObject({ code: -32602 satisfies (typeof SolanaJsonRpcErrorCode)['JSON_RPC_INVALID_PARAMS'], message: expect.any(String), diff --git a/packages/rpc-core/src/rpc-methods/getFeeForMessage.ts b/packages/rpc-core/src/rpc-methods/getFeeForMessage.ts index 1928826d91b6..82dc6410dbe8 100644 --- a/packages/rpc-core/src/rpc-methods/getFeeForMessage.ts +++ b/packages/rpc-core/src/rpc-methods/getFeeForMessage.ts @@ -1,4 +1,6 @@ -import { Base64EncodedBytes, Commitment, RpcResponse, Slot, U64UnsafeBeyond2Pow53Minus1 } from './common'; +import { SerializedMessageBytesBase64 } from '@solana/transactions'; + +import { Commitment, RpcResponse, Slot, U64UnsafeBeyond2Pow53Minus1 } from './common'; /** Fee corresponding to the message at the specified blockhash */ type GetFeeForMessageApiResponse = RpcResponse; @@ -8,8 +10,7 @@ export interface GetFeeForMessageApi { * Returns the fee the network will charge for a particular Message */ getFeeForMessage( - /** Base-64 encoded message */ - message: Base64EncodedBytes, + message: SerializedMessageBytesBase64, config?: Readonly<{ commitment?: Commitment; minContextSlot?: Slot; diff --git a/packages/transactions/src/serializers/message.ts b/packages/transactions/src/serializers/message.ts index c8f12f24f689..fe3965eaea17 100644 --- a/packages/transactions/src/serializers/message.ts +++ b/packages/transactions/src/serializers/message.ts @@ -11,6 +11,7 @@ import { import { getBase58EncodedAddressCodec } from '@solana/addresses'; import { CompiledMessage } from '../message'; +import { SerializedMessageBytes } from '../types'; import { getAddressTableLookupCodec } from './address-table-lookup'; import { getMessageHeaderCodec } from './header'; import { getInstructionCodec } from './instruction'; @@ -42,9 +43,9 @@ function deserialize(bytes: Uint8Array, offset = 0): [CompiledMessage, number] { ]; } -function serialize(compiledMessage: CompiledMessage) { +function serialize(compiledMessage: CompiledMessage): SerializedMessageBytes { if (compiledMessage.version === 'legacy') { - return struct(getPreludeStructSerializerTuple()).serialize(compiledMessage); + return struct(getPreludeStructSerializerTuple()).serialize(compiledMessage) as SerializedMessageBytes; } else { return mapSerializer( struct([ @@ -60,7 +61,7 @@ function serialize(compiledMessage: CompiledMessage) { addressTableLookups: value.addressTableLookups ?? [], } as Exclude; } - ).serialize(compiledMessage); + ).serialize(compiledMessage) as SerializedMessageBytes; } } diff --git a/packages/transactions/src/types.ts b/packages/transactions/src/types.ts index 3a6220459d1a..ecb4ae07ca9f 100644 --- a/packages/transactions/src/types.ts +++ b/packages/transactions/src/types.ts @@ -1,5 +1,9 @@ import { IAccountMeta, IInstruction } from '@solana/instructions'; +/** A string of bytes that are definitely a serialized message */ +export type SerializedMessageBytes = Uint8Array & { readonly __serializedMessageBytes: unique symbol }; +export type SerializedMessageBytesBase64 = string & { readonly __serializedMessageBytesBase64: unique symbol }; + export type BaseTransaction = Readonly<{ instructions: readonly IInstruction[]; version: TransactionVersion;