diff --git a/README.md b/README.md index 87c260e..91214a3 100644 --- a/README.md +++ b/README.md @@ -168,9 +168,9 @@ const { - **`getTokenMetaData`**: An async function to get meta data for an array of Token Ids. If chain ID is not null, then we get the meta data without needing to access the blockchain at all because we use Dig-A-Hash predictable storage paths based on that chain ID. ## useEvmNft -The useEvmNft composable fetches NFT data from an ERC721 contract and retrieves metadata either directly from the blockchain or via the Dig-A-Hash storage pattern. It supports pagination, sorting, and retrieving the current owner of a token. +The useEvmNft composable is the core composable used internally by both useEvmNftGallery, and useEvmMetaDataGallery. -This is used internally by useEvmNftGallery, and useEvmMetaDataGallery. This composable can still be used directly but the best way to use this composable is through useEvmNftGallery or useEvmMetaDataGallery as both composables will pass the public functions from useEvmNft through to the host component. For example, the functions getNftPage(), getTokenOwner(), and getTokenMetaData() returned from this useEvmNftGallery, and useEvmMetaDataGallery are passed through from useEvmNft. +This composable can be used directly to create new composables or just to use the helper functions it exposes (examples below). - **`pageSize`** (`number`): The number of NFTs to display per page. - **`provider`** (`object`): The `ethers.js` provider instance. @@ -184,29 +184,58 @@ This is used internally by useEvmNftGallery, and useEvmMetaDataGallery. This com #### Returns an object containing: - **`getNfts`** (`function`): Fetches NFTs based on the current page, pagination size, and sorting order. - **`getTokenOwner`** (`function`): Retrieves the owner of a specific NFT. -- **`getMetaDataBatch`** (`function`): Retrieves metadata for a batch of NFTs. - **`getTokenMetaData`** (`function`): Retrieves metadata for specific token IDs. - **`loadingMessage`** (`ref`): A reactive reference to track the loading status message. -#### Example Usage +### Example Usage getTokenMetaData +Easily get Dig-A-Hash Meta Data for an array of NFTs using ```getTokenMetaData```. Note that the ethers provider is not needed here, we are just fetching Meta Data using a simple http request, there will be no on-chain validation using this function. +```typescript +import { + useEvmNft, + type Nft, +} from 'vue-evm-nft'; -```javascript -import { useEvmNft, blockchains, dahNftV2Abi } from 'vue-evm-nft'; - -const { getNfts, getTokenOwner, loadingMessage } = useEvmNft( - 10, // pageSize - blockchains.fantom.publicRpc, // ethers.js provider - null, // holderPublicKey - '0xOwnerPublicKey', // contractOwnerPublicKey - '0xContractAddress', // contractAddress - dahNftV2Abi, // contractABI - 1 // chainId (if applicable) -); - -const loadNFTs = async () => { - const nfts = await getNfts(1, true); // Fetches the first page in ascending order - console.log(nfts); -}; +const metaData = ref([]); + +onMounted(async () => { + const evmNft = await useEvmNft( + itemsPerPage, // This is ignored. Use any number. + null, // null Ethers provider is faster. + null, + contractPublicKey, + contractAddress, + abi, + chainId + ); + + metaData.value = await evmNft.getTokenMetaData([1]); +}); +``` + +### Example Usage getTokenOwner +Get the current holder of the token using ```getTokenOwner```. The ethers provider is needed here because we need to lookup the current holder on-chain. +```typescript +import { ethers } from 'ethers'; // import ethers 6 +import { + useEvmNft, + type Nft, +} from 'vue-evm-nft'; + +const tokenOwner = ref(''); + +onMounted(async () => { + const evmNft = await useEvmNft( + itemsPerPage, // This is ignored. Use any number. + new ethers.JsonRpcProvider(blockchains.avalanche.publicRpc), + null, + contractPublicKey, + contractAddress, + abi, + chainId + ); + + tokenOwner.value = await evmNft.getTokenOwner(1); +}); ``` ## useNftStore @@ -290,7 +319,7 @@ nftStore.addCollection('myCollection'); nftStore.setCollectionItems(1, [{ tokenId: 1, metaData: {} }], 'myCollection'); // Retrieve a large image URL for an NFT -const largeImageUrl = nftStore.getImageLarge(nft.metaData); +const largeImageUrl = nftStore.getImageLarge(nft.metaData.image); // Get a specific public attribute value from the NFT metadata const attributeValue = nftStore.getPublicAttributeValue(nft.metaData, 'rarity'); diff --git a/package-lock.json b/package-lock.json index 04ca3ec..fa80c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vue-evm-nft", - "version": "1.1.1", + "version": "1.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vue-evm-nft", - "version": "1.1.1", + "version": "1.2.2", "license": "ISC", "dependencies": { "vue-tsc": "^2.1.10" @@ -938,10 +938,13 @@ "peer": true }, "node_modules/@types/node": { - "version": "18.15.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", - "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", - "dev": true + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/@vercel/nft": { "version": "0.26.5", @@ -2044,9 +2047,9 @@ } }, "node_modules/ethers": { - "version": "6.13.3", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.3.tgz", - "integrity": "sha512-/DzbZOLVtoO4fKvvQwpEucHAQgIwBGWuRvBdwE/lMXgXvvHHTSkn7XqAQ2b+gjJzZDJjWA9OD05bVceVOsBHbg==", + "version": "6.13.4", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.4.tgz", + "integrity": "sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==", "dev": true, "funding": [ { @@ -2062,9 +2065,9 @@ "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", - "@types/node": "18.15.13", + "@types/node": "22.7.5", "aes-js": "4.0.0-beta.5", - "tslib": "2.4.0", + "tslib": "2.7.0", "ws": "8.17.1" }, "engines": { @@ -3133,9 +3136,9 @@ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -4034,9 +4037,9 @@ "dev": true }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "dev": true }, "node_modules/type-fest": { @@ -4063,6 +4066,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", diff --git a/package.json b/package.json index 9158aab..4cc2a28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-evm-nft", - "version": "1.2.1", + "version": "1.2.2", "main": "src/index.js", "types": "src/types/index.d.ts", "scripts": { diff --git a/src/types/useEvmMetaDataGallery.d.ts b/src/types/useEvmMetaDataGallery.d.ts index 38c7b44..b5411d5 100644 --- a/src/types/useEvmMetaDataGallery.d.ts +++ b/src/types/useEvmMetaDataGallery.d.ts @@ -37,5 +37,5 @@ export declare function useEvmMetaDataGallery(config: EvmMetaDataOptions): { toggleSortOrder: () => Promise; getNftPage: (iPage: number) => Promise; getTokenOwner: (tokenId: number) => Promise; - getTokenMetaData: (tokenIds: number[]) => Promise; + getTokenMetaData: (tokenIds: number[]) => Promise; }; diff --git a/src/types/useEvmNft.d.ts b/src/types/useEvmNft.d.ts index 0f220ef..5d6dc77 100644 --- a/src/types/useEvmNft.d.ts +++ b/src/types/useEvmNft.d.ts @@ -1,6 +1,6 @@ import { Ref } from 'vue'; import { ethers } from 'ethers'; -import { NftMetaData } from './useNftStore'; +import { Nft, NftMetaData } from './useNftStore'; /** * Composable for interacting with EVM-based NFT contracts. @@ -16,7 +16,7 @@ import { NftMetaData } from './useNftStore'; */ export declare function useEvmNft( pageSize: number, - provider: ethers.JsonRpcProvider, + provider: ethers.JsonRpcProvider | null, holderPublicKey: string | null, contractOwnerPublicKey: string, contractAddress: string, @@ -34,51 +34,18 @@ export declare function useEvmNft( page: number, isAscending: boolean ) => Promise<{ - tokens: Array<{ - tokenId: number; - metaData: NftMetaData | null; - metaDataUrl: string; - owner: string | null; - privateData: object | null; - }>; + tokens: Nft[]; pageSize: number; count: number; }>; - /** - * Fetches metadata for a batch of token IDs. - * - * @param batchedTokenIds - Array of objects with `tokenId` and `owner` properties. - * @param isAscending - Whether to sort metadata in ascending order. - * @returns Promise resolving to an array of tokens with metadata and owner information. - */ - getMetaDataBatch: ( - batchedTokenIds: Array<{ tokenId: number; owner: string | null }>, - isAscending: boolean - ) => Promise< - Array<{ - tokenId: number; - metaDataUrl: string; - metaData: NftMetaData | null; - privateData: object | null; - owner: string | null; - }> - >; - /** * Fetches metadata for a list of token IDs. * * @param tokenIds - Array of token IDs. * @returns Promise resolving to an array of token objects with metadata. */ - getTokenMetaData: (tokenIds: number[]) => Promise< - Array<{ - tokenId: number; - metaDataUrl: string; - metaData: NftMetaData | null; - privateData: object | null; - }> - >; + getTokenMetaData: (tokenIds: number[]) => Promise; /** * Fetches the owner of a specific token. diff --git a/src/types/useEvmNftGallery.d.ts b/src/types/useEvmNftGallery.d.ts index 0b447bf..d941334 100644 --- a/src/types/useEvmNftGallery.d.ts +++ b/src/types/useEvmNftGallery.d.ts @@ -1,5 +1,5 @@ import { type ref, Ref } from 'vue'; -import { NftMetaData } from './useNftStore'; +import { type Nft } from './useNftStore'; /** * The EvmNftOptions configuration object for the useEvmNftGallery composable. @@ -33,12 +33,12 @@ export interface EvmNftOptions { export declare function useEvmNftGallery(config: EvmNftOptions): { page: ref; numberOfPages: ref; - nfts: ref; + nfts: Ref; isAscending: ref; isLoading: ref; loadingMessage: ref; toggleSortOrder: () => Promise; getNftPage: (iPage: number) => Promise; getTokenOwner: (tokenId: number) => Promise; - getTokenMetaData: (tokenIds: number[]) => Promise; + getTokenMetaData: (tokenIds: number[]) => Promise; }; diff --git a/src/types/useNftStore.d.ts b/src/types/useNftStore.d.ts index 82c9ebe..3779924 100644 --- a/src/types/useNftStore.d.ts +++ b/src/types/useNftStore.d.ts @@ -1,6 +1,6 @@ import { StoreDefinition } from 'pinia'; -interface MetaDataAttribute { +export interface MetaDataAttribute { trait_type: string; value: string; } @@ -8,11 +8,18 @@ interface MetaDataAttribute { export interface NftMetaData { image: string; name: string; - tokenId?: number; description: string; attributes?: MetaDataAttribute[]; } +export interface Nft { + tokenId: number; + metaData: NftMetaData; + metaDataUrl: string; + owner: string | null; + privateData: object | null; +} + export interface NftCollection { items: Record; // Items are stored by page number. itemCount: number; // Total number of items in the collection. diff --git a/tests/dahDemoV1/useEvmNftGallery.history.test.js b/tests/dahDemoV1/useEvmNftGallery.history.test.js index 6e3387f..57e00dd 100644 --- a/tests/dahDemoV1/useEvmNftGallery.history.test.js +++ b/tests/dahDemoV1/useEvmNftGallery.history.test.js @@ -124,7 +124,7 @@ test('should fetch page 1 of all NFTs on contract in desc order', async (t) => { abi: dahDemoV1Abi, chainId: blockchains.fantom.chainId, holderPublicKey: null, - chainId: blockchains.fantom.publicRpc, + rpc: blockchains.fantom.publicRpc, itemsPerPage: 24, nftStoreItemCollectionName: 'a1', isAscendingSort: false, diff --git a/tests/dahDemoV1/useEvmNftGallery.holder.test.js b/tests/dahDemoV1/useEvmNftGallery.holder.test.js index fa95f78..ff5e2d5 100644 --- a/tests/dahDemoV1/useEvmNftGallery.holder.test.js +++ b/tests/dahDemoV1/useEvmNftGallery.holder.test.js @@ -25,7 +25,7 @@ test('should fetch page 1 of NFTs by holder', async (t) => { contractAddress, abi: dahDemoV1Abi, chainId, - contractPublicKey, + holderPublicKey: contractPublicKey, rpc: blockchains.avalanche.publicRpc, itemsPerPage, nftStoreItemCollectionName, @@ -65,7 +65,7 @@ test('should fetch page 2 of NFTs by holder', async (t) => { contractAddress, abi: dahDemoV1Abi, chainId, - contractPublicKey, + holderPublicKey: contractPublicKey, rpc: blockchains.avalanche.publicRpc, itemsPerPage, nftStoreItemCollectionName, diff --git a/tests/dahNftV2/useEvmNftGallery.history.test.js b/tests/dahNftV2/useEvmNftGallery.history.test.js index d57b685..42e0478 100644 --- a/tests/dahNftV2/useEvmNftGallery.history.test.js +++ b/tests/dahNftV2/useEvmNftGallery.history.test.js @@ -68,7 +68,7 @@ test('should fetch page 2 of all NFTs on contract', async (t) => { abi: dahNftV2Abi, chainId, holderPublicKey: null, - rpc: blockchains.polygon.publicRpc, // otherwise batch too large + rpc: blockchains.polygon.altPublicRpc[2], // otherwise batch too large itemsPerPage, nftStoreItemCollectionName, isAscendingSort: true, diff --git a/tests/dahNftV2/useEvmNftGallery.holder.test.js b/tests/dahNftV2/useEvmNftGallery.holder.test.js index 7e03cf7..11854be 100644 --- a/tests/dahNftV2/useEvmNftGallery.holder.test.js +++ b/tests/dahNftV2/useEvmNftGallery.holder.test.js @@ -25,7 +25,7 @@ test('should fetch page 1 of NFTs by holder', async (t) => { contractAddress, abi: dahNftV2Abi, chainId, - contractPublicKey, + holderPublicKey: contractPublicKey, rpc: blockchains.polygon.publicRpc, itemsPerPage, nftStoreItemCollectionName, @@ -65,8 +65,8 @@ test('should fetch page 2 of NFTs by holder', async (t) => { contractAddress, abi: dahNftV2Abi, chainId, - contractPublicKey, - rpc: blockchains.polygon.publicRpc, // otherwise batch too large + holderPublicKey: contractPublicKey, + rpc: blockchains.polygon.altPublicRpc[2], // otherwise batch too large itemsPerPage, nftStoreItemCollectionName, isAscendingSort: true,