diff --git a/web3.js/src/connection.ts b/web3.js/src/connection.ts index 080b3053a60728..06d687785bc2ba 100644 --- a/web3.js/src/connection.ts +++ b/web3.js/src/connection.ts @@ -32,8 +32,12 @@ import {NonceAccount} from './nonce-account'; import {PublicKey} from './publickey'; import {Signer} from './keypair'; import {MS_PER_SLOT} from './timing'; -import {Transaction, TransactionStatus} from './transaction'; -import {Message} from './message'; +import { + Transaction, + TransactionStatus, + TransactionVersion, +} from './transaction'; +import {Message, MessageHeader, MessageV0, VersionedMessage} from './message'; import {AddressLookupTableAccount} from './programs/address-lookup-table/state'; import assert from './utils/assert'; import {sleep} from './utils/sleep'; @@ -395,6 +399,32 @@ function notificationResultAndContext(value: Struct) { }); } +/** + * @internal + */ +function versionedMessageFromResponse( + version: TransactionVersion | undefined, + response: MessageResponse, +): VersionedMessage { + if (version === 0) { + return new MessageV0({ + header: response.header, + staticAccountKeys: response.accountKeys.map( + accountKey => new PublicKey(accountKey), + ), + recentBlockhash: response.recentBlockhash, + compiledInstructions: response.instructions.map(ix => ({ + programIdIndex: ix.programIdIndex, + accountKeyIndexes: ix.accounts, + data: bs58.decode(ix.data), + })), + addressTableLookups: response.addressTableLookups!, + }); + } else { + return new Message(response); + } +} + /** * The level of commitment desired when querying state *
@@ -457,6 +487,14 @@ export type GetBalanceConfig = {
 export type GetBlockConfig = {
   /** The level of finality desired */
   commitment?: Finality;
+};
+
+/**
+ * Configuration object for changing `getBlock` query behavior
+ */
+export type GetVersionedBlockConfig = {
+  /** The level of finality desired */
+  commitment?: Finality;
   /** The max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned */
   maxSupportedTransactionVersion?: number;
 };
@@ -537,6 +575,14 @@ export type GetSlotLeaderConfig = {
 export type GetTransactionConfig = {
   /** The level of finality desired */
   commitment?: Finality;
+};
+
+/**
+ * Configuration object for changing `getTransaction` query behavior
+ */
+export type GetVersionedTransactionConfig = {
+  /** The level of finality desired */
+  commitment?: Finality;
   /** The max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned */
   maxSupportedTransactionVersion?: number;
 };
@@ -869,6 +915,8 @@ export type ConfirmedTransactionMeta = {
   postTokenBalances?: Array | null;
   /** The error result of transaction processing */
   err: TransactionError | null;
+  /** The collection of addresses loaded using address lookup tables */
+  loadedAddresses?: LoadedAddresses;
 };
 
 /**
@@ -890,6 +938,38 @@ export type TransactionResponse = {
   blockTime?: number | null;
 };
 
+/**
+ * A processed transaction from the RPC API
+ */
+export type VersionedTransactionResponse = {
+  /** The slot during which the transaction was processed */
+  slot: number;
+  /** The transaction */
+  transaction: {
+    /** The transaction message */
+    message: VersionedMessage;
+    /** The transaction signatures */
+    signatures: string[];
+  };
+  /** Metadata produced from the transaction */
+  meta: ConfirmedTransactionMeta | null;
+  /** The unix timestamp of when the transaction was processed */
+  blockTime?: number | null;
+  /** The transaction version */
+  version?: TransactionVersion;
+};
+
+/**
+ * A processed transaction message from the RPC API
+ */
+type MessageResponse = {
+  accountKeys: string[];
+  header: MessageHeader;
+  instructions: CompiledInstruction[];
+  recentBlockhash: string;
+  addressTableLookups?: ParsedAddressTableLookup[];
+};
+
 /**
  * A confirmed transaction on the ledger
  *
@@ -942,6 +1022,18 @@ export type ParsedInstruction = {
   parsed: any;
 };
 
+/**
+ * A parsed address table lookup
+ */
+export type ParsedAddressTableLookup = {
+  /** Address lookup table account key */
+  accountKey: PublicKey;
+  /** Parsed instruction info */
+  writableIndexes: number[];
+  /** Parsed instruction info */
+  readonlyIndexes: number[];
+};
+
 /**
  * A parsed transaction message
  */
@@ -952,6 +1044,8 @@ export type ParsedMessage = {
   instructions: (ParsedInstruction | PartiallyDecodedInstruction)[];
   /** Recent blockhash */
   recentBlockhash: string;
+  /** Address table lookups used to load additional accounts */
+  addressTableLookups?: ParsedAddressTableLookup[] | null;
 };
 
 /**
@@ -983,6 +1077,8 @@ export type ParsedTransactionWithMeta = {
   meta: ParsedTransactionMeta | null;
   /** The unix timestamp of when the transaction was processed */
   blockTime?: number | null;
+  /** The version of the transaction message */
+  version?: TransactionVersion;
 };
 
 /**
@@ -1006,6 +1102,47 @@ export type BlockResponse = {
     };
     /** Metadata produced from the transaction */
     meta: ConfirmedTransactionMeta | null;
+    /** The transaction version */
+    version?: TransactionVersion;
+  }>;
+  /** Vector of block rewards */
+  rewards?: Array<{
+    /** Public key of reward recipient */
+    pubkey: string;
+    /** Reward value in lamports */
+    lamports: number;
+    /** Account balance after reward is applied */
+    postBalance: number | null;
+    /** Type of reward received */
+    rewardType: string | null;
+  }>;
+  /** The unix timestamp of when the block was processed */
+  blockTime: number | null;
+};
+
+/**
+ * A processed block fetched from the RPC API
+ */
+export type VersionedBlockResponse = {
+  /** Blockhash of this block */
+  blockhash: Blockhash;
+  /** Blockhash of this block's parent */
+  previousBlockhash: Blockhash;
+  /** Slot index of this block's parent */
+  parentSlot: number;
+  /** Vector of transactions with status meta and original message */
+  transactions: Array<{
+    /** The transaction */
+    transaction: {
+      /** The transaction message */
+      message: VersionedMessage;
+      /** The transaction signatures */
+      signatures: string[];
+    };
+    /** Metadata produced from the transaction */
+    meta: ConfirmedTransactionMeta | null;
+    /** The transaction version */
+    version?: TransactionVersion;
   }>;
   /** Vector of block rewards */
   rewards?: Array<{
@@ -1728,6 +1865,12 @@ const GetSignatureStatusesRpcResult = jsonRpcResultAndContext(
  */
 const GetMinimumBalanceForRentExemptionRpcResult = jsonRpcResult(number());
 
+const AddressTableLookupStruct = pick({
+  accountKey: PublicKeyFromString,
+  writableIndexes: array(number()),
+  readonlyIndexes: array(number()),
+});
+
 const ConfirmedTransactionResult = pick({
   signatures: array(string()),
   message: pick({
@@ -1745,6 +1888,7 @@ const ConfirmedTransactionResult = pick({
       }),
     ),
     recentBlockhash: string(),
+    addressTableLookups: optional(array(AddressTableLookupStruct)),
   }),
 });
 
@@ -1805,6 +1949,7 @@ const ParsedConfirmedTransactionResult = pick({
     ),
     instructions: array(ParsedOrRawInstruction),
     recentBlockhash: string(),
+    addressTableLookups: optional(nullable(array(AddressTableLookupStruct))),
   }),
 });
 
@@ -1874,6 +2019,8 @@ const ParsedConfirmedTransactionMetaResult = pick({
   loadedAddresses: optional(LoadedAddressesResult),
 });
 
+const TransactionVersionStruct = union([literal(0), literal('legacy')]);
+
 /**
  * Expected JSON RPC response for the "getBlock" message
  */
@@ -1887,6 +2034,7 @@ const GetBlockRpcResult = jsonRpcResult(
         pick({
           transaction: ConfirmedTransactionResult,
           meta: nullable(ConfirmedTransactionMetaResult),
+          version: optional(TransactionVersionStruct),
         }),
       ),
       rewards: optional(
@@ -1962,6 +2110,7 @@ const GetTransactionRpcResult = jsonRpcResult(
       meta: ConfirmedTransactionMetaResult,
       blockTime: optional(nullable(number())),
       transaction: ConfirmedTransactionResult,
+      version: optional(TransactionVersionStruct),
     }),
   ),
 );
@@ -1976,6 +2125,7 @@ const GetParsedTransactionRpcResult = jsonRpcResult(
       transaction: ParsedConfirmedTransactionResult,
       meta: nullable(ParsedConfirmedTransactionMetaResult),
       blockTime: optional(nullable(number())),
+      version: optional(TransactionVersionStruct),
     }),
   ),
 );
@@ -3644,11 +3794,32 @@ export class Connection {
 
   /**
    * Fetch a processed block from the cluster.
+   *
+   * @deprecated Instead, call `getBlock` using a `GetVersionedBlockConfig` by
+   * setting the `maxSupportedTransactionVersion` property.
    */
   async getBlock(
     slot: number,
     rawConfig?: GetBlockConfig,
-  ): Promise {
+  ): Promise;
+
+  /**
+   * Fetch a processed block from the cluster.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getBlock(
+    slot: number,
+    rawConfig?: GetVersionedBlockConfig,
+  ): Promise;
+
+  /**
+   * Fetch a processed block from the cluster.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getBlock(
+    slot: number,
+    rawConfig?: GetVersionedBlockConfig,
+  ): Promise {
     const {commitment, config} = extractCommitmentFromConfig(rawConfig);
     const args = this._buildArgsAtLeastConfirmed(
       [slot],
@@ -3668,16 +3839,14 @@ export class Connection {
 
     return {
       ...result,
-      transactions: result.transactions.map(({transaction, meta}) => {
-        const message = new Message(transaction.message);
-        return {
-          meta,
-          transaction: {
-            ...transaction,
-            message,
-          },
-        };
-      }),
+      transactions: result.transactions.map(({transaction, meta, version}) => ({
+        meta,
+        transaction: {
+          ...transaction,
+          message: versionedMessageFromResponse(version, transaction.message),
+        },
+        version,
+      })),
     };
   }
 
@@ -3739,11 +3908,33 @@ export class Connection {
 
   /**
    * Fetch a confirmed or finalized transaction from the cluster.
+   *
+   * @deprecated Instead, call `getTransaction` using a
+   * `GetVersionedTransactionConfig` by setting the
+   * `maxSupportedTransactionVersion` property.
    */
   async getTransaction(
     signature: string,
     rawConfig?: GetTransactionConfig,
-  ): Promise {
+  ): Promise;
+
+  /**
+   * Fetch a confirmed or finalized transaction from the cluster.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getTransaction(
+    signature: string,
+    rawConfig: GetVersionedTransactionConfig,
+  ): Promise;
+
+  /**
+   * Fetch a confirmed or finalized transaction from the cluster.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getTransaction(
+    signature: string,
+    rawConfig?: GetVersionedTransactionConfig,
+  ): Promise {
     const {commitment, config} = extractCommitmentFromConfig(rawConfig);
     const args = this._buildArgsAtLeastConfirmed(
       [signature],
@@ -3764,7 +3955,10 @@ export class Connection {
       ...result,
       transaction: {
         ...result.transaction,
-        message: new Message(result.transaction.message),
+        message: versionedMessageFromResponse(
+          result.version,
+          result.transaction.message,
+        ),
       },
     };
   }
@@ -3774,8 +3968,8 @@ export class Connection {
    */
   async getParsedTransaction(
     signature: TransactionSignature,
-    commitmentOrConfig?: GetTransactionConfig | Finality,
-  ): Promise {
+    commitmentOrConfig?: GetVersionedTransactionConfig | Finality,
+  ): Promise {
     const {commitment, config} =
       extractCommitmentFromConfig(commitmentOrConfig);
     const args = this._buildArgsAtLeastConfirmed(
@@ -3797,8 +3991,8 @@ export class Connection {
    */
   async getParsedTransactions(
     signatures: TransactionSignature[],
-    commitmentOrConfig?: GetTransactionConfig | Finality,
-  ): Promise<(ParsedConfirmedTransaction | null)[]> {
+    commitmentOrConfig?: GetVersionedTransactionConfig | Finality,
+  ): Promise<(ParsedTransactionWithMeta | null)[]> {
     const {commitment, config} =
       extractCommitmentFromConfig(commitmentOrConfig);
     const batch = signatures.map(signature => {
@@ -3829,11 +4023,37 @@ export class Connection {
   /**
    * Fetch transaction details for a batch of confirmed transactions.
    * Similar to {@link getParsedTransactions} but returns a {@link TransactionResponse}.
+   *
+   * @deprecated Instead, call `getTransactions` using a
+   * `GetVersionedTransactionConfig` by setting the
+   * `maxSupportedTransactionVersion` property.
    */
   async getTransactions(
     signatures: TransactionSignature[],
     commitmentOrConfig?: GetTransactionConfig | Finality,
-  ): Promise<(TransactionResponse | null)[]> {
+  ): Promise<(TransactionResponse | null)[]>;
+
+  /**
+   * Fetch transaction details for a batch of confirmed transactions.
+   * Similar to {@link getParsedTransactions} but returns a {@link
+   * VersionedTransactionResponse}.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getTransactions(
+    signatures: TransactionSignature[],
+    commitmentOrConfig: GetVersionedTransactionConfig | Finality,
+  ): Promise<(VersionedTransactionResponse | null)[]>;
+
+  /**
+   * Fetch transaction details for a batch of confirmed transactions.
+   * Similar to {@link getParsedTransactions} but returns a {@link
+   * VersionedTransactionResponse}.
+   */
+  // eslint-disable-next-line no-dupe-class-members
+  async getTransactions(
+    signatures: TransactionSignature[],
+    commitmentOrConfig: GetVersionedTransactionConfig | Finality,
+  ): Promise<(VersionedTransactionResponse | null)[]> {
     const {commitment, config} =
       extractCommitmentFromConfig(commitmentOrConfig);
     const batch = signatures.map(signature => {
@@ -3862,7 +4082,10 @@ export class Connection {
         ...result,
         transaction: {
           ...result.transaction,
-          message: new Message(result.transaction.message),
+          message: versionedMessageFromResponse(
+            result.version,
+            result.transaction.message,
+          ),
         },
       };
     });
diff --git a/web3.js/test/connection.test.ts b/web3.js/test/connection.test.ts
index 5cc8fc7d491cfc..9ad1ebf36514e3 100644
--- a/web3.js/test/connection.test.ts
+++ b/web3.js/test/connection.test.ts
@@ -3256,7 +3256,6 @@ describe('Connection', function () {
           11111,
         );
 
-        console.log('create mint');
         const mintPubkey2 = await splToken.createMint(
           connection as any,
           payerKeypair,
@@ -4249,30 +4248,44 @@ describe('Connection', function () {
       expect(version['solana-core']).to.be.ok;
     }).timeout(20 * 1000);
 
-    it('getAddressLookupTable', async () => {
+    let lookupTableKey: PublicKey;
+    const lookupTableAddresses = new Array(10)
+      .fill(0)
+      .map(() => Keypair.generate().publicKey);
+
+    describe('address lookup table program', () => {
+      const connection = new Connection(url);
       const payer = Keypair.generate();
 
-      await helpers.airdrop({
-        connection,
-        address: payer.publicKey,
-        amount: LAMPORTS_PER_SOL,
+      before(async () => {
+        await helpers.airdrop({
+          connection,
+          address: payer.publicKey,
+          amount: 10 * LAMPORTS_PER_SOL,
+        });
       });
 
-      const lookupTableAddresses = new Array(10)
-        .fill(0)
-        .map(() => Keypair.generate().publicKey);
+      it('createLookupTable', async () => {
+        const recentSlot = await connection.getSlot('finalized');
+
+        let createIx: TransactionInstruction;
+        [createIx, lookupTableKey] =
+          AddressLookupTableProgram.createLookupTable({
+            recentSlot,
+            payer: payer.publicKey,
+            authority: payer.publicKey,
+          });
 
-      const recentSlot = await connection.getSlot('finalized');
-      const [createIx, lookupTableKey] =
-        AddressLookupTableProgram.createLookupTable({
-          recentSlot,
-          payer: payer.publicKey,
-          authority: payer.publicKey,
+        await helpers.processTransaction({
+          connection,
+          transaction: new Transaction().add(createIx),
+          signers: [payer],
+          commitment: 'processed',
         });
+      });
 
-      // create, extend, and fetch
-      {
-        const transaction = new Transaction().add(createIx).add(
+      it('extendLookupTable', async () => {
+        const transaction = new Transaction().add(
           AddressLookupTableProgram.extendLookupTable({
             lookupTable: lookupTableKey,
             addresses: lookupTableAddresses,
@@ -4280,44 +4293,32 @@ describe('Connection', function () {
             payer: payer.publicKey,
           }),
         );
+
         await helpers.processTransaction({
           connection,
           transaction,
           signers: [payer],
           commitment: 'processed',
         });
+      });
 
-        const lookupTableResponse = await connection.getAddressLookupTable(
-          lookupTableKey,
-          {
-            commitment: 'processed',
-          },
-        );
-        const lookupTableAccount = lookupTableResponse.value;
-        if (!lookupTableAccount) {
-          expect(lookupTableAccount).to.be.ok;
-          return;
-        }
-        expect(lookupTableAccount.isActive()).to.be.true;
-        expect(lookupTableAccount.state.authority).to.eql(payer.publicKey);
-        expect(lookupTableAccount.state.addresses).to.eql(lookupTableAddresses);
-      }
-
-      // freeze and fetch
-      {
+      it('freezeLookupTable', async () => {
         const transaction = new Transaction().add(
           AddressLookupTableProgram.freezeLookupTable({
             lookupTable: lookupTableKey,
             authority: payer.publicKey,
           }),
         );
+
         await helpers.processTransaction({
           connection,
           transaction,
           signers: [payer],
           commitment: 'processed',
         });
+      });
 
+      it('getAddressLookupTable', async () => {
         const lookupTableResponse = await connection.getAddressLookupTable(
           lookupTableKey,
           {
@@ -4331,50 +4332,31 @@ describe('Connection', function () {
         }
         expect(lookupTableAccount.isActive()).to.be.true;
         expect(lookupTableAccount.state.authority).to.be.undefined;
-      }
+        expect(lookupTableAccount.state.addresses).to.eql(lookupTableAddresses);
+      });
     });
 
-    it('sendRawTransaction with v0 transaction', async () => {
+    describe('v0 transaction', () => {
+      const connection = new Connection(url);
       const payer = Keypair.generate();
 
-      await helpers.airdrop({
-        connection,
-        address: payer.publicKey,
-        amount: 10 * LAMPORTS_PER_SOL,
-      });
-
-      const lookupTableAddresses = [Keypair.generate().publicKey];
-      const recentSlot = await connection.getSlot('finalized');
-      const [createIx, lookupTableKey] =
-        AddressLookupTableProgram.createLookupTable({
-          recentSlot,
-          payer: payer.publicKey,
-          authority: payer.publicKey,
-        });
-
-      // create, extend, and fetch lookup table
-      {
-        const transaction = new Transaction().add(createIx).add(
-          AddressLookupTableProgram.extendLookupTable({
-            lookupTable: lookupTableKey,
-            addresses: lookupTableAddresses,
-            authority: payer.publicKey,
-            payer: payer.publicKey,
-          }),
-        );
-        await helpers.processTransaction({
+      before(async () => {
+        await helpers.airdrop({
           connection,
-          transaction,
-          signers: [payer],
-          commitment: 'processed',
+          address: payer.publicKey,
+          amount: 10 * LAMPORTS_PER_SOL,
         });
+      });
 
+      // wait for lookup table to be usable
+      before(async () => {
         const lookupTableResponse = await connection.getAddressLookupTable(
           lookupTableKey,
           {
             commitment: 'processed',
           },
         );
+
         const lookupTableAccount = lookupTableResponse.value;
         if (!lookupTableAccount) {
           expect(lookupTableAccount).to.be.ok;
@@ -4383,7 +4365,7 @@ describe('Connection', function () {
 
         // eslint-disable-next-line no-constant-condition
         while (true) {
-          const latestSlot = await connection.getSlot('processed');
+          const latestSlot = await connection.getSlot('confirmed');
           if (latestSlot > lookupTableAccount.state.lastExtendedSlot) {
             break;
           } else {
@@ -4391,15 +4373,23 @@ describe('Connection', function () {
             await sleep(500);
           }
         }
-      }
+      });
 
-      // create, serialize, send and confirm versioned transaction
-      {
+      let signature;
+      let addressTableLookups;
+      it('send and confirm', async () => {
         const {blockhash, lastValidBlockHeight} =
           await connection.getLatestBlockhash();
         const transferIxData = encodeData(SYSTEM_INSTRUCTION_LAYOUTS.Transfer, {
           lamports: BigInt(LAMPORTS_PER_SOL),
         });
+        addressTableLookups = [
+          {
+            accountKey: lookupTableKey,
+            writableIndexes: [0],
+            readonlyIndexes: [],
+          },
+        ];
         const transaction = new VersionedTransaction(
           new MessageV0({
             header: {
@@ -4416,20 +4406,14 @@ describe('Connection', function () {
                 data: transferIxData,
               },
             ],
-            addressTableLookups: [
-              {
-                accountKey: lookupTableKey,
-                writableIndexes: [0],
-                readonlyIndexes: [],
-              },
-            ],
+            addressTableLookups,
           }),
         );
         transaction.sign([payer]);
-        const signature = bs58.encode(transaction.signatures[0]);
+        signature = bs58.encode(transaction.signatures[0]);
         const serializedTransaction = transaction.serialize();
         await connection.sendRawTransaction(serializedTransaction, {
-          preflightCommitment: 'processed',
+          preflightCommitment: 'confirmed',
         });
 
         await connection.confirmTransaction(
@@ -4438,16 +4422,106 @@ describe('Connection', function () {
             blockhash,
             lastValidBlockHeight,
           },
-          'processed',
+          'confirmed',
         );
 
         const transferToKey = lookupTableAddresses[0];
         const transferToAccount = await connection.getAccountInfo(
           transferToKey,
-          'processed',
+          'confirmed',
         );
         expect(transferToAccount?.lamports).to.be.eq(LAMPORTS_PER_SOL);
-      }
-    });
+      });
+
+      it('getTransaction (failure)', async () => {
+        await expect(
+          connection.getTransaction(signature, {
+            commitment: 'confirmed',
+          }),
+        ).to.be.rejectedWith(
+          'failed to get transaction: Transaction version (0) is not supported',
+        );
+      });
+
+      let transactionSlot;
+      it('getTransaction', async () => {
+        // fetch v0 transaction
+        const fetchedTransaction = await connection.getTransaction(signature, {
+          commitment: 'confirmed',
+          maxSupportedTransactionVersion: 0,
+        });
+        if (fetchedTransaction === null) {
+          expect(fetchedTransaction).to.not.be.null;
+          return;
+        }
+        transactionSlot = fetchedTransaction.slot;
+        expect(fetchedTransaction.version).to.eq(0);
+        expect(fetchedTransaction.meta?.loadedAddresses).to.eql({
+          readonly: [],
+          writable: [lookupTableAddresses[0]],
+        });
+        expect(
+          fetchedTransaction.transaction.message.addressTableLookups,
+        ).to.eql(addressTableLookups);
+      });
+
+      it('getParsedTransaction (failure)', async () => {
+        await expect(
+          connection.getParsedTransaction(signature, {
+            commitment: 'confirmed',
+          }),
+        ).to.be.rejectedWith(
+          'failed to get transaction: Transaction version (0) is not supported',
+        );
+      });
+
+      it('getParsedTransaction', async () => {
+        const parsedTransaction = await connection.getParsedTransaction(
+          signature,
+          {
+            commitment: 'confirmed',
+            maxSupportedTransactionVersion: 0,
+          },
+        );
+        expect(parsedTransaction).to.not.be.null;
+        expect(parsedTransaction?.version).to.eq(0);
+        expect(parsedTransaction?.meta?.loadedAddresses).to.eql({
+          readonly: [],
+          writable: [lookupTableAddresses[0]],
+        });
+        expect(
+          parsedTransaction?.transaction.message.addressTableLookups,
+        ).to.eql(addressTableLookups);
+      });
+
+      it('getBlock (failure)', async () => {
+        await expect(
+          connection.getBlock(transactionSlot, {
+            maxSupportedTransactionVersion: undefined,
+            commitment: 'confirmed',
+          }),
+        ).to.be.rejectedWith(
+          'failed to get confirmed block: Transaction version (0) is not supported',
+        );
+      });
+
+      it('getBlock', async () => {
+        const block = await connection.getBlock(transactionSlot, {
+          maxSupportedTransactionVersion: 0,
+          commitment: 'confirmed',
+        });
+        expect(block).to.not.be.null;
+        if (block === null) throw new Error(); // unreachable
+
+        let foundTx = false;
+        for (const tx of block.transactions) {
+          if (tx.transaction.signatures[0] === signature) {
+            foundTx = true;
+            expect(tx.version).to.eq(0);
+          }
+        }
+        expect(foundTx).to.be.true;
+      });
+    }).timeout(5 * 1000);
   }
 });