|
|
@@ -7,7 +7,12 @@ created: '06-16-2024'
|
|
|
updated: '06-18-2024'
|
|
|
---
|
|
|
|
|
|
-This is an intial guide on how to create a next-gen Nft (Core Asset) on the Solana blockchain using the Metaplex Mpl Core protocol. Mpl Core Assets provide the next wave of NFT projects on Solana with greater creativity and a broad dynamic experiance for both creators and owners with Mpl Core's unique plugin system.
|
|
|
+This is an intial guide on how to create a next-gen Nft (Core Asset) on the Solana blockchain using the Metaplex Mpl Core protocol. Mpl Core Assets provide the next wave of NFT projects on Solana with greater creativity and a broad dynamic experiance for both creators and owners with Mpl Core's simplied design and unique plugin system.
|
|
|
+
|
|
|
+## Prerequisite
|
|
|
+
|
|
|
+- Code Editor of your choice (recomended Visual Studio Code)
|
|
|
+- Node 18.x.x or above.
|
|
|
|
|
|
## Initial Setup
|
|
|
|
|
|
@@ -25,6 +30,8 @@ npm init
|
|
|
|
|
|
Install the required pacakges for this guide.
|
|
|
|
|
|
+{% packagesUsed packages=["umi", "umiDefaults" ,"tokenMetadata", "core", "@solana/spl-token"] type="npm" /%}
|
|
|
+
|
|
|
```js
|
|
|
npm i @metaplex-foundation/umi
|
|
|
```
|
|
|
@@ -41,39 +48,81 @@ npm i @metaplex-foundation/mplcore
|
|
|
npm i @metaplex-foundation/umi-uploader-irys;
|
|
|
```
|
|
|
|
|
|
-## Setting up Umi
|
|
|
+### Imports and Wrapper Function
|
|
|
|
|
|
-This example is going to run through setting up Umi with a `generatedSigner()`. If you wish to try this example with React you'll need to setup Umi via the `React - Umi w/ Wallet Adapter` guide. Apart from the the wallet setup this guide will for fileStorage keys and wallet adapter.
|
|
|
+Here we will define all needed imports for this particular guide and create a wrapper function where all our code will execute.
|
|
|
|
|
|
```ts
|
|
|
-import { mplCore } from '@metaplex-foundation/mpl-token-metadata'
|
|
|
-import { generateSigner, signerIdentity } from '@metaplex-foundation/umi'
|
|
|
+import {
|
|
|
+ createFungible,
|
|
|
+ mplTokenMetadata,
|
|
|
+} from '@metaplex-foundation/mpl-token-metadata'
|
|
|
+import {
|
|
|
+ createTokenIfMissing,
|
|
|
+ findAssociatedTokenPda,
|
|
|
+ getSplAssociatedTokenProgramId,
|
|
|
+ mintTokensTo,
|
|
|
+} from '@metaplex-foundation/mpl-toolbox'
|
|
|
+import {
|
|
|
+ generateSigner,
|
|
|
+ percentAmount,
|
|
|
+ createGenericFile,
|
|
|
+ signerIdentity,
|
|
|
+} from '@metaplex-foundation/umi'
|
|
|
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
|
|
|
import { irysUploader } from '@metaplex-foundation/umi-uploader-irys'
|
|
|
+import { base58 } from '@metaplex-foundation/umi/serializers'
|
|
|
+import fs from 'fs'
|
|
|
+import path from 'path'
|
|
|
+
|
|
|
+// Create the wrapper function
|
|
|
+const createNft = async () => {
|
|
|
+ ///
|
|
|
+ ///
|
|
|
+ /// all our code will go in here
|
|
|
+ ///
|
|
|
+ ///
|
|
|
+}
|
|
|
+
|
|
|
+// run the wrapper function
|
|
|
+createNft()
|
|
|
+```
|
|
|
+
|
|
|
+## Setting up Umi
|
|
|
+
|
|
|
+This example is going to run through setting up Umi with a `generatedSigner()`. If you wish to try this example with React you'll need to setup Umi via the `React - Umi w/ Wallet Adapter` guide. Apart from the the wallet setup this guide will for fileStorage keys and wallet adapter.
|
|
|
+
|
|
|
+### Generating a New Wallet
|
|
|
|
|
|
+```ts
|
|
|
const umi = createUmi('https://api.devnet.solana.com')
|
|
|
.use(mplCore())
|
|
|
.use(irysUploader())
|
|
|
|
|
|
+// Generate a new keypair signer.
|
|
|
const signer = generateSigner(umi)
|
|
|
|
|
|
+// Tell umit to use the new signer.
|
|
|
umi.use(signerIdentity(signer))
|
|
|
-```
|
|
|
|
|
|
-The first 3 lines we are importing packages that required to create an SPL Token.
|
|
|
-
|
|
|
-```ts
|
|
|
-import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'
|
|
|
-import { generateSigner, signerIdentity } from '@metaplex-foundation/umi'
|
|
|
-import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
|
|
|
+// This will airdrop SOL on devnet only for testing.
|
|
|
+await umi.rpc.airdrop(umi.identity.publickey)
|
|
|
```
|
|
|
|
|
|
-The following lines we generate a new signer (private key) and we assign it umi.
|
|
|
-
|
|
|
+### Use an Existing Wallet
|
|
|
```ts
|
|
|
+const umi = createUmi('https://api.devnet.solana.com')
|
|
|
+ .use(mplCore())
|
|
|
+ .use(irysUploader())
|
|
|
+
|
|
|
+// Generate a new keypair signer.
|
|
|
const signer = generateSigner(umi)
|
|
|
|
|
|
-umi.use(signerIdentity(signer))
|
|
|
+// You will need to us fs and navigation the filesystem to
|
|
|
+// load the wallet you wish to use via relative pathing.
|
|
|
+const walletFile = const imageFile = fs.readFileSync(
|
|
|
+ path.join(__dirname, './keypair.json')
|
|
|
+ )
|
|
|
```
|
|
|
|
|
|
## Creating the Nft
|
|
|
@@ -88,21 +137,6 @@ Umi comes with downloadable storage plugins that allow you to upload to storage
|
|
|
This example is using a localscript/node.js approach using Irys to upload to Arweave. If you wish to upload files to a different storage provider or from the browser you will need to take a different approach. Importing and using `fs` won't work in a browser scenario.
|
|
|
{% /callout %}
|
|
|
|
|
|
-#### New Imports
|
|
|
-
|
|
|
-```ts
|
|
|
-// import fs and path by adding it under the rest of the imports at the start of your file.
|
|
|
-import fs from 'fs'
|
|
|
-import path from 'path'
|
|
|
-
|
|
|
-// we also add the `createGenericFile` import to the original list of umi imports.
|
|
|
-import {
|
|
|
- generateSigner,
|
|
|
- signerIdentity,
|
|
|
- createGenericFile,
|
|
|
-} from '@metaplex-foundation/umi'
|
|
|
-```
|
|
|
-
|
|
|
```ts
|
|
|
// use `fs` to read file via a string path.
|
|
|
// You will need to understand the concept of pathing from a computing perspective.
|
|
|
@@ -206,29 +240,19 @@ There are a few things we need to take into account when creating and minting a
|
|
|
- If we are minting the Tokens then we need a Token Account (holds the minted tokens in a persons wallet)
|
|
|
- Mint the token.
|
|
|
|
|
|
-#### New Import
|
|
|
-
|
|
|
-Add this import to the rest of your imports at the top of the page. You may be added to the existing `@metaplex-foundation/mpl-toolbox` import if there is one.
|
|
|
-
|
|
|
-```ts
|
|
|
-import {
|
|
|
- create
|
|
|
-} from '@metaplex-foundation/mpl-core'
|
|
|
-```
|
|
|
-
|
|
|
Now we can proceed to create the mintTokenTo instruction
|
|
|
|
|
|
```ts
|
|
|
-const assetSigner = generateSigner(umi);
|
|
|
+const assetSigner = generateSigner(umi)
|
|
|
|
|
|
const createTx = await create(umi, {
|
|
|
- asset: assetSigner,
|
|
|
- collection: collection,
|
|
|
- name: "My Asset",
|
|
|
- uri: "https://example.com/my-asset.json",
|
|
|
- }).sendAndConfirm(umi);
|
|
|
+ asset: assetSigner,
|
|
|
+ collection: collection,
|
|
|
+ name: 'My Asset',
|
|
|
+ uri: 'https://example.com/my-asset.json',
|
|
|
+}).sendAndConfirm(umi)
|
|
|
|
|
|
- // finally we can deserialize the signature that we can check on chain.
|
|
|
+// finally we can deserialize the signature that we can check on chain.
|
|
|
// import { base58 } from "@metaplex-foundation/umi/serializers";
|
|
|
|
|
|
console.log(base58.deserialize(tx.signature)[0])
|
|
|
@@ -237,113 +261,121 @@ console.log(base58.deserialize(tx.signature)[0])
|
|
|
## Full Code Example
|
|
|
|
|
|
```ts
|
|
|
+import { create } from "@metaplex-foundation/mpl-core";
|
|
|
import {
|
|
|
- createFungible,
|
|
|
- mplTokenMetadata,
|
|
|
-} from '@metaplex-foundation/mpl-token-metadata'
|
|
|
-import {
|
|
|
- createTokenIfMissing,
|
|
|
- findAssociatedTokenPda,
|
|
|
- getSplAssociatedTokenProgramId,
|
|
|
- mintTokensTo,
|
|
|
-} from '@metaplex-foundation/mpl-toolbox'
|
|
|
-import {
|
|
|
- generateSigner,
|
|
|
- percentAmount,
|
|
|
createGenericFile,
|
|
|
+ generateSigner,
|
|
|
signerIdentity,
|
|
|
-} from '@metaplex-foundation/umi'
|
|
|
-import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
|
|
|
-import { irysUploader } from '@metaplex-foundation/umi-uploader-irys'
|
|
|
-import { base58 } from '@metaplex-foundation/umi/serializers'
|
|
|
-import fs from 'fs'
|
|
|
-import path from 'path'
|
|
|
-
|
|
|
-const createAndMintTokens = async () => {
|
|
|
- const umi = createUmi('https://api.devnet.solana.com')
|
|
|
- .use(mplTokenMetadata())
|
|
|
- .use(irysUploader())
|
|
|
-
|
|
|
- const signer = generateSigner(umi)
|
|
|
-
|
|
|
- umi.use(signerIdentity(signer))
|
|
|
-
|
|
|
- // use `fs` to read file via a string path.
|
|
|
- // You will need to understand the concept of pathing from a computing perspective.
|
|
|
-
|
|
|
- const imageFile = fs.readFileSync(
|
|
|
- path.join(__dirname, '..', '/assets/islandDao.jpg')
|
|
|
- )
|
|
|
-
|
|
|
- // Use `createGenericFile` to transform the file into a `GenericFile` type
|
|
|
- // that umi can understand. Make sure you set the mimi tag type correctly
|
|
|
- // otherwise Arweave will not know how to display your image.
|
|
|
-
|
|
|
- const umiImageFile = createGenericFile(imageFile, 'island-dao.jpeg', {
|
|
|
- tags: [{ name: 'Content-Type', value: 'image/jpeg' }],
|
|
|
- })
|
|
|
-
|
|
|
- // Here we upload the image to Arweave via Irys and we get returned a uri
|
|
|
- // address where the file is located. You can log this out but as the
|
|
|
- // uploader can takes an array of files it also returns an array of uris.
|
|
|
- // To get the uri we want we can call index [0] in the array.
|
|
|
-
|
|
|
- const imageUri = await umi.uploader.upload([umiImageFile]).catch((err) => {
|
|
|
- throw new Error(err)
|
|
|
- })
|
|
|
-
|
|
|
- console.log(imageUri[0])
|
|
|
-
|
|
|
- // Uploading the tokens metadata to Arweave via Irys
|
|
|
-
|
|
|
- const metadata = {
|
|
|
- "name": "My Nft",
|
|
|
- "description": "This is an Nft on Solana",
|
|
|
- "image": imageUri,
|
|
|
- "external_url": "https://example.com",
|
|
|
- "attributes": [
|
|
|
- {
|
|
|
- "trait_type": "trait1",
|
|
|
- "value": "value1"
|
|
|
- },
|
|
|
- {
|
|
|
- "trait_type": "trait2",
|
|
|
- "value": "value2"
|
|
|
- }
|
|
|
- ],
|
|
|
- "properties": {
|
|
|
- "files": [
|
|
|
- {
|
|
|
- "uri": "https://arweave.net/my-image",
|
|
|
- "type": "image/png"
|
|
|
- },
|
|
|
- ],
|
|
|
- "category": "image"
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Call upon umi's uploadJson function to upload our metadata to Arweave via Irys.
|
|
|
-
|
|
|
-const metadataUri = await umi.uploader.uploadJson(metadata).catch((err) => {
|
|
|
-throw new Error(err)
|
|
|
-})
|
|
|
-
|
|
|
-// Creating the mintIx
|
|
|
-
|
|
|
-const assetSigner = generateSigner(umi);
|
|
|
-
|
|
|
-const createTx = await create(umi, {
|
|
|
- asset: assetSigner,
|
|
|
- collection: collection,
|
|
|
- name: "My Asset",
|
|
|
- uri: "https://example.com/my-asset.json",
|
|
|
- }).sendAndConfirm(umi);
|
|
|
-
|
|
|
- // finally we can deserialize the signature that we can check on chain.
|
|
|
-// import { base58 } from "@metaplex-foundation/umi/serializers";
|
|
|
-
|
|
|
-console.log(base58.deserialize(tx.signature)[0])
|
|
|
-}
|
|
|
+ sol,
|
|
|
+} from "@metaplex-foundation/umi";
|
|
|
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
|
|
+import { base58 } from "@metaplex-foundation/umi/serializers";
|
|
|
+import fs from "fs";
|
|
|
+import path from "path";
|
|
|
+
|
|
|
+const createNft = async () => {
|
|
|
+
|
|
|
+ //
|
|
|
+ // ** Setting Up Umi **
|
|
|
+ //
|
|
|
+
|
|
|
+ const umi = createUmi("https://api.devnet.solana.com");
|
|
|
+
|
|
|
+ const signer = generateSigner(umi);
|
|
|
+
|
|
|
+ umi.use(signerIdentity(signer));
|
|
|
+
|
|
|
+ // Airdrop 1 SOL to the identity
|
|
|
+ // if you end up with a 429 too many requests error, you may have to use
|
|
|
+ // the filesystem wallet method or change rpcs.
|
|
|
+ await umi.rpc.airdrop(umi.identity.publicKey, sol(1));
|
|
|
+
|
|
|
+ //
|
|
|
+ // ** Upload an image to Arweave **
|
|
|
+ //
|
|
|
+
|
|
|
+ // use `fs` to read file via a string path.
|
|
|
+ // You will need to understand the concept of pathing from a computing perspective.
|
|
|
+
|
|
|
+ const imageFile = fs.readFileSync(
|
|
|
+ path.join(__dirname, "../assets/images/0.png")
|
|
|
+ );
|
|
|
+
|
|
|
+ // Use `createGenericFile` to transform the file into a `GenericFile` type
|
|
|
+ // that umi can understand. Make sure you set the mimi tag type correctly
|
|
|
+ // otherwise Arweave will not know how to display your image.
|
|
|
+
|
|
|
+ const umiImageFile = createGenericFile(imageFile, "0.png", {
|
|
|
+ tags: [{ name: "Content-Type", value: "image/jpeg" }],
|
|
|
+ });
|
|
|
+
|
|
|
+ // Here we upload the image to Arweave via Irys and we get returned a uri
|
|
|
+ // address where the file is located. You can log this out but as the
|
|
|
+ // uploader can takes an array of files it also returns an array of uris.
|
|
|
+ // To get the uri we want we can call index [0] in the array.
|
|
|
+
|
|
|
+ const imageUri = await umi.uploader.upload([umiImageFile]).catch((err) => {
|
|
|
+ throw new Error(err);
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ console.log("imageUri: " + imageUri[0]);
|
|
|
+
|
|
|
+
|
|
|
+ //
|
|
|
+ // ** Upload Metadata to Arweave **
|
|
|
+ //
|
|
|
+
|
|
|
+ const metadata = {
|
|
|
+ "name": "My Nft",
|
|
|
+ "description": "This is an Nft on Solana",
|
|
|
+ "image": imageUri[0],
|
|
|
+ "external_url": "https://example.com",
|
|
|
+ "attributes": [
|
|
|
+ {
|
|
|
+ "trait_type": "trait1",
|
|
|
+ "value": "value1"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "trait_type": "trait2",
|
|
|
+ "value": "value2"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "properties": {
|
|
|
+ "files": [
|
|
|
+ {
|
|
|
+ "uri": imageUri[0],
|
|
|
+ "type": "image/jpeg"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "category": "image"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Call upon umi's uploadJson function to upload our metadata to Arweave via Irys.
|
|
|
+
|
|
|
+ const metadataUri = await umi.uploader.uploadJson(metadata).catch((err) => {
|
|
|
+ throw new Error(err)
|
|
|
+ })
|
|
|
+
|
|
|
+ //
|
|
|
+ // ** Creating the Nft **
|
|
|
+ //
|
|
|
+
|
|
|
+ // We generate a signer for the Nft
|
|
|
+ const nftSigner = generateSigner(umi);
|
|
|
+
|
|
|
+ const tx = await create(umi, {
|
|
|
+ asset: nftSigner,
|
|
|
+ name: "My Nft",
|
|
|
+ uri: metadataUri,
|
|
|
+ }).sendAndConfirm(umi);
|
|
|
+
|
|
|
+ // finally we can deserialize the signature that we can check on chain.
|
|
|
+ const signature = base58.deserialize(tx.signature);
|
|
|
+ console.log(signature)
|
|
|
+};
|
|
|
+
|
|
|
+createNft();
|
|
|
|
|
|
-createAndMintTokens()
|
|
|
```
|