Repository: alchemyplatform/alchemy-sdk-js
Pull request #90 (2022-08-09 23:21:40)

Add the `pageSize` parameter for getNftsForContract()
Updating to match the public [changelog entry](https://docs.alchemy.com/changelog/06182022).

Renaming the field to `pageSize` in order to better standardize with `pageKey`.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9851f658..647d2b53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 - Removed the deprecated `TOKEN` enum from `AssetTransfersCategory`.
 - Added support for Optimism Goerli network via the `Network.OPT_GOERLI` enum.
 - Added the `transact` namespace for functionality relating to sending transactions. This includes the Flashbots methods `sendPrivateTransaction` and `cancelPrivateTransaction`.
+- Added the `pageSize` parameter in `GetNftsForContractOption` to specify the number of NFTs fetched when using `alchemy.nft.getNftsForContract`.
 
 
 ## 2.0.1
diff --git a/README.md b/README.md
index db2d784e..d0142f6b 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 # Alchemy SDK for Javascript
 
-The Alchemy SDK is the most comprehensive, stable, and powerful Javascript SDK available today to interact with the blockchain. 
+The Alchemy SDK is the most comprehensive, stable, and powerful Javascript SDK available today to interact with the blockchain.
 
-It supports the exact same syntax and functionality of the Ethers.js `AlchemyProvider` and `WebSocketProvider`, making it a 1:1 mapping for anyone using the Ethers.js `Provider`. However, it adds a significant amount of improved functionality on top of Ethers, such as easy access to Alchemy’s Enhanced and NFT APIs, robust WebSockets, and quality-of life improvements such as automated retries. 
+It supports the exact same syntax and functionality of the Ethers.js `AlchemyProvider` and `WebSocketProvider`, making it a 1:1 mapping for anyone using the Ethers.js `Provider`. However, it adds a significant amount of improved functionality on top of Ethers, such as easy access to Alchemy’s Enhanced and NFT APIs, robust WebSockets, and quality-of life improvements such as automated retries.
 
 The SDK leverages Alchemy's hardened node infrastructure, guaranteeing best-in-class node reliability, scalability, and data correctness, and is undergoing active development by Alchemy's engineers.
 
@@ -113,7 +113,7 @@ import { Alchemy } from 'alchemy-sdk';
 const alchemy = new Alchemy();
 
 // Listen to all new pending transactions.
-alchemy.ws.on("block", res => console.log(res));
+alchemy.ws.on('block', res => console.log(res));
 
 // Listen to only the next transaction on the USDC contract.
 alchemy.ws.once(
@@ -145,10 +145,12 @@ right. The Alchemy SDK automatically handles these failures with no configuratio
 ## Alchemy Transact
 
 The `transact` namespace contains methods used for sending transactions. The unique methods to the `transact` namespace are:
+
 - `sendPrivateTransaction()`: Send a private transaction through Flashbots.
 - `cancelPrivateTransaction()`: Cancel a private transaction sent with Flashbots.
 
 The `transact` namespace also aliases over several commonly used methods from the `core` namespace for convenience:
+
 - `getTransaction()`: Returns the transaction for the given transaction hash.
 - `sendTransaction()`: Sends a standard transaction to the network to be mined.
 - `waitForTransaction()`: Waits for a transaction to be mined and returns the transaction receipt.
diff --git a/src/internal/nft-api.ts b/src/internal/nft-api.ts
index 5953ac64..e30dfead 100644
--- a/src/internal/nft-api.ts
+++ b/src/internal/nft-api.ts
@@ -151,7 +151,8 @@ export async function getNftsForContract(
   >(config, AlchemyApiType.NFT, 'getNFTsForCollection', srcMethod, {
     contractAddress,
     startToken: options?.pageKey,
-    withMetadata
+    withMetadata,
+    limit: options?.pageSize ?? undefined
   });
 
   return {
@@ -435,6 +436,7 @@ interface GetNftsForNftContractAlchemyParams {
   contractAddress: string;
   startToken?: string;
   withMetadata: boolean;
+  limit?: number;
 }
 
 /**
diff --git a/src/types/types.ts b/src/types/types.ts
index 5f377a59..d3286316 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -424,7 +424,7 @@ export interface GetFloorPriceResponse {
   readonly looksRare: FloorPriceMarketplace | FloorPriceError;
 }
 
-/** The refresh result response object returned by {@link refreshNftContract}. */
+/** The refresh result response object returned by {@link refreshContract}. */
 export interface RefreshContractResult {
   /** The NFT contract address that was passed in to be refreshed. */
   contractAddress: string;
@@ -495,7 +495,7 @@ export interface RawContract {
 
 /**
  * Optional parameters object for the {@link getNftsForContract} and
- * {@link getNftsForNftContractIterator} functions.
+ * {@link getNftsForContractIterator} functions.
  *
  * This interface is used to fetch NFTs with their associated metadata. To get
  * Nfts without their associated metadata, use {@link GetBaseNftsForContractOptions}.
@@ -511,11 +511,14 @@ export interface GetNftsForContractOptions {
 
   /** Optional boolean flag to omit NFT metadata. Defaults to `false`. */
   omitMetadata?: boolean;
+
+  /** Sets the total number of NFTs to return in the response. Defaults to 100. */
+  pageSize?: number;
 }
 
 /**
  * Optional parameters object for the {@link getNftsForContract} and
- * {@link getNftsForNftContractIterator} functions.
+ * {@link getNftsForContractIterator} functions.
  *
  * This interface is used to fetch NFTs without their associated metadata. To
  * get Nfts with their associated metadata, use {@link GetNftsForContractOptions}.
@@ -531,6 +534,9 @@ export interface GetBaseNftsForContractOptions {
 
   /** Optional boolean flag to omit NFT metadata. Defaults to `false`. */
   omitMetadata: false;
+
+  /** Sets the total number of NFTs to return in the response. Defaults to 100. */
+  pageSize?: number;
 }
 
 /**


Test patch:
diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts
index 3347d377..81678b75 100644
--- a/test/integration/integration.test.ts
+++ b/test/integration/integration.test.ts
@@ -7,6 +7,8 @@ import {
 
 /** Temporary test */
 // TODO: REMOVE these tests once we have more comprehensive unit testing.
+jest.setTimeout(50000);
+
 describe('E2E integration tests', () => {
   let alchemy: Alchemy;
   const ownerAddress = '0x65d25E3F2696B73b850daA07Dd1E267dCfa67F2D';
@@ -25,7 +27,8 @@ describe('E2E integration tests', () => {
 
   it('test', async () => {
     const provider = await alchemy.config.getProvider();
-    console.log(await provider.getBalance(ownerAddress, 'latest'));
+    const res = await provider.getBalance(ownerAddress, 'latest');
+    expect(res).toBeDefined();
   });
 
   // TODO: add unit test coverage. Integration tests are just sanity tests for now.
@@ -39,7 +42,6 @@ describe('E2E integration tests', () => {
       '0xaba7161a7fb69c88e16ed9f455ce62b791ee4d03'
     );
     expect(contractDeployer.blockNumber).toEqual(12287507);
-    console.log(contractDeployer);
 
     // ENS
     contractAddress = '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85';
@@ -48,7 +50,6 @@ describe('E2E integration tests', () => {
       '0x4fe4e666be5752f1fdd210f4ab5de2cc26e3e0e8'
     );
     expect(contractDeployer.blockNumber).toEqual(9380410);
-    console.log(contractDeployer);
   });
 
   it('getAssetTransfers', async () => {
@@ -63,7 +64,6 @@ describe('E2E integration tests', () => {
       withMetadata: true
     });
     const firstTransfer = allTransfers.transfers[0];
-    console.log('firstTransfer', firstTransfer);
 
     // General checks
     expect(firstTransfer.category).toEqual(AssetTransfersCategory.ERC721);
@@ -77,8 +77,6 @@ describe('E2E integration tests', () => {
   });
 
   it('getNftMetadata', async () => {
-    const provider = await alchemy.config.getProvider();
-    console.log(await provider.getBlockNumber());
     const contractAddress = '0x0510745d2ca36729bed35c818527c4485912d99e';
     const tokenId = 403;
     const response = await alchemy.nft.getNftMetadata(
@@ -86,8 +84,7 @@ describe('E2E integration tests', () => {
       tokenId,
       NftTokenType.UNKNOWN
     );
-
-    console.log('res', response);
+    expect(response.media).toBeDefined();
   });
 
   it('getOwnersForNft', async () => {
@@ -97,7 +94,7 @@ describe('E2E integration tests', () => {
       contractAddress,
       tokenId
     );
-    console.log('res', response);
+    expect(response.owners.length).toBeGreaterThan(0);
   });
 
   it('getOwnersForNft from NFT', async () => {
@@ -105,17 +102,12 @@ describe('E2E integration tests', () => {
       excludeFilters: [NftExcludeFilters.SPAM],
       omitMetadata: true
     });
-    console.log('nfts', nfts);
-    const owners = await alchemy.nft.getOwnersForNft(
+    expect(nfts.ownedNfts.length).toBeGreaterThan(0);
+    const response = await alchemy.nft.getOwnersForNft(
       nfts.ownedNfts[0].contract.address,
       nfts.ownedNfts[0].tokenId
     );
-    console.log('owner', owners);
-  });
-
-  it('getNftsForOwner()', async () => {
-    const nfts = await alchemy.nft.getNftsForOwner('vitalik.eth');
-    console.log('owner', nfts);
+    expect(response.owners.length).toBeGreaterThan(0);
   });
 
   it('getNftsForOwner() spam check', async () => {
@@ -127,34 +119,32 @@ describe('E2E integration tests', () => {
   });
 
   it('getOwnersForNftContract', async () => {
-    const owners = await alchemy.nft.getOwnersForContract(contractAddress);
-    console.log('owners', owners);
+    const response = await alchemy.nft.getOwnersForContract(contractAddress);
+    expect(response.owners.length).toBeGreaterThan(0);
   });
 
   it('getNftsForNftContract with pageKey', async () => {
-    let nftsForNftContract = await alchemy.nft.getNftsForContract(
+    const nftsForNftContract = await alchemy.nft.getNftsForContract(
       contractAddress
     );
 
-    console.log(
-      'nftsForNftContract: ',
-      nftsForNftContract.pageKey,
-      nftsForNftContract.nfts.length
-    );
-    nftsForNftContract = await alchemy.nft.getNftsForContract(contractAddress, {
+    const nextPage = await alchemy.nft.getNftsForContract(contractAddress, {
       pageKey: nftsForNftContract.pageKey
     });
-    console.log(
-      'nftsForNftContract: ',
-      nftsForNftContract.pageKey,
-      nftsForNftContract.nfts.length
+    expect(nftsForNftContract.nfts).not.toEqual(nextPage.nfts);
+  });
+
+  it('getNftsForContract with limit', async () => {
+    const nftsForNftContract = await alchemy.nft.getNftsForContract(
+      contractAddress,
+      { pageSize: 10 }
     );
+    expect(nftsForNftContract.nfts.length).toEqual(10);
   });
 
   it('getIterator', async () => {
     jest.setTimeout(15000);
-    console.log('lets paginate');
-    const allNfts = [];
+    let allNfts = [];
     let totalCount = 0;
     for await (const nft of alchemy.nft.getNftsForOwnerIterator(ownerAddress)) {
       if (totalCount === 10) {
@@ -163,6 +153,9 @@ describe('E2E integration tests', () => {
       allNfts.push(nft);
       totalCount += 1;
     }
+    expect(allNfts.length).toEqual(totalCount);
+    allNfts = [];
+    totalCount = 0;
 
     for await (const nft of alchemy.nft.getNftsForOwnerIterator(ownerAddress, {
       omitMetadata: false
@@ -173,12 +166,11 @@ describe('E2E integration tests', () => {
       allNfts.push(nft);
       totalCount += 1;
     }
-    console.log('done', allNfts.length, allNfts);
+    expect(allNfts.length).toEqual(totalCount);
   });
 
   it('getNftsForNftContractIterator', async () => {
     jest.setTimeout(15000);
-    console.log('lets paginate');
     const allNfts = [];
     let totalCount = 0;
     for await (const nft of alchemy.nft.getNftsForContractIterator(
@@ -193,7 +185,7 @@ describe('E2E integration tests', () => {
       allNfts.push(nft);
       totalCount += 1;
     }
-    console.log('done', allNfts.length, allNfts[100], totalCount);
+    expect(allNfts.length).toEqual(totalCount);
   });
 
   it('refreshNftMetadata()', async () => {
@@ -208,22 +200,27 @@ describe('E2E integration tests', () => {
   it('refreshNftContract()', async () => {
     const contractAddress = '0x0510745d2ca36729bed35c818527c4485912d99e';
     const result = await alchemy.nft.refreshContract(contractAddress);
-    console.log('result', result);
+    expect(result.contractAddress).toBeDefined();
   });
 
   describe('README examples', () => {
     it('Example 1: Getting the Nfts owned by an address', async () => {
       void alchemy.nft.getNftsForOwner('0xshah.eth').then(nfts => {
-        console.log(nfts.totalCount);
+        expect(nfts.totalCount).toBeDefined();
       });
 
       // Get all the image urls for all the NFTs an address owns.
+      let totalCount = 0;
       for await (const nft of alchemy.nft.getNftsForOwnerIterator(
         '0xshah.eth'
       )) {
-        console.log(nft.media);
-        console.log('done', nft);
+        if (totalCount === 10) {
+          break;
+        }
+        totalCount++;
+        expect(nft.media).toBeDefined();
       }
+      totalCount = 0;
 
       // Filter out spam NFTs.
       for await (const nft of alchemy.nft.getNftsForOwnerIterator(
@@ -232,7 +229,11 @@ describe('E2E integration tests', () => {
           excludeFilters: [NftExcludeFilters.SPAM]
         }
       )) {
-        console.log(nft.media);
+        if (totalCount === 10) {
+          break;
+        }
+        totalCount++;
+        expect(nft.media).toBeDefined();
       }
     });
 
@@ -240,6 +241,8 @@ describe('E2E integration tests', () => {
       // Bored Ape Yacht Club contract address.
       const baycAddress = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D';
 
+      let count = 0;
+
       for await (const nft of alchemy.nft.getNftsForContractIterator(
         baycAddress,
         {
@@ -247,17 +250,22 @@ describe('E2E integration tests', () => {
           omitMetadata: true
         }
       )) {
+        if (count === 5) {
+          break;
+        }
+        count++;
         await alchemy.nft
           .getOwnersForNft(nft.contract.address, nft.tokenId)
-          .then(response =>
-            console.log('owners:', response.owners, 'tokenId:', nft.tokenId)
-          );
+          .then(response => {
+            expect(response.owners).toBeDefined();
+            expect(nft.tokenId).toBeDefined();
+          });
       }
     });
 
     it('Example 3: Token balances', async () => {
-      await alchemy.core.getTokenBalances(ownerAddress).then(console.log);
-      await alchemy.core.getBalance('0xshah.eth').then(console.log);
+      await alchemy.core.getTokenBalances(ownerAddress);
+      await alchemy.core.getBalance('0xshah.eth');
     });
   });
 });
diff --git a/test/integration/provider.test.ts b/test/integration/provider.test.ts
index 9fdee0b6..288cc4db 100644
--- a/test/integration/provider.test.ts
+++ b/test/integration/provider.test.ts
@@ -2,6 +2,7 @@ import { Alchemy, AlchemyWebSocketProvider, AlchemyProvider } from '../../src';
 import { EthersNetwork } from '../../src/util/const';
 import { AlchemyProvider as EthersAlchemyProvider } from '@ethersproject/providers';
 
+jest.setTimeout(50000);
 /**
  * These integrations are sanity checks to ensure that the SDK's overriden
  * implementation of {@link AlchemyProvider} is working as expected.
@@ -23,6 +24,10 @@ describe('AlchemyProvider', () => {
     provider = await alchemy.config.getProvider();
   });
 
+  afterEach(async () => {
+    await wsProvider.destroy();
+  });
+
   // TODO(ethers): Extract into helper method to verify all inputs.
   it('methods should return the same result', async () => {
     const expected = await ethersProvider.send('eth_getBalance', [
@@ -46,10 +51,9 @@ describe('AlchemyProvider', () => {
         toAddress: address,
         hashesOnly: true
       },
-      res => {
-        expect(res.to).toEqual(address.toLowerCase());
-        console.log(res);
-        if (eventCount === 10) {
+      (res: string) => {
+        expect(res.substring(0, 2)).toEqual('0x');
+        if (eventCount === 5) {
           done();
         }
         eventCount++;
@@ -66,8 +70,7 @@ describe('AlchemyProvider', () => {
         toAddress: address,
         hashesOnly: true
       },
-      res => {
-        console.log('1', res);
+      () => {
         eventCount++;
         if (eventCount > 1) {
           done();
@@ -81,8 +84,7 @@ describe('AlchemyProvider', () => {
         toAddress: address,
         hashesOnly: true
       },
-      res => {
-        console.log('2', res);
+      () => {
         eventCount++;
         if (eventCount > 1) {
           done();
@@ -102,8 +104,7 @@ describe('AlchemyProvider', () => {
       {
         method: 'alchemy_pendingTransactions'
       },
-      msg => {
-        console.log('message', msg);
+      () => {
         if (eventCount === 10) {
           done();
         }
@@ -111,22 +112,15 @@ describe('AlchemyProvider', () => {
       }
     );
   });
-
+  //
   it('handles blocks', done => {
-    let eventCount = 0;
-    ethersProvider.on('block', res => {
-      console.log('block from ethers', res);
-    });
     wsProvider.on('block', res => {
-      console.log('block from provider', res);
-      if (eventCount == 10) {
-        done();
-      }
-      eventCount++;
+      expect(typeof res).toEqual('number');
+      done();
     });
   });
 
-  // TODO(ethers): Write tests to make sure that the SDK's provider instance
-  // has the correct network mappings compared to Ethers.
-  it('network mappings should be correct', () => {});
+  // // TODO(ethers): Write tests to make sure that the SDK's provider instance
+  // // has the correct network mappings compared to Ethers.
+  // it('network mappings should be correct', () => {});
 });
diff --git a/test/unit/nft-api.test.ts b/test/unit/nft-api.test.ts
index c5fc93fb..964884e2 100644
--- a/test/unit/nft-api.test.ts
+++ b/test/unit/nft-api.test.ts
@@ -537,7 +537,7 @@ describe('NFT module', () => {
     });
   });
 
-  describe('getNftsForNftContract()', () => {
+  describe('getNftsForContract()', () => {
     const contractAddress = '0xCA1';
     const pageKey = 'page-key0';
     const baseResponse: RawGetBaseNftsForContractResponse = {
@@ -577,7 +577,8 @@ describe('NFT module', () => {
         mock.onGet().reply(200, mockResponse);
         await alchemy.nft.getNftsForContract(contractAddress, {
           pageKey,
-          omitMetadata
+          omitMetadata,
+          pageSize: 90
         });
         expect(mock.history.get.length).toEqual(1);
         expect(mock.history.get[0].params).toHaveProperty(
@@ -592,6 +593,7 @@ describe('NFT module', () => {
           'withMetadata',
           expectedWithMetadata
         );
+        expect(mock.history.get[0].params).toHaveProperty('limit', 90);
       }
     );