Repository: alchemyplatform/alchemy-sdk-js (ts)

Issue:
alchemy.nft.getNftMetadata() ignores refreshCache
### [REQUIRED] Environment

- Browser version: Chrome Version 108.0.5359.124 (Official Build) (x86_64)
- Alchemy SDK version: 2.2.5

### [REQUIRED] Describe the problem

The `alchemy.nft.getNftMetadata()` API call does not include the `refreshCache` parameter as documented here:

https://docs.alchemy.com/reference/getnftmetadata

#### How to reproduce:

Calling the API with a 5th parameter does not refresh the token metadata.

Calling the API with a non-boolean value should also trigger an error:

```
	await alchemy.nft.getNftMetadata('0xe785E82358879F061BC3dcAC6f0444462D4b5330', 44, undefined, undefined, 'nonsense')
		.then(data => metadata = data)
		.catch(e => error = e);

```

VS Code does not expose the 5th parameter:
<img width="1087" alt="Screen Shot 2022-12-28 at 18 25 37" src="https://user-images.githubusercontent.com/229161/209874828-e1fcbde9-8293-4641-a0b7-10f5dbed9504.png">


#### Relevant code or sample repro:

https://github.com/alchemyplatform/alchemy-sdk-js/blob/main/src/api/nft-namespace.ts#L81

```
  getNftMetadata(
    contractAddress: string,
    tokenId: BigNumberish,
    tokenType?: NftTokenType,
    tokenUriTimeoutInMs?: number
  ): Promise<Nft> {
    return getNftMetadata(
      this.config,
      contractAddress,
      tokenId,
      tokenType,
      tokenUriTimeoutInMs
    );
  }
```




Pull request description:
Add support for refreshCache in getNftMetadata
Fixes #226.

Added the `refreshCache` option under a new overload since we now have 3 optional params for `getNftMetadata`.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dafc4c29..a26bb96a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 ### Minor Changes
 
 - Added the `tokenUriTimeoutInMs` option to `NftNamespace.getNftsForContract()` to specify the timeout duration for fetching an NFT's underlying metadata.
+- Added support for the `refreshCache` option in `NftNamespace.getNftMetadata()`. This option is now available when using the `options` overload. The original method without the `options` overload is now deprecated.
 
 ## 2.2.5
 
diff --git a/src/api/nft-namespace.ts b/src/api/nft-namespace.ts
index f1123a84..87e88554 100644
--- a/src/api/nft-namespace.ts
+++ b/src/api/nft-namespace.ts
@@ -29,6 +29,7 @@ import {
   GetContractsForOwnerOptions,
   GetContractsForOwnerResponse,
   GetFloorPriceResponse,
+  GetNftMetadataOptions,
   GetNftSalesOptions,
   GetNftSalesOptionsByContractAddress,
   GetNftSalesResponse,
@@ -77,20 +78,40 @@ export class NftNamespace {
    *   website hosting the metadata to respond. If you want to only access the
    *   cache and not live fetch any metadata for cache misses then set this value to 0.
    * @public
+   * @deprecated Please use the method with the `options` overload. This method
+   * will be removed in a subsequent release.
    */
   getNftMetadata(
     contractAddress: string,
     tokenId: BigNumberish,
     tokenType?: NftTokenType,
     tokenUriTimeoutInMs?: number
+  ): Promise<Nft>;
+  getNftMetadata(
+    contractAddress: string,
+    tokenId: BigNumberish,
+    options?: GetNftMetadataOptions
+  ): Promise<Nft>;
+  getNftMetadata(
+    contractAddress: string,
+    tokenId: BigNumberish,
+    optionsOrTokenType?: GetNftMetadataOptions | NftTokenType,
+    tokenUriTimeoutInMs?: number
   ): Promise<Nft> {
-    return getNftMetadata(
-      this.config,
-      contractAddress,
-      tokenId,
-      tokenType,
-      tokenUriTimeoutInMs
-    );
+    let options: GetNftMetadataOptions;
+    if (typeof optionsOrTokenType === 'object') {
+      options = {
+        tokenType: optionsOrTokenType.tokenType,
+        tokenUriTimeoutInMs: optionsOrTokenType.tokenUriTimeoutInMs,
+        refreshCache: optionsOrTokenType.refreshCache
+      };
+    } else {
+      options = {
+        tokenType: optionsOrTokenType,
+        tokenUriTimeoutInMs
+      };
+    }
+    return getNftMetadata(this.config, contractAddress, tokenId, options);
   }
 
   /**
diff --git a/src/internal/nft-api.ts b/src/internal/nft-api.ts
index ecffd038..e112339f 100644
--- a/src/internal/nft-api.ts
+++ b/src/internal/nft-api.ts
@@ -8,6 +8,7 @@ import {
   GetContractsForOwnerOptions,
   GetContractsForOwnerResponse,
   GetFloorPriceResponse,
+  GetNftMetadataOptions,
   GetNftSalesOptions,
   GetNftSalesOptionsByContractAddress,
   GetNftSalesResponse,
@@ -69,8 +70,7 @@ export async function getNftMetadata(
   config: AlchemyConfig,
   contractAddress: string,
   tokenId: BigNumberish,
-  tokenType?: NftTokenType,
-  tokenUriTimeoutInMs?: number,
+  options?: GetNftMetadataOptions,
   srcMethod = 'getNftMetadata'
 ): Promise<Nft> {
   const response = await requestHttpWithBackoff<GetNftMetadataParams, RawNft>(
@@ -81,8 +81,12 @@ export async function getNftMetadata(
     {
       contractAddress,
       tokenId: BigNumber.from(tokenId!).toString(),
-      tokenType: tokenType !== NftTokenType.UNKNOWN ? tokenType : undefined,
-      tokenUriTimeoutInMs
+      tokenType:
+        options?.tokenType !== NftTokenType.UNKNOWN
+          ? options?.tokenType
+          : undefined,
+      tokenUriTimeoutInMs: options?.tokenUriTimeoutInMs,
+      refreshCache: options?.refreshCache
     }
   );
   return getNftFromRaw(response);
@@ -511,7 +515,6 @@ export async function refreshNftMetadata(
     contractAddress,
     tokenIdString,
     undefined,
-    undefined,
     srcMethod
   );
   const second = await refresh(
diff --git a/src/types/types.ts b/src/types/types.ts
index b80283cc..b45a88cc 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -790,6 +790,27 @@ export interface NftContractTokenBalance {
   balance: number;
 }
 
+/**
+ * Additional options for the {@link NftNamespace.getNftMetadata} method.
+ */
+export interface GetNftMetadataOptions {
+  /** Optional field to specify the type of token to speed up the query. */
+  tokenType?: NftTokenType;
+  /**
+   * No set timeout by default - When metadata is requested, this parameter is
+   * the timeout (in milliseconds) for the website hosting the metadata to
+   * respond. If you want to only access the cache and not live fetch any
+   * metadata for cache misses then set this value to 0.
+   */
+  tokenUriTimeoutInMs?: number;
+
+  /**
+   * Whether to refresh the metadata for the given NFT token before returning
+   * the response. Defaults to false for faster response times.
+   */
+  refreshCache?: boolean;
+}
+
 /**
  * Represents an NFT token to fetch metadata for in a
  * {@link NftNamespace.getNftMetadataBatch} method.


Test patch:
diff --git a/test/unit/nft-api.test.ts b/test/unit/nft-api.test.ts
index fd9b7ed8..a750d87b 100644
--- a/test/unit/nft-api.test.ts
+++ b/test/unit/nft-api.test.ts
@@ -150,7 +150,8 @@ describe('NFT module', () => {
       contractAddress: string,
       tokenId: string,
       tokenType?: NftTokenType,
-      timeoutInMs?: number
+      timeoutInMs?: number,
+      refreshCache?: boolean
     ) {
       expect(actualNft).toEqual(expectedNft);
       expect(actualNft.tokenId).toEqual(tokenId);
@@ -168,21 +169,25 @@ describe('NFT module', () => {
         'tokenUriTimeoutInMs',
         timeoutInMs ?? undefined
       );
+      expect(mock.history.get[0].params).toHaveProperty(
+        'refreshCache',
+        refreshCache ?? undefined
+      );
     }
 
     it('can be called with raw parameters', async () => {
       verifyNftMetadata(
-        await alchemy.nft.getNftMetadata(
-          contractAddress,
-          tokenId,
-          NftTokenType.ERC1155,
-          timeoutInMs
-        ),
+        await alchemy.nft.getNftMetadata(contractAddress, tokenId, {
+          tokenType: NftTokenType.ERC1155,
+          tokenUriTimeoutInMs: timeoutInMs,
+          refreshCache: true
+        }),
         expectedNft,
         contractAddress,
         tokenId,
         NftTokenType.ERC1155,
-        timeoutInMs
+        timeoutInMs,
+        true
       );
     });
 
@@ -1648,7 +1653,10 @@ describe('NFT module', () => {
         contractAddress
       );
       expect(mock.history.get[0].params).toHaveProperty('tokenId', tokenId);
-      expect(mock.history.get[0].params).not.toHaveProperty('refreshCache');
+      expect(mock.history.get[0].params).toHaveProperty(
+        'refreshCache',
+        undefined
+      );
       expect(mock.history.get[1].params).toHaveProperty(
         'contractAddress',
         contractAddress