From 7fa1424f2d283c0d2027a9932f8c89e59886b346 Mon Sep 17 00:00:00 2001 From: Mike MacCana Date: Wed, 21 Aug 2024 14:50:57 -0400 Subject: [PATCH] Use Umi for NFTs, metaplex-foundation/js is deprecated and no longer functional --- labs/create-metaplex-collection.ts | 71 ++ labs/create-metaplex-nft-collection.ts | 91 --- labs/create-metaplex-nft.ts | 121 ++-- labs/package-lock.json | 635 ++++++------------ labs/package.json | 3 +- labs/sample-nft-collection-offchain-data.json | 5 + labs/sample-nft-offchain-data.json | 5 + labs/update-metaplex-nft.ts | 96 +-- labs/verify-metaplex-collection.ts | 65 ++ 9 files changed, 427 insertions(+), 665 deletions(-) create mode 100644 labs/create-metaplex-collection.ts delete mode 100644 labs/create-metaplex-nft-collection.ts create mode 100644 labs/sample-nft-collection-offchain-data.json create mode 100644 labs/sample-nft-offchain-data.json create mode 100644 labs/verify-metaplex-collection.ts diff --git a/labs/create-metaplex-collection.ts b/labs/create-metaplex-collection.ts new file mode 100644 index 0000000..4500164 --- /dev/null +++ b/labs/create-metaplex-collection.ts @@ -0,0 +1,71 @@ +// See https://developers.metaplex.com/token-metadata/collections +import { + createNft, + fetchDigitalAsset, + mplTokenMetadata, +} from "@metaplex-foundation/mpl-token-metadata"; +import { + airdropIfRequired, + getExplorerLink, + getKeypairFromFile, +} from "@solana-developers/helpers"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { + generateSigner, + keypairIdentity, + percentAmount, +} from "@metaplex-foundation/umi"; +import { Connection, LAMPORTS_PER_SOL, clusterApiUrl } from "@solana/web3.js"; + +// create a new connection to the cluster's API +const connection = new Connection(clusterApiUrl("devnet")); + +// initialize a keypair for the user +const user = await getKeypairFromFile(); + +await airdropIfRequired( + connection, + user.publicKey, + 1 * LAMPORTS_PER_SOL, + 0.1 * LAMPORTS_PER_SOL +); + +console.log("Loaded user:", user.publicKey.toBase58()); + +// Create Umi Instance, using the same endpoint as our connection, +// and using our user to sign transactions +const umi = createUmi(connection.rpcEndpoint); +umi.use(mplTokenMetadata()); +const umiKeypair = umi.eddsa.createKeypairFromSecretKey(user.secretKey); +umi.use(keypairIdentity(umiKeypair)); + +console.log(`Creating collection...`); +// This mint is like a factory for creating NFTs +// Except it only makes one NFT, and it's a collection! +const collectionMint = generateSigner(umi); +const transaction = await createNft(umi, { + mint: collectionMint, + name: "My Collection", + symbol: "MC", + // https://developers.metaplex.com/token-metadata/token-standard#the-non-fungible-standard + uri: "https://raw.githubusercontent.com/solana-developers/professional-education/main/labs/sample-nft-collection-offchain-data.json", + sellerFeeBasisPoints: percentAmount(0), + isCollection: true, +}); + +await transaction.sendAndConfirm(umi); + +const createdCollectionNft = await fetchDigitalAsset( + umi, + collectionMint.publicKey +); + +console.log( + `Created collection 📦! Address is: ${getExplorerLink( + "address", + createdCollectionNft.mint.publicKey, + "devnet" + )}` +); + +console.log("✅ Finished successfully!"); diff --git a/labs/create-metaplex-nft-collection.ts b/labs/create-metaplex-nft-collection.ts deleted file mode 100644 index e412bb2..0000000 --- a/labs/create-metaplex-nft-collection.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Adapted from https://github.com/Unboxed-Software/solana-metaplex/blob/solution/src/index.ts -// and updated to work with the latest version of the Metaplex SDK - -import { Connection, clusterApiUrl, LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { - getKeypairFromFile, - airdropIfRequired, -} from "@solana-developers/helpers"; -import { - Metaplex, - keypairIdentity, - irysStorage, - toMetaplexFile, -} from "@metaplex-foundation/js"; -import { readFile } from "fs/promises"; - -// create a new connection to the cluster's API -const connection = new Connection(clusterApiUrl("devnet")); - -// initialize a keypair for the user -const user = await getKeypairFromFile(); - -await airdropIfRequired( - connection, - user.publicKey, - 1 * LAMPORTS_PER_SOL, - 0.1 * LAMPORTS_PER_SOL -); - -console.log("Loaded user:", user.publicKey.toBase58()); - -// metaplex set up -const metaplex = Metaplex.make(connection) - .use(keypairIdentity(user)) - .use( - irysStorage({ - address: "https://devnet.irys.xyz", - providerUrl: "https://api.devnet.solana.com", - }) - ); - -const collectionNftData = { - name: "TestCollectionNFT", - symbol: "TEST", - description: "Test Description Collection", - sellerFeeBasisPoints: 100, - imageFile: "nft.png", - isCollection: true, - collectionAuthority: user, -}; - -// Load file into Metaplex -const buffer = await readFile(collectionNftData.imageFile); -const file = toMetaplexFile(buffer, collectionNftData.imageFile); - -// upload image and get image uri -const imageUri = await metaplex.storage().upload(file); -console.log("image uri:", imageUri); - -// upload metadata and get metadata uri (off chain metadata) -const uploadMetadataOutput = await metaplex.nfts().uploadMetadata({ - name: collectionNftData.name, - symbol: collectionNftData.symbol, - description: collectionNftData.description, - image: imageUri, -}); - -const collectionUri = uploadMetadataOutput.uri; -console.log("Collection off-chain metadata URI:", collectionUri); - -// create a collection NFT using the URI from the metadata -const createNftOutput = await metaplex.nfts().create( - { - uri: collectionUri, - name: collectionNftData.name, - sellerFeeBasisPoints: collectionNftData.sellerFeeBasisPoints, - symbol: collectionNftData.symbol, - isCollection: true, - }, - { commitment: "finalized" } -); - -const collectionNft = createNftOutput.nft; - -console.log( - `Collection NFT: https://explorer.solana.com/address/${collectionNft.address.toString()}?cluster=devnet` -); - -console.log(`Collection NFT address is`, collectionNft.address.toString()); - -console.log("✅ Finished successfully!"); diff --git a/labs/create-metaplex-nft.ts b/labs/create-metaplex-nft.ts index 6ed0e70..376fe88 100644 --- a/labs/create-metaplex-nft.ts +++ b/labs/create-metaplex-nft.ts @@ -1,23 +1,28 @@ -// Adapted from https://github.com/Unboxed-Software/solana-metaplex/blob/solution/src/index.ts -// and updated to work with the latest version of the Metaplex SDK - +// See https://developers.metaplex.com/token-metadata +// and https://developers.metaplex.com/token-metadata/collections#associating-nfts-to-collection-nfts import { - Connection, - clusterApiUrl, - PublicKey, - LAMPORTS_PER_SOL, -} from "@solana/web3.js"; + createNft, + fetchDigitalAsset, + mplTokenMetadata, +} from "@metaplex-foundation/mpl-token-metadata"; import { - getKeypairFromFile, airdropIfRequired, + getExplorerLink, + getKeypairFromFile, } from "@solana-developers/helpers"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; import { - Metaplex, + generateSigner, keypairIdentity, - irysStorage, - toMetaplexFile, -} from "@metaplex-foundation/js"; -import { readFileSync } from "fs"; + percentAmount, + publicKey, +} from "@metaplex-foundation/umi"; +import { + Connection, + LAMPORTS_PER_SOL, + PublicKey, + clusterApiUrl, +} from "@solana/web3.js"; // create a new connection to the cluster's API const connection = new Connection(clusterApiUrl("devnet")); @@ -34,72 +39,42 @@ await airdropIfRequired( console.log("Loaded user:", user.publicKey.toBase58()); -// metaplex set up -const metaplex = Metaplex.make(connection) - .use(keypairIdentity(user)) - .use( - irysStorage({ - address: "https://devnet.irys.xyz", - providerUrl: "https://api.devnet.solana.com", - timeout: 60000, - }) - ); - -// Substitute in your collection NFT address from create-metaplex-nft-collection.ts -const collectionNftAddress = new PublicKey("YOUR_COLLECTION_NFT_ADDRESS_HERE"); +// Create Umi Instance, using the same endpoint as our connection, +// and using our user to sign transactions +const umi = createUmi(connection.rpcEndpoint).use(mplTokenMetadata()); +const umiKeypair = umi.eddsa.createKeypairFromSecretKey(user.secretKey); +umi.use(keypairIdentity(umiKeypair)); -// example data for a new NFT -const nftData = { - name: "Name", - symbol: "SYMBOL", - description: "Description", - sellerFeeBasisPoints: 0, - imageFile: "nft.png", -}; - -// Load the file into Metaplex -const buffer = readFileSync(nftData.imageFile); -const file = toMetaplexFile(buffer, nftData.imageFile); - -// upload image and get image uri -const imageUri = await metaplex.storage().upload(file); -console.log("image uri:", imageUri); +const collectionAddress = new PublicKey( + "GyddqwoWKffNjgLZZwENHvjaegQqdy3wmiEuumGeiEvn" +); -// upload metadata and get metadata uri (off chain metadata) -const uploadMetadataOutput = await metaplex.nfts().uploadMetadata({ - name: nftData.name, - symbol: nftData.symbol, - description: nftData.description, - image: imageUri, +// Generate an NFT +console.log(`Creating NFT...`); +const mint = generateSigner(umi); +const transaction = await createNft(umi, { + mint, + name: "My NFT", + // https://developers.metaplex.com/token-metadata/token-standard#the-non-fungible-standard + uri: "https://raw.githubusercontent.com/solana-developers/professional-education/main/labs/sample-nft-offchain-data.json", + sellerFeeBasisPoints: percentAmount(0), + collection: { + // See https://developers.metaplex.com/umi/public-keys-and-signers + key: publicKey(collectionAddress), + verified: false, + }, }); -const metadataUri = uploadMetadataOutput.uri; +await transaction.sendAndConfirm(umi); -// create an NFT using the URI from the metadata -const createNftOutput = await metaplex.nfts().create( - { - uri: metadataUri, // metadata URI - name: nftData.name, - sellerFeeBasisPoints: nftData.sellerFeeBasisPoints, - symbol: nftData.symbol, - collection: collectionNftAddress, - }, - { commitment: "finalized" } -); -const nft = createNftOutput.nft; +const createdNft = await fetchDigitalAsset(umi, mint.publicKey); console.log( - `Token Mint: https://explorer.solana.com/address/${nft.address.toString()}?cluster=devnet` + `✨🖼️ Created NFT! Address is: ${getExplorerLink( + "address", + createdNft.mint.publicKey, + "devnet" + )}` ); -await metaplex.nfts().verifyCollection({ - // Verify our collection as a Certified Collection - // See https://developers.metaplex.com/token-metadata/collections - mintAddress: nft.mint.address, - collectionMintAddress: collectionNftAddress, - isSizedCollection: true, -}); - -console.log(`Created NFT address is`, nft.address.toString()); - console.log("✅ Finished successfully!"); diff --git a/labs/package-lock.json b/labs/package-lock.json index 61407b9..6a2c434 100644 --- a/labs/package-lock.json +++ b/labs/package-lock.json @@ -10,7 +10,8 @@ "license": "ISC", "dependencies": { "@coral-xyz/anchor": "^0.30.1", - "@metaplex-foundation/mpl-token-metadata": "^2.13.0", + "@metaplex-foundation/mpl-token-metadata": "^3.2.1", + "@metaplex-foundation/umi-bundle-defaults": "^0.9.2", "@solana-developers/helpers": "^2.3.0", "@solana/spl-memo": "^0.2.5", "@solana/spl-token": "^0.4.1", @@ -438,62 +439,216 @@ "node": ">=12" } }, - "node_modules/@metaplex-foundation/beet": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@metaplex-foundation/beet/-/beet-0.7.2.tgz", - "integrity": "sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg==", + "node_modules/@metaplex-foundation/mpl-token-metadata": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-3.2.1.tgz", + "integrity": "sha512-26W1NhQwDWmLOg/pBRYut7x/vEs/5kFS2sWVEY5/X0f2jJOLhnd4NaZQcq+5u+XZsXvm1jq2AtrRGPNK43oqWQ==", + "license": "Apache-2.0", "dependencies": { - "ansicolors": "^0.3.2", - "assert": "^2.1.0", - "bn.js": "^5.2.0", - "debug": "^4.3.3" + "@metaplex-foundation/mpl-toolbox": "^0.9.4" + }, + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" } }, - "node_modules/@metaplex-foundation/beet-solana": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@metaplex-foundation/beet-solana/-/beet-solana-0.4.1.tgz", - "integrity": "sha512-/6o32FNUtwK8tjhotrvU/vorP7umBuRFvBZrC6XCk51aKidBHe5LPVPA5AjGPbV3oftMfRuXPNd9yAGeEqeCDQ==", + "node_modules/@metaplex-foundation/mpl-toolbox": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-toolbox/-/mpl-toolbox-0.9.4.tgz", + "integrity": "sha512-fd6JxfoLbj/MM8FG2x91KYVy1U6AjBQw4qjt7+Da3trzQaWnSaYHDcYRG/53xqfvZ9qofY1T2t53GXPlD87lnQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" + } + }, + "node_modules/@metaplex-foundation/umi": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi/-/umi-0.9.2.tgz", + "integrity": "sha512-9i4Acm4pruQfJcpRrc2EauPBwkfDN0I9QTvJyZocIlKgoZwD6A6wH0PViH1AjOVG5CQCd1YI3tJd5XjYE1ElBw==", + "license": "MIT", + "peer": true, "dependencies": { - "@metaplex-foundation/beet": ">=0.1.0", - "@solana/web3.js": "^1.56.2", - "bs58": "^5.0.0", - "debug": "^4.3.4" + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers": "^0.9.0" } }, - "node_modules/@metaplex-foundation/cusper": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz", - "integrity": "sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA==" + "node_modules/@metaplex-foundation/umi-bundle-defaults": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-bundle-defaults/-/umi-bundle-defaults-0.9.2.tgz", + "integrity": "sha512-kV3tfvgvRjVP1p9OFOtH+ibOtN9omVJSwKr0We4/9r45e5LTj+32su0V/rixZUkG1EZzzOYBsxhtIE0kIw/Hrw==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-downloader-http": "^0.9.2", + "@metaplex-foundation/umi-eddsa-web3js": "^0.9.2", + "@metaplex-foundation/umi-http-fetch": "^0.9.2", + "@metaplex-foundation/umi-program-repository": "^0.9.2", + "@metaplex-foundation/umi-rpc-chunk-get-accounts": "^0.9.2", + "@metaplex-foundation/umi-rpc-web3js": "^0.9.2", + "@metaplex-foundation/umi-serializer-data-view": "^0.9.2", + "@metaplex-foundation/umi-transaction-factory-web3js": "^0.9.2" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2", + "@solana/web3.js": "^1.72.0" + } }, - "node_modules/@metaplex-foundation/mpl-token-metadata": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-2.13.0.tgz", - "integrity": "sha512-Fl/8I0L9rv4bKTV/RAl5YIbJe9SnQPInKvLz+xR1fEc4/VQkuCn3RPgypfUMEKWmCznzaw4sApDxy6CFS4qmJw==", - "dependencies": { - "@metaplex-foundation/beet": "^0.7.1", - "@metaplex-foundation/beet-solana": "^0.4.0", - "@metaplex-foundation/cusper": "^0.0.2", - "@solana/spl-token": "^0.3.6", - "@solana/web3.js": "^1.66.2", - "bn.js": "^5.2.0", - "debug": "^4.3.4" + "node_modules/@metaplex-foundation/umi-downloader-http": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-downloader-http/-/umi-downloader-http-0.9.2.tgz", + "integrity": "sha512-tzPT9hBwenzTzAQg07rmsrqZfgguAXELbcJrsYMoASp5VqWFXYIP00g94KET6XLjWUXH4P1J2zoa6hGennPXHA==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2" } }, - "node_modules/@metaplex-foundation/mpl-token-metadata/node_modules/@solana/spl-token": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz", - "integrity": "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==", + "node_modules/@metaplex-foundation/umi-eddsa-web3js": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-eddsa-web3js/-/umi-eddsa-web3js-0.9.2.tgz", + "integrity": "sha512-hhPCxXbYIp4BC4z9gK78sXpWLkNSrfv4ndhF5ruAkdIp7GcRVYKj0QnOUO6lGYGiIkNlw20yoTwOe1CT//OfTQ==", + "license": "MIT", "dependencies": { - "@solana/buffer-layout": "^4.0.0", - "@solana/buffer-layout-utils": "^0.2.0", - "@solana/spl-token-metadata": "^0.1.2", - "buffer": "^6.0.3" + "@metaplex-foundation/umi-web3js-adapters": "^0.9.2", + "@noble/curves": "^1.0.0" }, - "engines": { - "node": ">=16" + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-http-fetch": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-http-fetch/-/umi-http-fetch-0.9.2.tgz", + "integrity": "sha512-YCZuBu24T9ZzEDe4+w12LEZm/fO9pkyViZufGgASC5NX93814Lvf6Ssjn/hZzjfA7CvZbvLFbmujc6CV3Q/m9Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.7" }, "peerDependencies": { - "@solana/web3.js": "^1.88.0" + "@metaplex-foundation/umi": "^0.9.2" + } + }, + "node_modules/@metaplex-foundation/umi-options": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-options/-/umi-options-0.8.9.tgz", + "integrity": "sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==", + "license": "MIT", + "peer": true + }, + "node_modules/@metaplex-foundation/umi-program-repository": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-program-repository/-/umi-program-repository-0.9.2.tgz", + "integrity": "sha512-g3+FPqXEmYsBa8eETtUE2gb2Oe3mqac0z3/Ur1TvAg5TtIy3mzRzOy/nza+sgzejnfcxcVg835rmpBaxpBnjDA==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2" + } + }, + "node_modules/@metaplex-foundation/umi-public-keys": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-public-keys/-/umi-public-keys-0.8.9.tgz", + "integrity": "sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-rpc-chunk-get-accounts": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-rpc-chunk-get-accounts/-/umi-rpc-chunk-get-accounts-0.9.2.tgz", + "integrity": "sha512-YRwVf6xH0jPBAUgMhEPi+UbjioAeqTXmjsN2TnmQCPAmHbrHrMRj0rlWYwFLWAgkmoxazYrXP9lqOFRrfOGAEA==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2" + } + }, + "node_modules/@metaplex-foundation/umi-rpc-web3js": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-rpc-web3js/-/umi-rpc-web3js-0.9.2.tgz", + "integrity": "sha512-MqcsBz8B4wGl6jxsf2Jo/rAEpYReU9VCSR15QSjhvADHMmdFxCIZCCAgE+gDE2Vuanfl437VhOcP3g5Uw8C16Q==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-web3js-adapters": "^0.9.2" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-serializer-data-view": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializer-data-view/-/umi-serializer-data-view-0.9.2.tgz", + "integrity": "sha512-5vGptadJxUxvUcyrwFZxXlEc6Q7AYySBesizCtrBFUY8w8PnF2vzmS45CP1MLySEATNH6T9mD4Rs0tLb87iQyA==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2" + } + }, + "node_modules/@metaplex-foundation/umi-serializers": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers/-/umi-serializers-0.9.0.tgz", + "integrity": "sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers-core": "^0.8.9", + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9", + "@metaplex-foundation/umi-serializers-numbers": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-core": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-core/-/umi-serializers-core-0.8.9.tgz", + "integrity": "sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==", + "license": "MIT", + "peer": true + }, + "node_modules/@metaplex-foundation/umi-serializers-encodings": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-encodings/-/umi-serializers-encodings-0.8.9.tgz", + "integrity": "sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-numbers": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-numbers/-/umi-serializers-numbers-0.8.9.tgz", + "integrity": "sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-transaction-factory-web3js": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-transaction-factory-web3js/-/umi-transaction-factory-web3js-0.9.2.tgz", + "integrity": "sha512-fR1Kf21uylMFd1Smkltmj4jTNxhqSWf416owsJ+T+cvJi2VCOcOwq/3UFzOrpz78fA0RhsajKYKj0HYsRnQI1g==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-web3js-adapters": "^0.9.2" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-web3js-adapters": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-web3js-adapters/-/umi-web3js-adapters-0.9.2.tgz", + "integrity": "sha512-RQqUTtHYY9fmEMnq7s3Hiv/81flGaoI0ZVVoafnFVaQLnxU6QBKxtboRZHk43XtD9CiFh5f9izrMJX7iK7KlOA==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.2", + "@solana/web3.js": "^1.72.0" } }, "node_modules/@noble/curves": { @@ -751,11 +906,6 @@ "node": ">= 8.0.0" } }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==" - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -768,32 +918,6 @@ "node": ">= 8" } }, - "node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/base-x": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", @@ -954,24 +1078,6 @@ "node": ">=6.14.2" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1030,59 +1136,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -1114,25 +1167,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -1239,14 +1273,6 @@ "node": ">=8" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1260,32 +1286,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1297,75 +1297,6 @@ "node": ">= 6" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -1393,26 +1324,6 @@ } ] }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1424,17 +1335,6 @@ "node": ">=8" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1443,20 +1343,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1468,21 +1354,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1492,20 +1363,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -1633,46 +1490,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -1689,14 +1506,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1771,22 +1580,6 @@ } ] }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -1857,18 +1650,6 @@ "node": ">=6.14.2" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -1891,24 +1672,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", diff --git a/labs/package.json b/labs/package.json index ec15d2e..dbc3893 100644 --- a/labs/package.json +++ b/labs/package.json @@ -11,7 +11,8 @@ "license": "ISC", "dependencies": { "@coral-xyz/anchor": "^0.30.1", - "@metaplex-foundation/mpl-token-metadata": "^2.13.0", + "@metaplex-foundation/mpl-token-metadata": "^3.2.1", + "@metaplex-foundation/umi-bundle-defaults": "^0.9.2", "@solana-developers/helpers": "^2.3.0", "@solana/spl-memo": "^0.2.5", "@solana/spl-token": "^0.4.1", diff --git a/labs/sample-nft-collection-offchain-data.json b/labs/sample-nft-collection-offchain-data.json new file mode 100644 index 0000000..6afae13 --- /dev/null +++ b/labs/sample-nft-collection-offchain-data.json @@ -0,0 +1,5 @@ +{ + "name": "Test Collection", + "description": "Description for test collection", + "image": "https://raw.githubusercontent.com/solana-developers/professional-education/main/labs/collection.png" +} diff --git a/labs/sample-nft-offchain-data.json b/labs/sample-nft-offchain-data.json new file mode 100644 index 0000000..3594edc --- /dev/null +++ b/labs/sample-nft-offchain-data.json @@ -0,0 +1,5 @@ +{ + "name": "Test NFT", + "description": "Description for test NFT", + "image": "https://raw.githubusercontent.com/solana-developers/professional-education/main/labs/nft.png" +} diff --git a/labs/update-metaplex-nft.ts b/labs/update-metaplex-nft.ts index a3fa5e8..900695c 100644 --- a/labs/update-metaplex-nft.ts +++ b/labs/update-metaplex-nft.ts @@ -1,24 +1,18 @@ -// Adapted from https://github.com/Unboxed-Software/solana-metaplex/blob/solution/src/index.ts -// and updated to work with the latest version of the Metaplex SDK - +import "dotenv/config"; import { - Connection, - clusterApiUrl, - PublicKey, - LAMPORTS_PER_SOL, -} from "@solana/web3.js"; + fetchDigitalAsset, + fetchMetadataFromSeeds, + mplTokenMetadata, + updateV1, +} from "@metaplex-foundation/mpl-token-metadata"; import { - getKeypairFromFile, airdropIfRequired, getExplorerLink, + getKeypairFromFile, } from "@solana-developers/helpers"; -import { - Metaplex, - keypairIdentity, - irysStorage, - toMetaplexFile, -} from "@metaplex-foundation/js"; -import { readFileSync } from "fs"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { keypairIdentity, publicKey } from "@metaplex-foundation/umi"; +import { Connection, LAMPORTS_PER_SOL, clusterApiUrl } from "@solana/web3.js"; // create a new connection to the cluster's API const connection = new Connection(clusterApiUrl("devnet")); @@ -35,61 +29,35 @@ await airdropIfRequired( console.log("Loaded user:", user.publicKey.toBase58()); -// metaplex set up -const metaplex = Metaplex.make(connection) - .use(keypairIdentity(user)) - .use( - irysStorage({ - address: "https://devnet.irys.xyz", - providerUrl: "https://api.devnet.solana.com", - timeout: 60000, - }) - ); - -// Load the NFT using the mint address -const nftAddress: PublicKey = new PublicKey("YOUR_NFT_ADDRESS_HERE"); -const nft = await metaplex.nfts().findByMint({ mintAddress: nftAddress }); - -// example data for updating an existing NFT -const updatedNftData = { - name: "Updated", - symbol: "UPDATED", - description: "Updated Description", - sellerFeeBasisPoints: 100, - imageFile: "updated.png", -}; +// Create Umi Instance, using the same endpoint as our connection, +// and using our user to sign transactions +const umi = createUmi(connection.rpcEndpoint).use(mplTokenMetadata()); +const umiKeypair = umi.eddsa.createKeypairFromSecretKey(user.secretKey); +umi.use(keypairIdentity(umiKeypair)); -// Load the image file into Metaplex -const buffer = readFileSync(updatedNftData.imageFile); -const file = toMetaplexFile(buffer, updatedNftData.imageFile); +// Our NFT address we made earlier +// const mint = publicKey("YOUR_MINT_ADDRESS_HERE"); +const mint = publicKey("4CpLPpxvZJJViUrUpnLj5gX1ZNRvCB7jdcrjLGb9Wixi"); -// Upload the new image and get image URI -const imageUri = await metaplex.storage().upload(file); -console.log("image uri:", imageUri); - -// Upload new off-chain metadata -const uploadMetadataOutput = await metaplex.nfts().uploadMetadata({ - name: updatedNftData.name, - symbol: updatedNftData.symbol, - description: updatedNftData.description, - image: imageUri, +// Update the NFT metadata +const initialMetadata = await fetchMetadataFromSeeds(umi, { + mint, }); - -const updatedUri = uploadMetadataOutput.uri; - -// update the NFT metadata -const { response } = await metaplex.nfts().update( - { - nftOrSft: nft, - uri: updatedUri, +await updateV1(umi, { + mint, + data: { + ...initialMetadata, + name: "Updated Asset", + symbol: "Updated", }, - { commitment: "finalized" } -); +}).sendAndConfirm(umi); + +const createdNft = await fetchDigitalAsset(umi, mint); console.log( `NFT updated with new metadata URI: ${getExplorerLink( - "transaction", - response.signature, + "address", + createdNft.mint.publicKey, "devnet" )}` ); diff --git a/labs/verify-metaplex-collection.ts b/labs/verify-metaplex-collection.ts new file mode 100644 index 0000000..c5a71ca --- /dev/null +++ b/labs/verify-metaplex-collection.ts @@ -0,0 +1,65 @@ +// See https://developers.metaplex.com/token-metadata +// and https://developers.metaplex.com/token-metadata/collections#associating-nfts-to-collection-nfts +import { + mplTokenMetadata, + verifyCollectionV1, +} from "@metaplex-foundation/mpl-token-metadata"; +import { + airdropIfRequired, + getExplorerLink, + getKeypairFromFile, +} from "@solana-developers/helpers"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { + keypairIdentity, + publicKey, + createSignerFromKeypair, +} from "@metaplex-foundation/umi"; +import { + Connection, + LAMPORTS_PER_SOL, + PublicKey, + clusterApiUrl, +} from "@solana/web3.js"; + +// create a new connection to the cluster's API +const connection = new Connection(clusterApiUrl("devnet")); + +// initialize a keypair for the user +const user = await getKeypairFromFile(); + +await airdropIfRequired( + connection, + user.publicKey, + 1 * LAMPORTS_PER_SOL, + 0.1 * LAMPORTS_PER_SOL +); + +console.log("Loaded user:", user.publicKey.toBase58()); + +// Create Umi Instance, using the same endpoint as our connection, +// and using our user to sign transactions +const umi = createUmi(connection.rpcEndpoint).use(mplTokenMetadata()); +const umiKeypair = umi.eddsa.createKeypairFromSecretKey(user.secretKey); +umi.use(keypairIdentity(umiKeypair)); + +const collectionAddress = new PublicKey( + "GyddqwoWKffNjgLZZwENHvjaegQqdy3wmiEuumGeiEvn" +); + +const nftAddress = new PublicKey( + "AHKg2uDKR1dwz7R2bAGjZc8wDn8PkCGH4SD5VH7WU45o" +); + +// Make a metaplex signer from the 'user' keypair. +const signer = createSignerFromKeypair(umi, umiKeypair); + +// See https://developers.metaplex.com/token-metadata/collections +await verifyCollectionV1(umi, { + // The NFT we want to verify inside the collection. + metadata: publicKey(nftAddress), + // The Collection NFT that is already set on the Metadata account of the NFT but not yet verified. + collectionMint: publicKey(collectionAddress), + // The Update Authority of the Collection NFT as a signer + authority: signer, +}).sendAndConfirm(umi);